Retarget all managed projects to .NET 10 (net10.0-windows10.0.17763.0)#1947
Closed
tyrielv wants to merge 16 commits into
Closed
Retarget all managed projects to .NET 10 (net10.0-windows10.0.17763.0)#1947tyrielv wants to merge 16 commits into
tyrielv wants to merge 16 commits into
Conversation
5efd7d7 to
b58e509
Compare
Phase 3 of the .NET 10 migration: TFM retarget, API migrations, and build/CI updates consolidated into a single PR. Foundation: - Pin .NET 10 SDK in global.json (10.0.100 with latestFeature rollback) - Centralize TargetFramework in Directory.Build.props - Bump default dev version to 0.3.0.1 (official builds use 2.0.x.x) - Update MSBuild SDK versions (Traversal 4.1.0, NoTargets 3.7.0) - Update CI to .NET 10 SDK and windows-2025 runner Package updates: - Microsoft.Data.Sqlite 2.2.4 -> 9.0.4 - Microsoft.Build.* 16.0.461 -> 17.12.6 - Microsoft.Windows.ProjFS 1.1.19156.1 -> 2.1.0 - System.ServiceProcess.ServiceController 4.5.0 -> 9.0.4 - Add System.Diagnostics.EventLog, System.Management, System.IO.Pipes.AccessControl - Remove GVFS.ProjFS package (ProjFS is now a Windows OS feature) - Replace CommandLineParser and Newtonsoft.Json stale references API migrations: - HttpUtility.UrlEncode -> WebUtility.UrlEncode (OrgInfoApiClient) - Assembly.Location -> Environment.ProcessPath (ProcessHelper, HooksInstaller) - NamedPipeServerStream ctor -> NamedPipeServerStreamAcl.Create (WindowsPlatform) - Directory.Get/SetAccessControl -> DirectoryInfo extensions (WindowsFileSystem, GVFSService) - Directory.CreateDirectory(path, security) -> DirectorySecurity.CreateDirectory(path) - Remove HttpRequestor machine.config lock and ServicePointManager usage - Remove UseDefaultCredentials from HttpClientHandler - Remove all .NET Framework assembly references (System.Web, System.Net.Http, etc.) Scripts and installer: - Update output paths from net471 to net10.0-windows10.0.17763.0\win-x64 - Remove ProjFS native DLL bundling from layout.bat (OS feature now) - Update installer MinVersion to 10.0.17763 Test fixes: - Add missing IVirtualizationInstance members to MockVirtualizationInstance - Remove stale GVFS.Service.UI project reference from UnitTests All 803 unit tests pass (792 passed, 11 pre-existing skips). Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
6984f48 to
b7a0793
Compare
NativeAOT compiles managed projects to native binaries, eliminating the .NET runtime dependency. This fixes CI functional tests which run on machines without .NET 10 installed. Changes: - Add SelfContained=true and PublishAot=true to Directory.Build.props - Add OptimizationPreference=Speed for performance-critical system component - Opt out test projects from AOT (NUnit uses reflection for discovery) - Opt out GVFS.MSBuild (netstandard2.0 build task) - Switch Build.bat from 'dotnet build' to 'dotnet publish' for managed projects - Update all script output paths to include publish\ subdirectory - Pin global.json to SDK 10.0.202 with rollForward=disable Output: ~20 MB native GVFS.exe, 36.7 MB installer (vs 107 MB self-contained) Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
c272226 to
69c8913
Compare
Before: Functional tests hung indefinitely in CI with no output, making it impossible to diagnose failures. The gvfs process calls had no timeout and stdout/stderr were not streamed to the CI log. Changes: - Add [CI-DEBUG] logging to CloneAndMount and gvfs process invocations - Add 5-minute timeout per gvfs process invocation in GVFSProcess.CallGVFS - Redirect and stream stderr via BeginErrorReadLine for real-time CI output - Add timeout-minutes: 60 to functional-tests.yaml test step - Add --workers=1 to CI test runner for sequential execution Result: Tests no longer hang indefinitely. CI logs show real-time gvfs output for diagnosis. 48/56 tests passed in first run with output, revealing 6 specific failures to investigate (previously all 56 timed out silently). Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Before: GVFS.Mount crashed with ArgumentNullException in
OnPlaceholderFileCreated/OnPlaceholderFolderCreated/OnPlaceholderFileHydrated
when ProjFS passed null for triggeringProcessImageFileName. This caused 5 of
6 CI test failures (4 Native_ProjFS tests + ResetHardDirectoryWithOneFileWrite).
Root cause: The new pure-C# ProjFS managed API (Microsoft.Windows.ProjFS 2.1.0)
uses Marshal.PtrToStringUni(pData->TriggeringProcessImageFileName) which returns
null when the native pointer is IntPtr.Zero (process ID 0, kernel operations).
The old C++/CLI wrapper explicitly checked for NULL and returned String.Empty:
(callbackData->TriggeringProcessImageFileName != NULL)
? gcnew String(...) : String::Empty
ConcurrentDictionary.AddOrUpdate does not accept null keys, so the null
propagated up and crashed the mount process.
Fix: Null-coalesce to string.Empty in all three AddOrUpdate call sites,
matching the old C++/CLI behavior. This is defense-in-depth; the upstream
fix belongs in Microsoft.Windows.ProjFS (tracked but not yet filed).
Result: All 5 mount-crash test failures resolved. Slice 1 (which had the
same crash pattern) now passes 4/4 in CI.
Assisted-by: Claude Opus 4.6
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Before: TruncatedObjectRedownloaded test failed 4/4 in CI. In the previous CI run, file content had null bytes; in the latest run, the CorruptObjects count assertion failed (expected initialCount+1, got initialCount). Root cause: .NET 10's DeflateStream silently returns partial data on truncated zlib input instead of throwing InvalidDataException (behavioral change from .NET Framework 4.7.1). Verified with a standalone test: - .NET 4.7.1: DeflateStream throws InvalidDataException on truncated zlib - .NET 10: DeflateStream returns 30 of 45 expected bytes, no exception In GitRepo.GetLooseBlobStateAtPath, the code relied on InvalidDataException to detect corrupt objects and move them to the CorruptObjects folder. Without the exception, the truncated object was treated as valid (LooseBlobState.Exists) and partial/corrupt content was served to the caller. Fix: Wrap the DeflateStream in a CountingStream that tracks bytes read. After writeAction completes, compare actual bytes read to the size declared in the git object header. If fewer bytes were read, mark the object as corrupt. CountingStream fully delegates all operations to the inner stream, only adding byte counting on Read/ReadByte. Result: TruncatedObjectRedownloaded (slice 1) now passes 4/4 in CI. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Documentation for picking up the .NET 10 NativeAOT migration work from a clean slate. Includes: - docs/net10-migration-status.md: Current CI results, remaining failures with analysis, .NET 10 behavioral changes discovered, build commands, deferred items - docs/miniksa-reference.md: What was applied from miniksa's prototype branch, what wasn't applied yet, and what original work we did beyond his branch Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
69c8913 to
380033d
Compare
SDK 10.0.202 no longer available via winget; 10.0.203 is current servicing release. Same feature band, runtime updated from 10.0.5 to 10.0.7. Also required reinstalling NuGet credential provider for ARM64 — the previous x64-only binary fails with 0x800700C1 on ARM64 .NET 10. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
HydrateFile used direct P/Invoke to kernel32 CreateFile/ReadFile with incorrect parameter types (uint/int for pointer-sized params). Under NativeAOT with high concurrency (ProcessorCount*2 threads), this caused intermittent ACCESS_VIOLATION (0xC0000005) crashes in CI slices 2 and 3. Replace with managed FileStream which uses the runtime's own validated interop layer. Benchmarked at equivalent throughput (~36-40K files/s) in the multi-threaded scenario matching HydrateFilesStage usage. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
.NET Framework defaults ProcessStartInfo.UseShellExecute to true (uses ShellExecuteEx, no handle inheritance) while .NET 10 defaults it to false (uses CreateProcess, handles inherited). Without this, GVFS.Mount.exe inherits the caller's stdout/stderr pipe handles and holds them open as a daemon, causing callers that read to EOF (e.g. git's hook runner via ProcessHelper.Run) to block indefinitely. This was the root cause of the slice 9 functional test timeout: the post-command hook's gvfs mount spawns GVFS.Mount.exe which inherits the hook's stderr handle. Git waits for the hook to close stderr, but it never does because the mount daemon keeps the handle alive. Credit: miniksa's net10-nativeaot prototype had this fix; it was missed during the migration. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
.NET 10's File.SetLastWriteTime uses FILE_WRITE_ATTRIBUTES without triggering ProjFS hydration, unlike .NET Framework 4.7.1 which fully hydrated the placeholder. This left the file as a virtual placeholder in git's index, causing 'pathspec did not match' on checkout. Simulate the user scenario (edit + undo) by reading and writing the file back unchanged before adjusting the timestamp. This hydrates the placeholder into a full file, matching the real-world state a user would be in. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Port 7 changes from miniksa/user/miniksa/net10-nativeaot that were missed during our net10-tfm-retarget migration: - ProcessHelper: null-safety fallback to AppContext.BaseDirectory (3 methods) - InProcessMount: wrap Console.Title in try-catch for headless processes - HttpRequestor: switch to SocketsHttpHandler with connection pool tuning - GitAuthentication: add ConfigureSocketsHandlerSslIfNeeded for SocketsHttpHandler - Settings: null-safety fallback for Environment.ProcessPath - WindowsPhysicalDiskInfo: replace WMI (System.Management) with kernel32 P/Invoke - Remove System.Management package dependency The Console.Title fix is in the same mount startup path affected by the UseShellExecute=true fix and may have contributed to slice 9 hangs. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyler Vella <tyrielv@gmail.com>
Port remaining NativeAOT enablement changes from miniksa's prototype: Build infrastructure: - Directory.Build.targets: _CopyNativeHooks target replicates production layout during dev builds (native C++ hooks + managed peer exes) - Directory.Packages.props: remove System.Management (replaced by P/Invoke), add Microsoft.NET.Test.Sdk for CLI test project CLI backward-compatibility tests (88 tests, all passing): - FastFetchCliTests: 17 tests validating option aliases and defaults - GvfsMainCliTests: 15+ verb tests for clone/mount/prefetch/sparse/etc - GvfsMountCliTests: mount defaults (verbosity, keywords, StartedByService) - Already caught a real bug: --Allow-index-metadata case mismatch InternalsVisibleTo + BuildRootCommand wrappers: - Expose BuildRootCommand from Program classes for CLI test access - Added to GVFS, GVFS.Mount, FastFetch projects Other changes: - GVFSEnlistment: reduce mount poll interval 500ms→100ms - FastFetchVerb: fix --Allow → --allow option name (case-sensitive) - doc/design-net10-nativeaot.md: miniksa's 6-phase migration design doc - scripts/publish-aot.ps1: end-to-end AOT publish pipeline for dev loop Assisted-by: Claude Opus 4.6 Signed-off-by: Tyler Vella <tyrielv@gmail.com>
Add explicit parameterless constructors and { get; set; } to
NamedPipeMessages Response classes that had only parameterized
constructors. System.Text.Json source generators require a
parameterless constructor for deserialization.
Also add miniksa's overview slide deck doc for review.
Assisted-by: Claude Opus 4.6
Signed-off-by: Tyler Vella <tyrielv@gmail.com>
0b0d956 to
562ef62
Compare
.NET 10's FileInfo property setters (CreationTime, LastAccessTime, LastWriteTime, Attributes) no longer open write handles that trigger ProjFS hydration. Only actual file content I/O (read+write) causes hydration. The original ExpandedFileAttributesAreUpdated test relied on CreationTime's setter triggering hydration as a side effect. Replace it with two tests: 1. PlaceholderMetadataSurvivesHydration: Sets all metadata (timestamps + Hidden) on a ProjFS placeholder, verifies the file remains a placeholder, asserts properties took effect, then hydrates via FileStream read+write and verifies CreationTime and Hidden survived the conversion. (LastAccessTime/LastWriteTime are inherently updated by the hydration I/O and cannot be asserted post-hydration.) 2. HydratedFileTimestampsAndAttributesAreUpdated: Hydrates a file via read+write first, asserts it is hydrated, then sets all timestamps and Hidden, and verifies everything sticks. Additional .NET 10 behavioral changes documented: - File.WriteAllText uses FileMode.Create which fails on Hidden files - FileStream with FileMode.Open works on Hidden files Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
562ef62 to
95d2b12
Compare
The AOT enablement commit added a solution folder named 'GVFS' to hold GVFS.CommandLine.Tests, but that name conflicts with the existing GVFS.csproj project entry also named 'GVFS'. Remove the solution folder and its nesting entry; the test project remains in the solution at the root level. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 3 of the .NET 10 migration: TFM retarget, API migrations, and build/CI updates consolidated into a single PR.
Foundation:
Package updates:
API migrations:
Scripts and installer:
Test fixes:
All 803 unit tests pass (792 passed, 11 pre-existing skips).
Assisted-by: Claude Opus 4.6