Files
zoitechat/.github/workflows/windows-build.yml
2026-02-01 17:19:34 -07:00

315 lines
14 KiB
YAML

name: Windows Build
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
windows_build:
runs-on: windows-2019
permissions:
contents: read
id-token: write
attestations: write
artifact-metadata: write
strategy:
matrix:
platform: [x64, win32]
arch: [x64, x86]
exclude:
- platform: x64
arch: x86
- platform: win32
arch: x64
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.14.2"
architecture: ${{ matrix.arch }}
- name: Install Dependencies (GTK3 toolchain + packagers)
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
New-Item -Name "deps" -ItemType "Directory" -Force | Out-Null
# Inno Setup + IDP (kept as-is)
Invoke-WebRequest http://files.jrsoftware.org/is/5/innosetup-5.5.9-unicode.exe -OutFile deps\innosetup-unicode.exe
& deps\innosetup-unicode.exe /VERYSILENT | Out-Null
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/idpsetup-1.5.1.exe -OutFile deps\idpsetup.exe
& deps\idpsetup.exe /VERYSILENT
# WinSparkle / gendef / perl (kept as-is)
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/gendef-20111031.7z -OutFile deps\gendef.7z
& 7z.exe x deps\gendef.7z -oC:\gtk-build | Out-Null
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/WinSparkle-20151011.7z -OutFile deps\WinSparkle.7z
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle | Out-Null
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/perl-5.20.0-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.20\${{ matrix.platform }} | Out-Null
# -----------------------------
# GTK3 stack (MSVC) from wingtk/gvsbuild (robust layout detection)
# -----------------------------
$wantArch = if ("${{ matrix.platform }}" -eq "x64") { "x64" } else { "x86" }
$headers = @{
"User-Agent" = "zoitechat-ci"
"Authorization" = "Bearer $env:GITHUB_TOKEN"
"X-GitHub-Api-Version" = "2022-11-28"
}
$releases = Invoke-RestMethod -Headers $headers -Uri "https://api.github.com/repos/wingtk/gvsbuild/releases?per_page=20"
$asset = $null
foreach ($rel in $releases) {
$asset = $rel.assets |
Where-Object {
$_.name -match "(?i)^GTK3_.*_${wantArch}\.zip$" -or
($_.name -match "(?i)gtk3" -and $_.name -match "(?i)\b${wantArch}\b" -and $_.name -match "(?i)\.zip$")
} |
Select-Object -First 1
if ($asset) { break }
}
$usingLegacyGtk2 = $false
if (-not $asset) {
if ($wantArch -eq "x86") {
# Fallback: keep 32-bit build working if GTK3 x86 isn't published.
$usingLegacyGtk2 = $true
Write-Host "No GTK3 x86 bundle found in recent wingtk/gvsbuild releases. Falling back to legacy GTK2 bundle for win32 to keep builds working."
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/gtk-${{ matrix.platform }}-2018-08-29-openssl1.1.7z -OutFile deps\gtk-legacy-${{ matrix.platform }}.7z
if (Test-Path C:\gtk-build\gtk) { Remove-Item C:\gtk-build\gtk -Recurse -Force }
& 7z.exe x deps\gtk-legacy-${{ matrix.platform }}.7z -oC:\gtk-build\gtk | Out-Null
} else {
Write-Host "Available assets in recent releases:"
foreach ($rel in $releases) {
Write-Host ("Release: " + $rel.tag_name)
$rel.assets | ForEach-Object { Write-Host (" - " + $_.name) }
}
throw "Could not find a GTK3 $wantArch bundle in wingtk/gvsbuild recent releases."
}
}
if (-not $usingLegacyGtk2) {
$bundlePath = "deps\GTK3_Gvsbuild_${wantArch}.zip"
Invoke-WebRequest $asset.browser_download_url -OutFile $bundlePath
$extractRoot = "C:\gtk3-bundle"
if (Test-Path $extractRoot) { Remove-Item $extractRoot -Recurse -Force }
New-Item -Path $extractRoot -ItemType Directory -Force | Out-Null
Expand-Archive -Force $bundlePath -DestinationPath $extractRoot
# Infer the "release" root from a guaranteed artifact: gtk-3.0.lib or libgtk-3-0.dll
$gtk3lib = Get-ChildItem -Path $extractRoot -Recurse -Filter "gtk-3.0.lib" -ErrorAction SilentlyContinue | Select-Object -First 1
$gtk3dll = $null
if (-not $gtk3lib) {
$gtk3dll = Get-ChildItem -Path $extractRoot -Recurse -Include "libgtk-3-0.dll","gtk-3*.dll" -ErrorAction SilentlyContinue | Select-Object -First 1
}
if ($gtk3lib) {
$gtkLibDir = Split-Path $gtk3lib.FullName -Parent
$releaseDir = Split-Path $gtkLibDir -Parent
} elseif ($gtk3dll) {
$gtkBinDir = Split-Path $gtk3dll.FullName -Parent
$releaseDir = Split-Path $gtkBinDir -Parent
} else {
throw "GTK3 bundle extracted, but neither gtk-3.0.lib nor libgtk-3-0.dll was found. Layout unexpected."
}
$gtkBin = Join-Path $releaseDir "bin"
if (-not (Test-Path $gtkBin)) {
throw "GTK3 release root inferred, but bin/ was not found at: $gtkBin"
}
# Normalize expected path:
# C:\gtk-build\gtk\<platform>\release -> <releaseDir>
$platDir = "C:\gtk-build\gtk\${{ matrix.platform }}"
New-Item -Path $platDir -ItemType Directory -Force | Out-Null
$link = Join-Path $platDir "release"
if (Test-Path $link) { Remove-Item $link -Recurse -Force -ErrorAction SilentlyContinue }
New-Item -Path $platDir -Name "release" -ItemType Junction -Value $releaseDir | Out-Null
# Find any glib-genmarshal in the bundle (exe or script)
$genAny = Get-ChildItem -Path $extractRoot -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '(?i)^glib-genmarshal(\.exe|\.py)?$' } |
Select-Object -First 1
$genTarget = Join-Path $gtkBin "glib-genmarshal" # what your vcxproj calls via python.exe
if ($genAny) {
$genPath = $genAny.FullName.Replace('\','\\')
$pyWrapper = @(
'import os, subprocess, sys',
"tool = r`"$genPath`"",
'if tool.lower().endswith(".py"):',
' sys.exit(subprocess.call([sys.executable, tool] + sys.argv[1:]))',
'else:',
' sys.exit(subprocess.call([tool] + sys.argv[1:]))'
) -join "`r`n"
Set-Content -Path $genTarget -Value $pyWrapper -Encoding ASCII
} else {
# Fallback: install MSYS2 glib2 tools and run their glib-genmarshal with PATH set.
Write-Host "glib-genmarshal not found in GTK3 bundle. Installing MSYS2 glib2 tools as a fallback."
choco install msys2 -y --no-progress
$msysBash = "C:\msys64\usr\bin\bash.exe"
if (-not (Test-Path $msysBash)) { throw "MSYS2 install failed: bash.exe not found." }
$mingw = if ("${{ matrix.platform }}" -eq "x64") { "mingw64" } else { "mingw32" }
$pkg = if ("${{ matrix.platform }}" -eq "x64") { "mingw-w64-x86_64-glib2" } else { "mingw-w64-i686-glib2" }
& $msysBash -lc "pacman -Sy --noconfirm --needed $pkg"
$msysGen = "C:\msys64\$mingw\bin\glib-genmarshal.exe"
if (-not (Test-Path $msysGen)) { throw "MSYS2 glib-genmarshal.exe not found at expected path: $msysGen" }
$msysBin = (Split-Path $msysGen -Parent).Replace('\','\\')
$msysGenEsc = $msysGen.Replace('\','\\')
$pyWrapper = @(
'import os, subprocess, sys',
"exe = r`"$msysGenEsc`"",
'env = os.environ.copy()',
"env[`"PATH`"] = r`"$msysBin`" + `";`" + env.get(`"PATH`",`"`")",
'sys.exit(subprocess.call([exe] + sys.argv[1:], env=env))'
) -join "`r`n"
Set-Content -Path $genTarget -Value $pyWrapper -Encoding ASCII
}
# Compatibility aliases while vcxproj still names GTK2 libs.
$gtkLib = Join-Path "C:\gtk-build\gtk\${{ matrix.platform }}\release\lib" ""
if (Test-Path $gtkLib) {
$gtk3 = Get-ChildItem -Path $gtkLib -Filter "gtk-3*.lib" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($gtk3 -and (-not (Test-Path (Join-Path $gtkLib "gtk-win32-2.0.lib")))) {
Copy-Item $gtk3.FullName (Join-Path $gtkLib "gtk-win32-2.0.lib") -Force
}
$gdk3 = Get-ChildItem -Path $gtkLib -Filter "gdk-3*.lib" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($gdk3 -and (-not (Test-Path (Join-Path $gtkLib "gdk-win32-2.0.lib")))) {
Copy-Item $gdk3.FullName (Join-Path $gtkLib "gdk-win32-2.0.lib") -Force
}
}
}
# Resolve python root from setup-python
$pyRoot = $env:pythonLocation
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
# Create BOTH paths because the .vcxproj hard-codes python-3.14\...
foreach ($pyDir in @("C:\gtk-build\python-3.14.2", "C:\gtk-build\python-3.14")) {
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
$target = Join-Path $pyDir "${{ matrix.platform }}"
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
New-Item -Path $pyDir -Name "${{ matrix.platform }}" -ItemType Junction -Value $pyRoot | Out-Null
}
python -m pip install --upgrade pip
python -m pip install cffi
- name: Build
run: |
if "${{ matrix.platform }}"=="x64" (
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -host_arch=amd64 -arch=amd64
) else (
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -host_arch=amd64 -arch=x86
)
set "PYTHON_DIR=C:\gtk-build\python-3.14.2\${{ matrix.platform }}"
if not exist "%PYTHON_DIR%\libs\python314.lib" (
echo Missing %PYTHON_DIR%\libs\python314.lib
dir "%PYTHON_DIR%\libs"
exit /b 1
)
set "LIB=%PYTHON_DIR%\libs;%LIB%"
set "INCLUDE=%PYTHON_DIR%\include;%INCLUDE%"
set "GTKROOT=C:\gtk-build\gtk\${{ matrix.platform }}\release"
if not exist "%GTKROOT%\bin" (
echo GTKROOT bin not found at %GTKROOT%\bin
dir "C:\gtk-build\gtk\${{ matrix.platform }}"
exit /b 1
)
set "PATH=%GTKROOT%\bin;%PATH%"
set "INCLUDE=%GTKROOT%\include;%INCLUDE%"
set "LIB=%GTKROOT%\lib;%LIB%"
rem Build LuaJIT (MSVC) if lua.h isn't present in the GTK bundle.
if not exist "%GTKROOT%\include\luajit-2.1\lua.h" (
set "LUABASE=C:\gtk-build\luajit\${{ matrix.platform }}"
if not exist "%LUABASE%\include\lua.h" (
rmdir /s /q C:\gtk-build\luajit-src 2>nul
git clone --depth 1 --branch v2.1 https://github.com/LuaJIT/LuaJIT.git C:\gtk-build\luajit-src
pushd C:\gtk-build\luajit-src\src
call msvcbuild.bat
popd
mkdir "%LUABASE%\include" 2>nul
copy /y C:\gtk-build\luajit-src\src\lua.h "%LUABASE%\include\"
copy /y C:\gtk-build\luajit-src\src\lauxlib.h "%LUABASE%\include\"
copy /y C:\gtk-build\luajit-src\src\luaconf.h "%LUABASE%\include\"
copy /y C:\gtk-build\luajit-src\src\luajit.h "%LUABASE%\include\" 2>nul
mkdir "%LUABASE%\lib" 2>nul
copy /y C:\gtk-build\luajit-src\src\lua51.lib "%LUABASE%\lib\"
)
set "INCLUDE=%LUABASE%\include;%INCLUDE%"
set "LIB=%LUABASE%\lib;%LIB%"
) else (
set "INCLUDE=%GTKROOT%\include\luajit-2.1;%INCLUDE%"
)
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
shell: cmd
- name: Preparing Artifacts
run: |
move ..\zoitechat-build\${{ matrix.platform }}\ZoiteChat*.exe .\
move ..\zoitechat-build .\
shell: cmd
- name: Upload Installer
id: upload_installer
uses: actions/upload-artifact@v4
with:
name: Installer ${{ matrix.arch }}
path: ZoiteChat*.exe
- name: Attest Installer (Artifact Attestation)
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
uses: actions/attest-build-provenance@v3
with:
subject-name: Installer ${{ matrix.arch }}
subject-digest: sha256:${{ steps.upload_installer.outputs.artifact-digest }}
- name: Upload Build Files
id: upload_buildfiles
uses: actions/upload-artifact@v4
with:
name: Build Files ${{ matrix.arch }}
path: zoitechat-build
- name: Attest Build Files (Artifact Attestation)
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
uses: actions/attest-build-provenance@v3
with:
subject-name: Build Files ${{ matrix.arch }}
subject-digest: sha256:${{ steps.upload_buildfiles.outputs.artifact-digest }}