From 5be8ab9af75a7e1bd315c23584bc30464a37e146 Mon Sep 17 00:00:00 2001 From: benya Date: Sun, 15 Feb 2026 22:34:03 +0300 Subject: [PATCH] fix(ci): improve release build diagnostics and encoding - run build.py with UTF-8 env in release workflow - capture full build output to dist/build.log and print it on failure - extend ISCC output decoding in build.py with UTF-16 fallbacks --- .gitea/workflows/release.yml | 14 +++++++- build.py | 66 ++++++++++++++++++------------------ 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index ed6e818..a0324c9 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -114,8 +114,20 @@ jobs: - name: Build release zip if: env.CONTINUE == 'true' shell: powershell + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" run: | - python build.py + $ErrorActionPreference = "Continue" + $buildLog = "dist/build.log" + New-Item -ItemType Directory -Force -Path "dist" | Out-Null + python build.py *>&1 | Tee-Object -FilePath $buildLog + $code = $LASTEXITCODE + if ($code -ne 0) { + Write-Host "Build failed with exit code $code. Dumping build log:" + Get-Content -Path $buildLog -Raw + exit $code + } - name: Ensure artifacts exist if: env.CONTINUE == 'true' diff --git a/build.py b/build.py index 29594a2..25c6349 100644 --- a/build.py +++ b/build.py @@ -4,7 +4,7 @@ import subprocess import sys from app_version import APP_VERSION -# --- Конфигурация --- +# --- Configuration --- APP_NAME = "AnabasisManager" UPDATER_NAME = "AnabasisUpdater" VERSION = APP_VERSION # Единая версия приложения @@ -32,38 +32,38 @@ def write_version_marker(): os.makedirs(DIST_DIR, exist_ok=True) with open(marker_path, "w", encoding="utf-8") as f: f.write(str(VERSION).strip() + "\n") - print(f"[OK] Обновлен маркер версии: {marker_path}") + print(f"[OK] Version marker written: {marker_path}") except Exception as e: - print(f"[ERROR] Не удалось записать version.txt: {e}") + print(f"[ERROR] Failed to write version.txt: {e}") sys.exit(1) def copy_icon_to_dist(): icon_abs_path = os.path.abspath(ICON_PATH) if not os.path.exists(icon_abs_path): - print("[WARN] icon.ico не найден, пропуск копирования иконки в dist.") + print("[WARN] icon.ico not found, skipping icon copy into dist.") return try: os.makedirs("dist", exist_ok=True) os.makedirs(DIST_DIR, exist_ok=True) shutil.copy2(icon_abs_path, os.path.join("dist", "icon.ico")) shutil.copy2(icon_abs_path, os.path.join(DIST_DIR, "icon.ico")) - print("[OK] Иконка скопирована в dist/icon.ico и dist/AnabasisManager/icon.ico") + print("[OK] Icon copied to dist/icon.ico and dist/AnabasisManager/icon.ico") except Exception as e: - print(f"[ERROR] Не удалось скопировать icon.ico в dist: {e}") + print(f"[ERROR] Failed to copy icon.ico into dist: {e}") sys.exit(1) def ensure_project_root(): missing = [name for name in SAFE_CLEAN_ROOT_FILES if not os.path.exists(name)] if missing: - print("[ERROR] Скрипт нужно запускать из корня проекта.") - print(f"[ERROR] Не найдены: {', '.join(missing)}") + print("[ERROR] Run this script from the project root.") + print(f"[ERROR] Missing files: {', '.join(missing)}") sys.exit(1) def run_build(): - print(f"--- 1. Запуск PyInstaller для {APP_NAME} v{VERSION} ---") + print(f"--- 1. Running PyInstaller for {APP_NAME} v{VERSION} ---") icon_abs_path = os.path.abspath(ICON_PATH) has_icon = os.path.exists(icon_abs_path) @@ -86,14 +86,14 @@ def run_build(): try: subprocess.check_call(command) - print("\n[OK] Сборка PyInstaller завершена.") + print("\n[OK] PyInstaller build completed.") except subprocess.CalledProcessError as e: - print(f"\n[ERROR] Ошибка при сборке: {e}") + print(f"\n[ERROR] Build failed: {e}") sys.exit(1) def run_updater_build(): - print(f"\n--- 1.2 Сборка {UPDATER_NAME} ---") + print(f"\n--- 1.2 Building {UPDATER_NAME} ---") icon_abs_path = os.path.abspath(ICON_PATH) has_icon = os.path.exists(icon_abs_path) updater_spec_dir = os.path.join("build", "updater_spec") @@ -116,14 +116,14 @@ def run_updater_build(): command = [arg for arg in command if arg] try: subprocess.check_call(command) - print(f"[OK] {UPDATER_NAME} собран.") + print(f"[OK] {UPDATER_NAME} built.") except subprocess.CalledProcessError as e: - print(f"[ERROR] Ошибка при сборке {UPDATER_NAME}: {e}") + print(f"[ERROR] Failed to build {UPDATER_NAME}: {e}") sys.exit(1) def run_cleanup(): - print(f"\n--- 2. Оптимизация папки {APP_NAME} ---") + print(f"\n--- 2. Optimizing {APP_NAME} folder ---") # Пытаемся найти папку PySide6 внутри сборки pyside_path = os.path.join(DIST_DIR, "PySide6") @@ -138,21 +138,21 @@ def run_cleanup(): shutil.rmtree(path) else: os.remove(path) - print(f"Удалено: {item}") + print(f"Removed: {item}") except Exception as e: - print(f"Пропуск {item}: {e}") + print(f"Skipped {item}: {e}") def create_archive(): - print(f"\n--- 3. Создание архива {ARCHIVE_NAME}.zip ---") + print(f"\n--- 3. Creating archive {ARCHIVE_NAME}.zip ---") try: # Создаем zip-архив из папки DIST_DIR # base_name - имя файла без расширения, format - 'zip', root_dir - что упаковываем shutil.make_archive(os.path.join("dist", ARCHIVE_NAME), 'zip', DIST_DIR) - print(f"[OK] Архив создан: dist/{ARCHIVE_NAME}.zip") + print(f"[OK] Archive created: dist/{ARCHIVE_NAME}.zip") except Exception as e: - print(f"[ERROR] Не удалось создать архив: {e}") + print(f"[ERROR] Failed to create archive: {e}") def _find_iscc(): @@ -175,7 +175,7 @@ def _decode_process_output(raw_bytes): return "" if isinstance(raw_bytes, str): return raw_bytes - for enc in ("utf-8", "cp1251", "cp866"): + for enc in ("utf-8-sig", "utf-16", "utf-16-le", "cp1251", "cp866", "latin-1"): try: return raw_bytes.decode(enc) except Exception: @@ -184,20 +184,20 @@ def _decode_process_output(raw_bytes): def build_installer(): - print(f"\n--- 4. Создание установщика {INSTALLER_NAME} ---") + print(f"\n--- 4. Building installer {INSTALLER_NAME} ---") if os.name != "nt": - print("[INFO] Установщик Inno Setup создается только на Windows. Шаг пропущен.") + print("[INFO] Inno Setup installer is built only on Windows. Step skipped.") return if not os.path.exists(INSTALLER_SCRIPT): - print(f"[ERROR] Не найден скрипт установщика: {INSTALLER_SCRIPT}") + print(f"[ERROR] Installer script not found: {INSTALLER_SCRIPT}") sys.exit(1) if not os.path.exists(DIST_DIR): - print(f"[ERROR] Не найдена папка сборки приложения: {DIST_DIR}") + print(f"[ERROR] Build output folder not found: {DIST_DIR}") sys.exit(1) iscc_path = _find_iscc() if not iscc_path: - print("[ERROR] Не найден Inno Setup Compiler (ISCC.exe).") - print("[ERROR] Установите Inno Setup 6 или задайте переменную окружения ISCC_PATH.") + print("[ERROR] Inno Setup Compiler (ISCC.exe) not found.") + print("[ERROR] Install Inno Setup 6 or set ISCC_PATH environment variable.") sys.exit(1) icon_abs_path = os.path.abspath(ICON_PATH) @@ -224,11 +224,11 @@ def build_installer(): raise RuntimeError(f"ISCC exited with code {completed.returncode}") installer_path = os.path.join("dist", INSTALLER_NAME) if not os.path.exists(installer_path): - print(f"[ERROR] Установщик не создан: {installer_path}") + print(f"[ERROR] Installer was not created: {installer_path}") sys.exit(1) - print(f"[OK] Установщик создан: {installer_path}") + print(f"[OK] Installer created: {installer_path}") except Exception as e: - print(f"[ERROR] Ошибка при создании установщика: {e}") + print(f"[ERROR] Failed to build installer: {e}") sys.exit(1) @@ -248,7 +248,7 @@ if __name__ == "__main__": build_installer() print("\n" + "=" * 30) - print("ПРОЦЕСС ЗАВЕРШЕН") - print(f"Файл для отправки: dist/{ARCHIVE_NAME}.zip") - print(f"Установщик: dist/{INSTALLER_NAME}") + print("BUILD COMPLETED") + print(f"Release archive: dist/{ARCHIVE_NAME}.zip") + print(f"Installer: dist/{INSTALLER_NAME}") print("=" * 30)