mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-10-07 14:28:15 +03:00
@@ -85,25 +85,34 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- id: linux-amd64
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-24.04
|
||||
python-version: "3.13"
|
||||
python-architecture: x64
|
||||
pyside6-version: "6.8.3"
|
||||
linux-appimage-builder: linuxdeploy
|
||||
windows-compatibility: ""
|
||||
windows-architecture: ""
|
||||
- id: linux-arm64
|
||||
os: ubuntu-22.04-arm
|
||||
os: ubuntu-24.04-arm
|
||||
python-version: "3.11"
|
||||
python-architecture: arm64
|
||||
pyside6-version: "6.5.3"
|
||||
pyside6-version: "6.8.3"
|
||||
linux-appimage-builder: appimagetool
|
||||
windows-compatibility: ""
|
||||
windows-architecture: ""
|
||||
- id: windows-win7-amd64
|
||||
- id: windows7-amd64
|
||||
os: windows-2025
|
||||
python-version: "3.13"
|
||||
python-architecture: x64
|
||||
pyside6-version: "6.8.3"
|
||||
windows-compatibility: windows
|
||||
windows-compatibility: windows7
|
||||
windows-architecture: amd64
|
||||
- id: windows10-amd64
|
||||
os: windows-2025
|
||||
python-version: "3.13"
|
||||
python-architecture: x64
|
||||
pyside6-version: "6.8.3"
|
||||
windows-compatibility: windows10
|
||||
windows-architecture: amd64
|
||||
- id: windows-arm64
|
||||
os: windows-11-vs2026-arm
|
||||
@@ -127,12 +136,7 @@ jobs:
|
||||
windows-compatibility: ""
|
||||
windows-architecture: ""
|
||||
env:
|
||||
XRAY_CORE_VERSION: "1.8.26.9"
|
||||
HYSTERIA_VERSION: "1.3.5.4"
|
||||
HYSTERIA2_VERSION: "2.12.1.1"
|
||||
TUN2SOCKS_VERSION: "2.7.0.1"
|
||||
GO_WIN7_VERSION: "1.26.7"
|
||||
NUITKA_VERSION: "4.1.3"
|
||||
WIX_VERSION: "6.0.2"
|
||||
WIX_INSTALLER_SHA256: "A8A5CC7443353CEF3AB900C60CD7A3A5EE601746319D104AC7B12AD0CED2345C"
|
||||
steps:
|
||||
@@ -151,7 +155,26 @@ jobs:
|
||||
- name: Install Linux dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y libxcb-cursor0 qt6-base-dev patchelf ccache zlib1g-dev
|
||||
|
||||
if [ "$RUNNER_ARCH" == "ARM64" ]; then
|
||||
if apt-cache show libminizip1t64 >/dev/null 2>&1; then
|
||||
minizip_package='libminizip1t64'
|
||||
else
|
||||
minizip_package='libminizip1'
|
||||
fi
|
||||
|
||||
sudo apt install -y \
|
||||
libevent-2.1-7 \
|
||||
libwebp7 \
|
||||
"$minizip_package"
|
||||
fi
|
||||
|
||||
sudo apt install -y \
|
||||
libxcb-cursor0 \
|
||||
qt6-base-dev \
|
||||
patchelf \
|
||||
ccache \
|
||||
zlib1g-dev
|
||||
sudo apt install -y flatpak flatpak-builder elfutils patchelf
|
||||
sudo apt install -y rpm
|
||||
flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
@@ -159,20 +182,33 @@ jobs:
|
||||
flatpak install -y flathub org.kde.Platform//6.8 org.kde.Sdk//6.8
|
||||
|
||||
if [ "$RUNNER_ARCH" == "X64" ]; then
|
||||
appimage_arch="x86_64"
|
||||
echo "QMAKE=$(command -v qmake6)" >> "$GITHUB_ENV"
|
||||
|
||||
linuxdeploy_arch="x86_64"
|
||||
linuxdeploy_tools=".linuxdeploy-bin"
|
||||
mkdir "$linuxdeploy_tools"
|
||||
echo "${PWD}/${linuxdeploy_tools}" >> "$GITHUB_PATH"
|
||||
|
||||
wget -P "$linuxdeploy_tools" \
|
||||
"https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-${linuxdeploy_arch}.AppImage"
|
||||
wget -P "$linuxdeploy_tools" \
|
||||
"https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/latest/download/linuxdeploy-plugin-qt-${linuxdeploy_arch}.AppImage"
|
||||
wget -P "$linuxdeploy_tools" \
|
||||
"https://github.com/AppImage/appimagetool/releases/latest/download/appimagetool-${linuxdeploy_arch}.AppImage"
|
||||
chmod +x "$linuxdeploy_tools"/*
|
||||
elif [ "$RUNNER_ARCH" == "ARM64" ]; then
|
||||
appimage_arch="aarch64"
|
||||
appimage_tools=".appimage-tools"
|
||||
mkdir "$appimage_tools"
|
||||
echo "${PWD}/${appimage_tools}" >> "$GITHUB_PATH"
|
||||
|
||||
wget -P "$appimage_tools" \
|
||||
"https://github.com/AppImage/appimagetool/releases/latest/download/appimagetool-${appimage_arch}.AppImage"
|
||||
chmod +x "$appimage_tools"/*
|
||||
else
|
||||
echo "Unknown runner architecture: $RUNNER_ARCH"
|
||||
exit 1
|
||||
fi
|
||||
echo "appimagetool architecture is $appimage_arch"
|
||||
|
||||
appimage_tools=".appimage-tools"
|
||||
mkdir ${appimage_tools}
|
||||
echo "${PWD}/${appimage_tools}" >> $GITHUB_PATH
|
||||
wget -P ${appimage_tools} https://github.com/AppImage/appimagetool/releases/latest/download/appimagetool-${appimage_arch}.AppImage
|
||||
chmod +x ${appimage_tools}/*
|
||||
if: runner.os == 'Linux'
|
||||
- name: Install Windows dependencies
|
||||
shell: pwsh
|
||||
@@ -221,14 +257,14 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
check-latest: true
|
||||
if: matrix.id == 'windows-win7-amd64'
|
||||
if: matrix.id == 'windows7-amd64'
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: ${{ matrix.python-architecture }}
|
||||
check-latest: true
|
||||
if: matrix.id != 'windows-win7-amd64'
|
||||
if: matrix.id != 'windows7-amd64'
|
||||
- name: Verify runner and Python architecture
|
||||
env:
|
||||
EXPECTED_ARCHITECTURE: ${{ matrix.python-architecture }}
|
||||
@@ -268,7 +304,7 @@ jobs:
|
||||
uses: LorenEteval/setup-pyside6-win7@v1
|
||||
with:
|
||||
pyside6-version: ${{ matrix.pyside6-version }}
|
||||
if: matrix.id == 'windows-win7-amd64'
|
||||
if: matrix.id == 'windows7-amd64'
|
||||
- name: Install Python build and runtime dependencies
|
||||
run: |
|
||||
python -c "import sys; print(sys.version)"
|
||||
@@ -304,7 +340,7 @@ jobs:
|
||||
)
|
||||
PY
|
||||
|
||||
if [[ "${{ matrix.id }}" != "windows-win7-amd64" ]]; then
|
||||
if [[ "${{ matrix.id }}" != "windows7-amd64" ]]; then
|
||||
if [[ "$RUNNER_OS" == "Windows" ]]; then
|
||||
pyside_binary_policy="--only-binary=PySide6-Essentials,PySide6-Addons"
|
||||
else
|
||||
@@ -322,8 +358,7 @@ jobs:
|
||||
fi
|
||||
|
||||
python -m pip install -r .binary-requirements.txt
|
||||
|
||||
python -m pip install "Nuitka==${NUITKA_VERSION}" imageio requests
|
||||
python -m pip install nuitka imageio requests
|
||||
python -c "import importlib.metadata; print('zxing-cpp', importlib.metadata.version('zxing-cpp'))"
|
||||
python -c "from PySide6 import QtCore; print(f'PySide6/Qt {QtCore.__version__}')"
|
||||
- name: Install patched Go for Windows7
|
||||
@@ -339,87 +374,97 @@ jobs:
|
||||
echo "GOROOT=${go_root}" >> $GITHUB_ENV
|
||||
echo "CGO_ENABLED=1" >> $GITHUB_ENV
|
||||
echo "${go_root}\\bin" >> $GITHUB_PATH
|
||||
if: matrix.id == 'windows-win7-amd64'
|
||||
- name: Build Windows7 bindings from source with patched Go
|
||||
if: matrix.id == 'windows7-amd64'
|
||||
- name: Set up Go for Windows10
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: "1.26"
|
||||
check-latest: true
|
||||
cache: false
|
||||
if: matrix.id == 'windows10-amd64'
|
||||
- name: Build Windows AMD64 bindings from source
|
||||
shell: pwsh
|
||||
env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CGO_ENABLED: "1"
|
||||
CMAKE_BUILD_PARALLEL_LEVEL: "2"
|
||||
GOPROXY: https://proxy.golang.org,direct
|
||||
run: |
|
||||
command -v go
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$PSNativeCommandUseErrorActionPreference = $true
|
||||
|
||||
Write-Output "Go executable: $((Get-Command go -ErrorAction Stop).Source)"
|
||||
go version
|
||||
go env GOROOT GOOS GOARCH CGO_ENABLED
|
||||
command -v gcc
|
||||
Write-Output "GCC executable: $((Get-Command gcc -ErrorAction Stop).Source)"
|
||||
gcc -dumpmachine
|
||||
|
||||
short_temp="$RUNNER_TEMP/w7"
|
||||
mkdir -p "$short_temp"
|
||||
short_temp="$(cygpath -w "$short_temp")"
|
||||
export TMPDIR="$short_temp"
|
||||
export TEMP="$short_temp"
|
||||
export TMP="$short_temp"
|
||||
$temporaryDirectory = Join-Path $env:RUNNER_TEMP 'bindings'
|
||||
New-Item -ItemType Directory -Force -Path $temporaryDirectory | Out-Null
|
||||
foreach ($name in 'TMPDIR', 'TEMP', 'TMP') {
|
||||
Set-Item -Path "Env:$name" -Value $temporaryDirectory
|
||||
}
|
||||
|
||||
python - <<'PY'
|
||||
import os
|
||||
import tempfile
|
||||
Write-Output "Configured temporary directory: $temporaryDirectory"
|
||||
python -c "import tempfile; print(f'Python temporary directory: {tempfile.gettempdir()}')"
|
||||
|
||||
print(f'Configured temporary directory: {os.environ["TEMP"]}')
|
||||
print(f'Python temporary directory: {tempfile.gettempdir()}')
|
||||
PY
|
||||
|
||||
python -m pip install \
|
||||
"setuptools>=68" \
|
||||
wheel \
|
||||
"cmake>=3.15" \
|
||||
python -m pip install `
|
||||
"setuptools>=68" `
|
||||
wheel `
|
||||
"cmake>=3.15" `
|
||||
"pybind11>=3.0.1,<3.1"
|
||||
|
||||
rm -rf win7-wheels
|
||||
mkdir win7-wheels
|
||||
bindings=(
|
||||
"Xray-core==${XRAY_CORE_VERSION}"
|
||||
"hysteria2==${HYSTERIA2_VERSION}"
|
||||
"tun2socks==${TUN2SOCKS_VERSION}"
|
||||
$wheelDirectory = Join-Path $temporaryDirectory 'wheels'
|
||||
New-Item -ItemType Directory -Force -Path $wheelDirectory | Out-Null
|
||||
|
||||
$bindings = @(
|
||||
'Xray-core'
|
||||
'hysteria2'
|
||||
'tun2socks'
|
||||
)
|
||||
|
||||
for binding in "${bindings[@]}"
|
||||
do
|
||||
python -m pip wheel \
|
||||
--no-cache-dir \
|
||||
--no-build-isolation \
|
||||
--no-deps \
|
||||
--no-binary=:all: \
|
||||
--wheel-dir win7-wheels \
|
||||
"${binding}"
|
||||
done
|
||||
foreach ($binding in $bindings) {
|
||||
python -m pip wheel `
|
||||
--no-cache-dir `
|
||||
--no-build-isolation `
|
||||
--no-deps `
|
||||
--no-binary=:all: `
|
||||
--wheel-dir $wheelDirectory `
|
||||
$binding
|
||||
}
|
||||
|
||||
python - <<'PY'
|
||||
from pathlib import Path
|
||||
$wheels = @(
|
||||
Get-ChildItem -LiteralPath $wheelDirectory -Filter '*.whl' -File |
|
||||
Sort-Object Name
|
||||
)
|
||||
Write-Output 'Locally built wheels:'
|
||||
|
||||
wheels = sorted(Path('win7-wheels').glob('*.whl'))
|
||||
print('Locally built wheels:')
|
||||
foreach ($wheel in $wheels) {
|
||||
Write-Output " $($wheel.Name)"
|
||||
}
|
||||
|
||||
for wheel in wheels:
|
||||
print(f' {wheel.name}')
|
||||
$unexpectedWheels = @($wheels | Where-Object Name -NotLike '*win_amd64.whl')
|
||||
|
||||
if len(wheels) != 3 or any('win_amd64.whl' not in wheel.name for wheel in wheels):
|
||||
raise SystemExit('expected exactly three locally built win_amd64 wheels')
|
||||
PY
|
||||
if ($wheels.Count -ne 3 -or $unexpectedWheels.Count -ne 0) {
|
||||
throw 'Expected exactly three locally built win_amd64 wheels'
|
||||
}
|
||||
|
||||
python -m pip install \
|
||||
--no-index \
|
||||
--force-reinstall \
|
||||
--no-deps \
|
||||
win7-wheels/*.whl
|
||||
if: matrix.id == 'windows-win7-amd64'
|
||||
- name: Install Windows7-compatible Hysteria1 wheel
|
||||
$wheelPaths = @($wheels.FullName)
|
||||
python -m pip install `
|
||||
--no-index `
|
||||
--force-reinstall `
|
||||
--no-deps `
|
||||
$wheelPaths
|
||||
if: matrix.id == 'windows7-amd64' || matrix.id == 'windows10-amd64'
|
||||
- name: Install pre-built Hysteria1 wheel
|
||||
run: |
|
||||
python -m pip install \
|
||||
--only-binary=hysteria \
|
||||
--no-cache-dir \
|
||||
--no-deps \
|
||||
"hysteria==${HYSTERIA_VERSION}"
|
||||
if: matrix.id == 'windows-win7-amd64'
|
||||
hysteria
|
||||
if: matrix.id == 'windows7-amd64' || matrix.id == 'windows10-amd64'
|
||||
- name: Install supported binding wheels
|
||||
run: |
|
||||
if [[ "$RUNNER_OS" == "Windows" ]]; then
|
||||
@@ -432,12 +477,12 @@ jobs:
|
||||
${binary_policy} \
|
||||
--no-cache-dir \
|
||||
--no-deps \
|
||||
"Xray-core==${XRAY_CORE_VERSION}" \
|
||||
"hysteria==${HYSTERIA_VERSION}" \
|
||||
"hysteria2==${HYSTERIA2_VERSION}" \
|
||||
"tun2socks==${TUN2SOCKS_VERSION}"
|
||||
if: matrix.id != 'windows-win7-amd64'
|
||||
- name: Verify Windows binding imports
|
||||
Xray-core \
|
||||
hysteria \
|
||||
hysteria2 \
|
||||
tun2socks
|
||||
if: matrix.id != 'windows7-amd64' && matrix.id != 'windows10-amd64'
|
||||
- name: Verify Windows native and UI imports
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import importlib
|
||||
@@ -448,22 +493,113 @@ jobs:
|
||||
|
||||
# Native extension imports fail here if a wheel has the wrong machine type.
|
||||
print('All native bindings imported successfully')
|
||||
|
||||
# Some Windows PySide6 distributions intentionally omit Qt WebEngine.
|
||||
# The application must still import and use its non-WebEngine map fallback.
|
||||
from Furious.Widget.EndpointInfoWidget import EndpointInfoWidget
|
||||
|
||||
print(f'Endpoint UI imported successfully: {EndpointInfoWidget.__name__}')
|
||||
PY
|
||||
if: runner.os == 'Windows'
|
||||
- name: Verify Qt WebEngine imports
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
python - <<'PY'
|
||||
from PySide6 import QtWebChannel, QtWebEngineCore, QtWebEngineWidgets
|
||||
|
||||
print(f'QtWebChannel imported successfully: {QtWebChannel.__name__}')
|
||||
print(f'QtWebEngineCore imported successfully: {QtWebEngineCore.__name__}')
|
||||
print(f'QtWebEngineWidgets imported successfully: {QtWebEngineWidgets.__name__}')
|
||||
PY
|
||||
- name: Download latest asset files
|
||||
run: |
|
||||
python Deploy.py --download
|
||||
- name: Run deploy script
|
||||
env:
|
||||
APPIMAGE_EXTRACT_AND_RUN: "1"
|
||||
LINUX_APPIMAGE_BUILDER: ${{ matrix.linux-appimage-builder }}
|
||||
run: |
|
||||
if [[ -n "${{ matrix.windows-compatibility }}" ]]; then
|
||||
export WIN_VER_COMPATIBLE="${{ matrix.windows-compatibility }}"
|
||||
fi
|
||||
python Deploy.py
|
||||
- name: Verify macOS application bundle
|
||||
if: runner.os == 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
app='app/Furious-GUI.app'
|
||||
source_app='Furious-Deploy/Furious-GUI.app'
|
||||
macos_root="${app}/Contents/MacOS"
|
||||
qt_root="${macos_root}/PySide6/Qt"
|
||||
webengine_framework="${qt_root}/lib/QtWebEngineCore.framework"
|
||||
relocated_qt_root="${app}/Contents/Frameworks/PySide6/Qt"
|
||||
relocated_webengine_framework="${relocated_qt_root}/lib/QtWebEngineCore.framework"
|
||||
webengine_helper="${relocated_webengine_framework}/Helpers/QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess"
|
||||
failures=0
|
||||
|
||||
check() {
|
||||
local description="$1"
|
||||
local status
|
||||
shift
|
||||
|
||||
echo "::group::${description}"
|
||||
|
||||
if "$@"; then
|
||||
status=0
|
||||
echo "PASS: ${description} (exit code ${status})"
|
||||
else
|
||||
status=$?
|
||||
echo "::error title=macOS bundle verification::FAIL: ${description} (exit code ${status})"
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
|
||||
echo '::endgroup::'
|
||||
}
|
||||
|
||||
echo "Application bundle: ${app}"
|
||||
echo "Qt runtime root: ${qt_root}"
|
||||
echo "Relocated Qt root: ${relocated_qt_root}"
|
||||
echo "WebEngine framework link: ${webengine_framework}"
|
||||
echo "Relocated WebEngine framework: ${relocated_webengine_framework}"
|
||||
echo "WebEngine helper: ${webengine_helper}"
|
||||
|
||||
check 'Application bundle exists' test -d "${app}"
|
||||
check 'Nuitka source application bundle exists' test -d "${source_app}"
|
||||
framework_paths=(
|
||||
"${webengine_framework}"
|
||||
"${relocated_webengine_framework}"
|
||||
"${relocated_webengine_framework}/Versions/Current"
|
||||
"${relocated_webengine_framework}/QtWebEngineCore"
|
||||
)
|
||||
check 'Relocated framework paths can be inspected' ls -ld "${framework_paths[@]}"
|
||||
check 'MacOS WebEngine framework entry is a symbolic link' test -L "${webengine_framework}"
|
||||
check 'Framework Current version is a symbolic link' test -L "${relocated_webengine_framework}/Versions/Current"
|
||||
check 'Framework top-level binary is a symbolic link' test -L "${relocated_webengine_framework}/QtWebEngineCore"
|
||||
check 'WebEngine process helper is executable' test -x "${webengine_helper}"
|
||||
resource_paths=(
|
||||
"${macos_root}/icudtl.dat"
|
||||
"${macos_root}/qtwebengine_resources.pak"
|
||||
)
|
||||
check 'WebEngine resource metadata can be inspected' ls -l@ "${resource_paths[@]}"
|
||||
check 'Chromium ICU data exists' test -f "${resource_paths[0]}"
|
||||
check 'Chromium resource pack exists' test -f "${resource_paths[1]}"
|
||||
check 'Nuitka source code signature is valid' codesign --verify --deep --strict --verbose=2 "${source_app}"
|
||||
check 'Staged application code signature is valid' codesign --verify --deep --strict --verbose=2 "${app}"
|
||||
size_paths=(
|
||||
"${app}"
|
||||
"${app}/Contents/Frameworks"
|
||||
"${app}/Contents/MacOS"
|
||||
)
|
||||
check 'Application bundle sizes can be measured' du -sh "${size_paths[@]}"
|
||||
|
||||
if ((failures > 0)); then
|
||||
echo "::error title=macOS bundle verification::${failures} check(s) failed"
|
||||
exit 1
|
||||
fi
|
||||
- name: Verify packaged Windows binaries
|
||||
env:
|
||||
EXPECTED_WINDOWS_ARCHITECTURE: ${{ matrix.windows-architecture }}
|
||||
WINDOWS_COMPATIBILITY: ${{ matrix.windows-compatibility }}
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import os
|
||||
@@ -477,10 +613,19 @@ jobs:
|
||||
expected = os.environ['EXPECTED_WINDOWS_ARCHITECTURE']
|
||||
expectedMachine = machineCodes[expected]
|
||||
root = Path('Furious-Deploy/Furious.dist')
|
||||
compatibilityDll = root / 'api-ms-win-core-path-l1-1-0.dll'
|
||||
|
||||
if (
|
||||
os.environ['WINDOWS_COMPATIBILITY'] == 'windows7'
|
||||
and not compatibilityDll.is_file()
|
||||
):
|
||||
raise SystemExit(f'Windows 7 compatibility DLL is missing: {compatibilityDll}')
|
||||
|
||||
binaries = sorted(
|
||||
{
|
||||
*root.rglob('*.exe'),
|
||||
*root.rglob('*.pyd'),
|
||||
*([compatibilityDll] if compatibilityDll.is_file() else []),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -568,6 +713,6 @@ jobs:
|
||||
with:
|
||||
identifier: LorenEteval.Furious
|
||||
release-tag: ${{ github.ref_name }}
|
||||
installers-regex: '^Furious-.*-(?:windows-amd64|windows11-arm64)\.zip$'
|
||||
installers-regex: '^Furious-.*-(?:windows10-amd64|windows11-arm64)\.zip$'
|
||||
max-versions-to-keep: 5
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
|
||||
Reference in New Issue
Block a user