openvibers/source.openvibe.games Commits
sh files
Changes
nnew file mode 100755@@@ -0,0 +1,168 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"++cd "$ROOT"++BRANCH="$(git rev-parse --abbrev-ref HEAD)"++WORKFLOW="windows-source-sdk-dlls.yml"++++if [[ -x tools/openvibe-gh-repo.sh ]]; then++ # shellcheck disable=SC1091++ REPO="$(tools/openvibe-gh-repo.sh)"++else++ REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"++fi++REPO="${REPO%.git}"++++echo "[openvibe] fix Windows MSBuild Python shim + rerun"++echo "[openvibe] root=$ROOT"++echo "[openvibe] branch=$BRANCH"++echo "[openvibe] repo=$REPO"++++# Keep setup-python in the workflow. This is still useful, but vcvars/msbuild can lose PATH,++# so the PowerShell build script will also create its own python.bat shim.++python3 - <<'PY'++from pathlib import Path++p = Path('.github/workflows/windows-source-sdk-dlls.yml')++if p.exists():++ s = p.read_text()++ if 'actions/setup-python@v5' not in s:++ marker = ' - name: Setup MSBuild\n'++ block = """ - name: Setup Python\n uses: actions/setup-python@v5\n with:\n python-version: '3.x'\n\n"""++ if marker in s:++ s = s.replace(marker, block + marker, 1)++ else:++ s += "\n" + block++ p.write_text(s)++PY++++python3 - <<'PY'++from pathlib import Path++p = Path('tools/build-sdk-windows.ps1')++s = p.read_text()++marker = 'OPENVIBE_PYTHON_SHIM_FOR_MSBUILD'++func = r'''++# OPENVIBE_PYTHON_SHIM_FOR_MSBUILD++function Ensure-PythonCommandForMSBuild {++ $shimDir = Join-Path $Root "artifacts/python-shim"++ New-Item -ItemType Directory -Force -Path $shimDir | Out-Null++ $log = Join-Path $LogDir "python-version.txt"++ "=== Ensure-PythonCommandForMSBuild ===" | Out-File $log++ "initial PATH=$env:PATH" | Out-File $log -Append++ "pythonLocation=$env:pythonLocation" | Out-File $log -Append++ "Python_ROOT_DIR=$env:Python_ROOT_DIR" | Out-File $log -Append++++ $candidates = New-Object System.Collections.Generic.List[string]++ foreach ($base in @($env:pythonLocation, $env:Python_ROOT_DIR, $env:Python3_ROOT_DIR)) {++ if ($base) {++ [void]$candidates.Add((Join-Path $base "python.exe"))++ [void]$candidates.Add((Join-Path $base "python3.exe"))++ }++ }++++ foreach ($name in @("python.exe", "python3.exe", "py.exe")) {++ $cmd = Get-Command $name -ErrorAction SilentlyContinue++ if ($cmd -and $cmd.Source) { [void]$candidates.Add($cmd.Source) }++ }++++ foreach ($rootCandidate in @("C:\hostedtoolcache\windows\Python", "C:\hostedtoolcache\windows\PyPy")) {++ if (Test-Path $rootCandidate) {++ Get-ChildItem -Path $rootCandidate -Recurse -File -Filter "python.exe" -ErrorAction SilentlyContinue |++ Sort-Object LastWriteTime -Descending |++ ForEach-Object { [void]$candidates.Add($_.FullName) }++ }++ }++++ $py = $null++ foreach ($cand in ($candidates | Select-Object -Unique)) {++ if ($cand -and (Test-Path $cand)) {++ $py = (Resolve-Path $cand).Path++ break++ }++ }++++ if (-not $py) {++ "candidate list:" | Out-File $log -Append++ $candidates | Out-File $log -Append++ throw "Python executable not found after setup-python/vcvars. Check python-version.txt in the debug artifact."++ }++++ $pyDir = Split-Path -Parent $py++ $bat = Join-Path $shimDir "python.bat"++ $cmdFile = Join-Path $shimDir "python.cmd"++ $batContent = @"++@echo off++"$py" %*++exit /b %ERRORLEVEL%++"@++ Set-Content -Encoding ascii -Path $bat -Value $batContent++ Set-Content -Encoding ascii -Path $cmdFile -Value $batContent++++ # Put the shim first so custom build steps that literally run `python` find it.++ $env:PATH = "$shimDir;$pyDir;$env:PATH"++ "selected python=$py" | Out-File $log -Append++ "shimDir=$shimDir" | Out-File $log -Append++ "updated PATH=$env:PATH" | Out-File $log -Append++ & $py --version 2>&1 | Tee-Object -FilePath $log -Append++ & cmd.exe /d /s /c "where python" 2>&1 | Tee-Object -FilePath $log -Append++}++'''++++if marker not in s:++ # Insert after Require function, before Find-VcVars64.++ needle = 'function Find-VcVars64 {'++ if needle not in s:++ raise SystemExit('Could not find Find-VcVars64 insertion point in tools/build-sdk-windows.ps1')++ s = s.replace(needle, func + '\n' + needle, 1)++++# Ensure it is called after tool where dumps in the x64 dev shell, before apply-openvibe-sdk/build.++call = 'Ensure-PythonCommandForMSBuild'++if call not in s.split('# Apply OpenVibe source files', 1)[0]:++ needle = 'where.exe lib | Out-File (Join-Path $LogDir "where-lib.txt")\n'++ if needle in s:++ s = s.replace(needle, needle + call + '\n', 1)++ else:++ needle2 = 'Say "cl.exe already available"\n'++ if needle2 not in s:++ raise SystemExit('Could not find tool setup insertion point in tools/build-sdk-windows.ps1')++ s = s.replace(needle2, needle2 + call + '\n', 1)++++p.write_text(s)++PY++++# Commit patch.++echo "[openvibe] git diff summary"++git diff --stat .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1 || true++++git add .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1++if git diff --cached --quiet; then++ echo "[openvibe] no code changes to commit"++else++ git commit -m "Make python available to Windows MSBuild custom steps"++fi++++echo "[openvibe] pushing $BRANCH"++git push origin "$BRANCH"++++# Trigger and watch/download using the repo helper if available.++if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then++ echo "[openvibe] triggering clean Windows DLL build"++ tools/trigger-windows-dll-build-clean.sh++else++ echo "[openvibe] triggering workflow"++ gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"++ sleep 8++ RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"++ echo "[openvibe] watching run $RUN_ID"++ gh run watch --repo "$REPO" "$RUN_ID" --exit-status || true++ if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then++ tools/windows-workflow-debug-and-install.sh || true++ fi++fi++++if [[ -x tools/verify-openvibe-dll-content.sh ]]; then++ echo "[openvibe] verify DLL content"++ tools/verify-openvibe-dll-content.sh || true++fi++++echo "[openvibe] done"nnew file mode 100755@@@ -0,0 +1,146 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"++cd "$ROOT"++BRANCH="$(git branch --show-current)"++REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"++++echo "[openvibe] fix Windows server vcxproj python custom build commands + rerun"++echo "[openvibe] root=$ROOT"++echo "[openvibe] branch=$BRANCH"++echo "[openvibe] repo=$REPO"++++python3 - <<'PY'++from pathlib import Path++p = Path('tools/build-sdk-windows.ps1')++s = p.read_text()++orig = s++++# 1) Ensure the robust MSBuild python shim is actually called. The previous patch++# defined Ensure-PythonCommandForMSBuild but only called Ensure-PythonOnPath.++needle = 'Ensure-PythonOnPath\n'++if needle in s and 'Ensure-PythonCommandForMSBuild\n' not in s.split(needle,1)[1][:120]:++ s = s.replace(needle, 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild\n', 1)++++# 2) Make Ensure-PythonCommandForMSBuild publish the selected shim path for later project-file patching.++needle2 = '$env:PATH = "$shimDir;$pyDir;$env:PATH"\n'++if needle2 in s and '$script:OpenVibePythonCommand' not in s[s.find(needle2):s.find(needle2)+260]:++ s = s.replace(needle2, needle2 + ' $script:OpenVibePythonCommand = $bat\n', 1)++++# 3) Add a generated vcxproj patcher. The Source SDK VPC emits custom build commands++# that literally start with `python ...`. On hosted Windows, MSBuild sometimes does++# not resolve batch shims through PATH for these custom steps. Rewriting generated++# project files to call the absolute shim is deterministic.++marker = 'function Patch-GeneratedPythonCustomBuildCommands'++func = r'''++function Patch-GeneratedPythonCustomBuildCommands {++ if (-not $script:OpenVibePythonCommand -or !(Test-Path $script:OpenVibePythonCommand)) {++ Ensure-PythonCommandForMSBuild++ }++++ $py = $script:OpenVibePythonCommand++ Say "patching generated vcxproj python custom build commands to $py"++ $patchLog = Join-Path $LogDir "python-vcxproj-patches.txt"++ "python command=$py" | Out-File $patchLog++++ $projects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*.vcxproj" -ErrorAction SilentlyContinue)++ foreach ($proj in $projects) {++ $text = Get-Content -Raw $proj.FullName++ $before = $text++++ # XML command bodies can contain python at line start, after XML tags, or after &&.++ # Replace only executable tokens, preserving surrounding whitespace/tags.++ $text = [regex]::Replace($text, '(?im)(^|[>`r`n])([ `t]*)python(\.exe)?([ `t]+)', {++ param($m)++ return $m.Groups[1].Value + $m.Groups[2].Value + '"' + $py + '"' + $m.Groups[4].Value++ })++ $text = [regex]::Replace($text, '(?i)(&&[ `t]*)python(\.exe)?([ `t]+)', {++ param($m)++ return $m.Groups[1].Value + '"' + $py + '"' + $m.Groups[3].Value++ })++ $text = [regex]::Replace($text, '(?i)(&&[ `t]*)python(\.exe)?([ `t]+)', {++ param($m)++ return $m.Groups[1].Value + '"' + $py + '"' + $m.Groups[3].Value++ })++++ if ($text -ne $before) {++ Set-Content -Encoding utf8 -Path $proj.FullName -Value $text++ "patched $($proj.FullName)" | Out-File $patchLog -Append++ }++ }++}++'''++if marker not in s:++ insert_after = 'function Invoke-MSBuildProject([System.IO.FileInfo]$proj, [string]$label) {'++ idx = s.find(insert_after)++ if idx == -1:++ raise SystemExit('Could not find Invoke-MSBuildProject insertion point')++ s = s[:idx] + func + '\n\n' + s[idx:]++++# 4) Call patcher after project generation/project discovery but before building dependencies/client/server.++call = 'Patch-GeneratedPythonCustomBuildCommands\n\nBuild-SourceSdkDependencyProjects'++if call not in s:++ s = s.replace('Build-SourceSdkDependencyProjects\n\n$beforeBuild = Get-Date', call + '\n\n$beforeBuild = Get-Date', 1)++++if s == orig:++ print('[openvibe] build-sdk-windows.ps1 already had python project patch')++else:++ p.write_text(s)++ print('[openvibe] patched tools/build-sdk-windows.ps1')++PY++++cat > docs/WINDOWS_SERVER_PYTHON_CUSTOM_BUILD.md <<'EOF_DOC'++# Windows server.vcxproj Python custom build fix++++Valve Source SDK's generated server project uses custom build commands that run++`python` while converting `.nut` script resources into generated headers. On++GitHub-hosted Windows runners, `actions/setup-python` may succeed while the VPC++emitted MSBuild custom step still cannot resolve the literal `python` command.++++The Windows build script now:++++1. creates a deterministic `python.bat`/`python.cmd` shim;++2. prepends it to PATH;++3. rewrites generated `.vcxproj` custom build command bodies so `python ...`++ uses the absolute shim path.++++This keeps the generated Source SDK tree disposable while making CI builds++reliable.++EOF_DOC++++echo "[openvibe] git diff summary"++git diff --stat++++git add tools/build-sdk-windows.ps1 docs/WINDOWS_SERVER_PYTHON_CUSTOM_BUILD.md++if git diff --cached --quiet; then++ echo "[openvibe] no changes to commit"++else++ git commit -m "Patch Windows server Python custom build commands"++fi++++echo "[openvibe] pushing $BRANCH"++git push origin "$BRANCH"++++if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then++ echo "[openvibe] triggering clean Windows DLL build"++ tools/trigger-windows-dll-build-clean.sh || true++else++ echo "[openvibe] triggering workflow via gh"++ gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"++ sleep 5++ RUN_ID="$(gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"++ echo "[openvibe] watching run $RUN_ID"++ gh run watch "$RUN_ID" --repo "$REPO" --exit-status || true++fi++++if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then++ echo "[openvibe] downloading diagnostics/artifacts"++ tools/windows-workflow-debug-and-install.sh || true++fi++++if [[ -x tools/verify-openvibe-dll-content.sh ]]; then++ tools/verify-openvibe-dll-content.sh || true++fi++++echo "[openvibe] done"nnew file mode 100755@@@ -0,0 +1,188 @@++#!/usr/bin/env bash++set -euo pipefail++++log(){ printf '[openvibe] %s\n' "$*"; }++warn(){ printf '[openvibe warn] %s\n' "$*" >&2; }++++ROOT="${OPENVIBE_ROOT:-$(pwd)}"++cd "$ROOT"++BRANCH="$(git branch --show-current)"++REPO="$(git config --get remote.origin.url | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"++: "${REPO:=OpenVibers/OpenVibe.Games}"++++log "fix Windows server build: QuickJS C++ header + vscript nut headers + real msbuild failures"++log "root=$ROOT"++log "branch=$BRANCH"++log "repo=$REPO"++++python3 - <<'PY'++from pathlib import Path++p = Path('tools/build-sdk-windows.ps1')++text = p.read_text()++++# 1) Make Invoke-MSBuildProject stop leaking Tee-Object output into the function return value.++old = '& msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log'++new = '& msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log | Out-Host'++if old in text:++ text = text.replace(old, new)++++# 2) Ensure the stronger python shim is actually called and exposes paths for other helpers.++if 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild' not in text and 'Ensure-PythonOnPath\r\nEnsure-PythonCommandForMSBuild' not in text:++ text = text.replace('Ensure-PythonOnPath\n', 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild\n')++++# Add script-scoped python vars inside Ensure-PythonCommandForMSBuild if not present.++if '$script:OpenVibePythonExe = $py' not in text:++ text = text.replace(' $pyDir = Split-Path -Parent $py\n', ' $pyDir = Split-Path -Parent $py\n $script:OpenVibePythonExe = $py\n')++if '$script:OpenVibePythonShim = $bat' not in text:++ text = text.replace(' Set-Content -Encoding ascii -Path $cmdFile -Value $batContent\n', ' Set-Content -Encoding ascii -Path $cmdFile -Value $batContent\n $script:OpenVibePythonShim = $bat\n')++++helper = r'''++++# OPENVIBE_FIX_QJS_CPP_AND_NUT_HEADERS++function Patch-QuickJsHeaderForMsvcCpp {++ $header = Join-Path $Sdk "src/game/shared/openvibe/third_party/quickjs/quickjs.h"++ if (!(Test-Path $header)) {++ Say "QuickJS header not present yet, skipping C++ compatibility patch: $header"++ return++ }++++ $log = Join-Path $LogDir "quickjs-cpp-header-patch.txt"++ "header=$header" | Out-File $log++ $q = Get-Content -Raw $header++ $orig = $q++++ $mkvalOld = '#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }'++ $mkvalNew = @'++#if defined(__cplusplus)++static inline JSValue JS_MKVAL_CPP(int tag, uint32_t val) { JSValue v; v.u.uint64 = (uint32_t)val; v.tag = tag; return v; }++#define JS_MKVAL(tag, val) JS_MKVAL_CPP((tag), (uint32_t)(val))++#else++#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }++#endif++'@++ $q = $q.Replace($mkvalOld, $mkvalNew)++++ $mkptrOld = '#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }'++ $mkptrNew = @'++#if defined(__cplusplus)++static inline JSValue JS_MKPTR_CPP(int tag, void *p) { JSValue v; v.u.ptr = p; v.tag = tag; return v; }++#define JS_MKPTR(tag, p) JS_MKPTR_CPP((tag), (void *)(p))++#else++#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }++#endif++'@++ $q = $q.Replace($mkptrOld, $mkptrNew)++++ $nanOld = '#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }'++ $nanNew = @'++#if defined(__cplusplus)++static inline JSValue JS_NAN_CPP(void) { JSValue v; v.u.float64 = JS_FLOAT64_NAN; v.tag = JS_TAG_FLOAT64; return v; }++#define JS_NAN JS_NAN_CPP()++#else++#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }++#endif++'@++ $q = $q.Replace($nanOld, $nanNew)++++ $q = $q.Replace(' JSCFunctionType ft = { .generic_magic = func };', ' JSCFunctionType ft; memset(&ft, 0, sizeof(ft)); ft.generic_magic = func;')++++ if ($q -ne $orig) {++ Set-Content -Encoding ascii -Path $header -Value $q++ "patched=1" | Out-File $log -Append++ Say "patched QuickJS header for MSVC C++ compound literal/designated initializer compatibility"++ } else {++ "patched=0" | Out-File $log -Append++ Say "QuickJS header C++ patch made no changes"++ }++}++++function Ensure-ServerNutHeaders {++ $serverDir = Join-Path $Src "game/server"++ $textToArray = Join-Path $Src "devtools/bin/texttoarray.py"++ $log = Join-Path $LogDir "server-nut-headers.txt"++ "serverDir=$serverDir" | Out-File $log++ "textToArray=$textToArray" | Out-File $log -Append++ "pythonExe=$script:OpenVibePythonExe" | Out-File $log -Append++ "pythonShim=$script:OpenVibePythonShim" | Out-File $log -Append++++ if (!(Test-Path $textToArray)) {++ throw "Missing Source SDK texttoarray.py at $textToArray"++ }++ if (-not $script:OpenVibePythonExe -or !(Test-Path $script:OpenVibePythonExe)) {++ throw "Python exe was not captured for nut header generation. Check python-version.txt."++ }++++ foreach ($name in @("spawn_helper", "vscript_server")) {++ $input = Join-Path $serverDir "$name.nut"++ $out = Join-Path $serverDir "${name}_nut.h"++ if (!(Test-Path $input)) {++ "missing input $input" | Out-File $log -Append++ continue++ }++ Say "generating $out from $input"++ & $script:OpenVibePythonExe $textToArray $input "g_Script_$name" | Set-Content -Encoding ascii -Path $out++ if ($LASTEXITCODE -ne 0) { throw "texttoarray.py failed for $input" }++ if (!(Test-Path $out)) { throw "Expected generated nut header was not created: $out" }++ "generated $out" | Out-File $log -Append++ }++}++'''++++if 'function Patch-QuickJsHeaderForMsvcCpp' not in text:++ marker = 'function Find-VcVars64 {'++ text = text.replace(marker, helper + '\n' + marker)++++# Call QuickJS C++ header patch right after QuickJS lib build and before Set-Location.++if 'Patch-QuickJsHeaderForMsvcCpp\n\nSet-Location $Src' not in text:++ text = text.replace('\nSet-Location $Src\n', '\nPatch-QuickJsHeaderForMsvcCpp\n\nSet-Location $Src\n')++++# Generate vscript headers after project generation/patches and before dependency/client/server builds.++if 'Ensure-ServerNutHeaders\nBuild-SourceSdkDependencyProjects' not in text:++ text = text.replace('Build-SourceSdkDependencyProjects\n', 'Ensure-ServerNutHeaders\nBuild-SourceSdkDependencyProjects\n')++++p.write_text(text)++PY++++cat > docs/WINDOWS_SERVER_QJS_CPP_NUT_HEADERS.md <<'EOF'++# Windows server build: QuickJS C++ and vscript nut headers++++The Windows HL2MP server build needs two extra fixes on hosted Actions runners:++++- The Source SDK custom build step for `vscript_server.nut` can report success without producing `vscript_server_nut.h`, so the build script pre-generates `spawn_helper_nut.h` and `vscript_server_nut.h` with `devtools/bin/texttoarray.py`.++- QuickJS is compiled as C with `clang-cl`, but its header is also included from C++ server files. The script patches the SDK copy of `quickjs.h` so MSVC C++ can compile QuickJS value macros without C compound literals/designated initializers.++++The patch is applied only to the generated SDK copy under `engine/source-sdk-2013`, not to the vendored source files.++EOF++++log "git diff summary"++git diff --stat++++git add tools/build-sdk-windows.ps1 docs/WINDOWS_SERVER_QJS_CPP_NUT_HEADERS.md++if git diff --cached --quiet; then++ log "nothing new to commit"++else++ git commit -m "Fix Windows server QuickJS C++ and nut headers"++fi++++log "pushing $BRANCH"++git push origin "$BRANCH"++++if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then++ log "triggering clean Windows DLL build"++ tools/trigger-windows-dll-build-clean.sh || true++elif [[ -x tools/gh-windows-build-and-install.sh ]]; then++ log "triggering Windows DLL build/install helper"++ tools/gh-windows-build-and-install.sh || true++else++ log "triggering workflow directly"++ gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"++ sleep 5++ run_id="$(gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"++ log "watching run $run_id"++ gh run watch "$run_id" --repo "$REPO" --exit-status || true++ if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then++ tools/windows-workflow-debug-and-install.sh "$run_id" || true++ fi++fi++++log "done"
new file mode 100755
new file mode 100755
@@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
+cd "$ROOT"
+BRANCH="$(git rev-parse --abbrev-ref HEAD)"
+WORKFLOW="windows-source-sdk-dlls.yml"
+
+if [[ -x tools/openvibe-gh-repo.sh ]]; then
+ # shellcheck disable=SC1091
+ REPO="$(tools/openvibe-gh-repo.sh)"
+else
+ REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
+fi
+REPO="${REPO%.git}"
+
+echo "[openvibe] fix Windows MSBuild Python shim + rerun"
+echo "[openvibe] root=$ROOT"
+echo "[openvibe] branch=$BRANCH"
+echo "[openvibe] repo=$REPO"
+
+# Keep setup-python in the workflow. This is still useful, but vcvars/msbuild can lose PATH,
+# so the PowerShell build script will also create its own python.bat shim.
+python3 - <<'PY'
+from pathlib import Path
+p = Path('.github/workflows/windows-source-sdk-dlls.yml')
+if p.exists():
+ s = p.read_text()
+ if 'actions/setup-python@v5' not in s:
+ marker = ' - name: Setup MSBuild\n'
+ block = """ - name: Setup Python\n uses: actions/setup-python@v5\n with:\n python-version: '3.x'\n\n"""
+ if marker in s:
+ s = s.replace(marker, block + marker, 1)
+ else:
+ s += "\n" + block
+ p.write_text(s)
+PY
+
+python3 - <<'PY'
+from pathlib import Path
+p = Path('tools/build-sdk-windows.ps1')
+s = p.read_text()
+marker = 'OPENVIBE_PYTHON_SHIM_FOR_MSBUILD'
+func = r'''
+# OPENVIBE_PYTHON_SHIM_FOR_MSBUILD
+function Ensure-PythonCommandForMSBuild {
+ $shimDir = Join-Path $Root "artifacts/python-shim"
+ New-Item -ItemType Directory -Force -Path $shimDir | Out-Null
+ $log = Join-Path $LogDir "python-version.txt"
+ "=== Ensure-PythonCommandForMSBuild ===" | Out-File $log
+ "initial PATH=$env:PATH" | Out-File $log -Append
+ "pythonLocation=$env:pythonLocation" | Out-File $log -Append
+ "Python_ROOT_DIR=$env:Python_ROOT_DIR" | Out-File $log -Append
+
+ $candidates = New-Object System.Collections.Generic.List[string]
+ foreach ($base in @($env:pythonLocation, $env:Python_ROOT_DIR, $env:Python3_ROOT_DIR)) {
+ if ($base) {
+ [void]$candidates.Add((Join-Path $base "python.exe"))
+ [void]$candidates.Add((Join-Path $base "python3.exe"))
+ }
+ }
+
+ foreach ($name in @("python.exe", "python3.exe", "py.exe")) {
+ $cmd = Get-Command $name -ErrorAction SilentlyContinue
+ if ($cmd -and $cmd.Source) { [void]$candidates.Add($cmd.Source) }
+ }
+
+ foreach ($rootCandidate in @("C:\hostedtoolcache\windows\Python", "C:\hostedtoolcache\windows\PyPy")) {
+ if (Test-Path $rootCandidate) {
+ Get-ChildItem -Path $rootCandidate -Recurse -File -Filter "python.exe" -ErrorAction SilentlyContinue |
+ Sort-Object LastWriteTime -Descending |
+ ForEach-Object { [void]$candidates.Add($_.FullName) }
+ }
+ }
+
+ $py = $null
+ foreach ($cand in ($candidates | Select-Object -Unique)) {
+ if ($cand -and (Test-Path $cand)) {
+ $py = (Resolve-Path $cand).Path
+ break
+ }
+ }
+
+ if (-not $py) {
+ "candidate list:" | Out-File $log -Append
+ $candidates | Out-File $log -Append
+ throw "Python executable not found after setup-python/vcvars. Check python-version.txt in the debug artifact."
+ }
+
+ $pyDir = Split-Path -Parent $py
+ $bat = Join-Path $shimDir "python.bat"
+ $cmdFile = Join-Path $shimDir "python.cmd"
+ $batContent = @"
+@echo off
+"$py" %*
+exit /b %ERRORLEVEL%
+"@
+ Set-Content -Encoding ascii -Path $bat -Value $batContent
+ Set-Content -Encoding ascii -Path $cmdFile -Value $batContent
+
+ # Put the shim first so custom build steps that literally run `python` find it.
+ $env:PATH = "$shimDir;$pyDir;$env:PATH"
+ "selected python=$py" | Out-File $log -Append
+ "shimDir=$shimDir" | Out-File $log -Append
+ "updated PATH=$env:PATH" | Out-File $log -Append
+ & $py --version 2>&1 | Tee-Object -FilePath $log -Append
+ & cmd.exe /d /s /c "where python" 2>&1 | Tee-Object -FilePath $log -Append
+}
+'''
+
+if marker not in s:
+ # Insert after Require function, before Find-VcVars64.
+ needle = 'function Find-VcVars64 {'
+ if needle not in s:
+ raise SystemExit('Could not find Find-VcVars64 insertion point in tools/build-sdk-windows.ps1')
+ s = s.replace(needle, func + '\n' + needle, 1)
+
+# Ensure it is called after tool where dumps in the x64 dev shell, before apply-openvibe-sdk/build.
+call = 'Ensure-PythonCommandForMSBuild'
+if call not in s.split('# Apply OpenVibe source files', 1)[0]:
+ needle = 'where.exe lib | Out-File (Join-Path $LogDir "where-lib.txt")\n'
+ if needle in s:
+ s = s.replace(needle, needle + call + '\n', 1)
+ else:
+ needle2 = 'Say "cl.exe already available"\n'
+ if needle2 not in s:
+ raise SystemExit('Could not find tool setup insertion point in tools/build-sdk-windows.ps1')
+ s = s.replace(needle2, needle2 + call + '\n', 1)
+
+p.write_text(s)
+PY
+
+# Commit patch.
+echo "[openvibe] git diff summary"
+git diff --stat .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1 || true
+
+git add .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1
+if git diff --cached --quiet; then
+ echo "[openvibe] no code changes to commit"
+else
+ git commit -m "Make python available to Windows MSBuild custom steps"
+fi
+
+echo "[openvibe] pushing $BRANCH"
+git push origin "$BRANCH"
+
+# Trigger and watch/download using the repo helper if available.
+if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
+ echo "[openvibe] triggering clean Windows DLL build"
+ tools/trigger-windows-dll-build-clean.sh
+else
+ echo "[openvibe] triggering workflow"
+ gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"
+ sleep 8
+ RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
+ echo "[openvibe] watching run $RUN_ID"
+ gh run watch --repo "$REPO" "$RUN_ID" --exit-status || true
+ if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then
+ tools/windows-workflow-debug-and-install.sh || true
+ fi
+fi
+
+if [[ -x tools/verify-openvibe-dll-content.sh ]]; then
+ echo "[openvibe] verify DLL content"
+ tools/verify-openvibe-dll-content.sh || true
+fi
+
+echo "[openvibe] done"
new file mode 100755
new file mode 100755
@@ -0,0 +1,146 @@
@@ -0,0 +1,146 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
+cd "$ROOT"
+BRANCH="$(git branch --show-current)"
+REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
+
+echo "[openvibe] fix Windows server vcxproj python custom build commands + rerun"
+echo "[openvibe] root=$ROOT"
+echo "[openvibe] branch=$BRANCH"
+echo "[openvibe] repo=$REPO"
+
+python3 - <<'PY'
+from pathlib import Path
+p = Path('tools/build-sdk-windows.ps1')
+s = p.read_text()
+orig = s
+
+# 1) Ensure the robust MSBuild python shim is actually called. The previous patch
+# defined Ensure-PythonCommandForMSBuild but only called Ensure-PythonOnPath.
+needle = 'Ensure-PythonOnPath\n'
+if needle in s and 'Ensure-PythonCommandForMSBuild\n' not in s.split(needle,1)[1][:120]:
+ s = s.replace(needle, 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild\n', 1)
+
+# 2) Make Ensure-PythonCommandForMSBuild publish the selected shim path for later project-file patching.
+needle2 = '$env:PATH = "$shimDir;$pyDir;$env:PATH"\n'
+if needle2 in s and '$script:OpenVibePythonCommand' not in s[s.find(needle2):s.find(needle2)+260]:
+ s = s.replace(needle2, needle2 + ' $script:OpenVibePythonCommand = $bat\n', 1)
+
+# 3) Add a generated vcxproj patcher. The Source SDK VPC emits custom build commands
+# that literally start with `python ...`. On hosted Windows, MSBuild sometimes does
+# not resolve batch shims through PATH for these custom steps. Rewriting generated
+# project files to call the absolute shim is deterministic.
+marker = 'function Patch-GeneratedPythonCustomBuildCommands'
+func = r'''
+function Patch-GeneratedPythonCustomBuildCommands {
+ if (-not $script:OpenVibePythonCommand -or !(Test-Path $script:OpenVibePythonCommand)) {
+ Ensure-PythonCommandForMSBuild
+ }
+
+ $py = $script:OpenVibePythonCommand
+ Say "patching generated vcxproj python custom build commands to $py"
+ $patchLog = Join-Path $LogDir "python-vcxproj-patches.txt"
+ "python command=$py" | Out-File $patchLog
+
+ $projects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*.vcxproj" -ErrorAction SilentlyContinue)
+ foreach ($proj in $projects) {
+ $text = Get-Content -Raw $proj.FullName
+ $before = $text
+
+ # XML command bodies can contain python at line start, after XML tags, or after &&.
+ # Replace only executable tokens, preserving surrounding whitespace/tags.
+ $text = [regex]::Replace($text, '(?im)(^|[>`r`n])([ `t]*)python(\.exe)?([ `t]+)', {
+ param($m)
+ return $m.Groups[1].Value + $m.Groups[2].Value + '"' + $py + '"' + $m.Groups[4].Value
+ })
+ $text = [regex]::Replace($text, '(?i)(&&[ `t]*)python(\.exe)?([ `t]+)', {
+ param($m)
+ return $m.Groups[1].Value + '"' + $py + '"' + $m.Groups[3].Value
+ })
+ $text = [regex]::Replace($text, '(?i)(&&[ `t]*)python(\.exe)?([ `t]+)', {
+ param($m)
+ return $m.Groups[1].Value + '"' + $py + '"' + $m.Groups[3].Value
+ })
+
+ if ($text -ne $before) {
+ Set-Content -Encoding utf8 -Path $proj.FullName -Value $text
+ "patched $($proj.FullName)" | Out-File $patchLog -Append
+ }
+ }
+}
+'''
+if marker not in s:
+ insert_after = 'function Invoke-MSBuildProject([System.IO.FileInfo]$proj, [string]$label) {'
+ idx = s.find(insert_after)
+ if idx == -1:
+ raise SystemExit('Could not find Invoke-MSBuildProject insertion point')
+ s = s[:idx] + func + '\n\n' + s[idx:]
+
+# 4) Call patcher after project generation/project discovery but before building dependencies/client/server.
+call = 'Patch-GeneratedPythonCustomBuildCommands\n\nBuild-SourceSdkDependencyProjects'
+if call not in s:
+ s = s.replace('Build-SourceSdkDependencyProjects\n\n$beforeBuild = Get-Date', call + '\n\n$beforeBuild = Get-Date', 1)
+
+if s == orig:
+ print('[openvibe] build-sdk-windows.ps1 already had python project patch')
+else:
+ p.write_text(s)
+ print('[openvibe] patched tools/build-sdk-windows.ps1')
+PY
+
+cat > docs/WINDOWS_SERVER_PYTHON_CUSTOM_BUILD.md <<'EOF_DOC'
+# Windows server.vcxproj Python custom build fix
+
+Valve Source SDK's generated server project uses custom build commands that run
+`python` while converting `.nut` script resources into generated headers. On
+GitHub-hosted Windows runners, `actions/setup-python` may succeed while the VPC
+emitted MSBuild custom step still cannot resolve the literal `python` command.
+
+The Windows build script now:
+
+1. creates a deterministic `python.bat`/`python.cmd` shim;
+2. prepends it to PATH;
+3. rewrites generated `.vcxproj` custom build command bodies so `python ...`
+ uses the absolute shim path.
+
+This keeps the generated Source SDK tree disposable while making CI builds
+reliable.
+EOF_DOC
+
+echo "[openvibe] git diff summary"
+git diff --stat
+
+git add tools/build-sdk-windows.ps1 docs/WINDOWS_SERVER_PYTHON_CUSTOM_BUILD.md
+if git diff --cached --quiet; then
+ echo "[openvibe] no changes to commit"
+else
+ git commit -m "Patch Windows server Python custom build commands"
+fi
+
+echo "[openvibe] pushing $BRANCH"
+git push origin "$BRANCH"
+
+if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
+ echo "[openvibe] triggering clean Windows DLL build"
+ tools/trigger-windows-dll-build-clean.sh || true
+else
+ echo "[openvibe] triggering workflow via gh"
+ gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"
+ sleep 5
+ RUN_ID="$(gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
+ echo "[openvibe] watching run $RUN_ID"
+ gh run watch "$RUN_ID" --repo "$REPO" --exit-status || true
+fi
+
+if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then
+ echo "[openvibe] downloading diagnostics/artifacts"
+ tools/windows-workflow-debug-and-install.sh || true
+fi
+
+if [[ -x tools/verify-openvibe-dll-content.sh ]]; then
+ tools/verify-openvibe-dll-content.sh || true
+fi
+
+echo "[openvibe] done"
new file mode 100755
new file mode 100755
@@ -0,0 +1,188 @@
@@ -0,0 +1,188 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+log(){ printf '[openvibe] %s\n' "$*"; }
+warn(){ printf '[openvibe warn] %s\n' "$*" >&2; }
+
+ROOT="${OPENVIBE_ROOT:-$(pwd)}"
+cd "$ROOT"
+BRANCH="$(git branch --show-current)"
+REPO="$(git config --get remote.origin.url | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
+: "${REPO:=OpenVibers/OpenVibe.Games}"
+
+log "fix Windows server build: QuickJS C++ header + vscript nut headers + real msbuild failures"
+log "root=$ROOT"
+log "branch=$BRANCH"
+log "repo=$REPO"
+
+python3 - <<'PY'
+from pathlib import Path
+p = Path('tools/build-sdk-windows.ps1')
+text = p.read_text()
+
+# 1) Make Invoke-MSBuildProject stop leaking Tee-Object output into the function return value.
+old = '& msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log'
+new = '& msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log | Out-Host'
+if old in text:
+ text = text.replace(old, new)
+
+# 2) Ensure the stronger python shim is actually called and exposes paths for other helpers.
+if 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild' not in text and 'Ensure-PythonOnPath\r\nEnsure-PythonCommandForMSBuild' not in text:
+ text = text.replace('Ensure-PythonOnPath\n', 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild\n')
+
+# Add script-scoped python vars inside Ensure-PythonCommandForMSBuild if not present.
+if '$script:OpenVibePythonExe = $py' not in text:
+ text = text.replace(' $pyDir = Split-Path -Parent $py\n', ' $pyDir = Split-Path -Parent $py\n $script:OpenVibePythonExe = $py\n')
+if '$script:OpenVibePythonShim = $bat' not in text:
+ text = text.replace(' Set-Content -Encoding ascii -Path $cmdFile -Value $batContent\n', ' Set-Content -Encoding ascii -Path $cmdFile -Value $batContent\n $script:OpenVibePythonShim = $bat\n')
+
+helper = r'''
+
+# OPENVIBE_FIX_QJS_CPP_AND_NUT_HEADERS
+function Patch-QuickJsHeaderForMsvcCpp {
+ $header = Join-Path $Sdk "src/game/shared/openvibe/third_party/quickjs/quickjs.h"
+ if (!(Test-Path $header)) {
+ Say "QuickJS header not present yet, skipping C++ compatibility patch: $header"
+ return
+ }
+
+ $log = Join-Path $LogDir "quickjs-cpp-header-patch.txt"
+ "header=$header" | Out-File $log
+ $q = Get-Content -Raw $header
+ $orig = $q
+
+ $mkvalOld = '#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }'
+ $mkvalNew = @'
+#if defined(__cplusplus)
+static inline JSValue JS_MKVAL_CPP(int tag, uint32_t val) { JSValue v; v.u.uint64 = (uint32_t)val; v.tag = tag; return v; }
+#define JS_MKVAL(tag, val) JS_MKVAL_CPP((tag), (uint32_t)(val))
+#else
+#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }
+#endif
+'@
+ $q = $q.Replace($mkvalOld, $mkvalNew)
+
+ $mkptrOld = '#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }'
+ $mkptrNew = @'
+#if defined(__cplusplus)
+static inline JSValue JS_MKPTR_CPP(int tag, void *p) { JSValue v; v.u.ptr = p; v.tag = tag; return v; }
+#define JS_MKPTR(tag, p) JS_MKPTR_CPP((tag), (void *)(p))
+#else
+#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
+#endif
+'@
+ $q = $q.Replace($mkptrOld, $mkptrNew)
+
+ $nanOld = '#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }'
+ $nanNew = @'
+#if defined(__cplusplus)
+static inline JSValue JS_NAN_CPP(void) { JSValue v; v.u.float64 = JS_FLOAT64_NAN; v.tag = JS_TAG_FLOAT64; return v; }
+#define JS_NAN JS_NAN_CPP()
+#else
+#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
+#endif
+'@
+ $q = $q.Replace($nanOld, $nanNew)
+
+ $q = $q.Replace(' JSCFunctionType ft = { .generic_magic = func };', ' JSCFunctionType ft; memset(&ft, 0, sizeof(ft)); ft.generic_magic = func;')
+
+ if ($q -ne $orig) {
+ Set-Content -Encoding ascii -Path $header -Value $q
+ "patched=1" | Out-File $log -Append
+ Say "patched QuickJS header for MSVC C++ compound literal/designated initializer compatibility"
+ } else {
+ "patched=0" | Out-File $log -Append
+ Say "QuickJS header C++ patch made no changes"
+ }
+}
+
+function Ensure-ServerNutHeaders {
+ $serverDir = Join-Path $Src "game/server"
+ $textToArray = Join-Path $Src "devtools/bin/texttoarray.py"
+ $log = Join-Path $LogDir "server-nut-headers.txt"
+ "serverDir=$serverDir" | Out-File $log
+ "textToArray=$textToArray" | Out-File $log -Append
+ "pythonExe=$script:OpenVibePythonExe" | Out-File $log -Append
+ "pythonShim=$script:OpenVibePythonShim" | Out-File $log -Append
+
+ if (!(Test-Path $textToArray)) {
+ throw "Missing Source SDK texttoarray.py at $textToArray"
+ }
+ if (-not $script:OpenVibePythonExe -or !(Test-Path $script:OpenVibePythonExe)) {
+ throw "Python exe was not captured for nut header generation. Check python-version.txt."
+ }
+
+ foreach ($name in @("spawn_helper", "vscript_server")) {
+ $input = Join-Path $serverDir "$name.nut"
+ $out = Join-Path $serverDir "${name}_nut.h"
+ if (!(Test-Path $input)) {
+ "missing input $input" | Out-File $log -Append
+ continue
+ }
+ Say "generating $out from $input"
+ & $script:OpenVibePythonExe $textToArray $input "g_Script_$name" | Set-Content -Encoding ascii -Path $out
+ if ($LASTEXITCODE -ne 0) { throw "texttoarray.py failed for $input" }
+ if (!(Test-Path $out)) { throw "Expected generated nut header was not created: $out" }
+ "generated $out" | Out-File $log -Append
+ }
+}
+'''
+
+if 'function Patch-QuickJsHeaderForMsvcCpp' not in text:
+ marker = 'function Find-VcVars64 {'
+ text = text.replace(marker, helper + '\n' + marker)
+
+# Call QuickJS C++ header patch right after QuickJS lib build and before Set-Location.
+if 'Patch-QuickJsHeaderForMsvcCpp\n\nSet-Location $Src' not in text:
+ text = text.replace('\nSet-Location $Src\n', '\nPatch-QuickJsHeaderForMsvcCpp\n\nSet-Location $Src\n')
+
+# Generate vscript headers after project generation/patches and before dependency/client/server builds.
+if 'Ensure-ServerNutHeaders\nBuild-SourceSdkDependencyProjects' not in text:
+ text = text.replace('Build-SourceSdkDependencyProjects\n', 'Ensure-ServerNutHeaders\nBuild-SourceSdkDependencyProjects\n')
+
+p.write_text(text)
+PY
+
+cat > docs/WINDOWS_SERVER_QJS_CPP_NUT_HEADERS.md <<'EOF'
+# Windows server build: QuickJS C++ and vscript nut headers
+
+The Windows HL2MP server build needs two extra fixes on hosted Actions runners:
+
+- The Source SDK custom build step for `vscript_server.nut` can report success without producing `vscript_server_nut.h`, so the build script pre-generates `spawn_helper_nut.h` and `vscript_server_nut.h` with `devtools/bin/texttoarray.py`.
+- QuickJS is compiled as C with `clang-cl`, but its header is also included from C++ server files. The script patches the SDK copy of `quickjs.h` so MSVC C++ can compile QuickJS value macros without C compound literals/designated initializers.
+
+The patch is applied only to the generated SDK copy under `engine/source-sdk-2013`, not to the vendored source files.
+EOF
+
+log "git diff summary"
+git diff --stat
+
+git add tools/build-sdk-windows.ps1 docs/WINDOWS_SERVER_QJS_CPP_NUT_HEADERS.md
+if git diff --cached --quiet; then
+ log "nothing new to commit"
+else
+ git commit -m "Fix Windows server QuickJS C++ and nut headers"
+fi
+
+log "pushing $BRANCH"
+git push origin "$BRANCH"
+
+if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
+ log "triggering clean Windows DLL build"
+ tools/trigger-windows-dll-build-clean.sh || true
+elif [[ -x tools/gh-windows-build-and-install.sh ]]; then
+ log "triggering Windows DLL build/install helper"
+ tools/gh-windows-build-and-install.sh || true
+else
+ log "triggering workflow directly"
+ gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"
+ sleep 5
+ run_id="$(gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
+ log "watching run $run_id"
+ gh run watch "$run_id" --repo "$REPO" --exit-status || true
+ if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then
+ tools/windows-workflow-debug-and-install.sh "$run_id" || true
+ fi
+fi
+
+log "done"