Nuitka + GitHub Actions: standalone vs zip
Сравниваю два режима сборки Nuitka в CI: --onefile (standalone) и --module + zip-архив. Разбираю размер, время сборки, время запуска и поведение с C-расширениями.
Почему Nuitka
Nuitka компилирует Python в C, а затем в нативный бинарник. В отличие от PyInstaller (который просто пакует интерпретатор + байткод), Nuitka реально компилирует — это даёт:
- Быстрее запуск (нет декомпиляции байткода)
- Меньше размер (нет встроенного CPython)
- Сложнее реверс-инжиниринг
GitHub Actions workflow
name: build
on:
push:
tags: ['v*']
workflow_dispatch:
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
mode: [standalone, zip]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Nuitka
run: |
pip install nuitka ordered-set zstandard
- name: Build standalone
if: matrix.mode == 'standalone'
run: |
python -m nuitka \
--onefile \
--enable-plugin=numpy \
--include-package=app \
--include-data-dir=config=config \
--output-dir=dist \
--output-filename=app \
main.py
- name: Build zip
if: matrix.mode == 'zip'
run: |
python -m nuitka \
--module \
--enable-plugin=numpy \
--include-package=app \
--output-dir=dist \
main.py
cd dist
zip -r app.zip *.so *.pyd 2>/dev/null || zip -r app.zip *
- name: Upload
uses: actions/upload-artifact@v4
with:
name: app-${{ matrix.os }}-${{ matrix.mode }}
path: dist/
Сравнение
| Параметр | Standalone (onefile) | Zip (module) |
|---|---|---|
| Размер (Linux x86_64) | ~45 MB | ~12 MB |
| Время сборки (GHA runner) | ~4 min | ~2.5 min |
| Время запуска | ~800ms (распаковка) | ~150ms |
| Зависимости на целевой | Нет (всё внутри) | Python 3.12 + glibc |
| C-расширения | Включаются автоматически | Нужен --include-package |
| Кросс-компиляция | Нет | Нет |
Подводные камни
Standalone
--onefileраспаковывается в/tmpпри каждом запуске. На системах сnoexecна tmpfs — падает. Решение:--onefile-tempdir-spec.- Антивирусы (особенно Windows Defender) часто флагают onefile-бинарники как PUP.
--onefile-tempdir-spec=%LOCALAPPDATA%/appпомогает. - Нельзя обновить модуль без пересборки всего.
Zip
- На целевой машине должен быть Python той же версии (или совместимой).
- На Windows
.pyd— это DLL; нуженPATHилиos.add_dll_directory. - Легче реверсить (можно распаковать и посмотреть
.so).
Вывод
Для CLI-утилит, которые юзер скачивает один раз — standalone. Для библиотек/модулей, которые ставятся в существующее Python-окружение — zip. В CI лучше собирать оба и дать юзеру выбор.