Đóng gói & Phân phối
Bài học này hướng dẫn cách đóng gói ứng dụng thành file EXE di động để phân phối cho người dùng cuối.
1. Tổng quan
1.1. Mục tiêu
Tạo Gói di động mà người dùng chỉ cần:
- Giải nén
- Tạo file
.envvới khóa API - Chạy
GuideAI.exe
1.2. Thành phần gói
dist/GuideAI/
├── GuideAI.exe # File thực thi chính
├── browsers/ # Chromium browser (~300MB)
│ ├── chromium-xxxx/
│ └── ffmpeg-xxxx/
├── .env.example # Template cấu hình
├── README.txt # Hướng dẫn cho người dùng
└── [các file hỗ trợ]2. File cấu hình PyInstaller
2.1. Cấu trúc guide_ai.spec
python
# -*- mode: python ; coding: utf-8 -*-
"""
PyInstaller spec file for Guide AI Tool
Build command: pyinstaller guide_ai.spec
"""
import sys
import os
from pathlib import Path
block_cipher = None
# Tìm đường dẫn Playwright browsers
def get_playwright_browsers_path():
"""Tìm thư mục chứa Playwright browsers"""
possible_paths = [
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'ms-playwright'),
os.path.join(os.environ.get('USERPROFILE', ''), '.cache', 'ms-playwright'),
]
for path in possible_paths:
if os.path.exists(path):
return path
return None2.2. Các file dữ liệu
python
# Collect data files
datas = [
('src', 'src'), # Source modules
('.env.example', '.'), # Config template
]
# Thêm Playwright driver (QUAN TRỌNG cho recording)
playwright_pkg = get_playwright_package_path()
if playwright_pkg:
driver_path = os.path.join(playwright_pkg, 'driver')
if os.path.exists(driver_path):
datas.append((driver_path, 'playwright/driver'))
print(f"[INFO] Bundling Playwright driver from: {driver_path}")2.3. Import ẩn
python
hiddenimports=[
# Playwright
'playwright',
'playwright.sync_api',
'playwright._impl',
'playwright._impl._driver',
# AI Libraries
'google.generativeai',
'google.ai',
'openai',
# Utilities
'PIL', 'PIL.Image',
'jinja2',
'dotenv',
'requests',
# Internal modules
'src.analyzer',
'src.config_manager',
'src.doc_generator',
'src.models',
'src.orchestrator',
'src.parser',
'src.recorder',
'src.replayer',
'src.uploader',
'src.auth_manager',
]2.4. Loại trừ (Giảm kích thước)
python
excludes=[
'pytest',
'pytest_mock',
'hypothesis',
'tests',
'tkinter',
'matplotlib',
'numpy',
'pandas',
]2.5. Cấu hình EXE
python
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='GuideAI',
debug=False,
strip=False,
upx=True, # Nén với UPX
console=True, # Console app (không phải GUI)
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='GuideAI',
)3. Hook thời gian chạy
3.1. runtime_hook.py
File này chạy trước khi ứng dụng khởi động:
python
import os
import sys
def setup_environment():
"""Setup environment cho bundled EXE."""
if getattr(sys, 'frozen', False):
# Đang chạy từ EXE
exe_dir = os.path.dirname(sys.executable)
# Set PLAYWRIGHT_BROWSERS_PATH
browsers_path = os.path.join(exe_dir, 'browsers')
if os.path.exists(browsers_path):
os.environ['PLAYWRIGHT_BROWSERS_PATH'] = browsers_path
# Set working directory
os.chdir(exe_dir)
setup_environment()4. Script đóng gói
4.1. build_exe.bat (Windows)
batch
@echo off
echo ========================================
echo Building GuideAI Portable Package
echo ========================================
REM Activate venv
call .venv\Scripts\activate
REM Clean previous build
rmdir /s /q dist 2>nul
rmdir /s /q build 2>nul
REM Build with PyInstaller
pyinstaller guide_ai.spec --clean
REM Copy browsers
echo Copying Playwright browsers...
xcopy /E /I "%LOCALAPPDATA%\ms-playwright" "dist\GuideAI\browsers"
REM Copy config files
copy .env.example dist\GuideAI\
copy README.txt dist\GuideAI\ 2>nul
echo ========================================
echo Build complete! Output: dist\GuideAI\
echo ========================================4.2. build_exe.ps1 (PowerShell)
powershell
Write-Host "Building GuideAI Portable Package" -ForegroundColor Cyan
# Activate venv
& .\.venv\Scripts\Activate.ps1
# Clean
Remove-Item -Recurse -Force dist, build -ErrorAction SilentlyContinue
# Build
pyinstaller guide_ai.spec --clean
# Copy browsers
$browsersPath = "$env:LOCALAPPDATA\ms-playwright"
if (Test-Path $browsersPath) {
Copy-Item -Recurse $browsersPath "dist\GuideAI\browsers"
}
# Copy configs
Copy-Item .env.example "dist\GuideAI\"
Write-Host "Build complete!" -ForegroundColor Green5. Xử lý Playwright trong EXE
5.1. Phát hiện chế độ đóng gói
python
def is_bundled_exe() -> bool:
"""Check if running as bundled EXE."""
return getattr(sys, 'frozen', False)5.2. Lấy driver Playwright
python
def get_playwright_driver() -> tuple:
"""Get Playwright driver paths."""
if is_bundled_exe():
base_path = getattr(sys, '_MEIPASS', os.path.dirname(sys.executable))
exe_dir = os.path.dirname(sys.executable)
search_paths = [
os.path.join(base_path, 'playwright', 'driver'),
os.path.join(exe_dir, 'playwright', 'driver'),
]
for driver_path in search_paths:
node_exe = os.path.join(driver_path, 'node.exe')
cli_js = os.path.join(driver_path, 'package', 'cli.js')
if os.path.exists(node_exe) and os.path.exists(cli_js):
return (node_exe, cli_js)
raise FileNotFoundError("Playwright driver not found")
# Normal Python
from playwright._impl._driver import compute_driver_executable
return compute_driver_executable()5.3. Bắt đầu ghi từ EXE
python
def start_recording(url: str, output_path: Path) -> bool:
if is_bundled_exe():
node_exe, cli_js = get_playwright_driver()
cmd = [node_exe, cli_js, "codegen",
"--target", "python", "-o", str(output_path), url]
else:
cmd = [sys.executable, "-m", "playwright", "codegen",
"--target", "python", "-o", str(output_path), url]
subprocess.run(cmd, check=True)6. Kích thước & Tối ưu
6.1. Phân tích kích thước
| Component | Size | Ghi chú |
|---|---|---|
| GuideAI.exe | ~50MB | Python + dependencies |
| Chromium browser | ~300MB | Playwright browser |
| Node.js driver | ~50MB | Playwright driver |
| Total | ~400-500MB | Sau nén: ~200MB |
6.2. Tối ưu kích thước
python
# Trong spec file
excludes=[
'pytest', 'hypothesis', # Testing
'tkinter', # GUI (không dùng)
'matplotlib', 'numpy', # Data science
]7. Phân phối
7.1. Đóng gói
batch
REM Nén thành ZIP
powershell Compress-Archive -Path dist\GuideAI -DestinationPath GuideAI-v1.0.zip7.2. Hướng dẫn cho người dùng
README.txt
==========
HƯỚNG DẪN SỬ DỤNG GUIDEAI
1. Giải nén file GuideAI-v1.0.zip
2. Tạo file .env trong thư mục GuideAI với nội dung:
GOOGLE_API_KEY=your_api_key_here
(Lấy API key miễn phí tại: https://aistudio.google.com/apikey)
3. Chạy GuideAI.exe
4. Làm theo hướng dẫn trên màn hình8. Xử lý sự cố
8.1. "Không tìm thấy trình duyệt"
Nguyên nhân: Thư mục browsers/ không được sao chép
Giải pháp:
1. Kiểm tra dist/GuideAI/browsers/ tồn tại
2. Nếu không, sao chép thủ công từ %LOCALAPPDATA%\ms-playwright8.2. Phần mềm diệt virus cảnh báo
Nguyên nhân: EXE từ PyInstaller thường bị cảnh báo nhầm
Giải pháp:
1. Thêm ngoại lệ trong phần mềm diệt virus
2. Hoặc ký EXE với chứng chỉ ký mã8.3. "Không tìm thấy GOOGLE_API_KEY"
Nguyên nhân: Chưa tạo file .env
Giải pháp:
1. Sao chép .env.example thành .env
2. Điền khóa API vào file .env9. Ma trận tương thích
| Windows | Hỗ trợ | Ghi chú |
|---|---|---|
| Windows 11 | ✅ | Đầy đủ |
| Windows 10 | ✅ | Đầy đủ |
| Windows 8.1 | ⚠️ | Có thể hoạt động |
| Windows 7 | ❌ | Không hỗ trợ |
Yêu cầu:
- Chỉ hỗ trợ 64-bit (x64)
- ~500MB dung lượng đĩa
- Kết nối internet (cho API AI)
Tổng kết
Quy trình đóng gói EXE di động:
- File spec PyInstaller: Cấu hình chi tiết các phụ thuộc
- Hook thời gian chạy: Thiết lập môi trường khi khởi động
- Đóng gói Playwright: Sao chép driver + trình duyệt
- Phân phối: Gói ZIP kèm hướng dẫn