From c4d22cd4a3fbb62cd9c87c2a1883a0c84a6422e4 Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 11:20:19 -0700
Subject: [PATCH 01/12] perf(windows): optimize Windows CI build and bump
GitHub Actions versions
- Trim unused Qt6 modules in Windows CI to cut ~10-12 min download/extract time
- Cache Vulkan SDK in C:\VulkanSDK to skip redundant 800MB Chocolatey downloads
- Disable Windows Defender real-time scanning & add exclusions for build directories
- Enable sccache disk persistence via actions/cache@v6 for C:\.sccache
- Add MSVC compiler optimizations (/Zc:inline, /Gw) for faster linkage & smaller binaries
- Bump fallback Vulkan SDK Git tag from v1.4.304 to v1.4.354 in vulkan.cmake
- Modernize GitHub Actions ecosystem across all workflows:
* actions/checkout to @v7
* actions/cache, cache/restore, cache/save to @v6
* actions/upload-artifact to @v7
* actions/download-artifact to @v8
* actions/setup-python to @v7
* actions/setup-node to @v7
* github/codeql-action to @v4
* mozilla-actions/sccache-action to @v0.0.11
- Grant explicit actions: write permissions for cache persistence
Refs #44
Signed-off-by: Michael Oliver
---
.github/actions/build-windows/action.yml | 52 +++++++++++++++++++-----
.github/workflows/branch-build.yml | 30 +++++++-------
.github/workflows/codeql.yml | 6 +--
.github/workflows/dev-build.yml | 45 ++++++++++----------
.github/workflows/pr-checks.yml | 10 ++---
.github/workflows/publish-packages.yml | 4 +-
.github/workflows/release.yml | 49 +++++++++++-----------
cmake/defaults/cxx_msvc_defaults.cmake | 2 +
cmake/dependencies/vulkan.cmake | 6 +--
9 files changed, 120 insertions(+), 84 deletions(-)
diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml
index 88c46534..21d67e37 100644
--- a/.github/actions/build-windows/action.yml
+++ b/.github/actions/build-windows/action.yml
@@ -34,7 +34,7 @@ inputs:
default: 'd3dcompiler_47 opengl32sw qt3d qtactiveqt qtbase qtconnectivity qtdeclarative qtgraphicaleffects qtimageformats qtlocation qtmultimedia qtquickcontrols qtquickcontrols2 qtremoteobjects qtscxml qtsensors qtsvg qttools qttranslations qtwebchannel qtwebsockets qtwebview qtwinextras qtxmlpatterns'
windows_qt6_modules:
required: false
- default: 'debug_info qt3d qt5compat qtactiveqt qtcharts qtconnectivity qtdatavis3d qtgrpc qthttpserver qtimageformats qtlanguageserver qtlocation qtlottie qtmultimedia qtnetworkauth qtpdf qtpositioning qtquick3d qtquick3dphysics qtquickeffectmaker qtquicktimeline qtremoteobjects qtscxml qtsensors qtserialbus qtserialport qtshadertools qtspeech qtvirtualkeyboard qtwebchannel qtwebengine qtwebsockets qtwebview'
+ default: 'qt5compat qtimageformats qtmultimedia qtpdf qtshadertools'
windows_qt6_archives:
required: false
default: 'd3dcompiler_47 opengl32sw qtbase qtdeclarative qtsvg qttools qttranslations'
@@ -42,6 +42,12 @@ inputs:
runs:
using: "composite"
steps:
+ - name: Disable Windows Defender Realtime Monitoring for Build Trees
+ shell: powershell
+ run: |
+ Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction SilentlyContinue
+ Add-MpPreference -ExclusionPath "${{ github.workspace }}", "C:\.sccache", "C:\Program Files\OpenUTVDeps", "C:\VulkanSDK" -ErrorAction SilentlyContinue
+
- name: Download and Install Prebuilt Dependencies
shell: powershell
env:
@@ -191,7 +197,7 @@ runs:
shell: powershell
- name: Cache CMake for Windows
- uses: actions/cache@v5
+ uses: actions/cache@v6
id: windows-cmake-cache
with:
path: "C:/Program Files/CMake"
@@ -202,10 +208,21 @@ runs:
run: choco install cmake --version=${{ inputs.cmake-version }} --allow-downgrade --yes --no-progress
shell: powershell
- - name: Install build tools (Ninja, JOM, flex, bison, nasm, patch, pkg-config, Vulkan)
+ - name: Cache Vulkan SDK for Windows
+ uses: actions/cache@v6
+ id: windows-vulkan-cache
+ with:
+ path: "C:/VulkanSDK"
+ key: ${{ runner.os }}-vulkan-sdk-1.4.350
+
+ - name: Install build tools (Ninja, flex, bison, nasm, patch, pkg-config, Vulkan)
run: |
choco feature enable -n allowEmptyChecksums
- choco install ninja jom winflexbison3 nasm patch pkgconfiglite vulkan-sdk --yes --no-progress --allow-empty-checksums --ignore-checksums
+ $chocoPkgs = "ninja winflexbison3 nasm patch pkgconfiglite"
+ if ("${{ steps.windows-vulkan-cache.outputs.cache-hit }}" -ne "true") {
+ $chocoPkgs += " vulkan-sdk"
+ }
+ choco install $chocoPkgs --yes --no-progress --allow-empty-checksums --ignore-checksums
"C:\ProgramData\chocolatey\bin" | Add-Content -Path $env:GITHUB_PATH
$VulkanDir = Get-ChildItem -Path "C:\VulkanSDK" -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1
if ($VulkanDir) {
@@ -294,22 +311,27 @@ runs:
shell: powershell
- name: Setup sccache
- # Used for this run only (SCCACHE_GHA_ENABLED=false); not stored in Actions cache.
- # For sccache to actually cache the Windows/MSVC build, CMake must use it as the compiler
- # launcher, e.g. -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
- # in the Configure UTV step (and sccache must be on PATH when the build runs).
- uses: mozilla-actions/sccache-action@v0.0.10
+ uses: mozilla-actions/sccache-action@v0.0.11
with:
version: "v0.15.0"
- - name: Configure sccache for bash and powershell
+ - name: Configure sccache directory and cache
run: |
- # sccache is installed to cargo bin
if (Test-Path "C:\Users\runneradmin\.cargo\bin") {
"C:\Users\runneradmin\.cargo\bin" | Add-Content -Path $env:GITHUB_PATH
}
+ "SCCACHE_DIR=C:\.sccache" | Add-Content -Path $env:GITHUB_ENV
+ "SCCACHE_CACHE_SIZE=5G" | Add-Content -Path $env:GITHUB_ENV
shell: powershell
+ - name: Restore Sccache
+ uses: actions/cache/restore@v6
+ with:
+ path: "C:/.sccache"
+ key: ${{ runner.os }}-sccache-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-sccache-
+
- name: Configure UTV
run: |
$PrefixPaths = "C:/Qt/${{ inputs.qt-version }}/msvc2022_64"
@@ -455,3 +477,11 @@ runs:
}
shell: powershell
+ - name: Save Sccache
+ if: always()
+ uses: actions/cache/save@v6
+ with:
+ path: "C:/.sccache"
+ key: ${{ runner.os }}-sccache-${{ github.sha }}
+
+
diff --git a/.github/workflows/branch-build.yml b/.github/workflows/branch-build.yml
index 5c8a2fe2..fcf8b626 100644
--- a/.github/workflows/branch-build.yml
+++ b/.github/workflows/branch-build.yml
@@ -10,10 +10,12 @@ on:
- 'CMakeLists.txt'
- 'build.sh'
- '.github/actions/build-windows/**'
+ - '.github/workflows/**'
- 'requirements.txt'
permissions:
contents: read
+ actions: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
@@ -29,10 +31,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Setup Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v7
with:
python-version: '3.14'
@@ -49,14 +51,14 @@ jobs:
runs-on: macos-26
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -68,7 +70,7 @@ jobs:
shell: bash
- name: Restore Homebrew cache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: |
~/Library/Caches/Homebrew/downloads
@@ -87,7 +89,7 @@ jobs:
- name: Save Homebrew cache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: |
~/Library/Caches/Homebrew/downloads
@@ -95,7 +97,7 @@ jobs:
key: ${{ runner.os }}-brew-${{ steps.date.outputs.date }}-${{ hashFiles('**/build.sh') }}
- name: Restore CCache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -112,7 +114,7 @@ jobs:
- name: Save CCache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -129,7 +131,7 @@ jobs:
shell: bash
- name: Upload macOS Branch Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-macOS-arm64
path: _dist/*.zip
@@ -145,14 +147,14 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
steps:
- name: Check out repository code
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -174,7 +176,7 @@ jobs:
Compress-Archive -Path "utv-windows-x64" -DestinationPath "_dist\$ZipName" -CompressionLevel Optimal
- name: Upload Windows Branch Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-windows-x64
path: _dist/*.zip
@@ -188,7 +190,7 @@ jobs:
continue-on-error: true
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
@@ -213,7 +215,7 @@ jobs:
- name: Upload Linux Branch Artifact
if: success()
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-linux-x64
path: _dist/*.tar.gz
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 807c9723..0f656eda 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -15,12 +15,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Initialize CodeQL
- uses: github/codeql-action/init@v3
+ uses: github/codeql-action/init@v4
with:
languages: python
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
+ uses: github/codeql-action/analyze@v4
diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml
index 02ea6910..3f9de16a 100644
--- a/.github/workflows/dev-build.yml
+++ b/.github/workflows/dev-build.yml
@@ -14,6 +14,7 @@ on:
permissions:
contents: write
+ actions: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
@@ -29,10 +30,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Setup Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v7
with:
python-version: '3.14'
@@ -52,7 +53,7 @@ jobs:
short_sha: ${{ steps.ver.outputs.short_sha }}
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -74,14 +75,14 @@ jobs:
runs-on: macos-26
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -93,7 +94,7 @@ jobs:
shell: bash
- name: Restore Homebrew cache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: |
~/Library/Caches/Homebrew/downloads
@@ -112,7 +113,7 @@ jobs:
- name: Save Homebrew cache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: |
~/Library/Caches/Homebrew/downloads
@@ -120,7 +121,7 @@ jobs:
key: ${{ runner.os }}-brew-${{ steps.date.outputs.date }}-${{ hashFiles('**/build.sh') }}
- name: Restore CCache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -137,7 +138,7 @@ jobs:
- name: Save CCache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -151,7 +152,7 @@ jobs:
shell: bash
- name: Upload Unsigned App Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: utv-dev-unsigned-${{ needs.version.outputs.short_sha }}
path: _build/stage/unsigned/UTV.app.tar.gz
@@ -163,10 +164,10 @@ jobs:
runs-on: macos-26
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Download Unsigned App Artifact
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: utv-dev-unsigned-${{ needs.version.outputs.short_sha }}
path: _build/stage/unsigned
@@ -256,7 +257,7 @@ jobs:
shell: bash
- name: Upload Signed macOS Dev Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-dev-macOS-arm64
path: _build/stage/app/UTV-dev-macOS-arm64.zip
@@ -281,14 +282,14 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
steps:
- name: Check out repository code
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -309,7 +310,7 @@ jobs:
Compress-Archive -Path "utv-windows-x64" -DestinationPath "_dist\$ZipName" -CompressionLevel Optimal
- name: Upload Windows Dev Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-dev-windows-x64
path: _dist/UTV-dev-windows-x64.zip
@@ -322,7 +323,7 @@ jobs:
continue-on-error: true
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
@@ -345,7 +346,7 @@ jobs:
- name: Upload Linux Dev Artifact
if: success()
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-dev-linux-x64
path: _dist/UTV-dev-linux-x64.tar.gz
@@ -364,7 +365,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -373,21 +374,21 @@ jobs:
- name: Download macOS Dev Artifact
continue-on-error: true
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: UTV-dev-macOS-arm64
path: _dist
- name: Download Windows Dev Artifact
continue-on-error: true
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: UTV-dev-windows-x64
path: _dist
- name: Download Linux Dev Artifact
continue-on-error: true
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: UTV-dev-linux-x64
path: _dist
diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml
index ac30f120..c82f96ab 100644
--- a/.github/workflows/pr-checks.yml
+++ b/.github/workflows/pr-checks.yml
@@ -42,12 +42,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v7
with:
node-version: 20
@@ -63,10 +63,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Setup Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v7
with:
python-version: '3.14'
@@ -84,7 +84,7 @@ jobs:
continue-on-error: true
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml
index 9c62cfcb..67040472 100644
--- a/.github/workflows/publish-packages.yml
+++ b/.github/workflows/publish-packages.yml
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Download Release Assets
env:
@@ -152,7 +152,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Pack and Push to Chocolatey Community Repository
shell: powershell
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 07c6cffb..f8ea9ba2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,6 +27,7 @@ on:
permissions:
contents: write
pull-requests: read
+ actions: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
@@ -41,10 +42,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Setup Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v7
with:
python-version: '3.14'
@@ -63,7 +64,7 @@ jobs:
clean_version: ${{ steps.versioning.outputs.clean_version }}
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -99,14 +100,14 @@ jobs:
runs-on: macos-26
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -118,7 +119,7 @@ jobs:
shell: bash
- name: Restore Homebrew cache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: |
~/Library/Caches/Homebrew/downloads
@@ -137,7 +138,7 @@ jobs:
- name: Save Homebrew cache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: |
~/Library/Caches/Homebrew/downloads
@@ -145,7 +146,7 @@ jobs:
key: ${{ runner.os }}-brew-${{ steps.date.outputs.date }}-${{ hashFiles('**/build.sh') }}
- name: Restore CCache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -162,7 +163,7 @@ jobs:
- name: Save CCache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -176,7 +177,7 @@ jobs:
shell: bash
- name: Upload Unsigned App Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: utv-app-unsigned-${{ needs.version.outputs.version }}
path: _build/stage/unsigned/UTV.app.tar.gz
@@ -189,10 +190,10 @@ jobs:
runs-on: macos-26
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Download Unsigned App Artifact
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: utv-app-unsigned-${{ needs.version.outputs.version }}
path: _build/stage/unsigned
@@ -355,7 +356,7 @@ jobs:
shell: bash
- name: Upload Signed Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-${{ needs.version.outputs.version }}-macOS-arm64
path: _build/stage/app/UTV-${{ needs.version.outputs.version }}-macOS-arm64.zip
@@ -381,14 +382,14 @@ jobs:
SCCACHE_GHA_ENABLED: "true"
steps:
- name: Check out repository code
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -411,7 +412,7 @@ jobs:
Write-Host "Created _dist\$ZipName successfully."
- name: Upload Windows Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-${{ needs.version.outputs.version }}-windows-x64
path: _dist/UTV-${{ needs.version.outputs.version }}-windows-x64.zip
@@ -424,7 +425,7 @@ jobs:
runs-on: windows-2022
steps:
- name: Download Windows Artifact
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: UTV-${{ needs.version.outputs.version }}-windows-x64
path: _dist
@@ -445,7 +446,7 @@ jobs:
continue-on-error: true
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
@@ -473,7 +474,7 @@ jobs:
- name: Upload Linux Artifact
if: success()
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: UTV-${{ needs.version.outputs.version }}-linux-x64
path: _dist/UTV-${{ needs.version.outputs.version }}-linux-x64.tar.gz
@@ -493,7 +494,7 @@ jobs:
dnf config-manager --set-enabled crb || true
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
@@ -516,7 +517,7 @@ jobs:
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))
steps:
- name: Check out repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -525,21 +526,21 @@ jobs:
- name: Download macOS Artifact
continue-on-error: true
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: UTV-${{ needs.version.outputs.version }}-macOS-arm64
path: _dist
- name: Download Windows Artifact
continue-on-error: true
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: UTV-${{ needs.version.outputs.version }}-windows-x64
path: _dist
- name: Download Linux Artifact
continue-on-error: true
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v8
with:
name: UTV-${{ needs.version.outputs.version }}-linux-x64
path: _dist
diff --git a/cmake/defaults/cxx_msvc_defaults.cmake b/cmake/defaults/cxx_msvc_defaults.cmake
index 3d5b30e2..1d754a20 100644
--- a/cmake/defaults/cxx_msvc_defaults.cmake
+++ b/cmake/defaults/cxx_msvc_defaults.cmake
@@ -57,6 +57,8 @@ ADD_COMPILE_OPTIONS(
-utf-8
-Zc:forScope
-Zc:sizedDealloc-
+ -Zc:inline
+ -Gw
-Z7
)
diff --git a/cmake/dependencies/vulkan.cmake b/cmake/dependencies/vulkan.cmake
index 67aa410d..08cc0a3d 100644
--- a/cmake/dependencies/vulkan.cmake
+++ b/cmake/dependencies/vulkan.cmake
@@ -43,13 +43,13 @@ ELSE()
FETCHCONTENT_DECLARE(
vulkan_headers
GIT_REPOSITORY https://github.com/KhronosGroup/Vulkan-Headers.git
- GIT_TAG v1.4.304
+ GIT_TAG v1.4.354
GIT_SHALLOW TRUE
)
FETCHCONTENT_DECLARE(
vulkan_loader
GIT_REPOSITORY https://github.com/KhronosGroup/Vulkan-Loader.git
- GIT_TAG v1.4.304
+ GIT_TAG v1.4.354
GIT_SHALLOW TRUE
)
@@ -69,7 +69,7 @@ ELSE()
)
ENDIF()
SET(RV_DEPS_VULKAN_VERSION
- "1.4.304"
+ "1.4.354"
)
ENDIF()
From 003286fc21196e0e302ab1f37d46e5646c868452 Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 11:28:16 -0700
Subject: [PATCH 02/12] docs: update README build status workflow badge and add
tech stack & community flair
Signed-off-by: Michael Oliver
---
README.md | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index f55cbd8d..1057d163 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,18 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
From 4752167f3c1cff1a9989909b864de55dabd14b7d Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 11:35:03 -0700
Subject: [PATCH 03/12] feat(ci): add post-build Windows Defender scan on
staged application
Unexcludes the workspace and triggers an on-demand scan with MpCmdRun.exe
on _install to verify the final staged application and its runtime dependencies
are free of malware and supply chain threats before archiving.
Refs #44
Signed-off-by: Michael Oliver
---
.github/actions/build-windows/action.yml | 28 ++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml
index 21d67e37..15e71f65 100644
--- a/.github/actions/build-windows/action.yml
+++ b/.github/actions/build-windows/action.yml
@@ -469,6 +469,34 @@ runs:
}
shell: powershell
+ - name: Windows Defender Supply Chain Security Scan
+ shell: powershell
+ run: |
+ $installDir = Join-Path $env:GITHUB_WORKSPACE "_install"
+ if (Test-Path $installDir) {
+ Write-Host "Unexcluding staged application from Defender for security verification..."
+ Remove-MpPreference -ExclusionPath "${{ github.workspace }}" -ErrorAction SilentlyContinue
+
+ $mpCmd = "C:\Program Files\Windows Defender\MpCmdRun.exe"
+ if (Test-Path $mpCmd) {
+ Write-Host "Scanning staged application with Windows Defender: $installDir"
+ & $mpCmd -Scan -ScanType 3 -File "$installDir"
+ if ($LASTEXITCODE -eq 2) {
+ Write-Error "Windows Defender detected malware or malicious threat in staged application!"
+ exit 1
+ } elseif ($LASTEXITCODE -eq 0) {
+ Write-Host "Windows Defender scan clean. 0 threats detected."
+ } else {
+ Write-Warning "Windows Defender scan completed with exit code: $LASTEXITCODE"
+ }
+ } else {
+ Write-Host "Running PowerShell Start-MpScan on $installDir..."
+ Start-MpScan -ScanType CustomScan -ScanPath "$installDir"
+ }
+ } else {
+ Write-Warning "Directory _install not found for Defender scan."
+ }
+
- name: Sccache Statistics
if: always()
run: |
From af7063c228558841bd2f046826a7b920b14d5adc Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 11:41:47 -0700
Subject: [PATCH 04/12] chore(ci): bump actions in newly merged Linux jobs to
v7 and v6
Signed-off-by: Michael Oliver
---
.github/workflows/branch-build.yml | 10 +++++-----
.github/workflows/dev-build.yml | 10 +++++-----
.github/workflows/release.yml | 10 +++++-----
3 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/.github/workflows/branch-build.yml b/.github/workflows/branch-build.yml
index 7c4fb879..d5165112 100644
--- a/.github/workflows/branch-build.yml
+++ b/.github/workflows/branch-build.yml
@@ -195,7 +195,7 @@ jobs:
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -207,7 +207,7 @@ jobs:
shell: bash
- name: Restore Homebrew cache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: |
~/.cache/Homebrew/downloads
@@ -228,14 +228,14 @@ jobs:
- name: Save Homebrew cache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: |
~/.cache/Homebrew/downloads
key: ${{ runner.os }}-brew-${{ steps.date.outputs.date }}-${{ hashFiles('**/build.sh') }}
- name: Restore CCache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -252,7 +252,7 @@ jobs:
- name: Save CCache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml
index f555a4b2..a52edcc5 100644
--- a/.github/workflows/dev-build.yml
+++ b/.github/workflows/dev-build.yml
@@ -330,7 +330,7 @@ jobs:
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -342,7 +342,7 @@ jobs:
shell: bash
- name: Restore Homebrew cache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: |
~/.cache/Homebrew/downloads
@@ -363,14 +363,14 @@ jobs:
- name: Save Homebrew cache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: |
~/.cache/Homebrew/downloads
key: ${{ runner.os }}-brew-${{ steps.date.outputs.date }}-${{ hashFiles('**/build.sh') }}
- name: Restore CCache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -387,7 +387,7 @@ jobs:
- name: Save CCache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2cbbf13a..c6268017 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -453,7 +453,7 @@ jobs:
- name: Check out proprietary vendor SDKs
continue-on-error: true
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: OpenUTV/openutv-vendor-sdks
token: ${{ secrets.GH_TOKEN_DEPS_READ }}
@@ -465,7 +465,7 @@ jobs:
shell: bash
- name: Restore Homebrew cache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: |
~/.cache/Homebrew/downloads
@@ -486,14 +486,14 @@ jobs:
- name: Save Homebrew cache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: |
~/.cache/Homebrew/downloads
key: ${{ runner.os }}-brew-${{ steps.date.outputs.date }}-${{ hashFiles('**/build.sh') }}
- name: Restore CCache
- uses: actions/cache/restore@v5
+ uses: actions/cache/restore@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -510,7 +510,7 @@ jobs:
- name: Save CCache
if: always()
- uses: actions/cache/save@v5
+ uses: actions/cache/save@v6
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
From 48721f5ca5485a892f2812deff777b1b41f2372d Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 11:44:06 -0700
Subject: [PATCH 05/12] chore(scripts): remove emojis from changelog section
headings
Signed-off-by: Michael Oliver
---
scripts/generate-changelog.sh | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/scripts/generate-changelog.sh b/scripts/generate-changelog.sh
index bf4357ea..ad409032 100755
--- a/scripts/generate-changelog.sh
+++ b/scripts/generate-changelog.sh
@@ -32,21 +32,21 @@ format_section() {
fi
}
-format_section "โจ Features" "feat(\([^)]+\))?:"
-format_section "๐ Bug Fixes" "fix(\([^)]+\))?:"
-format_section "โก Performance" "perf(\([^)]+\))?:"
-format_section "โป๏ธ Refactoring" "refactor(\([^)]+\))?:"
-format_section "๐จ Code Style" "style(\([^)]+\))?:"
-format_section "๐๏ธ Build & Dependencies" "build(\([^)]+\))?:"
-format_section "๐ง CI/CD" "ci(\([^)]+\))?:"
-format_section "๐ Documentation" "docs(\([^)]+\))?:"
-format_section "๐งช Tests" "test(\([^)]+\))?:"
-format_section "๐ฆ Chores" "chore(\([^)]+\))?:"
+format_section "Features" "feat(\([^)]+\))?:"
+format_section "Bug Fixes" "fix(\([^)]+\))?:"
+format_section "Performance Improvements" "perf(\([^)]+\))?:"
+format_section "Refactoring" "refactor(\([^)]+\))?:"
+format_section "Code Style" "style(\([^)]+\))?:"
+format_section "Build & Dependencies" "build(\([^)]+\))?:"
+format_section "CI/CD" "ci(\([^)]+\))?:"
+format_section "Documentation" "docs(\([^)]+\))?:"
+format_section "Tests" "test(\([^)]+\))?:"
+format_section "Chores" "chore(\([^)]+\))?:"
# Check for non-conventional or unclassified commits
OTHER=$(git log --pretty=format:"* %s (%h)" "$RANGE" | grep -v -E "^\* (feat|fix|perf|refactor|style|build|ci|docs|test|chore)(\([^)]+\))?:" || true)
if [ -n "$OTHER" ]; then
- echo "### ๐ชต Other Changes"
+ echo "### Other Changes"
echo "$OTHER"
echo ""
fi
From a241649086df5e2dfb3af2a07ad513a54e927474 Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 11:50:51 -0700
Subject: [PATCH 06/12] fix(ci): separate chocolatey package installation
commands in Windows CI
Signed-off-by: Michael Oliver
---
.github/actions/build-windows/action.yml | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml
index 15e71f65..409612ac 100644
--- a/.github/actions/build-windows/action.yml
+++ b/.github/actions/build-windows/action.yml
@@ -218,11 +218,10 @@ runs:
- name: Install build tools (Ninja, flex, bison, nasm, patch, pkg-config, Vulkan)
run: |
choco feature enable -n allowEmptyChecksums
- $chocoPkgs = "ninja winflexbison3 nasm patch pkgconfiglite"
+ choco install ninja winflexbison3 nasm patch pkgconfiglite --yes --no-progress --allow-empty-checksums --ignore-checksums
if ("${{ steps.windows-vulkan-cache.outputs.cache-hit }}" -ne "true") {
- $chocoPkgs += " vulkan-sdk"
+ choco install vulkan-sdk --yes --no-progress --allow-empty-checksums --ignore-checksums
}
- choco install $chocoPkgs --yes --no-progress --allow-empty-checksums --ignore-checksums
"C:\ProgramData\chocolatey\bin" | Add-Content -Path $env:GITHUB_PATH
$VulkanDir = Get-ChildItem -Path "C:\VulkanSDK" -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1
if ($VulkanDir) {
From 6b1ac7426849cdaaa2cb75f33e94cf22e0875f63 Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 12:17:11 -0700
Subject: [PATCH 07/12] fix(ci): use native 7-Zip in Qt installer, add
qtwebengine, and revert Vulkan tag to v1.4.304
- In cmake/dependencies/vulkan.cmake, revert fallback Vulkan tag to v1.4.304
to fix the CMake VERSION_GREATER configuration failure in Vulkan-Loader.
- In .github/actions/build-windows/action.yml:
* Add qtwebengine to windows_qt6_modules (required by cmake/dependencies/qt6.cmake)
and remove unused qt5compat.
* Ensure 7-Zip is added to PATH via PowerShell.
* Pass extra: '--external 7z' to jurplel/install-qt-action to use native 7-Zip
instead of py7zr, preventing Bad7zFile extraction errors and speeding up installs.
Refs #44
Signed-off-by: Michael Oliver
---
.github/actions/build-windows/action.yml | 14 +++++++-------
cmake/dependencies/vulkan.cmake | 6 +++---
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml
index 409612ac..675b0df7 100644
--- a/.github/actions/build-windows/action.yml
+++ b/.github/actions/build-windows/action.yml
@@ -34,7 +34,7 @@ inputs:
default: 'd3dcompiler_47 opengl32sw qt3d qtactiveqt qtbase qtconnectivity qtdeclarative qtgraphicaleffects qtimageformats qtlocation qtmultimedia qtquickcontrols qtquickcontrols2 qtremoteobjects qtscxml qtsensors qtsvg qttools qttranslations qtwebchannel qtwebsockets qtwebview qtwinextras qtxmlpatterns'
windows_qt6_modules:
required: false
- default: 'qt5compat qtimageformats qtmultimedia qtpdf qtshadertools'
+ default: 'qtimageformats qtmultimedia qtpdf qtshadertools qtwebengine'
windows_qt6_archives:
required: false
default: 'd3dcompiler_47 opengl32sw qtbase qtdeclarative qtsvg qttools qttranslations'
@@ -248,13 +248,12 @@ runs:
shell: bash
- name: Prepare Qt folder and 7-Zip
+ shell: powershell
run: |
- mkdir -p /c/qt
- if [ -d "/c/Program Files/7-Zip" ]; then
- echo "/c/Program Files/7-Zip" >> $GITHUB_PATH
- echo "C:\Program Files\7-Zip" >> $GITHUB_PATH
- fi
- shell: bash
+ New-Item -ItemType Directory -Force -Path "C:\Qt" | Out-Null
+ if (Test-Path "C:\Program Files\7-Zip") {
+ "C:\Program Files\7-Zip" | Add-Content -Path $env:GITHUB_PATH
+ }
- name: Install Qt ${{ inputs.qt-version }}
uses: jurplel/install-qt-action@v4
@@ -271,6 +270,7 @@ runs:
setup-python: 'true'
set-env: 'true'
tools-only: 'false'
+ extra: '--external 7z'
aqtsource: 'git+https://github.com/miurahr/aqtinstall.git@master'
- name: Activate Bundled Python
diff --git a/cmake/dependencies/vulkan.cmake b/cmake/dependencies/vulkan.cmake
index 08cc0a3d..67aa410d 100644
--- a/cmake/dependencies/vulkan.cmake
+++ b/cmake/dependencies/vulkan.cmake
@@ -43,13 +43,13 @@ ELSE()
FETCHCONTENT_DECLARE(
vulkan_headers
GIT_REPOSITORY https://github.com/KhronosGroup/Vulkan-Headers.git
- GIT_TAG v1.4.354
+ GIT_TAG v1.4.304
GIT_SHALLOW TRUE
)
FETCHCONTENT_DECLARE(
vulkan_loader
GIT_REPOSITORY https://github.com/KhronosGroup/Vulkan-Loader.git
- GIT_TAG v1.4.354
+ GIT_TAG v1.4.304
GIT_SHALLOW TRUE
)
@@ -69,7 +69,7 @@ ELSE()
)
ENDIF()
SET(RV_DEPS_VULKAN_VERSION
- "1.4.354"
+ "1.4.304"
)
ENDIF()
From b79fb4c2dbc2aa4c82fe75235de448ba74671daa Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 12:38:14 -0700
Subject: [PATCH 08/12] feat(deps): add precompiled Vulkan to Linux
dependencies and add WebEngine submodules to Windows Qt
Signed-off-by: Michael Oliver
---
.github/actions/build-windows/action.yml | 2 +-
build.sh | 5 +++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml
index 675b0df7..29c5f39c 100644
--- a/.github/actions/build-windows/action.yml
+++ b/.github/actions/build-windows/action.yml
@@ -34,7 +34,7 @@ inputs:
default: 'd3dcompiler_47 opengl32sw qt3d qtactiveqt qtbase qtconnectivity qtdeclarative qtgraphicaleffects qtimageformats qtlocation qtmultimedia qtquickcontrols qtquickcontrols2 qtremoteobjects qtscxml qtsensors qtsvg qttools qttranslations qtwebchannel qtwebsockets qtwebview qtwinextras qtxmlpatterns'
windows_qt6_modules:
required: false
- default: 'qtimageformats qtmultimedia qtpdf qtshadertools qtwebengine'
+ default: 'qtimageformats qtmultimedia qtpdf qtpositioning qtshadertools qtwebchannel qtwebengine'
windows_qt6_archives:
required: false
default: 'd3dcompiler_47 opengl32sw qtbase qtdeclarative qtsvg qttools qttranslations'
diff --git a/build.sh b/build.sh
index 2b433d85..86c82885 100755
--- a/build.sh
+++ b/build.sh
@@ -122,12 +122,13 @@ if [ "${INSTALL_DEPS}" -eq 1 ]; then
libx11-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev \
libxrandr-dev libxrender-dev libxcomposite-dev libxdamage-dev libxtst-dev libxxf86vm-dev \
libxkbcommon-dev libxkbcommon-x11-dev libffi-dev \
- libasound2-dev libpulse-dev
+ libasound2-dev libpulse-dev libvulkan-dev
fi
brew install --formula \
ninja pkg-config ccache glew doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost \
- openimageio openjpeg webp yaml-cpp spdlog openjph jpeg-turbo
+ openimageio openjpeg webp yaml-cpp spdlog openjph jpeg-turbo \
+ vulkan-headers vulkan-loader
# RHEL / Rocky Setup
elif command -v dnf >/dev/null 2>&1; then
$SUDO dnf install -y epel-release dnf-plugins-core
From c0cd25ed4741548fa1f2f724fe5d7e1e7a48ec7e Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 12:49:06 -0700
Subject: [PATCH 09/12] fix(ci): remove --external 7z to avoid Windows parallel
extraction collision in aqtinstall
Signed-off-by: Michael Oliver
---
.github/actions/build-windows/action.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml
index 29c5f39c..39b6a7be 100644
--- a/.github/actions/build-windows/action.yml
+++ b/.github/actions/build-windows/action.yml
@@ -270,7 +270,6 @@ runs:
setup-python: 'true'
set-env: 'true'
tools-only: 'false'
- extra: '--external 7z'
aqtsource: 'git+https://github.com/miurahr/aqtinstall.git@master'
- name: Activate Bundled Python
From dfcdab5627aceb3069afbe61ad4d2ffe3558beba Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 13:14:13 -0700
Subject: [PATCH 10/12] fix(build): enforce generic dynamic library linking
across macOS and Linux
Signed-off-by: Michael Oliver
---
.github/workflows/branch-build.yml | 1 +
.github/workflows/dev-build.yml | 1 +
.github/workflows/release.yml | 1 +
build.sh | 12 +++
src/build/sanitize_homebrew_links.py | 147 ++++++++++++++++++++++-----
5 files changed, 134 insertions(+), 28 deletions(-)
diff --git a/.github/workflows/branch-build.yml b/.github/workflows/branch-build.yml
index d5165112..29406691 100644
--- a/.github/workflows/branch-build.yml
+++ b/.github/workflows/branch-build.yml
@@ -84,6 +84,7 @@ jobs:
brew install --formula \
ninja readline sqlite3 xz zlib tcl-tk@8 python-tk autoconf automake libtool python@3.14 yasm clang-format black meson nasm pkg-config glew ccache doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost openimageio openjpeg webp yaml-cpp spdlog icu4c openjph jpeg-turbo
+ brew upgrade openexr imath opencolorio libraw libtiff boost openimageio spdlog openjph || true
shell: bash
- name: Save Homebrew cache
diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml
index a52edcc5..9fc28c34 100644
--- a/.github/workflows/dev-build.yml
+++ b/.github/workflows/dev-build.yml
@@ -109,6 +109,7 @@ jobs:
brew install --formula \
ninja readline sqlite3 xz zlib tcl-tk@8 python-tk autoconf automake libtool python@3.14 yasm clang-format black meson nasm pkg-config glew ccache doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost openimageio openjpeg webp yaml-cpp spdlog icu4c openjph jpeg-turbo
+ brew upgrade openexr imath opencolorio libraw libtiff boost openimageio spdlog openjph || true
shell: bash
- name: Save Homebrew cache
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c6268017..720abc5d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -134,6 +134,7 @@ jobs:
brew install --formula \
ninja readline sqlite3 xz zlib tcl-tk@8 python-tk autoconf automake libtool python@3.14 yasm clang-format black meson nasm pkg-config glew ccache doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost openimageio openjpeg webp yaml-cpp spdlog icu4c openjph jpeg-turbo
+ brew upgrade openexr imath opencolorio libraw libtiff boost openimageio spdlog openjph || true
shell: bash
- name: Save Homebrew cache
diff --git a/build.sh b/build.sh
index 86c82885..d0732c07 100755
--- a/build.sh
+++ b/build.sh
@@ -459,6 +459,12 @@ fi
if [[ "$OSTYPE" == "darwin"* ]]; then
echo "--- Sanitizing Homebrew Links ---"
python3 "${PROJECT_ROOT}/src/build/sanitize_homebrew_links.py" "${BUILD_DIR}/stage"
+ if [ -d "${BUILD_DIR}/stage/app/UTV.app" ]; then
+ codesign --force --deep --sign - "${BUILD_DIR}/stage/app/UTV.app" 2>/dev/null || true
+ fi
+elif [[ "$OSTYPE" == "linux"* ]]; then
+ echo "--- Sanitizing Dynamic Library Links ---"
+ python3 "${PROJECT_ROOT}/src/build/sanitize_homebrew_links.py" "${BUILD_DIR}/stage"
fi
if [ "${INSTALL}" -eq 1 ]; then
@@ -468,6 +474,12 @@ if [ "${INSTALL}" -eq 1 ]; then
if [[ "$OSTYPE" == "darwin"* ]]; then
echo "--- Sanitizing Installed Homebrew Links ---"
python3 "${PROJECT_ROOT}/src/build/sanitize_homebrew_links.py" "${INST_DIR}"
+ if [ -d "${INST_DIR}/UTV.app" ]; then
+ codesign --force --deep --sign - "${INST_DIR}/UTV.app" 2>/dev/null || true
+ fi
+ elif [[ "$OSTYPE" == "linux"* ]]; then
+ echo "--- Sanitizing Installed Dynamic Library Links ---"
+ python3 "${PROJECT_ROOT}/src/build/sanitize_homebrew_links.py" "${INST_DIR}"
fi
fi
diff --git a/src/build/sanitize_homebrew_links.py b/src/build/sanitize_homebrew_links.py
index 1f0ca816..6a1bae6c 100644
--- a/src/build/sanitize_homebrew_links.py
+++ b/src/build/sanitize_homebrew_links.py
@@ -1,7 +1,13 @@
#!/usr/bin/env python3
#
-# Sanitize Homebrew Links for UTV
-# Converts /opt/homebrew/Cellar/... links to /opt/homebrew/opt/...
+# Sanitize Dynamic Library Links for UTV
+# Ensures generic unversioned dynamic library links across packages:
+# - macOS: Converts /opt/homebrew/Cellar/... links to /opt/homebrew/opt/...
+# and versioned dylibs (e.g. libOpenEXR-3_4.33.dylib, libImath-3_2.30.dylib)
+# to unversioned symlinks (e.g. libOpenEXR.dylib, libImath.dylib) via install_name_tool.
+# - Linux: Converts versioned SONAMEs (e.g. libOpenEXR-3_4.so.33) to
+# unversioned names (libOpenEXR.so) via patchelf if available.
+#
# Copyright (C) 2026 Makai Systems. All Rights Reserved.
#
@@ -11,14 +17,30 @@
import pathlib
import re
-BREW_PREFIX = os.environ.get("HOMEBREW_PREFIX", "/opt/homebrew" if os.uname().machine == "arm64" else "/usr/local")
+DEFAULT_BREW_PREFIX = (
+ "/opt/homebrew"
+ if (sys.platform == "darwin" and os.uname().machine == "arm64")
+ else ("/home/linuxbrew/.linuxbrew" if sys.platform.startswith("linux") else "/usr/local")
+)
+BREW_PREFIX = os.environ.get("HOMEBREW_PREFIX", DEFAULT_BREW_PREFIX)
CELLAR_PATTERN = re.compile(rf"{re.escape(BREW_PREFIX)}/Cellar/([^/]+)/[^/]+/(.+)")
+# System libraries that should never be renamed on Linux
+LINUX_SYSTEM_LIBS = {
+ "libc.so",
+ "libm.so",
+ "libdl.so",
+ "libpthread.so",
+ "librt.so",
+ "libstdc++.so",
+ "libgcc_s.so",
+ "ld-linux",
+}
+
-def get_dependencies(binary_path):
+def get_dependencies_macho(binary_path):
try:
output = subprocess.check_output(["otool", "-L", binary_path], text=True)
- # Skip first line (the binary itself)
deps = []
for line in output.splitlines()[1:]:
parts = line.strip().split()
@@ -29,9 +51,8 @@ def get_dependencies(binary_path):
return []
-def sanitize_binary(binary_path):
- print(f"--- Sanitizing: {os.path.basename(binary_path)}")
- deps = get_dependencies(binary_path)
+def sanitize_macho_binary(binary_path):
+ deps = get_dependencies_macho(binary_path)
changed = False
for dep in deps:
@@ -44,30 +65,89 @@ def sanitize_binary(binary_path):
remaining_path = match.group(2)
stable_path = f"{BREW_PREFIX}/opt/{pkg_name}/{remaining_path}"
- # Verify stable path exists
if os.path.exists(stable_path):
- print(f" Mapping Cellar link: {current_dep} -> {stable_path}")
+ print(f" [{os.path.basename(binary_path)}] Mapping Cellar link: {current_dep} -> {stable_path}")
try:
subprocess.check_call(["install_name_tool", "-change", current_dep, stable_path, binary_path])
changed = True
current_dep = stable_path
except subprocess.CalledProcessError as e:
- print(f" Error changing link: {e}")
+ print(f" [{os.path.basename(binary_path)}] Error changing Cellar link: {e}")
else:
- print(f" Warning: Stable path {stable_path} not found for {current_dep}")
-
- # 2. Map versioned OpenJPH dylib (e.g. libopenjph.0.31.dylib) to unversioned symlink (libopenjph.dylib)
- # This ensures OpenUTV remains compatible across Homebrew OpenJPH minor version updates
- if "openjph" in current_dep and re.search(r"libopenjph\.[0-9.]+\.dylib", current_dep):
- unversioned_jph = f"{BREW_PREFIX}/opt/openjph/lib/libopenjph.dylib"
- if os.path.exists(unversioned_jph):
- print(f" Mapping OpenJPH versioned link: {current_dep} -> {unversioned_jph}")
- try:
- subprocess.check_call(["install_name_tool", "-change", current_dep, unversioned_jph, binary_path])
- changed = True
- current_dep = unversioned_jph
- except subprocess.CalledProcessError as e:
- print(f" Error changing OpenJPH link: {e}")
+ # If stable path does not exist as-is, check if remaining_path has versioned dylib
+ base = os.path.basename(remaining_path)
+ generic_base = re.sub(r"(-[0-9_]+|\.[0-9]+).*\.dylib$", ".dylib", base)
+ alt_path = f"{BREW_PREFIX}/opt/{pkg_name}/lib/{generic_base}"
+ if os.path.exists(alt_path):
+ print(f" [{os.path.basename(binary_path)}] Mapping Cellar link: {current_dep} -> {alt_path}")
+ try:
+ subprocess.check_call(["install_name_tool", "-change", current_dep, alt_path, binary_path])
+ changed = True
+ current_dep = alt_path
+ except subprocess.CalledProcessError as e:
+ print(f" [{os.path.basename(binary_path)}] Error changing Cellar link: {e}")
+
+ # 2. Map versioned dylibs (e.g. libOpenEXR-3_4.33.dylib, libImath-3_2.30.dylib, libopenjph.0.31.dylib)
+ # to generic unversioned symlinks (e.g. libOpenEXR.dylib, libImath.dylib, libopenjph.dylib).
+ # This ensures OpenUTV remains compatible across package manager minor updates.
+ if current_dep.startswith(BREW_PREFIX) or "/opt/" in current_dep or "/Cellar/" in current_dep:
+ dir_name = os.path.dirname(current_dep)
+ base_name = os.path.basename(current_dep)
+ generic_name = re.sub(r"(-[0-9_]+|\.[0-9]+).*\.dylib$", ".dylib", base_name)
+
+ if generic_name != base_name:
+ generic_path = os.path.join(dir_name, generic_name)
+ # If dir_name was inside a Cellar version folder, redirect to opt
+ if "/Cellar/" in generic_path:
+ generic_path = re.sub(
+ rf"{re.escape(BREW_PREFIX)}/Cellar/([^/]+)/[^/]+/",
+ rf"{BREW_PREFIX}/opt/\1/",
+ generic_path,
+ )
+
+ if os.path.exists(generic_path) or os.path.exists(os.path.dirname(generic_path)):
+ print(
+ f" [{os.path.basename(binary_path)}] Mapping to generic dylib link: {current_dep} -> {generic_path}"
+ )
+ try:
+ subprocess.check_call(["install_name_tool", "-change", current_dep, generic_path, binary_path])
+ changed = True
+ current_dep = generic_path
+ except subprocess.CalledProcessError as e:
+ print(f" [{os.path.basename(binary_path)}] Error changing generic link: {e}")
+
+ return changed
+
+
+def sanitize_elf_binary(binary_path):
+ if not hasattr(sanitize_elf_binary, "patchelf_available"):
+ sanitize_elf_binary.patchelf_available = (
+ subprocess.call(["which", "patchelf"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
+ )
+
+ if not sanitize_elf_binary.patchelf_available:
+ return False
+
+ try:
+ output = subprocess.check_output(["patchelf", "--print-needed", binary_path], text=True)
+ needed_libs = [line.strip() for line in output.splitlines() if line.strip()]
+ except Exception:
+ return False
+
+ changed = False
+ for lib in needed_libs:
+ # Skip glibc and system standard libraries
+ if any(sys_lib in lib for sys_lib in LINUX_SYSTEM_LIBS):
+ continue
+
+ generic_lib = re.sub(r"(-[0-9_]+|\.so\.[0-9]+|\.[0-9]+).*", ".so", lib)
+ if generic_lib != lib and generic_lib.endswith(".so"):
+ print(f" [{os.path.basename(binary_path)}] Mapping needed ELF lib: {lib} -> {generic_lib}")
+ try:
+ subprocess.check_call(["patchelf", "--replace-needed", lib, generic_lib, binary_path])
+ changed = True
+ except subprocess.CalledProcessError as e:
+ print(f" [{os.path.basename(binary_path)}] Error replacing needed library {lib}: {e}")
return changed
@@ -85,15 +165,26 @@ def main():
print(f"Scanning for binaries in: {search_path}")
count = 0
- for root, _, files in os.walk(search_path):
+ for root, dirs, files in os.walk(search_path):
+ # Skip Python site-packages in app/lib to keep execution fast
+ if "site-packages" in root:
+ continue
+
for f in files:
full_path = os.path.join(root, f)
- # Basic check for Mach-O binaries
+ # Skip symlinks
+ if os.path.islink(full_path):
+ continue
+
+ # Basic check for Mach-O or ELF binaries
try:
file_info = subprocess.check_output(["file", "-b", full_path], text=True)
if "Mach-O" in file_info:
- if sanitize_binary(full_path):
+ if sanitize_macho_binary(full_path):
+ count += 1
+ elif "ELF" in file_info:
+ if sanitize_elf_binary(full_path):
count += 1
except Exception:
continue
From 0749e0aefe285f79ec347b6d00b9ec3e979cbccc Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Thu, 24 Sep 2026 13:21:08 -0700
Subject: [PATCH 11/12] ci: enable Homebrew auto-update across macOS and Linux
workflows
Signed-off-by: Michael Oliver
---
.github/workflows/branch-build.yml | 7 +++++++
.github/workflows/dev-build.yml | 7 +++++++
.github/workflows/release.yml | 7 +++++++
3 files changed, 21 insertions(+)
diff --git a/.github/workflows/branch-build.yml b/.github/workflows/branch-build.yml
index 29406691..ecfa35a7 100644
--- a/.github/workflows/branch-build.yml
+++ b/.github/workflows/branch-build.yml
@@ -80,7 +80,10 @@ jobs:
${{ runner.os }}-brew-
- name: Install Homebrew dependencies
+ env:
+ HOMEBREW_NO_AUTO_UPDATE: "0"
run: |
+ brew update
brew install --formula \
ninja readline sqlite3 xz zlib tcl-tk@8 python-tk autoconf automake libtool python@3.14 yasm clang-format black meson nasm pkg-config glew ccache doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost openimageio openjpeg webp yaml-cpp spdlog icu4c openjph jpeg-turbo
@@ -218,13 +221,17 @@ jobs:
${{ runner.os }}-brew-
- name: Install Homebrew dependencies
+ env:
+ HOMEBREW_NO_AUTO_UPDATE: "0"
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
sudo apt-get update && sudo apt-get install -y build-essential patchelf mold tcsh libgl1-mesa-dev libglu1-mesa-dev libegl1-mesa-dev libosmesa6-dev libudev-dev libaio-dev libreadline-dev libx11-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxrandr-dev libxrender-dev libxcomposite-dev libxdamage-dev libxtst-dev libxxf86vm-dev libxkbcommon-dev libxkbcommon-x11-dev libffi-dev libasound2-dev libpulse-dev
+ brew update
brew install --formula \
ninja pkg-config ccache glew doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost \
openimageio openjpeg webp yaml-cpp spdlog openjph jpeg-turbo
+ brew upgrade openexr imath opencolorio libraw libtiff boost openimageio spdlog openjph || true
shell: bash
- name: Save Homebrew cache
diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml
index 9fc28c34..4e511189 100644
--- a/.github/workflows/dev-build.yml
+++ b/.github/workflows/dev-build.yml
@@ -105,7 +105,10 @@ jobs:
${{ runner.os }}-brew-
- name: Install Homebrew dependencies
+ env:
+ HOMEBREW_NO_AUTO_UPDATE: "0"
run: |
+ brew update
brew install --formula \
ninja readline sqlite3 xz zlib tcl-tk@8 python-tk autoconf automake libtool python@3.14 yasm clang-format black meson nasm pkg-config glew ccache doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost openimageio openjpeg webp yaml-cpp spdlog icu4c openjph jpeg-turbo
@@ -353,13 +356,17 @@ jobs:
${{ runner.os }}-brew-
- name: Install Homebrew dependencies
+ env:
+ HOMEBREW_NO_AUTO_UPDATE: "0"
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
sudo apt-get update && sudo apt-get install -y build-essential patchelf mold tcsh libgl1-mesa-dev libglu1-mesa-dev libegl1-mesa-dev libosmesa6-dev libudev-dev libaio-dev libreadline-dev libx11-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxrandr-dev libxrender-dev libxcomposite-dev libxdamage-dev libxtst-dev libxxf86vm-dev libxkbcommon-dev libxkbcommon-x11-dev libffi-dev libasound2-dev libpulse-dev
+ brew update
brew install --formula \
ninja pkg-config ccache glew doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost \
openimageio openjpeg webp yaml-cpp spdlog openjph jpeg-turbo
+ brew upgrade openexr imath opencolorio libraw libtiff boost openimageio spdlog openjph || true
shell: bash
- name: Save Homebrew cache
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 720abc5d..2914e5fd 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -130,7 +130,10 @@ jobs:
${{ runner.os }}-brew-
- name: Install Homebrew dependencies
+ env:
+ HOMEBREW_NO_AUTO_UPDATE: "0"
run: |
+ brew update
brew install --formula \
ninja readline sqlite3 xz zlib tcl-tk@8 python-tk autoconf automake libtool python@3.14 yasm clang-format black meson nasm pkg-config glew ccache doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost openimageio openjpeg webp yaml-cpp spdlog icu4c openjph jpeg-turbo
@@ -476,13 +479,17 @@ jobs:
${{ runner.os }}-brew-
- name: Install Homebrew dependencies
+ env:
+ HOMEBREW_NO_AUTO_UPDATE: "0"
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
sudo apt-get update && sudo apt-get install -y build-essential patchelf mold tcsh libgl1-mesa-dev libglu1-mesa-dev libegl1-mesa-dev libosmesa6-dev libudev-dev libaio-dev libreadline-dev libx11-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxrandr-dev libxrender-dev libxcomposite-dev libxdamage-dev libxtst-dev libxxf86vm-dev libxkbcommon-dev libxkbcommon-x11-dev libffi-dev libasound2-dev libpulse-dev
+ brew update
brew install --formula \
ninja pkg-config ccache glew doctest qt pyside \
ffmpeg openexr imath opencolorio libraw libtiff libpng libspng boost \
openimageio openjpeg webp yaml-cpp spdlog openjph jpeg-turbo
+ brew upgrade openexr imath opencolorio libraw libtiff boost openimageio spdlog openjph || true
shell: bash
- name: Save Homebrew cache
From 0c925660a04629aa99aab2de37b1a8d0e4cc3ed8 Mon Sep 17 00:00:00 2001
From: Michael Oliver
Date: Fri, 25 Sep 2026 10:03:07 -0700
Subject: [PATCH 12/12] ci: upload uncompressed directories in branch CI to
prevent double zipping
Signed-off-by: Michael Oliver
---
.github/workflows/branch-build.yml | 31 +++++-------------------------
1 file changed, 5 insertions(+), 26 deletions(-)
diff --git a/.github/workflows/branch-build.yml b/.github/workflows/branch-build.yml
index ecfa35a7..ff34993d 100644
--- a/.github/workflows/branch-build.yml
+++ b/.github/workflows/branch-build.yml
@@ -122,22 +122,17 @@ jobs:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
- - name: Package Ad-Hoc Signed App
+ - name: Sign Ad-Hoc App
run: |
python3 "${PWD}/src/build/sanitize_homebrew_links.py" "_build/stage/app/UTV.app" || true
codesign --force --deep --sign - "_build/stage/app/UTV.app"
- mkdir -p _dist
- SHORT_SHA=$(git rev-parse --short=7 HEAD)
- SAFE_BRANCH=$(echo "${{ github.ref_name }}" | tr '/' '-')
- cd _build/stage/app
- ditto -c -k --keepParent UTV.app "${GITHUB_WORKSPACE}/_dist/UTV-${SAFE_BRANCH}-${SHORT_SHA}-macOS-arm64.zip"
shell: bash
- name: Upload macOS Branch Artifact
uses: actions/upload-artifact@v7
with:
name: UTV-macOS-arm64
- path: _dist/*.zip
+ path: _build/stage/app/UTV.app
retention-days: 5
build-windows:
@@ -167,21 +162,16 @@ jobs:
with:
version: 'dev'
- - name: Package Windows Branch Archive
+ - name: Stage Windows Branch Directory
shell: powershell
run: |
- $ShortSha = git rev-parse --short=7 HEAD
- $SafeBranch = "${{ github.ref_name }}".Replace("/", "-")
- $ZipName = "UTV-$SafeBranch-$ShortSha-windows-x64.zip"
- New-Item -ItemType Directory -Force -Path "_dist" | Out-Null
Rename-Item -Path "_install" -NewName "utv-windows-x64"
- Compress-Archive -Path "utv-windows-x64" -DestinationPath "_dist\$ZipName" -CompressionLevel Optimal
- name: Upload Windows Branch Artifact
uses: actions/upload-artifact@v7
with:
name: UTV-windows-x64
- path: _dist/*.zip
+ path: utv-windows-x64
retention-days: 5
build-linux:
@@ -265,22 +255,11 @@ jobs:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
- - name: Package Linux Branch Archive
- if: success()
- run: |
- mkdir -p _dist
- SHORT_SHA=$(git rev-parse --short=7 HEAD)
- SAFE_BRANCH=$(echo "${{ github.ref_name }}" | tr '/' '-')
- if [ -d "_build/stage/app" ]; then
- tar -czf "_dist/UTV-${SAFE_BRANCH}-${SHORT_SHA}-linux-x64.tar.gz" -C _build/stage/app .
- fi
- shell: bash
-
- name: Upload Linux Branch Artifact
if: success()
uses: actions/upload-artifact@v7
with:
name: UTV-linux-x64
- path: _dist/*.tar.gz
+ path: _build/stage/app
retention-days: 5