Skip to content

Commit 6a26958

Browse files
committed
CI: add upgrade test workflow with 5 scenarios
New upgrade-tests.yaml runs in parallel with functional tests. Downloads LKG release installer, installs it, then tests upgrade paths with the current build's installer. Scenarios: - staging-upgrade: LKG -> mount -> /KEEPMOUNTED=true -> unmount -> verify - clean-upgrade: LKG -> mount -> /KEEPMOUNTED=false -> verify traditional - double-staging: two staging installs back-to-back, verify second wins - staging-then-clean: staging install then clean install clears PendingUpgrade - mount-safety-deferral: service restart with mount running defers upgrade Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
1 parent bb791f0 commit 6a26958

2 files changed

Lines changed: 289 additions & 1 deletion

File tree

.github/workflows/build.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,17 @@ jobs:
247247
with:
248248
skip: ${{ needs.validate.outputs.skip }}
249249

250+
upgrade_tests:
251+
name: Upgrade Tests
252+
needs: [validate, build]
253+
uses: ./.github/workflows/upgrade-tests.yaml
254+
with:
255+
skip: ${{ needs.validate.outputs.skip }}
256+
250257
result:
251258
runs-on: ubuntu-latest
252259
name: Build, Unit and Functional Tests Successful
253-
needs: [functional_tests]
260+
needs: [functional_tests, upgrade_tests]
254261

255262
steps:
256263
- name: Success! # for easier identification of successful runs in the Checks Required for Pull Requests
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
name: Upgrade Tests
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
skip:
7+
description: 'URL of a previous successful run; if non-empty, all steps are skipped'
8+
required: false
9+
type: string
10+
default: ''
11+
lkg_release_tag:
12+
description: 'Tag of the last known good release to upgrade from'
13+
required: false
14+
type: string
15+
default: 'v1.0.26098.1'
16+
17+
permissions:
18+
contents: read
19+
actions: read
20+
21+
jobs:
22+
upgrade_test:
23+
runs-on: windows-2025
24+
name: Upgrade
25+
timeout-minutes: 30
26+
27+
strategy:
28+
matrix:
29+
configuration: [ Debug ]
30+
scenario:
31+
- staging-upgrade
32+
- clean-upgrade
33+
- double-staging
34+
- staging-then-clean
35+
- mount-safety-deferral
36+
fail-fast: false
37+
38+
steps:
39+
- name: Skip this job if there is a previous successful run
40+
if: inputs.skip != ''
41+
id: skip
42+
uses: actions/github-script@v9
43+
with:
44+
script: |
45+
core.info(`Skipping: There already is a successful run: ${{ inputs.skip }}`)
46+
return true
47+
48+
# -- Artifacts --
49+
50+
- name: Download LKG release installer
51+
if: steps.skip.outputs.result != 'true'
52+
shell: pwsh
53+
env:
54+
GITHUB_TOKEN: ${{ github.token }}
55+
run: |
56+
New-Item -ItemType Directory -Path gvfs-lkg -Force | Out-Null
57+
gh release download "${{ inputs.lkg_release_tag }}" --repo ${{ github.repository }} --pattern "SetupGVFS*.exe" --dir gvfs-lkg
58+
59+
- name: Download Git installer
60+
if: steps.skip.outputs.result != 'true'
61+
uses: actions/download-artifact@v8
62+
with:
63+
name: MicrosoftGit
64+
path: git
65+
66+
- name: Download current GVFS installer
67+
if: steps.skip.outputs.result != 'true'
68+
uses: actions/download-artifact@v8
69+
with:
70+
name: GVFS_${{ matrix.configuration }}
71+
path: gvfs-new
72+
73+
# -- Setup --
74+
75+
- name: Install Git
76+
if: steps.skip.outputs.result != 'true'
77+
shell: cmd
78+
run: git\install.bat
79+
80+
- name: Enable ProjFS
81+
if: steps.skip.outputs.result != 'true'
82+
shell: pwsh
83+
run: |
84+
$feature = Get-WindowsOptionalFeature -Online -FeatureName Client-ProjFS
85+
if ($feature.State -ne 'Enabled') {
86+
Enable-WindowsOptionalFeature -Online -FeatureName Client-ProjFS -NoRestart
87+
}
88+
89+
# -- Test Execution --
90+
91+
- name: Run upgrade test - ${{ matrix.scenario }}
92+
if: steps.skip.outputs.result != 'true'
93+
shell: pwsh
94+
run: |
95+
$ErrorActionPreference = 'Stop'
96+
97+
$lkgInstaller = (Get-ChildItem gvfs-lkg\SetupGVFS*.exe).FullName
98+
$newInstaller = (Get-ChildItem gvfs-new\SetupGVFS*.exe).FullName
99+
$installDir = "C:\Program Files\VFS for Git"
100+
$testRepo = "https://dev.azure.com/gvfs/ci/_git/ForTests"
101+
$enlistment = "C:\gvfs-upgrade-test"
102+
103+
function Install-GVFS($installer, [string[]]$extraArgs = @()) {
104+
$logDir = "C:\temp\gvfs-install-logs"
105+
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
106+
$logFile = Join-Path $logDir "gvfs-$(Get-Date -Format 'yyyyMMdd-HHmmss').log"
107+
$args = @("/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/LOG=$logFile") + $extraArgs
108+
Write-Host "Installing: $installer $($args -join ' ')"
109+
$proc = Start-Process -FilePath $installer -ArgumentList $args -Wait -PassThru
110+
if ($proc.ExitCode -ne 0) {
111+
Get-Content $logFile -Tail 30 -ErrorAction SilentlyContinue
112+
throw "Installer failed with exit code $($proc.ExitCode)"
113+
}
114+
Write-Host "Installed successfully"
115+
}
116+
117+
function Get-GVFSVersion {
118+
[System.Diagnostics.FileVersionInfo]::GetVersionInfo("$installDir\GVFS.exe").ProductVersion
119+
}
120+
121+
function Assert-ServiceRunning {
122+
$svc = sc.exe query GVFS.Service 2>&1 | Select-String "STATE"
123+
if ($svc -notmatch "RUNNING") { throw "GVFS.Service is not running: $svc" }
124+
}
125+
126+
function Mount-TestRepo {
127+
if (Test-Path $enlistment) {
128+
& "$installDir\gvfs.exe" mount $enlistment 2>&1
129+
} else {
130+
& "$installDir\gvfs.exe" clone $testRepo $enlistment 2>&1
131+
}
132+
if ($LASTEXITCODE -ne 0) { throw "Mount/clone failed" }
133+
$mountProc = Get-Process -Name "GVFS.Mount" -ErrorAction SilentlyContinue
134+
if (-not $mountProc) { throw "No GVFS.Mount process after mount" }
135+
return $mountProc.Id
136+
}
137+
138+
function Assert-MountAlive($expectedPid) {
139+
$proc = Get-Process -Id $expectedPid -ErrorAction SilentlyContinue
140+
if (-not $proc -or $proc.ProcessName -ne "GVFS.Mount") {
141+
throw "Mount process $expectedPid is no longer running"
142+
}
143+
}
144+
145+
function Unmount-TestRepo {
146+
& "$installDir\gvfs.exe" unmount $enlistment 2>&1
147+
Start-Sleep -Seconds 3
148+
}
149+
150+
function Restart-Service {
151+
sc.exe stop GVFS.Service | Out-Null
152+
Start-Sleep -Seconds 10
153+
sc.exe start GVFS.Service | Out-Null
154+
Start-Sleep -Seconds 10
155+
Assert-ServiceRunning
156+
}
157+
158+
function Assert-PendingUpgrade($expected) {
159+
$exists = Test-Path "$installDir\PendingUpgrade"
160+
if ($exists -ne $expected) {
161+
throw "PendingUpgrade directory: expected=$expected, actual=$exists"
162+
}
163+
}
164+
165+
# =============================================
166+
# Test scenarios
167+
# =============================================
168+
169+
switch ("${{ matrix.scenario }}") {
170+
171+
"staging-upgrade" {
172+
Write-Host "=== Scenario: Staging upgrade e2e ==="
173+
# Install LKG, mount, staging upgrade, unmount, verify completion
174+
Install-GVFS $lkgInstaller
175+
Assert-ServiceRunning
176+
$mountPid = Mount-TestRepo
177+
178+
Install-GVFS $newInstaller @("/KEEPMOUNTED=true")
179+
Assert-MountAlive $mountPid
180+
Assert-PendingUpgrade $true
181+
182+
Unmount-TestRepo
183+
Restart-Service
184+
Assert-PendingUpgrade $false
185+
Write-Host "PASS: Staging upgrade completed"
186+
}
187+
188+
"clean-upgrade" {
189+
Write-Host "=== Scenario: Clean upgrade (traditional) ==="
190+
# Install LKG, mount, clean upgrade — should unmount and remount
191+
Install-GVFS $lkgInstaller
192+
Assert-ServiceRunning
193+
$mountPid = Mount-TestRepo
194+
195+
Install-GVFS $newInstaller @("/KEEPMOUNTED=false")
196+
Assert-PendingUpgrade $false
197+
Assert-ServiceRunning
198+
Write-Host "PASS: Clean upgrade completed"
199+
}
200+
201+
"double-staging" {
202+
Write-Host "=== Scenario: Double staging install ==="
203+
# Install LKG, mount, staging install twice, verify second overwrites
204+
Install-GVFS $lkgInstaller
205+
Assert-ServiceRunning
206+
$mountPid = Mount-TestRepo
207+
208+
Install-GVFS $newInstaller @("/KEEPMOUNTED=true")
209+
Assert-MountAlive $mountPid
210+
Assert-PendingUpgrade $true
211+
212+
# Second staging install should overwrite PendingUpgrade
213+
Install-GVFS $newInstaller @("/KEEPMOUNTED=true")
214+
Assert-MountAlive $mountPid
215+
Assert-PendingUpgrade $true
216+
217+
Unmount-TestRepo
218+
Restart-Service
219+
Assert-PendingUpgrade $false
220+
Write-Host "PASS: Double staging handled correctly"
221+
}
222+
223+
"staging-then-clean" {
224+
Write-Host "=== Scenario: Staging then clean install ==="
225+
# Install LKG, mount, staging install, unmount, clean install
226+
# Verify PendingUpgrade is cleaned up by clean install
227+
Install-GVFS $lkgInstaller
228+
Assert-ServiceRunning
229+
$mountPid = Mount-TestRepo
230+
231+
Install-GVFS $newInstaller @("/KEEPMOUNTED=true")
232+
Assert-MountAlive $mountPid
233+
Assert-PendingUpgrade $true
234+
235+
Unmount-TestRepo
236+
# Now clean install — should remove PendingUpgrade
237+
Install-GVFS $newInstaller @("/KEEPMOUNTED=false")
238+
Assert-PendingUpgrade $false
239+
Assert-ServiceRunning
240+
Write-Host "PASS: Staging then clean install handled correctly"
241+
}
242+
243+
"mount-safety-deferral" {
244+
Write-Host "=== Scenario: Mount safety deferral ==="
245+
# Install LKG, mount, staging install, restart service WITH mount
246+
# running — upgrade should be deferred
247+
Install-GVFS $lkgInstaller
248+
Assert-ServiceRunning
249+
$mountPid = Mount-TestRepo
250+
251+
Install-GVFS $newInstaller @("/KEEPMOUNTED=true")
252+
Assert-MountAlive $mountPid
253+
Assert-PendingUpgrade $true
254+
255+
# Restart service WITHOUT unmounting — upgrade should defer
256+
Restart-Service
257+
Assert-MountAlive $mountPid
258+
Assert-PendingUpgrade $true
259+
Write-Host "Upgrade correctly deferred while mount running"
260+
261+
# Now unmount and restart — should complete
262+
Unmount-TestRepo
263+
Restart-Service
264+
Assert-PendingUpgrade $false
265+
Write-Host "PASS: Mount safety deferral works correctly"
266+
}
267+
268+
default {
269+
throw "Unknown scenario: ${{ matrix.scenario }}"
270+
}
271+
}
272+
273+
- name: Upload service logs
274+
if: always() && steps.skip.outputs.result != 'true'
275+
uses: actions/upload-artifact@v7
276+
continue-on-error: true
277+
with:
278+
name: UpgradeTest_Logs_${{ matrix.scenario }}
279+
path: |
280+
C:\ProgramData\GVFS\GVFS.Service\Logs\
281+
C:\temp\gvfs-install-logs\

0 commit comments

Comments
 (0)