mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-07-01 11:14:52 +02:00
ci: add CCMA release builds
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set SCRIPT_DIR=%~dp0
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%build.ps1"
|
||||
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo Build failed.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Build finished.
|
||||
pause
|
||||
@@ -0,0 +1,98 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$NoClean
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$repoRoot = Resolve-Path (Join-Path $scriptDir '..')
|
||||
Set-Location $repoRoot
|
||||
|
||||
function Get-VersionString {
|
||||
$versionFile = Join-Path $repoRoot 'VERSION'
|
||||
if (!(Test-Path $versionFile)) { return 'unknown' }
|
||||
return (Get-Content $versionFile -Raw).Trim()
|
||||
}
|
||||
|
||||
function Sanitize-FilePart([string]$text) {
|
||||
if ([string]::IsNullOrWhiteSpace($text)) { return 'unknown' }
|
||||
$s = $text.Trim()
|
||||
foreach ($ch in @('\','/','?',':','*','"','<','>','|')) {
|
||||
$s = $s.Replace($ch, '_')
|
||||
}
|
||||
$s = $s -replace '\s+', '_'
|
||||
return $s
|
||||
}
|
||||
|
||||
function Get-PlatformToken {
|
||||
return "windows"
|
||||
}
|
||||
|
||||
function Get-ArchToken {
|
||||
switch ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant()) {
|
||||
"x64" { return "amd64" }
|
||||
"arm64" { return "arm64" }
|
||||
default { return ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant()) }
|
||||
}
|
||||
}
|
||||
|
||||
$python = Join-Path $repoRoot '.venv\Scripts\python.exe'
|
||||
if (!(Test-Path $python)) {
|
||||
Write-Host "No .venv found. Creating venv..." -ForegroundColor Yellow
|
||||
python -m venv .venv
|
||||
if (!(Test-Path $python)) {
|
||||
throw "Failed to create venv; expected at $python"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Using Python: $python" -ForegroundColor Cyan
|
||||
|
||||
& $python -m pip install -r requirements.txt
|
||||
& $python -m pip install pyinstaller pillow
|
||||
|
||||
$pngIcon = Join-Path $repoRoot 'src\ccma\assets\icons\ccma.png'
|
||||
$icoOut = Join-Path $repoRoot 'build\app.ico'
|
||||
if (Test-Path $pngIcon) {
|
||||
Write-Host "Generating ICO: $icoOut" -ForegroundColor Cyan
|
||||
& $python -c @"
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
png = Path(r'$pngIcon')
|
||||
ico = Path(r'$icoOut')
|
||||
ico.parent.mkdir(parents=True, exist_ok=True)
|
||||
img = Image.open(png).convert('RGBA')
|
||||
img.save(ico, format='ICO', sizes=[(16,16),(24,24),(32,32),(48,48),(64,64),(128,128),(256,256)])
|
||||
print(str(ico))
|
||||
"@
|
||||
} else {
|
||||
Write-Host "PNG icon not found at: $pngIcon (building without icon)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
if (-not $NoClean) {
|
||||
if (Test-Path (Join-Path $repoRoot 'dist')) { Remove-Item -Recurse -Force (Join-Path $repoRoot 'dist') }
|
||||
if (Test-Path (Join-Path $repoRoot 'build\pyinstaller')) { Remove-Item -Recurse -Force (Join-Path $repoRoot 'build\pyinstaller') }
|
||||
}
|
||||
|
||||
Write-Host "Building with PyInstaller spec..." -ForegroundColor Cyan
|
||||
& $python -m PyInstaller --noconfirm --clean --distpath dist --workpath build\pyinstaller build\ccma.spec
|
||||
|
||||
$exe = Join-Path $repoRoot 'dist\ccma.exe'
|
||||
if (!(Test-Path $exe)) {
|
||||
throw "Build finished but exe not found at: $exe"
|
||||
}
|
||||
|
||||
$version = Get-VersionString
|
||||
$versionSafe = Sanitize-FilePart $version
|
||||
$platformToken = Get-PlatformToken
|
||||
$archToken = Get-ArchToken
|
||||
$versionedExe = Join-Path $repoRoot ("dist\ccma-$versionSafe-$platformToken-$archToken.exe")
|
||||
|
||||
Copy-Item -Force $exe $versionedExe
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Build OK" -ForegroundColor Green
|
||||
Write-Host "Base EXE: $exe" -ForegroundColor Green
|
||||
Write-Host "Versioned EXE: $versionedExe" -ForegroundColor Green
|
||||
Write-Host "Version: $version" -ForegroundColor Green
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
"""PyInstaller spec for CCMA.
|
||||
|
||||
Packaged output:
|
||||
- Windows: dist/ccma.exe
|
||||
- Linux: dist/ccma
|
||||
|
||||
Icon:
|
||||
- On Windows the spec auto-generates build/app.ico from the PNG icon if it does
|
||||
not already exist, so direct PyInstaller and scripted builds behave the same.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PyInstaller.utils.hooks import collect_data_files
|
||||
|
||||
block_cipher = None
|
||||
|
||||
spec_dir = Path(globals().get("SPECPATH", Path.cwd())).resolve()
|
||||
project_root = spec_dir.parent
|
||||
entry_script = project_root / "main.py"
|
||||
package_src = project_root / "src" / "ccma"
|
||||
assets_src = package_src / "assets"
|
||||
app_name_slug = "ccma"
|
||||
|
||||
win_icon_path = project_root / "build" / "app.ico"
|
||||
png_icon_path = assets_src / "icons" / "ccma.png"
|
||||
|
||||
if sys.platform == "win32" and not win_icon_path.exists() and png_icon_path.exists():
|
||||
try:
|
||||
from PIL import Image
|
||||
|
||||
img = Image.open(png_icon_path).convert("RGBA")
|
||||
win_icon_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
img.save(
|
||||
win_icon_path,
|
||||
format="ICO",
|
||||
sizes=[(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],
|
||||
)
|
||||
print(f"[spec] Generated ICO: {win_icon_path}")
|
||||
except Exception as exc:
|
||||
print(f"[spec] WARNING: ICO generation failed: {exc}")
|
||||
|
||||
win_icon = str(win_icon_path) if win_icon_path.exists() else None
|
||||
|
||||
datas: list[tuple[str, str]] = []
|
||||
if assets_src.exists():
|
||||
datas.append((str(assets_src), "ccma/assets"))
|
||||
|
||||
version_file = project_root / "VERSION"
|
||||
if version_file.exists():
|
||||
datas.append((str(version_file), "."))
|
||||
datas.append((str(version_file), "ccma"))
|
||||
|
||||
for optional_pkg in ("ttkbootstrap_icons", "ttkbootstrap_icons_mat"):
|
||||
try:
|
||||
datas += collect_data_files(optional_pkg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
hiddenimports = ["ttkbootstrap_icons", "ttkbootstrap_icons_mat"]
|
||||
|
||||
a = Analysis(
|
||||
[str(entry_script)],
|
||||
pathex=[str(project_root), str(project_root / "src")],
|
||||
binaries=[],
|
||||
datas=datas,
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name=app_name_slug,
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=win_icon,
|
||||
)
|
||||
Reference in New Issue
Block a user