mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-05-06 20:02:51 +08:00
feat(core): 支持PyInstaller打包并优化资源路径
- 修改app.py以支持PyInstaller打包后的资源路径 - 更新session.py以支持APP_DATA_DIR环境变量 - 增强webui.py以设置打包后的数据目录 - 添加pyproject.toml的PyInstaller依赖组 - 新增构建脚本和GitHub Actions工作流
This commit is contained in:
105
.github/workflows/build.yml
vendored
Normal file
105
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
name: 多平台打包发布
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: '版本号 (如 v1.0.0)'
|
||||
required: false
|
||||
default: 'dev'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: 打包 ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
artifact_name: codex-register.exe
|
||||
asset_name: codex-register-windows-x64.exe
|
||||
- os: ubuntu-latest
|
||||
artifact_name: codex-register
|
||||
asset_name: codex-register-linux-x64
|
||||
- os: macos-latest
|
||||
artifact_name: codex-register
|
||||
asset_name: codex-register-macos-arm64
|
||||
|
||||
steps:
|
||||
- name: 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 设置 Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pip'
|
||||
|
||||
- name: 安装依赖
|
||||
run: |
|
||||
pip install -r requirements.txt pyinstaller
|
||||
|
||||
- name: 打包
|
||||
run: |
|
||||
pyinstaller codex_register.spec --clean --noconfirm
|
||||
|
||||
- name: 上传构建产物
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.asset_name }}
|
||||
path: dist/${{ matrix.artifact_name }}
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
name: 创建发布
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: 下载所有构建产物
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist/
|
||||
|
||||
- name: 整理文件
|
||||
run: |
|
||||
mkdir -p release
|
||||
find dist/ -type f | while read f; do
|
||||
name=$(basename "$f")
|
||||
cp "$f" "release/$name"
|
||||
done
|
||||
ls -lh release/
|
||||
|
||||
- name: 创建 GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: release/*
|
||||
generate_release_notes: true
|
||||
body: |
|
||||
## OpenAI/Codex CLI 自动注册系统
|
||||
|
||||
### 下载说明
|
||||
| 平台 | 文件 |
|
||||
|------|------|
|
||||
| Windows x64 | `codex-register-windows-x64.exe` |
|
||||
| Linux x64 | `codex-register-linux-x64` |
|
||||
| macOS ARM64 | `codex-register-macos-arm64` |
|
||||
|
||||
### 使用方法
|
||||
```bash
|
||||
# Linux/macOS 需要先赋予执行权限
|
||||
chmod +x codex-register-*
|
||||
|
||||
# 启动 Web UI
|
||||
./codex-register
|
||||
|
||||
# 指定端口
|
||||
./codex-register --port 8080
|
||||
```
|
||||
20
build.bat
Normal file
20
build.bat
Normal file
@@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
REM Windows 打包脚本
|
||||
|
||||
echo === 构建平台: Windows ===
|
||||
|
||||
REM 安装打包依赖
|
||||
pip install pyinstaller --quiet
|
||||
|
||||
REM 执行打包
|
||||
pyinstaller codex_register.spec --clean --noconfirm
|
||||
|
||||
IF EXIST dist\codex-register.exe (
|
||||
FOR /F "tokens=*" %%i IN ('powershell -Command "[System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture"') DO SET ARCH=%%i
|
||||
SET OUTPUT=dist\codex-register-windows-%ARCH%.exe
|
||||
MOVE dist\codex-register.exe "%OUTPUT%"
|
||||
echo === 构建完成: %OUTPUT% ===
|
||||
) ELSE (
|
||||
echo === 构建失败,未找到输出文件 ===
|
||||
exit /b 1
|
||||
)
|
||||
49
build.sh
Normal file
49
build.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
# 跨平台打包脚本(在各平台上分别运行)
|
||||
|
||||
set -e
|
||||
|
||||
OS=$(uname -s)
|
||||
ARCH=$(uname -m)
|
||||
|
||||
case "$OS" in
|
||||
Darwin)
|
||||
PLATFORM="macos"
|
||||
EXT=""
|
||||
;;
|
||||
Linux)
|
||||
PLATFORM="linux"
|
||||
EXT=""
|
||||
;;
|
||||
MINGW*|CYGWIN*|MSYS*)
|
||||
PLATFORM="windows"
|
||||
EXT=".exe"
|
||||
;;
|
||||
*)
|
||||
PLATFORM="$OS"
|
||||
EXT=""
|
||||
;;
|
||||
esac
|
||||
|
||||
OUTPUT_NAME="codex-register-${PLATFORM}-${ARCH}${EXT}"
|
||||
|
||||
echo "=== 构建平台: ${PLATFORM} (${ARCH}) ==="
|
||||
echo "=== 输出文件: dist/${OUTPUT_NAME} ==="
|
||||
|
||||
# 安装打包依赖
|
||||
pip install pyinstaller --quiet 2>/dev/null || \
|
||||
uv run --with pyinstaller pyinstaller --version > /dev/null 2>&1
|
||||
|
||||
# 执行打包(优先用 uv,回退到直接调用)
|
||||
if command -v uv &>/dev/null; then
|
||||
uv run --with pyinstaller pyinstaller codex_register.spec --clean --noconfirm
|
||||
else
|
||||
pyinstaller codex_register.spec --clean --noconfirm
|
||||
fi
|
||||
|
||||
# 重命名输出文件
|
||||
mv dist/codex-register${EXT} dist/${OUTPUT_NAME} 2>/dev/null || \
|
||||
mv "dist/codex-register" "dist/${OUTPUT_NAME}" 2>/dev/null || true
|
||||
|
||||
echo "=== 构建完成: dist/${OUTPUT_NAME} ==="
|
||||
ls -lh dist/${OUTPUT_NAME}
|
||||
@@ -22,7 +22,6 @@ dev = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
codex-register = "cli:main"
|
||||
codex-webui = "webui:main"
|
||||
|
||||
[build-system]
|
||||
@@ -31,3 +30,8 @@ build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src"]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pyinstaller>=6.19.0",
|
||||
]
|
||||
|
||||
@@ -20,14 +20,14 @@ class DatabaseSessionManager:
|
||||
|
||||
def __init__(self, database_url: str = None):
|
||||
if database_url is None:
|
||||
# 默认使用项目根目录下的 SQLite 数据库
|
||||
db_path = os.path.join(
|
||||
# 优先使用 APP_DATA_DIR 环境变量(PyInstaller 打包后由 webui.py 设置)
|
||||
data_dir = os.environ.get('APP_DATA_DIR') or os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
|
||||
'data',
|
||||
'database.db'
|
||||
'data'
|
||||
)
|
||||
db_path = os.path.join(data_dir, 'database.db')
|
||||
# 确保目录存在
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
database_url = f"sqlite:///{db_path}"
|
||||
|
||||
self.database_url = database_url
|
||||
|
||||
@@ -4,6 +4,7 @@ FastAPI 应用主文件
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
@@ -21,11 +22,15 @@ from .task_manager import task_manager
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 获取项目根目录
|
||||
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
||||
# PyInstaller 打包后静态资源在 sys._MEIPASS,开发时在源码根目录
|
||||
if getattr(sys, 'frozen', False):
|
||||
_RESOURCE_ROOT = Path(sys._MEIPASS)
|
||||
else:
|
||||
_RESOURCE_ROOT = Path(__file__).parent.parent.parent
|
||||
|
||||
# 静态文件和模板目录
|
||||
STATIC_DIR = PROJECT_ROOT / "static"
|
||||
TEMPLATES_DIR = PROJECT_ROOT / "templates"
|
||||
STATIC_DIR = _RESOURCE_ROOT / "static"
|
||||
TEMPLATES_DIR = _RESOURCE_ROOT / "templates"
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
|
||||
35
webui.py
35
webui.py
@@ -8,8 +8,16 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到 Python 路径
|
||||
project_root = Path(__file__).parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
# PyInstaller 打包后 __file__ 在临时解压目录,需要用 sys.executable 所在目录作为数据目录
|
||||
import os
|
||||
if getattr(sys, 'frozen', False):
|
||||
# 打包后:使用可执行文件所在目录
|
||||
project_root = Path(sys.executable).parent
|
||||
_src_root = Path(sys._MEIPASS)
|
||||
else:
|
||||
project_root = Path(__file__).parent
|
||||
_src_root = project_root
|
||||
sys.path.insert(0, str(_src_root))
|
||||
|
||||
from src.core.utils import setup_logging
|
||||
from src.database.init_db import initialize_database
|
||||
@@ -18,6 +26,16 @@ from src.config.settings import get_settings
|
||||
|
||||
def setup_application():
|
||||
"""设置应用程序"""
|
||||
# 确保数据目录和日志目录在可执行文件所在目录(打包后也适用)
|
||||
data_dir = project_root / "data"
|
||||
logs_dir = project_root / "logs"
|
||||
data_dir.mkdir(exist_ok=True)
|
||||
logs_dir.mkdir(exist_ok=True)
|
||||
|
||||
# 将数据目录路径注入环境变量,供数据库配置使用
|
||||
os.environ.setdefault("APP_DATA_DIR", str(data_dir))
|
||||
os.environ.setdefault("APP_LOGS_DIR", str(logs_dir))
|
||||
|
||||
# 初始化数据库(必须先于获取设置)
|
||||
try:
|
||||
initialize_database()
|
||||
@@ -28,23 +46,16 @@ def setup_application():
|
||||
# 获取配置(需要数据库已初始化)
|
||||
settings = get_settings()
|
||||
|
||||
# 配置日志
|
||||
# 配置日志(日志文件写到实际 logs 目录)
|
||||
log_file = str(logs_dir / Path(settings.log_file).name)
|
||||
setup_logging(
|
||||
log_level=settings.log_level,
|
||||
log_file=settings.log_file
|
||||
log_file=log_file
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info("数据库初始化完成")
|
||||
|
||||
# 检查数据目录
|
||||
data_dir = project_root / "data"
|
||||
data_dir.mkdir(exist_ok=True)
|
||||
logger.info(f"数据目录: {data_dir}")
|
||||
|
||||
# 检查日志目录
|
||||
logs_dir = project_root / "logs"
|
||||
logs_dir.mkdir(exist_ok=True)
|
||||
logger.info(f"日志目录: {logs_dir}")
|
||||
|
||||
logger.info("应用程序设置完成")
|
||||
|
||||
Reference in New Issue
Block a user