diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 92e9ab675..79a4e4cc8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -5,11 +5,38 @@ on: branches: [ master, releases/shipped ] push: branches: [ master, releases/shipped ] + workflow_dispatch: + inputs: + git_version: + description: 'Microsoft Git version tag to include in the build (leave empty for default)' + required: false + type: string + +env: + GIT_VERSION: ${{ github.event.inputs.git_version || 'v2.50.1.vfs.0.1' }} jobs: + validate: + runs-on: windows-2025 + name: Validation + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Validate Microsoft Git version + shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + & "$env:GITHUB_WORKSPACE\.github\workflows\scripts\validate_release.ps1" ` + -Repository microsoft/git ` + -Tag $env:GIT_VERSION && ` + Write-Host ::notice title=Validation::Using microsoft/git version $env:GIT_VERSION + build: runs-on: windows-2025 name: Build and Unit Test + needs: validate strategy: matrix: @@ -41,6 +68,13 @@ jobs: shell: cmd run: src\scripts\CreateBuildArtifacts.bat ${{ matrix.configuration }} artifacts + - name: Download microsoft/git installers + shell: cmd + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release download %GIT_VERSION% --repo microsoft/git --pattern "Git*.exe" --dir artifacts\GVFS.Installers + - name: Upload functional tests drop uses: actions/upload-artifact@v4 with: @@ -59,20 +93,15 @@ jobs: name: Installers_${{ matrix.configuration }} path: artifacts\GVFS.Installers - - name: Upload NuGet packages - uses: actions/upload-artifact@v4 - with: - name: NuGetPackages_${{ matrix.configuration }} - path: artifacts\NuGetPackages - functional_test: - runs-on: windows-2025 + runs-on: ${{ matrix.architecture == 'arm64' && 'windows-11-arm' || 'windows-2025' }} name: Functional Tests needs: build strategy: matrix: configuration: [ Debug, Release ] + architecture: [ x86_64, arm64 ] steps: - name: Download installers @@ -103,7 +132,7 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: InstallationLogs_${{ matrix.configuration }} + name: InstallationLogs_${{ matrix.configuration }}_${{ matrix.architecture }} path: install\logs - name: Run functional tests @@ -117,14 +146,14 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: FunctionalTests_Results_${{ matrix.configuration }} + name: FunctionalTests_Results_${{ matrix.configuration }}_${{ matrix.architecture }} path: TestResult.xml - name: Upload Git trace2 output if: always() uses: actions/upload-artifact@v4 with: - name: GitTrace2_${{ matrix.configuration }} + name: GitTrace2_${{ matrix.configuration }}_${{ matrix.architecture }} path: C:\temp\git-trace2.log - name: ProjFS details (post-test) diff --git a/.github/workflows/scripts/validate_release.ps1 b/.github/workflows/scripts/validate_release.ps1 new file mode 100644 index 000000000..b9ee8c04d --- /dev/null +++ b/.github/workflows/scripts/validate_release.ps1 @@ -0,0 +1,124 @@ +param( + [Parameter(Mandatory=$true)] + [string]$Tag, + + [Parameter(Mandatory=$true)] + [string]$Repository +) + +function Write-GitHubActionsCommand { + param( + [Parameter(Mandatory=$true)] + [string]$Command, + + [Parameter(Mandatory=$true)] + [string]$Message, + + [Parameter(Mandatory=$true)] + [string]$Title + ) + + Write-Host "::$Command title=$Title::$Message" +} + + +function Write-GitHubActionsWarning { + param( + [Parameter(Mandatory=$true)] + [string]$Message, + + [Parameter(Mandatory=$false)] + [string]$Title = "Warning" + ) + + if ($env:GITHUB_ACTIONS -eq "true") { + Write-GitHubActionsCommand -Command "warning" -Message $Message -Title $Title + } else { + Write-Host "! Warning: $Message" -ForegroundColor Yellow + } +} + +function Write-GitHubActionsError { + param( + [Parameter(Mandatory=$true)] + [string]$Message, + + [Parameter(Mandatory=$false)] + [string]$Title = "Error" + ) + + if ($env:GITHUB_ACTIONS -eq "true") { + Write-GitHubActionsCommand -Command "error" -Message $Message -Title $Title + } else { + Write-Host "x Error: $Message" -ForegroundColor Red + } +} + +if ([string]::IsNullOrWhiteSpace($Tag)) { + Write-GitHubActionsError -Message "Tag parameter is required" + exit 1 +} + +if ([string]::IsNullOrWhiteSpace($Repository)) { + Write-GitHubActionsError -Message "Repository parameter is required" + exit 1 +} + +Write-Host "Validating $Repository release '$Tag'..." + +# Prepare headers for GitHub API +$headers = @{ + 'Accept' = 'application/vnd.github.v3+json' + 'User-Agent' = 'VFSForGit-Build' +} + +if ($env:GITHUB_TOKEN) { + $headers['Authorization'] = "Bearer $env:GITHUB_TOKEN" +} + +# Check if the tag exists in microsoft/git repository +try { + $releaseResponse = Invoke-RestMethod ` + -Uri "https://api.github.com/repos/$Repository/releases/tags/$Tag" ` + -Headers $headers + + Write-Host "✓ Tag '$Tag' found in $Repository" -ForegroundColor Green + Write-Host " Release : $($releaseResponse.name)" + Write-Host " Published : $($releaseResponse.published_at.ToString('u'))" + + # Check if this a pre-release + if ($releaseResponse.prerelease -eq $true) { + Write-GitHubActionsWarning ` + -Message "Using a pre-released version of $Repository" ` + -Title "Pre-release $Repository version" + } + + # Get the latest release for comparison + try { + $latestResponse = Invoke-RestMethod ` + -Uri "https://api.github.com/repos/$Repository/releases/latest" ` + -Headers $headers + $latestTag = $latestResponse.tag_name + + # Check if this is the latest release + if ($Tag -eq $latestTag) { + Write-Host "✓ Using the latest release" -ForegroundColor Green + exit 0 + } + + # Not the latest! + $warningTitle = "Outdated $Repository release" + $warningMsg = "Not using latest release of $Repository (latest: $latestTag)" + Write-GitHubActionsWarning -Message $warningMsg -Title $warningTitle + } catch { + Write-GitHubActionsWarning -Message "Could not check latest release info for ${Repository}: $($_.Exception.Message)" + } +} catch { + if ($_.Exception.Response.StatusCode -eq 404) { + Write-GitHubActionsError -Message "Tag '$Tag' does not exist in $Repository" + exit 1 + } else { + Write-GitHubActionsError -Message "Error validating release '$Tag': $($_.Exception.Message)" + exit 1 + } +} diff --git a/GVFS/GVFS.FunctionalTests/Tools/GitHelpers.cs b/GVFS/GVFS.FunctionalTests/Tools/GitHelpers.cs index 8aacac84a..76a77eb64 100644 --- a/GVFS/GVFS.FunctionalTests/Tools/GitHelpers.cs +++ b/GVFS/GVFS.FunctionalTests/Tools/GitHelpers.cs @@ -63,19 +63,55 @@ public static ProcessResult InvokeGitAgainstGVFSRepo( string command, Dictionary environmentVariables = null, bool removeWaitingMessages = true, - bool removeUpgradeMessages = true) + bool removeUpgradeMessages = true, + bool removePartialHydrationMessages = true, + bool removeFSMonitorMessages = true) { ProcessResult result = GitProcess.InvokeProcess(gvfsRepoRoot, command, environmentVariables); - string errors = result.Errors; + string output = FilterMessages(result.Output, false, false, false, removePartialHydrationMessages, removeFSMonitorMessages); + string errors = FilterMessages(result.Errors, true, removeWaitingMessages, removeUpgradeMessages, removePartialHydrationMessages, removeFSMonitorMessages); - if (!string.IsNullOrEmpty(errors) && (removeWaitingMessages || removeUpgradeMessages)) + return new ProcessResult( + output, + errors, + result.ExitCode); + } + + private static IEnumerable SplitLinesKeepingNewlines(string input) + { + for (int start = 0; start < input.Length; ) { - IEnumerable errorLines = errors.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); - IEnumerable filteredErrorLines = errorLines.Where(line => + int nextLine = input.IndexOf('\n', start) + 1; + + if (nextLine == 0) { - if (string.IsNullOrWhiteSpace(line) || + // No more newlines, yield the rest + nextLine = input.Length; + } + + yield return input.Substring(start, nextLine - start); + start = nextLine; + } + } + + private static string FilterMessages( + string input, + bool removeEmptyLines, + bool removeWaitingMessages, + bool removeUpgradeMessages, + bool removePartialHydrationMessages, + bool removeFSMonitorMessages) + { + if (!string.IsNullOrEmpty(input) && (removeWaitingMessages || removeUpgradeMessages || removePartialHydrationMessages || removeFSMonitorMessages)) + { + IEnumerable lines = SplitLinesKeepingNewlines(input); + IEnumerable filteredLines = lines.Where(line => + { + if ((removeEmptyLines && string.IsNullOrWhiteSpace(line)) || (removeUpgradeMessages && line.StartsWith("A new version of VFS for Git is available.")) || - (removeWaitingMessages && line.StartsWith("Waiting for "))) + (removeWaitingMessages && line.StartsWith("Waiting for ")) || + (removePartialHydrationMessages && line.StartsWith("You are in a partially-hydrated checkout with ")) || + (removeFSMonitorMessages && line.TrimEnd().EndsWith(" is incompatible with fsmonitor"))) { return false; } @@ -85,13 +121,10 @@ public static ProcessResult InvokeGitAgainstGVFSRepo( } }); - errors = filteredErrorLines.Any() ? string.Join(Environment.NewLine, filteredErrorLines) : string.Empty; + return filteredLines.Any() ? string.Join("", filteredLines) : string.Empty; } - return new ProcessResult( - result.Output, - errors, - result.ExitCode); + return input; } public static void ValidateGitCommand( diff --git a/GVFS/GVFS.Installers/GVFS.Installers.csproj b/GVFS/GVFS.Installers/GVFS.Installers.csproj index 13cf3e601..4470aeaba 100644 --- a/GVFS/GVFS.Installers/GVFS.Installers.csproj +++ b/GVFS/GVFS.Installers/GVFS.Installers.csproj @@ -13,8 +13,6 @@ - - @@ -24,13 +22,8 @@ - - - @@ -44,10 +37,6 @@ - - - - diff --git a/GVFS/GVFS.Installers/GVFS.Installers.template.nuspec b/GVFS/GVFS.Installers/GVFS.Installers.template.nuspec deleted file mode 100644 index 8991bd81e..000000000 --- a/GVFS/GVFS.Installers/GVFS.Installers.template.nuspec +++ /dev/null @@ -1,15 +0,0 @@ - - - - GVFS.Installers - $version$ - Microsoft - false - GVFS and G4W installers - - - - - - - diff --git a/GVFS/GVFS.Installers/info.bat b/GVFS/GVFS.Installers/info.bat index b068f9c69..ba61fbc7a 100644 --- a/GVFS/GVFS.Installers/info.bat +++ b/GVFS/GVFS.Installers/info.bat @@ -9,19 +9,31 @@ SET VFS_BUND_PROJFSLIB=C:\Program Files\VFS for Git\ProjFS\ProjectedFSLib.dll SET VFS_EXEC=C:\Program Files\VFS for Git\GVFS.exe SET GIT_EXEC=C:\Program Files\Git\cmd\git.exe +REM Lookup the current Windows version +FOR /F "tokens=*" %%i IN ('powershell -Command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"') DO SET OS_VER=%%i + +ECHO Print system information... +ECHO OS version: %OS_VER% +ECHO CPU architecture: %PROCESSOR_ARCHITECTURE% + +ECHO. ECHO Checking ProjFS Windows feature... powershell -Command "Get-WindowsOptionalFeature -Online -FeatureName Client-ProjFS" +ECHO. ECHO Checking ProjFS and GVFS services... ECHO GVFS.Service: sc query GVFS.Service +ECHO. ECHO Test.GVFS.Service: sc query Test.GVFS.Service +ECHO. ECHO prjflt: sc query prjflt +ECHO. ECHO Checking ProjFS files... IF EXIST "%SYS_PRJFLT%" ( ECHO [ FOUND ] %SYS_PRJFLT% diff --git a/GVFS/GVFS.Installers/install.bat b/GVFS/GVFS.Installers/install.bat index c629c75bc..8375be193 100644 --- a/GVFS/GVFS.Installers/install.bat +++ b/GVFS/GVFS.Installers/install.bat @@ -1,8 +1,18 @@ @ECHO OFF SETLOCAL +REM Determine the correct architecture for the installer +IF "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( + SET GIT_ARCH=64-bit +) ELSE IF "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( + SET GIT_ARCH=arm64 +) ELSE ( + ECHO Unknown architecture: %PROCESSOR_ARCHITECTURE% + exit 1 +) + REM Lookup full paths to Git and VFS for Git installers -FOR /F "tokens=* USEBACKQ" %%F IN ( `where /R %~dp0 Git*.exe` ) DO SET GIT_INSTALLER=%%F +FOR /F "tokens=* USEBACKQ" %%F IN ( `where /R %~dp0 Git*-%GIT_ARCH%.exe` ) DO SET GIT_INSTALLER=%%F FOR /F "tokens=* USEBACKQ" %%F IN ( `where /R %~dp0 SetupGVFS*.exe` ) DO SET GVFS_INSTALLER=%%F REM Create new empty directory for logs @@ -12,7 +22,7 @@ IF EXIST %LOGDIR% ( ) mkdir %LOGDIR% -ECHO Installing Git for Windows... +ECHO Installing Git (%GIT_ARCH%)... %GIT_INSTALLER% /LOG="%LOGDIR%\git.log" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /ALLOWDOWNGRADE=1 ECHO Installing VFS for Git... diff --git a/Version.props b/Version.props index 1547245ce..d1f6f341b 100644 --- a/Version.props +++ b/Version.props @@ -5,10 +5,8 @@ 0.2.173.2 - 2.20220414.4 v2.31.0.vfs.0.1 diff --git a/scripts/CreateBuildArtifacts.bat b/scripts/CreateBuildArtifacts.bat index 5cca08d5a..797ed0bf9 100644 --- a/scripts/CreateBuildArtifacts.bat +++ b/scripts/CreateBuildArtifacts.bat @@ -14,13 +14,6 @@ IF "%~2"=="" ( SET OUTROOT=%2 ) -REM Check NuGet is on the PATH -where /q nuget.exe -IF ERRORLEVEL 1 ( - ECHO ERROR: Could not find nuget.exe on the PATH - EXIT /B 1 -) - IF EXIST %OUTROOT% ( rmdir /s /q %OUTROOT% ) @@ -60,19 +53,6 @@ xcopy /S /Y ^ %VFS_OUTDIR%\GVFS.FunctionalTests\bin\%CONFIGURATION%\net471\win-x64 ^ %OUTROOT%\GVFS.FunctionalTests\ || GOTO ERROR -ECHO ^************************************* -ECHO ^* Creating Installers NuGet Package * -ECHO ^************************************* -mkdir %OUTROOT%\NuGetPackages -nuget.exe pack ^ - %VFS_OUTDIR%\GVFS.Installers\bin\%CONFIGURATION%\win-x64\GVFS.Installers.nuspec ^ - -BasePath %VFS_OUTDIR%\GVFS.Installers\bin\%CONFIGURATION%\win-x64 ^ - -OutputDirectory %OUTROOT%\NuGetPackages || GOTO ERROR - -REM Move the nuspec file to the NuGetPackages artifact directory -move %OUTROOT%\GVFS.Installers\GVFS.Installers.nuspec ^ - %OUTROOT%\NuGetPackages - GOTO :EOF :USAGE