Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ jobs:
if: steps.skip.outputs.result != 'true'
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.413
global-json-file: src/global.json

- name: Add MSBuild to PATH
if: steps.skip.outputs.result != 'true'
Expand Down
24 changes: 19 additions & 5 deletions .github/workflows/functional-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ jobs:

env:
ARTIFACT_PREFIX: ${{ inputs.output_prefix && format('{0}_', inputs.output_prefix) || '' }}
FT_MATRIX_NAME: ${{ format('{0}_{1}-{2}', matrix.configuration, matrix.architecture, matrix.nr) }}
FT_MATRIX_NAME: ${{ format('{0}_{1}-{2}-run{3}', matrix.configuration, matrix.architecture, matrix.nr, matrix.run) }}

strategy:
matrix:
configuration: [ Debug, Release ]
architecture: [ x86_64, arm64 ]
nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 10 parallel jobs to speed up the tests
configuration: [ Debug ]
architecture: [ arm64 ]
nr: [9] # Restricted to failed slice for flaky test diagnosis (restore after fix)
run: [1, 2, 3, 4, 5] # Multiple copies to increase repro odds
fail-fast: false # most failures are flaky tests, no need to stop the other jobs from succeeding

steps:
Expand Down Expand Up @@ -122,6 +123,18 @@ jobs:
shell: cmd
run: gvfs\install.bat

- name: Verify GVFS installation
if: steps.skip.outputs.result != 'true'
shell: cmd
continue-on-error: true
run: |
echo === GVFS Version ===
"C:\Program Files\VFS for Git\GVFS.exe" version
echo === Service Status ===
sc query GVFS.Service
echo === List Mounted ===
"C:\Program Files\VFS for Git\GVFS.exe" service --list-mounted

- name: ProjFS details (post-install)
if: steps.skip.outputs.result != 'true'
shell: cmd
Expand All @@ -141,10 +154,11 @@ jobs:
- name: Run functional tests
if: steps.skip.outputs.result != 'true'
shell: cmd
timeout-minutes: 60
run: |
SET PATH=C:\Program Files\VFS for Git;%PATH%
SET GIT_TRACE2_PERF=C:\temp\git-trace2.log
ft\GVFS.FunctionalTests.exe /result:TestResult.xml --ci --slice=${{ matrix.nr }},10
ft\GVFS.FunctionalTests.exe /result:TestResult.xml --ci --slice=${{ matrix.nr }},10 --workers=1

- name: Upload functional test results
if: always() && steps.skip.outputs.result != 'true'
Expand Down
4 changes: 4 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@

<!-- Managed project properties -->
<PropertyGroup Condition="'$(MSBuildProjectExtension)' == '.csproj'">
<TargetFramework>net10.0-windows10.0.17763.0</TargetFramework>
<LangVersion>latest</LangVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
<SelfContained>true</SelfContained>
<PublishAot>true</PublishAot>
<OptimizationPreference>Speed</OptimizationPreference>
<BaseOutputPath>$(ProjectOutPath)bin\</BaseOutputPath>
<BaseIntermediateOutputPath>$(ProjectOutPath)obj\</BaseIntermediateOutputPath>
</PropertyGroup>
Expand Down
63 changes: 47 additions & 16 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,17 +1,48 @@
<Project>

<!-- Define common properties that rely on SDK/props-defined properties -->
<PropertyGroup>
<Version>$(GVFSVersion)</Version>

<!--
We parse this version into System.Version in several places;
we should strip the commit ID from the attribute.
-->
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>

<!-- Include custom MSBuild targets/tasks -->
<Import Project="$(MSBuildThisFileDirectory)GVFS\GVFS.MSBuild\GVFS.targets" />

<Project>
<!-- Define common properties that rely on SDK/props-defined properties -->
<PropertyGroup>
<Version>$(GVFSVersion)</Version>
<!--
We parse this version into System.Version in several places;
we should strip the commit ID from the attribute.
-->
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>

<!-- Include custom MSBuild targets/tasks -->
<Import Project="$(MSBuildThisFileDirectory)GVFS\GVFS.MSBuild\GVFS.targets" />

<!--
Copy native C++ hook executables and managed peer executables to GVFS.exe
output directory. In a production install all binaries are co-located in one
directory. During dev builds each project has its own output; this target
replicates the production layout for the GVFS project.
-->
<Target Name="_CopyNativeHooks" AfterTargets="Build" Condition="'$(MSBuildProjectName)' == 'GVFS'">
<PropertyGroup>
<_ManagedOutFragment>bin\$(Configuration)\$(TargetFramework)\win-x64</_ManagedOutFragment>
</PropertyGroup>
<ItemGroup>
<!-- Native C++ hooks (built separately with VS MSBuild) -->
<_NativeHook Include="$(RepoOutPath)GitHooksLoader\bin\x64\$(Configuration)\GitHooksLoader.exe" />
<_NativeHook Include="$(RepoOutPath)GVFS.ReadObjectHook\bin\x64\$(Configuration)\GVFS.ReadObjectHook.exe" />
<_NativeHook Include="$(RepoOutPath)GVFS.PostIndexChangedHook\bin\x64\$(Configuration)\GVFS.PostIndexChangedHook.exe" />
<_NativeHook Include="$(RepoOutPath)GVFS.VirtualFileSystemHook\bin\x64\$(Configuration)\GVFS.VirtualFileSystemHook.exe" />
<!-- Managed peer executables that must be co-located with GVFS.exe -->
<_PeerExe Include="$(RepoOutPath)GVFS.Mount\$(_ManagedOutFragment)\GVFS.Mount.exe" />
<_PeerExe Include="$(RepoOutPath)GVFS.Mount\$(_ManagedOutFragment)\GVFS.Mount.dll" />
<_PeerExe Include="$(RepoOutPath)GVFS.Mount\$(_ManagedOutFragment)\GVFS.Mount.runtimeconfig.json" />
<_PeerExe Include="$(RepoOutPath)GVFS.Mount\$(_ManagedOutFragment)\GVFS.Mount.deps.json" />
<_PeerExe Include="$(RepoOutPath)GVFS.Hooks\$(_ManagedOutFragment)\GVFS.Hooks.exe" />
<_PeerExe Include="$(RepoOutPath)GVFS.Hooks\$(_ManagedOutFragment)\GVFS.Hooks.dll" />
<_PeerExe Include="$(RepoOutPath)GVFS.Hooks\$(_ManagedOutFragment)\GVFS.Hooks.runtimeconfig.json" />
<_PeerExe Include="$(RepoOutPath)GVFS.Hooks\$(_ManagedOutFragment)\GVFS.Hooks.deps.json" />
<_PeerExe Include="$(RepoOutPath)GVFS.Service\$(_ManagedOutFragment)\GVFS.Service.exe" />
<_PeerExe Include="$(RepoOutPath)GVFS.Service\$(_ManagedOutFragment)\GVFS.Service.dll" />
<_PeerExe Include="$(RepoOutPath)GVFS.Service\$(_ManagedOutFragment)\GVFS.Service.runtimeconfig.json" />
<_PeerExe Include="$(RepoOutPath)GVFS.Service\$(_ManagedOutFragment)\GVFS.Service.deps.json" />
</ItemGroup>
<Copy SourceFiles="@(_NativeHook)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true" Condition="Exists('%(FullPath)')" />
<Copy SourceFiles="@(_PeerExe)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true" Condition="Exists('%(FullPath)')" />
</Target>
</Project>
27 changes: 11 additions & 16 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>

<ItemGroup>
<!-- Serialization -->
<PackageVersion Include="System.Text.Json" Version="8.0.5" />

<!-- Storage -->
<PackageVersion Include="Microsoft.Data.Sqlite" Version="2.2.4" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />

<!-- Compression -->
<PackageVersion Include="SharpZipLib" Version="1.3.3" />
Expand All @@ -18,30 +16,27 @@
<PackageVersion Include="LibGit2Sharp.NativeBinaries" Version="2.0.322" />

<!-- ProjFS -->
<PackageVersion Include="GVFS.ProjFS" Version="2019.411.1" />
<PackageVersion Include="Microsoft.Windows.ProjFS" Version="1.1.19156.1" />
<PackageVersion Include="Microsoft.Windows.ProjFS" Version="2.1.0" />

<!-- Windows -->
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="4.5.0" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.4" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.4" />

<!-- Build / packaging -->
<PackageVersion Include="GVFS.VCRuntime" Version="0.2.0-build" />
<PackageVersion Include="MicroBuild.Core" Version="0.2.0" />
<PackageVersion Include="Microsoft.Build.Framework" Version="16.0.461" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="16.0.461" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.12.6" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
<PackageVersion Include="Tools.InnoSetup" Version="6.4.3" />

<!-- CLI -->
<PackageVersion Include="System.CommandLine" Version="2.0.5" />

<!-- Testing -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="Moq" Version="4.10.1" />
<PackageVersion Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageVersion Include="NUnitLite" Version="3.12.0" />

<!--
Future packages: pre-declared for Phase 2+ branches to avoid
merge conflicts on this file. Not yet referenced by any project.
-->
<PackageVersion Include="System.CommandLine" Version="2.0.5" />
<PackageVersion Include="System.IO.Pipes.AccessControl" Version="8.0.0" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion GVFS/FastFetch/FastFetch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net471</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
Expand All @@ -28,3 +27,4 @@
</ItemGroup>

</Project>

3 changes: 2 additions & 1 deletion GVFS/GVFS.CommandLine.Tests/GVFS.CommandLine.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<IsTestProject>true</IsTestProject>
<OutputType>Exe</OutputType>
<PublishAot>false</PublishAot>
<SelfContained>false</SelfContained>
</PropertyGroup>

<ItemGroup>
Expand Down
6 changes: 5 additions & 1 deletion GVFS/GVFS.Common/FileSystem/HooksInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ public static class HooksInstaller

static HooksInstaller()
{
ExecutingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
// Environment.ProcessPath can be null in NativeAOT or certain hosting scenarios.
string processPath = Environment.ProcessPath;
ExecutingDirectory = !string.IsNullOrEmpty(processPath)
? Path.GetDirectoryName(processPath)
: AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar);
}

public static string MergeHooksData(string[] defaultHooksLines, string filename, string hookName)
Expand Down
10 changes: 2 additions & 8 deletions GVFS/GVFS.Common/GVFS.Common.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="LibGit2Sharp.NativeBinaries" />
<PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="System.Text.Json" />
<PackageReference Include="SharpZipLib" />
</ItemGroup>

<ItemGroup>
<Reference Include="System.Net.Http" />
<Reference Include="System.Web" />
</ItemGroup>

<Target Name="_GenerateConstantsFile" BeforeTargets="BeforeCompile">
<!-- Generate GVFS constants file with the minimum Git version -->
<GenerateGVFSConstants MinimumGitVersion="$(MinimumGitVersion)" LibGit2FileName="$(libgit2_filename)" OutputFile="$(IntermediateOutputPath)GVFSConstants.g.cs" />
Expand All @@ -28,3 +21,4 @@
</Target>

</Project>

2 changes: 1 addition & 1 deletion GVFS/GVFS.Common/GVFSEnlistment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public static bool WaitUntilMounted(ITracer tracer, string pipeName, string enli
else
{
tracer.RelatedInfo($"{nameof(WaitUntilMounted)}: Waiting 500ms for mount process to be ready");
Thread.Sleep(500);
Thread.Sleep(100);
}
}
catch (BrokenPipeException e)
Expand Down
47 changes: 47 additions & 0 deletions GVFS/GVFS.Common/GVFSJsonContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using GVFS.Common.Http;
using GVFS.Common.NamedPipes;
using GVFS.Common.Tracing;
using GVFS.Common.Prefetch;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GVFS.Common
{
/// <summary>
/// Source-generated JSON serializer context for all types used in GVFS serialization.
/// This enables trim-safe and AOT-compatible JSON serialization without reflection.
/// </summary>
[JsonSourceGenerationOptions(
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = new[] { typeof(VersionConverter) })]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Dictionary<string, string>))]
[JsonSerializable(typeof(KeyValuePair<string, string>))]
[JsonSerializable(typeof(List<string>))]
[JsonSerializable(typeof(List<GitObjectsHttpRequestor.GitObjectSize>))]
[JsonSerializable(typeof(ServerGVFSConfig))]
[JsonSerializable(typeof(VersionResponse))]
[JsonSerializable(typeof(InternalVerbParameters))]
[JsonSerializable(typeof(CacheServerInfo))]
[JsonSerializable(typeof(NamedPipeMessages.GetStatus.Response), TypeInfoPropertyName = "GetStatusResponse")]
[JsonSerializable(typeof(NamedPipeMessages.DehydrateFolders.Request), TypeInfoPropertyName = "DehydrateFoldersRequest")]
[JsonSerializable(typeof(NamedPipeMessages.DehydrateFolders.Response), TypeInfoPropertyName = "DehydrateFoldersResponse")]
[JsonSerializable(typeof(NamedPipeMessages.Notification.Request), TypeInfoPropertyName = "NotificationRequest")]
[JsonSerializable(typeof(NamedPipeMessages.UnregisterRepoRequest))]
[JsonSerializable(typeof(NamedPipeMessages.UnregisterRepoRequest.Response), TypeInfoPropertyName = "UnregisterRepoResponse")]
[JsonSerializable(typeof(NamedPipeMessages.RegisterRepoRequest))]
[JsonSerializable(typeof(NamedPipeMessages.RegisterRepoRequest.Response), TypeInfoPropertyName = "RegisterRepoResponse")]
[JsonSerializable(typeof(NamedPipeMessages.EnableAndAttachProjFSRequest))]
[JsonSerializable(typeof(NamedPipeMessages.EnableAndAttachProjFSRequest.Response), TypeInfoPropertyName = "EnableAndAttachProjFSResponse")]
[JsonSerializable(typeof(NamedPipeMessages.GetActiveRepoListRequest))]
[JsonSerializable(typeof(NamedPipeMessages.GetActiveRepoListRequest.Response), TypeInfoPropertyName = "GetActiveRepoListResponse")]
[JsonSerializable(typeof(NamedPipeMessages.BaseResponse<string>))]
[JsonSerializable(typeof(TelemetryDaemonEventListener.PipeMessage))]
[JsonSerializable(typeof(PrettyConsoleEventListener.ConsoleOutputPayload))]
internal partial class GVFSJsonContext : JsonSerializerContext
{
}
}
37 changes: 22 additions & 15 deletions GVFS/GVFS.Common/GVFSJsonOptions.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,47 @@
using GVFS.Common.Tracing;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;

namespace GVFS.Common
{
/// <summary>
/// Shared JsonSerializerOptions and helpers for the GVFS codebase.
/// PropertyNameCaseInsensitive preserves backward compatibility with
/// Newtonsoft.Json's default case-insensitive deserialization.
/// </summary>
public static class GVFSJsonOptions
{
[UnconditionalSuppressMessage("AOT", "IL2026",
Justification = "DefaultJsonTypeInfoResolver fallback handles all types at runtime.")]
[UnconditionalSuppressMessage("AOT", "IL3050",
Justification = "DefaultJsonTypeInfoResolver fallback handles all types at runtime.")]
public static readonly JsonSerializerOptions Default = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
Converters = { new VersionConverter(), new Tracing.EventMetadataConverter() },
Converters = { new VersionConverter(), new EventMetadataConverter() },
TypeInfoResolverChain = { GVFSJsonContext.Default, new DefaultJsonTypeInfoResolver() },
};

/// <summary>
/// Serialize using the compile-time type. Use when <typeparamref name="T"/>
/// is the concrete type (not a base class with derived properties).
/// </summary>
[UnconditionalSuppressMessage("AOT", "IL2026",
Justification = "TypeInfoResolverChain includes DefaultJsonTypeInfoResolver.")]
[UnconditionalSuppressMessage("AOT", "IL3050",
Justification = "TypeInfoResolverChain includes DefaultJsonTypeInfoResolver.")]
public static string Serialize<T>(T value)
{
return JsonSerializer.Serialize(value, Default);
}

/// <summary>
/// Serialize using the runtime type. Use when calling from a base-class
/// method where compile-time type would lose derived-class properties
/// (e.g., BaseResponse&lt;T&gt;.ToMessage()).
/// </summary>
[UnconditionalSuppressMessage("AOT", "IL2026",
Justification = "TypeInfoResolverChain includes DefaultJsonTypeInfoResolver.")]
[UnconditionalSuppressMessage("AOT", "IL3050",
Justification = "TypeInfoResolverChain includes DefaultJsonTypeInfoResolver.")]
public static string Serialize(object value, Type inputType)
{
return JsonSerializer.Serialize(value, inputType, Default);
}

[UnconditionalSuppressMessage("AOT", "IL2026",
Justification = "TypeInfoResolverChain includes DefaultJsonTypeInfoResolver.")]
[UnconditionalSuppressMessage("AOT", "IL3050",
Justification = "TypeInfoResolverChain includes DefaultJsonTypeInfoResolver.")]
public static T Deserialize<T>(string json)
{
return JsonSerializer.Deserialize<T>(json, Default);
Expand Down
Loading
Loading