Skip to content

Commit fa4280e

Browse files
authored
ci(stx): manual driver-update workflow for the self-hosted AMD runner (#1881)
## Why this matters The self-hosted Strix CI runner periodically enters a multi-minute window where the `nomic-embed-text-v2-moe` MoE embedder aborts `llama-server` startup, failing the two embedder-loading checks (Test Lemonade Embeddings API, RAG Integration). Root cause is upstream (llama.cpp #16301 / lemonade #612); a newer AMD GPU driver may close or shorten that window. Today the only remedy is waiting for the GPU driver to self-recover. This gives a maintainer a one-click way to update the driver on the runner remotely. Safety by design: - **`workflow_dispatch` only** — never triggers on push/PR. - **Default action is `check-only`** — reports current + available driver, changes nothing. Installing and rebooting are separate, explicitly-selected options. - **Fails loudly** if it lacks Administrator rights for an install/reboot (no silent no-op). - Uses the native Windows Update COM API (no third-party module) to find driver-class updates and filters by title (default `AMD|Radeon|Display`). ## Test plan - [ ] Merge, then from the Actions tab run **STX Driver Update (manual)** with action `check-only` against `stx` — confirm it prints the current driver and whether a newer one is offered, and changes nothing. - [ ] If a newer driver is offered, re-run with `update-and-reboot` and confirm the driver version increases after the runner reconnects. - [ ] Re-run the previously-failing embedder checks and confirm the fault window is gone or shorter.
1 parent 390361e commit fa4280e

1 file changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
2+
# SPDX-License-Identifier: MIT
3+
4+
# Manual-only maintenance workflow for the self-hosted AMD (Strix) CI runner.
5+
#
6+
# Why this exists: the runner's AMD Vulkan stack intermittently enters a
7+
# multi-minute window where the MoE embedding model aborts llama-server
8+
# startup ("llama-server failed to start"), failing the embedder-loading CI
9+
# checks (Test Lemonade Embeddings API, RAG Integration). Upstream:
10+
# llama.cpp #16301 / lemonade #612. A newer GPU driver may close or shorten
11+
# that window. This workflow lets a maintainer update the driver remotely.
12+
#
13+
# It NEVER runs automatically -- workflow_dispatch only. Default action is
14+
# "check-only" (report current + available driver, change nothing). Installing
15+
# and/or rebooting must be explicitly selected at dispatch time.
16+
#
17+
# NOTE: a forced reboot under "update-and-reboot" takes the runner offline. The
18+
# runner reconnects after boot only if the GitHub Actions runner is registered
19+
# as a Windows service (auto-start). If it runs interactively, someone must
20+
# restart it by hand. The dispatched job itself may report as failed/cancelled
21+
# once the box reboots -- that is expected, not a real failure.
22+
23+
name: STX Driver Update (manual)
24+
25+
on:
26+
workflow_dispatch:
27+
inputs:
28+
runner_label:
29+
description: 'Self-hosted runner label to target'
30+
type: choice
31+
options:
32+
- stx
33+
- stx-test
34+
default: stx
35+
action:
36+
description: 'What to do on the runner'
37+
type: choice
38+
options:
39+
- check-only # report current + available driver, change nothing
40+
- update # install matching driver update(s), no reboot
41+
- update-and-reboot # install, then force-restart the machine
42+
default: check-only
43+
driver_filter:
44+
description: 'Regex matched against update titles to pick the GPU driver'
45+
type: string
46+
default: 'AMD|Radeon|Display'
47+
48+
concurrency:
49+
group: stx-driver-update
50+
cancel-in-progress: false
51+
52+
permissions:
53+
contents: read
54+
55+
jobs:
56+
driver-update:
57+
name: Driver update (${{ inputs.action }})
58+
runs-on: ${{ inputs.runner_label }}
59+
timeout-minutes: 60
60+
env:
61+
ACTION: ${{ inputs.action }}
62+
DRIVER_FILTER: ${{ inputs.driver_filter }}
63+
64+
steps:
65+
- name: Report current GPU driver
66+
shell: powershell
67+
run: |
68+
$ErrorActionPreference = "Stop"
69+
Write-Host "=== Current display adapters / drivers ==="
70+
Get-CimInstance Win32_VideoController |
71+
Select-Object Name, DriverVersion, DriverDate, Status, PNPDeviceID |
72+
Format-List
73+
Write-Host "=== OS / build ==="
74+
Get-CimInstance Win32_OperatingSystem |
75+
Select-Object Caption, Version, BuildNumber, LastBootUpTime |
76+
Format-List
77+
78+
- name: Check privileges
79+
shell: powershell
80+
run: |
81+
$ErrorActionPreference = "Stop"
82+
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
83+
$principal = New-Object Security.Principal.WindowsPrincipal($id)
84+
$isAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
85+
Write-Host "Running as: $($id.Name) (admin=$isAdmin)"
86+
if (-not $isAdmin -and $env:ACTION -ne "check-only") {
87+
Write-Host "[ERROR] Driver install/reboot requires Administrator."
88+
Write-Host " What to do: run the GitHub Actions runner service as an"
89+
Write-Host " administrator (or LocalSystem) on this machine, or perform the"
90+
Write-Host " driver update manually via AMD Adrenalin on the box."
91+
throw "Insufficient privileges for action '$($env:ACTION)'"
92+
}
93+
94+
- name: Search and optionally install driver updates
95+
shell: powershell
96+
run: |
97+
$ErrorActionPreference = "Stop"
98+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
99+
$filter = $env:DRIVER_FILTER
100+
Write-Host "Driver title filter: '$filter'"
101+
102+
# Use the native Windows Update COM API (no third-party module).
103+
$session = New-Object -ComObject Microsoft.Update.Session
104+
$searcher = $session.CreateUpdateSearcher()
105+
106+
# Include the Microsoft Update service so optional third-party (AMD)
107+
# driver updates are visible, not just the default WU catalog.
108+
$muServiceId = "7971f918-a847-4430-9279-4a52d1efe18d"
109+
try {
110+
$mgr = New-Object -ComObject Microsoft.Update.ServiceManager
111+
$mgr.AddService2($muServiceId, 7, "") | Out-Null
112+
$searcher.ServerSelection = 3 # ssOthers
113+
$searcher.ServiceID = $muServiceId
114+
Write-Host "Querying Microsoft Update for driver-class updates..."
115+
} catch {
116+
Write-Host "[WARN] Could not register Microsoft Update service: $($_.Exception.Message)"
117+
Write-Host "Falling back to the default Windows Update catalog."
118+
}
119+
120+
$result = $searcher.Search("IsInstalled=0 and Type='Driver'")
121+
$all = @($result.Updates)
122+
Write-Host "Driver-class updates offered: $($all.Count)"
123+
foreach ($u in $all) { Write-Host " - $($u.Title)" }
124+
125+
$matched = @($all | Where-Object { $_.Title -match $filter })
126+
Write-Host "Updates matching filter '$filter': $($matched.Count)"
127+
foreach ($u in $matched) { Write-Host " * $($u.Title)" }
128+
129+
if ($matched.Count -eq 0) {
130+
Write-Host "No newer matching driver available -- nothing to install."
131+
exit 0
132+
}
133+
134+
if ($env:ACTION -eq "check-only") {
135+
Write-Host "check-only: a newer matching driver IS available but will NOT be installed."
136+
exit 0
137+
}
138+
139+
# Accept EULAs and collect into an update collection.
140+
$toInstall = New-Object -ComObject Microsoft.Update.UpdateColl
141+
foreach ($u in $matched) {
142+
if (-not $u.EulaAccepted) { $u.AcceptEula() }
143+
$toInstall.Add($u) | Out-Null
144+
}
145+
146+
Write-Host "Downloading $($toInstall.Count) driver update(s)..."
147+
$downloader = $session.CreateUpdateDownloader()
148+
$downloader.Updates = $toInstall
149+
$dl = $downloader.Download()
150+
Write-Host "Download result code: $($dl.ResultCode) (2 = succeeded)"
151+
if ($dl.ResultCode -ne 2) {
152+
throw "Driver download failed with result code $($dl.ResultCode)"
153+
}
154+
155+
Write-Host "Installing driver update(s)..."
156+
$installer = $session.CreateUpdateInstaller()
157+
$installer.Updates = $toInstall
158+
$inst = $installer.Install()
159+
Write-Host "Install result code: $($inst.ResultCode) (2 = succeeded)"
160+
Write-Host "Reboot required by installer: $($inst.RebootRequired)"
161+
if ($inst.ResultCode -ne 2) {
162+
throw "Driver install failed with result code $($inst.ResultCode)"
163+
}
164+
165+
Write-Host "=== Driver after install ==="
166+
Get-CimInstance Win32_VideoController |
167+
Select-Object Name, DriverVersion, DriverDate | Format-List
168+
169+
- name: Force restart runner
170+
if: ${{ inputs.action == 'update-and-reboot' }}
171+
shell: powershell
172+
run: |
173+
$ErrorActionPreference = "Stop"
174+
Write-Host "Scheduling forced restart in 30 seconds so this job can report first."
175+
Write-Host "The runner will reconnect after boot only if it runs as an auto-start service."
176+
# /r restart, /t delay, /f force-close apps, /c comment
177+
shutdown /r /t 30 /f /c "GAIA CI: AMD driver-update reboot"
178+
Write-Host "Restart scheduled."

0 commit comments

Comments
 (0)