Skip to content

refactor: update shortcut for blur detection action from Ctrl+B to B … #21

refactor: update shortcut for blur detection action from Ctrl+B to B …

refactor: update shortcut for blur detection action from Ctrl+B to B … #21

Workflow file for this run

name: Build Desktop Binaries
on:
workflow_dispatch:
push:
tags:
- 'v*'
permissions:
contents: write
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: ${{ matrix.os }} PyInstaller build
runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
python: '3.12'
artifact_name: PhotoSort-Windows-x64
onnx_runtime: onnxruntime
- os: windows-latest
python: '3.12'
artifact_name: PhotoSort-Windows-x64-CUDA
onnx_runtime: onnxruntime-gpu
- os: macos-13 # Intel
python: '3.12'
artifact_name: PhotoSort-macOS-Intel
- os: macos-14 # Apple Silicon
python: '3.12'
artifact_name: PhotoSort-macOS-AppleSilicon
env:
PIP_DISABLE_PIP_VERSION_CHECK: '1'
PIP_NO_PYTHON_VERSION_WARNING: '1'
PIP_PROGRESS_BAR: 'off'
QT_QPA_PLATFORM: offscreen
# Enable file logging in packaged app for troubleshooting
PHOTOSORT_ENABLE_FILE_LOGGING: 'true'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Generate version module
shell: bash
# Write src/core/build_info.py VERSION from tag (strip leading 'v') or short SHA for ad-hoc builds
run: |
python - <<'PY'
import os, re, pathlib
ref = os.getenv('GITHUB_REF', '') or ''
sha = (os.getenv('GITHUB_SHA', '') or '')[:7]
m = re.match(r'refs/tags/v?(.*)$', ref)
if m and m.group(1):
version = m.group(1)
else:
version = f'dev-{sha}' if sha else 'dev'
path = pathlib.Path('src') / 'core' / 'build_info.py'
path.parent.mkdir(parents=True, exist_ok=True)
content = '# File auto-generated by CI. Do not edit.\nVERSION = ' + repr(version) + '\n'
path.write_text(content, encoding='utf-8')
print('Wrote', path, 'with version:', version)
PY
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/requirements-dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Python deps
shell: bash
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
# Override ONNX Runtime with matrix-specified package
if [ "$RUNNER_OS" = "Windows" ]; then
pip install --force-reinstall ${{ matrix.onnx_runtime }}
fi
# Build tools
pip install pyinstaller
- name: Generate app icons (platform-specific)
shell: bash
run: |
# Use committed assets/app_icon.png
mkdir -p assets
if [ "$RUNNER_OS" = "macOS" ]; then
if [ ! -f assets/photosort.icns ] || [ assets/app_icon.png -nt assets/photosort.icns ]; then
mkdir -p build/icons/PhotoSort.iconset
python - <<'PY'
import os
from PIL import Image
base = Image.open('assets/app_icon.png')
sizes = {
'icon_16x16.png': 16,
'icon_16x16@2x.png': 32,
'icon_32x32.png': 32,
'icon_32x32@2x.png': 64,
'icon_128x128.png': 128,
'icon_128x128@2x.png': 256,
'icon_256x256.png': 256,
'icon_256x256@2x.png': 512,
'icon_512x512.png': 512,
'icon_512x512@2x.png': 1024,
}
os.makedirs('build/icons/PhotoSort.iconset', exist_ok=True)
for name, sz in sizes.items():
base.resize((sz, sz), Image.LANCZOS).save(os.path.join('build/icons/PhotoSort.iconset', name))
PY
iconutil -c icns build/icons/PhotoSort.iconset -o assets/photosort.icns
fi
fi
- name: Ensure models dir exists
shell: bash
run: |
mkdir -p models
- name: Build with PyInstaller (Windows)
if: runner.os == 'Windows'
run: |
pyinstaller -w --onefile -n PhotoSort `
--icon assets/app_icon.ico `
--paths src `
--hidden-import core.build_info `
--add-data "src/ui/dark_theme.qss;." `
--add-data "assets/app_icon.ico;." `
--add-data "assets/app_icon.png;." `
--hidden-import pyexiv2 `
--hidden-import PyQt6.QtCore `
--hidden-import PyQt6.QtGui `
--hidden-import PyQt6.QtWidgets `
--hidden-import rawpy `
--hidden-import cv2 `
--hidden-import onnxruntime `
--hidden-import torchvision `
--hidden-import torch `
--hidden-import sklearn `
--hidden-import sentence_transformers `
--add-data "models;models" `
--runtime-hook runtime_hook.py `
src/main.py
- name: Collect artifact (Windows .exe)
if: runner.os == 'Windows'
shell: pwsh
run: |
$exe = "${{ matrix.artifact_name }}.exe"
Copy-Item "dist\PhotoSort.exe" $exe -Force
- name: Generate SHA256 checksum (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$exe = "${{ matrix.artifact_name }}.exe"
$hash = (Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()
"$hash $exe" | Out-File -Encoding ascii "$exe.sha256"
- name: Build with PyInstaller (macOS)
if: runner.os == 'macOS'
run: |
pyinstaller -w --name PhotoSort \
--icon assets/photosort.icns \
--paths src \
--hidden-import core.build_info \
--add-data src/ui/dark_theme.qss:. \
--add-data assets/app_icon.ico:. \
--add-data assets/app_icon.png:. \
--hidden-import PyQt6.QtCore \
--hidden-import PyQt6.QtGui \
--hidden-import PyQt6.QtWidgets \
--hidden-import rawpy \
--hidden-import pyexiv2 \
--hidden-import cv2 \
--hidden-import onnxruntime \
--hidden-import torchvision \
--hidden-import torch \
--hidden-import sklearn \
--hidden-import sentence_transformers \
--add-data models:models \
src/main.py
- name: Package artifact (macOS .dmg)
if: runner.os == 'macOS'
shell: bash
run: |
DMG="${{ matrix.artifact_name }}.dmg"
APP_PATH="dist/PhotoSort.app"
if [ -d "$APP_PATH" ]; then
hdiutil create -volname "PhotoSort" -srcfolder "$APP_PATH" -ov -format UDZO "$DMG"
else
echo "PhotoSort.app not found in dist" >&2
exit 1
fi
- name: Generate SHA256 checksum (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
DMG="${{ matrix.artifact_name }}.dmg"
shasum -a 256 "$DMG" > "$DMG.sha256"
- name: Archive artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_name }}.*
retention-days: 14
release:
if: startsWith(github.ref, 'refs/tags/')
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/**
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}