Skip to content

update the rule engine #27

update the rule engine

update the rule engine #27

Workflow file for this run

name: Build SmartEye
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
- dev
jobs:
validate:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
architecture: "x64"
cache: "pip"
cache-dependency-path: |
requirements.txt
requirements-dev.txt
- name: Install validation dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
- name: Ruff checks
run: |
python -m ruff check backend frontend utils scripts main.py tests --select F401,F821,F823,F811,F841,E722,B006,B007,B008,B023,B904,RUF012,RUF034
- name: Pytest
run: |
python -m pytest -q
build:
if: github.event_name == 'workflow_dispatch'
needs: validate
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
architecture: "x64"
cache: "pip"
cache-dependency-path: requirements.txt
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Remove duplicate top-level insightface path
shell: pwsh
run: |
$pythonRoot = (python -c "import sys; print(sys.prefix)").Trim()
$topLevelInsightface = Join-Path $pythonRoot "insightface"
if (Test-Path $topLevelInsightface) {
Write-Host "Removing duplicate package path: $topLevelInsightface"
Remove-Item -Path $topLevelInsightface -Recurse -Force
Write-Host "Done."
} else {
Write-Host "No duplicate top-level insightface path detected"
}
python -c "import insightface, sys; print('Python prefix:', sys.prefix); print('insightface module:', insightface.__file__)"
- name: Ensure setuptools is installed for Nuitka
run: |
pip install --upgrade setuptools
pip show setuptools
- name: Collect PySide6 plugins
shell: pwsh
run: |
$p = (python -c "import PySide6, os; print(os.path.join(os.path.dirname(PySide6.__file__), 'plugins'))").Trim()
if (Test-Path $p) {
Write-Host "Copying PySide6 plugins from $p"
Remove-Item -Path "$PWD\frontend\pyside6_plugins" -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item -Path $p -Destination "$PWD\frontend\pyside6_plugins" -Recurse -Force
Write-Host "PySide6 plugins copied to frontend/pyside6_plugins"
} else {
Write-Host "PySide6 plugins path not found: $p"
}
- name: Download model bundle (optional)
shell: pwsh
run: |
if (-not (Test-Path "data/models")) {
if ($env:MODEL_BUNDLE_URL) {
Write-Host "MODEL_BUNDLE_URL set - downloading model bundle..."
Invoke-WebRequest -Uri $env:MODEL_BUNDLE_URL -OutFile models.zip -UseBasicParsing
Expand-Archive models.zip -DestinationPath data/models
Remove-Item models.zip -Force
Write-Host "Model bundle downloaded and extracted to data/models"
} else {
Write-Host "MODEL_BUNDLE_URL not set; skipping model download"
}
} else {
Write-Host "data/models already exists - skipping download"
}
- name: Cache Nuitka build cache
uses: actions/cache@v4
with:
path: C:/Users/runneradmin/AppData/Local/Nuitka/Nuitka
key: nuitka-${{ runner.os }}-${{ hashFiles('requirements.txt', 'main.py') }}
restore-keys: |
nuitka-${{ runner.os }}-
- name: Stage scientific runtime libs for packaging
shell: pwsh
run: |
@'
import importlib.util
import pathlib
import shutil
root = pathlib.Path(".").resolve()
out_root = root / "build_assets"
out_root.mkdir(exist_ok=True)
def stage_lib_dir(pkg_name: str):
spec = importlib.util.find_spec(pkg_name)
if not spec or not spec.origin:
print(f"[warn] could not resolve {pkg_name}")
return
pkg_dir = pathlib.Path(spec.origin).resolve().parent
libs_dir = pkg_dir.parent / f"{pkg_name}.libs"
if not libs_dir.exists():
print(f"[info] no {pkg_name}.libs at {libs_dir}")
return
dst = out_root / f"{pkg_name}.libs"
if dst.exists():
shutil.rmtree(dst)
shutil.copytree(libs_dir, dst)
print(f"[ok] staged {libs_dir} -> {dst}")
stage_lib_dir("scipy")
stage_lib_dir("numpy")
'@ | python -
Get-ChildItem -Path "$PWD\build_assets" -Directory -ErrorAction SilentlyContinue | ForEach-Object {
Write-Host "Staged runtime dir: $($_.FullName)"
}
- name: Check for model bundle
id: check_models
shell: pwsh
run: |
$hasModels = (Get-ChildItem -Path "data/models" -ErrorAction SilentlyContinue | Measure-Object).Count -gt 0
"has_models=$($hasModels.ToString().ToLower())" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Check for object model file
id: check_obj
shell: pwsh
run: |
$hasObj = Test-Path "data/models/Obj-Detection.onnx"
"has_obj=$($hasObj.ToString().ToLower())" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Build with Nuitka
uses: Nuitka/Nuitka-Action@main
with:
nuitka-version: 2.7.11
script-name: main.py
mode: standalone
output-dir: build
output-file: SmartEye
jobs: "2"
lto: "yes"
windows-console-mode: disable
windows-icon-from-ico: frontend/assets/icons/icon.ico
enable-plugins: pyside6
show-scons: true
include-package: |
psutil
PySide6
shiboken6
reportlab
requests
pyqtgraph
onnx
onnxruntime
insightface
GPUtil
wmi
backend
frontend
utils
streamlink
numpy
scipy
skimage
albumentations
include-module: |
scipy._cyutility
scipy._lib._ccallback_c
include-package-data: |
reportlab
pyqtgraph
insightface
onnxruntime
streamlink
numpy
scipy
skimage
albumentations
include-data-dir: |
frontend/assets=frontend/assets
frontend/pyside6_plugins=PySide6/plugins
build_assets/scipy.libs=scipy.libs
build_assets/numpy.libs=numpy.libs
include-data-files: |
app_info.json=app_info.json
backend/database/schema.sql=backend/database/schema.sql
${{ steps.check_obj.outputs.has_obj == 'true' && 'data/models/Obj-Detection.onnx=data/models/Obj-Detection.onnx' || '' }}
nofollow-import-to: |
numpy.distutils
numpy.random.tests
numpy.random.tests.test_extending
scipy.tests
scipy.stats.tests
scipy.stats.tests.test_censored_data
scipy.stats.tests.test_continuous
sympy.polys.polyquinticconst
trio.testing
onnxruntime.training
onnxruntime.tools
onnx.backend.test
cv2.samples
pyqtgraph.tests
pyqtgraph.opengl
pyqtgraph.exporters
PySide6.QtWebEngine
PySide6.QtWebEngineWidgets
PySide6.QtWebEngineCore
PySide6.Qt3DCore
PySide6.Qt3DRender
PySide6.Qt3DInput
PySide6.Qt3DLogic
PySide6.Qt3DAnimation
PySide6.Qt3DExtras
PySide6.QtCharts
PySide6.QtDataVisualization
PySide6.QtLocation
PySide6.QtPositioning
PySide6.QtRemoteObjects
PySide6.QtSensors
PySide6.QtSerialPort
PySide6.QtTextToSpeech
PySide6.QtBluetooth
PySide6.QtNfc
unittest
test
distutils
setuptools
pkg_resources
pip
doctest
noinclude-data-files: |
qt6webenginecore.dll
qt6webenginequick.dll
qt6quick.dll
qt6quick3d.dll
qt6quick3druntimerender.dll
qt6quick3dutils.dll
qt6quickcontrols2.dll
qt6quickshapes.dll
qt6quicktemplates2.dll
qt6quicktest.dll
qt6quickwidgets.dll
qt6qml.dll
qt6qmlmeta.dll
qt6qmlmodels.dll
qt6qmlworkerscript.dll
qt6scxml.dll
qt6spatialaudio.dll
skimage/data/*
pyqtgraph/examples/*
assume-yes-for-downloads: true
- name: Compress executable with UPX
shell: pwsh
run: |
choco install upx -y
$exe = Get-ChildItem -Path "$PWD\build" -Filter *.exe -File -Recurse | Select-Object -First 1
if ($exe) {
Write-Host "Compressing $($exe.FullName) with UPX..."
upx -9 $exe.FullName
Write-Host "Compression complete!"
} else {
Write-Host "Warning: No .exe file found to compress"
}
- name: Create NSIS Installer
shell: pwsh
run: |
choco install nsis -y
$appInfo = Get-Content app_info.json | ConvertFrom-Json
$appVersion = $appInfo.version
$distDir = Get-ChildItem -Path (Join-Path $PWD 'build') -Directory -Filter *.dist | Select-Object -First 1
if (-not $distDir) {
throw "No .dist folder found"
}
$nsiContent = @"
!define APPNAME "SmartEye"
!define VERSION "$appVersion"
!define EXEFILE "SmartEye.exe"
SetCompressor /SOLID lzma
SetCompressorDictSize 64
Name "`${APPNAME} Setup"
!define MUI_ICON "frontend\assets\icons\icon.ico"
!define MUI_UNICON "frontend\assets\icons\icon.ico"
!include "MUI2.nsh"
OutFile "build\SmartEyeInstaller.exe"
InstallDir "`$PROGRAMFILES\`${APPNAME}"
Caption "`${APPNAME} Installer"
BrandingText "`${APPNAME}"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Section "Install"
SetOutPath "`$INSTDIR"
File /r "$($distDir.FullName)\*"
CreateDirectory "`$SMPROGRAMS\`${APPNAME}"
CreateShortcut "`$SMPROGRAMS\`${APPNAME}\`${APPNAME}.lnk" "`$INSTDIR\`${EXEFILE}"
CreateShortcut "`$DESKTOP\`${APPNAME}.lnk" "`$INSTDIR\`${EXEFILE}"
WriteUninstaller "`$INSTDIR\Uninstall.exe"
CreateShortcut "`$SMPROGRAMS\`${APPNAME}\Uninstall.lnk" "`$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\`${APPNAME}" "DisplayName" "`${APPNAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\`${APPNAME}" "UninstallString" "`$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\`${APPNAME}" "DisplayIcon" "`$INSTDIR\`${EXEFILE}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\`${APPNAME}" "DisplayVersion" "`${VERSION}"
SectionEnd
Section "Uninstall"
RMDir /r "`$INSTDIR"
Delete "`$DESKTOP\`${APPNAME}.lnk"
Delete "`$SMPROGRAMS\`${APPNAME}\`${APPNAME}.lnk"
Delete "`$SMPROGRAMS\`${APPNAME}\Uninstall.lnk"
RMDir "`$SMPROGRAMS\`${APPNAME}"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\`${APPNAME}"
SectionEnd
"@
$nsiContent | Out-File -FilePath "SmartEye.nsi" -Encoding UTF8
Write-Host "Compiling installer with LZMA compression (this may take a few minutes)..."
& "C:\Program Files (x86)\NSIS\makensis.exe" SmartEye.nsi
if ($LASTEXITCODE -ne 0) {
throw "NSIS compilation failed with exit code $LASTEXITCODE"
}
Write-Host "Installer created successfully!"
- name: Collect release files
id: release_files
shell: pwsh
run: |
$appInfo = Get-Content app_info.json | ConvertFrom-Json
$appName = $appInfo.name
$appVersion = $appInfo.version
$appBuild = Get-Date -Format "yyyyMMdd"
$installerPath = Join-Path $PWD "build\SmartEyeInstaller.exe"
if (-not (Test-Path $installerPath)) {
throw "Installer not found at $installerPath"
}
"app_name=$appName" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"app_version=$appVersion" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"app_build=$appBuild" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"installer_path=$installerPath" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Create or update release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: pwsh
run: |
$appName = "${{ steps.release_files.outputs.app_name }}"
$appVersion = "${{ steps.release_files.outputs.app_version }}"
$appBuild = "${{ steps.release_files.outputs.app_build }}"
$tag = "v$appVersion"
$name = "$appName v$appVersion (build $appBuild)"
$repo = "${{ github.repository }}"
$installer = "${{ steps.release_files.outputs.installer_path }}"
gh release view $tag --repo $repo *> $null
if ($LASTEXITCODE -ne 0) {
gh release create $tag $installer --repo $repo --title $name --generate-notes
} else {
gh release upload $tag $installer --repo $repo --clobber
}