← К списку статей

Как подготовить Python desktop-приложение к нормальным релизам

Практический разбор: PyInstaller, GitHub Actions, релизные ZIP-архивы, версия приложения и проверка обновлений без лишней магии.

У desktop-приложений на Python часто одна и та же проблема: код уже работает, интерфейс готов, но релиз все еще выглядит как ручной ритуал. Собрать exe, не забыть иконку, положить файл в архив, создать GitHub Release, проверить, что пользователь скачает правильную версию. Пока проект маленький, это терпимо. После нескольких правок такой процесс начинает мешать.

На примере учебного Tkinter-приложения я разложу, как сделать релизный контур спокойнее: один тег в Git, автоматическая сборка Windows exe, ZIP в GitHub Release и проверка обновлений внутри приложения.

Главное окно учебного валидатора API-ключей
Пример desktop-приложения на Tkinter, для которого настроен релизный контур.

Почему ручная сборка быстро начинает мешать

Ручная сборка почти всегда ломается не в сложном месте, а в мелочах. Один раз забыли обновить версию. Другой раз собрали exe с консольным окном. Потом отправили пользователю старый архив, потому что имя файла осталось прежним.

Для внутреннего инструмента это тоже проблема. Если пользователь не понимает, какая версия у него стоит и где брать новую, поддержка превращается в переписку: “скачайте еще раз”, “нет, не этот файл”, “удалите старую папку”.

Минимальный релизный контур

Для небольшого Python desktop-приложения обычно достаточно такого контура:

  1. версия приложения хранится в коде;
  2. релиз запускается Git-тегом вида v1.0.0;
  3. GitHub Actions собирает exe на Windows runner;
  4. PyInstaller упаковывает entrypoint в один оконный файл;
  5. workflow кладет exe в ZIP и загружает архив в GitHub Release;
  6. приложение умеет проверить последний Release и показать ссылку на скачивание.

Это не сложная система обновлений. Приложение не заменяет само себя на диске, не трогает запущенный exe и не требует отдельного updater-процесса. Оно только сообщает, что вышла новая версия, и открывает страницу загрузки. Для первого стабильного варианта этого обычно хватает.

Сборка через PyInstaller

У PyInstaller есть два важных режима для такого сценария: --onefile и --windowed. Первый собирает приложение в один exe. Второй убирает консольное окно, что нормально для Tkinter-интерфейса.

pyinstaller --noconfirm --clean --onefile --windowed ^
  --name OpenAiKeyChecker ^
  --icon assets/app.ico ^
  --add-data "assets/app.ico;assets" ^
  main.py

Отдельно стоит подумать о путях. В onefile-сборке PyInstaller распаковывает ресурсы во временную директорию. Если приложение хранит SQLite-базу рядом с исходниками, в exe это может внезапно стать временная папка. Поэтому для собранной версии лучше явно хранить пользовательские данные рядом с exe, а ресурсы читать из PyInstaller bundle.

Что Где хранить Почему
Иконка Внутри bundle Это ресурс приложения, он не меняется пользователем.
SQLite-база Рядом с exe Пользовательские данные должны переживать перезапуск и обновление.
ZIP релиза GitHub Release У пользователя должна быть одна понятная точка скачивания.

GitHub Actions: релиз по тегу

Самый простой триггер для релизов — push тега. Разработчик делает обычный коммит, затем создает тег:

git tag v1.0.0
git push origin v1.0.0

Workflow запускается только на такие теги, ставит зависимости, прогоняет тесты, подставляет версию из тега и собирает exe. После этого архив загружается в GitHub Release.

Ключевой момент — не пропускать тесты. Даже если проект небольшой, релизная сборка должна хотя бы проверить чистую логику: генерацию тестовых строк, работу SQLite-хранилища, HTTP-клиент через mock и проверку обновлений.

Релиз должен быть результатом повторяемого сценария, а не удачного запуска команды на локальной машине.

Проверка обновлений без самозамены

Полный автоапдейтер для Windows — отдельная задача. Нужно закрывать приложение, заменять exe, учитывать права доступа, антивирусы, недокачанные файлы и rollback. Для небольшого инструмента это часто лишний риск.

Более безопасный первый шаг — проверка GitHub Releases API:

https://api.github.com/repos/OWNER/REPO/releases/latest

Приложение сравнивает текущую версию с последним тегом. Если версия новее, показывает диалог и открывает страницу скачивания. Пользователь сам скачивает ZIP и заменяет приложение, когда ему удобно.

Что важно не забыть

  • добавить build/, dist/ и *.spec в .gitignore;
  • не коммитить локальную SQLite-базу;
  • проверять workflow на реальном теге, а не только локально;
  • показывать версию в окне “О программе”;
  • документировать, где лежит ZIP и как выпускать новый релиз;
  • не усложнять автообновление, пока нет реальной причины.

Итог

Хороший релизный контур для Python desktop-приложения не обязан быть большим. Достаточно, чтобы сборка была повторяемой, версия была видна пользователю, архив появлялся в GitHub Release автоматически, а приложение умело сообщить о новой версии.

После этого релиз перестает быть ручной операцией и становится частью обычного процесса разработки: коммит, тег, проверка workflow, готовый ZIP.