This commit is contained in:
@@ -37,3 +37,9 @@ env/
|
|||||||
.idea/
|
.idea/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
# ── T-524 打包 / 本地提示词产物 ──
|
||||||
|
db.sqlite
|
||||||
|
prompts/
|
||||||
|
title_prompt.txt
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""PyInstaller spec for cmshopee Windows onedir builds.
|
||||||
|
|
||||||
|
Local user data is intentionally not bundled. The release package must not
|
||||||
|
include config files, SQLite databases, Chrome profiles, generated images,
|
||||||
|
logs, prompts, or other operator data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
["main.py"],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[
|
||||||
|
"PySide6.QtCore",
|
||||||
|
"PySide6.QtGui",
|
||||||
|
"PySide6.QtWidgets",
|
||||||
|
],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
exclude_binaries=True,
|
||||||
|
name="cmshopee",
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=False,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
)
|
||||||
|
coll = COLLECT(
|
||||||
|
exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
name="cmshopee",
|
||||||
|
)
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
| 运行日志 | SQLite `run_logs` / `run_log_events` | 已定 | ③ dry-run 与真实更新都留痕;结构化内容走脱敏 |
|
| 运行日志 | SQLite `run_logs` / `run_log_events` | 已定 | ③ dry-run 与真实更新都留痕;结构化内容走脱敏 |
|
||||||
| 图片处理 | `requests`(下载)+ `Pillow`(按分辨率/jpg质量存盘) | 部分待定 | 下载旧封面;新封面按 resolution 生成、jpg_quality 存盘 |
|
| 图片处理 | `requests`(下载)+ `Pillow`(按分辨率/jpg质量存盘) | 部分待定 | 下载旧封面;新封面按 resolution 生成、jpg_quality 存盘 |
|
||||||
| 测试 | `python -m compileall app main.py` + `unittest` + 手动 CDP/AI 验证 | 已定(分层) | 配置/DB/Excel/prompts 用单测;CDP/Shopee 与真实 AI 属集成验证或 mock |
|
| 测试 | `python -m compileall app main.py` + `unittest` + 手动 CDP/AI 验证 | 已定(分层) | 配置/DB/Excel/prompts 用单测;CDP/Shopee 与真实 AI 属集成验证或 mock |
|
||||||
|
| 打包分发 | PyInstaller onedir(`cmshopee.spec`) | 已定 | 产出 Windows 免安装文件夹;不内置配置、DB、图片、日志、Chrome 登录态或提示词等本地数据 |
|
||||||
|
|
||||||
## 二、决策记录与演进
|
## 二、决策记录与演进
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
- **T-504 更新执行增强**:③ 支持 dry-run 预览、运行日志和按账号并行;默认 dry-run 关闭、并行关闭,不引入新依赖。
|
- **T-504 更新执行增强**:③ 支持 dry-run 预览、运行日志和按账号并行;默认 dry-run 关闭、并行关闭,不引入新依赖。
|
||||||
- **不引入数据库(指外部 DB)**:用 stdlib SQLite 足够;不引入 Postgres/MySQL 等。
|
- **不引入数据库(指外部 DB)**:用 stdlib SQLite 足够;不引入 Postgres/MySQL 等。
|
||||||
- **生产在 Windows 直跑**:开发期我们用过 WSL→Windows 的 `netsh portproxy`(9333→9222)连 CDP;但 GUI 与 Chrome 都在 Windows 时,直接连 `127.0.0.1:9222`,无需 portproxy。
|
- **生产在 Windows 直跑**:开发期我们用过 WSL→Windows 的 `netsh portproxy`(9333→9222)连 CDP;但 GUI 与 Chrome 都在 Windows 时,直接连 `127.0.0.1:9222`,无需 portproxy。
|
||||||
|
- **PyInstaller 使用 onedir 免安装包**:T-524 先做 `dist/cmshopee/cmshopee.exe` + `_internal/` 的文件夹分发,不做自动更新器;打包版启动时工作目录切到 exe 所在目录,用户本地配置、DB、图片、日志、登录态和提示词保留在程序目录,更新时只覆盖程序文件。
|
||||||
|
|
||||||
## 三、构建与运行命令
|
## 三、构建与运行命令
|
||||||
|
|
||||||
@@ -51,6 +53,8 @@
|
|||||||
| 启动 GUI | `python main.py` / `python -m app` |
|
| 启动 GUI | `python main.py` / `python -m app` |
|
||||||
| 单元测试(T-006 后) | `python -m unittest discover -s tests` |
|
| 单元测试(T-006 后) | `python -m unittest discover -s tests` |
|
||||||
| CI 自动验证 | GitHub Actions `.github/workflows/tests.yml`(Windows + Python 3.11 + `QT_QPA_PLATFORM=offscreen`) |
|
| CI 自动验证 | GitHub Actions `.github/workflows/tests.yml`(Windows + Python 3.11 + `QT_QPA_PLATFORM=offscreen`) |
|
||||||
|
| 安装打包依赖 | `python -m pip install -r requirements-build.txt` |
|
||||||
|
| 打包 exe | `powershell -ExecutionPolicy Bypass -File scripts\\build_exe.ps1` |
|
||||||
| 跑单账号演示 | `python prototypes/demo.py`(分步)/ `set AUTO=1 && python prototypes/demo.py`(自动) |
|
| 跑单账号演示 | `python prototypes/demo.py`(分步)/ `set AUTO=1 && python prototypes/demo.py`(自动) |
|
||||||
| 提交更新(真改线上) | `set UPDATE=1 && python prototypes/demo.py` |
|
| 提交更新(真改线上) | `set UPDATE=1 && python prototypes/demo.py` |
|
||||||
| 读取某账号 Cookie(调试) | `python prototypes/cookies.py` |
|
| 读取某账号 Cookie(调试) | `python prototypes/cookies.py` |
|
||||||
@@ -71,6 +75,7 @@ set AUTO=1 && python prototypes/demo.py
|
|||||||
## 四、依赖纪律
|
## 四、依赖纪律
|
||||||
|
|
||||||
- 运行时第三方依赖统一写入根目录 `requirements.txt` 并锁定版本;换机或 CI 使用 `python -m pip install -r requirements.txt` 安装。
|
- 运行时第三方依赖统一写入根目录 `requirements.txt` 并锁定版本;换机或 CI 使用 `python -m pip install -r requirements.txt` 安装。
|
||||||
|
- 打包依赖只写入 `requirements-build.txt`;PyInstaller 不属于运行时依赖,不写进 `requirements.txt`。
|
||||||
- 当前直接依赖锁定:PySide6 6.5.3、openpyxl 3.1.3、requests 2.31.0、websocket-client 1.6.1、Pillow 9.5.0。
|
- 当前直接依赖锁定:PySide6 6.5.3、openpyxl 3.1.3、requests 2.31.0、websocket-client 1.6.1、Pillow 9.5.0。
|
||||||
- 新增第三方依赖前,先在本文说明用途、替代方案和维护成本,并同步更新 `requirements.txt`。
|
- 新增第三方依赖前,先在本文说明用途、替代方案和维护成本,并同步更新 `requirements.txt`。
|
||||||
- GUI 框架固定为 PySide6,不允许混入 Tkinter/PyQt/Web 形成两套 UI。
|
- GUI 框架固定为 PySide6,不允许混入 Tkinter/PyQt/Web 形成两套 UI。
|
||||||
|
|||||||
+1
-1
@@ -116,7 +116,7 @@
|
|||||||
| T-522 | CI:自动跑语法 + 单元/ GUI 测试 | T-006, T-521 | 依据 `docs/engineering-review.md` P1。加 GitHub Actions,在 push/PR 上按 `requirements.txt` 安装依赖并跑 `python -m compileall app main.py` + `python -m unittest discover -s tests`(含 PySide6 环境下的 GUI 测试,`QT_QPA_PLATFORM=offscreen`);不连真实 Shopee/AI;失败即红灯,把"改完必跑测试"变强制门禁 | DONE |
|
| T-522 | CI:自动跑语法 + 单元/ GUI 测试 | T-006, T-521 | 依据 `docs/engineering-review.md` P1。加 GitHub Actions,在 push/PR 上按 `requirements.txt` 安装依赖并跑 `python -m compileall app main.py` + `python -m unittest discover -s tests`(含 PySide6 环境下的 GUI 测试,`QT_QPA_PLATFORM=offscreen`);不连真实 Shopee/AI;失败即红灯,把"改完必跑测试"变强制门禁 | DONE |
|
||||||
| T-523 | 拆分 `app/gui.py` 为 `app/gui/` 包 | T-511, T-514, T-515, T-516, T-517 | 依据 `docs/engineering-review.md` P0。把 6317 行 God-file 拆为包:`models.py`(3 个 TableModel)、`tabs/`(①~⑤各一文件)、`workers.py`(Generate/Apply/Collect/WriteBack/AccountLoginCheck/AIModelTest 从 gui 挪出,与 `app/workers.py` 基类归拢)、`widgets.py`(色板常量、空状态卡、批次总览等 helper)、`main_window.py`。纯结构重构、对外行为与公开符号不变(保留 `from app import gui` 及 `MainWindow`/各 Tab/Worker 的导入路径或提供兼容再导出),保留 PySide6 缺失优雅降级;68 个 GUI 测试全绿、不删减断言 | DONE |
|
| T-523 | 拆分 `app/gui.py` 为 `app/gui/` 包 | T-511, T-514, T-515, T-516, T-517 | 依据 `docs/engineering-review.md` P0。把 6317 行 God-file 拆为包:`models.py`(3 个 TableModel)、`tabs/`(①~⑤各一文件)、`workers.py`(Generate/Apply/Collect/WriteBack/AccountLoginCheck/AIModelTest 从 gui 挪出,与 `app/workers.py` 基类归拢)、`widgets.py`(色板常量、空状态卡、批次总览等 helper)、`main_window.py`。纯结构重构、对外行为与公开符号不变(保留 `from app import gui` 及 `MainWindow`/各 Tab/Worker 的导入路径或提供兼容再导出),保留 PySide6 缺失优雅降级;68 个 GUI 测试全绿、不删减断言 | DONE |
|
||||||
| T-523a | ②/③ 新一轮运行前清空界面日志显示 | T-519, T-505, T-523 | 问题:②AI生成和③更新shopee进入模块或启动新一轮任务时仍显示上一次运行日志,用户容易把旧失败、旧进度误认为本轮状态。方案:点击②「开始生成」、③「检查本轮更新」或③「开始更新」时,先清空对应界面的可见日志显示区,并写入本轮开始摘要;运行中只追加本轮日志,完成后保留本轮日志。只清空界面文本,不删除 SQLite `run_logs/run_log_events` 或本地 `logs/` 诊断日志;进入页面默认可显示“本轮日志会在开始运行后显示”,历史日志查看作为后续低优先级入口,不自动混入当前运行界面。同步 GUI 测试覆盖新一轮运行不会混入旧日志 | DONE |
|
| T-523a | ②/③ 新一轮运行前清空界面日志显示 | T-519, T-505, T-523 | 问题:②AI生成和③更新shopee进入模块或启动新一轮任务时仍显示上一次运行日志,用户容易把旧失败、旧进度误认为本轮状态。方案:点击②「开始生成」、③「检查本轮更新」或③「开始更新」时,先清空对应界面的可见日志显示区,并写入本轮开始摘要;运行中只追加本轮日志,完成后保留本轮日志。只清空界面文本,不删除 SQLite `run_logs/run_log_events` 或本地 `logs/` 诊断日志;进入页面默认可显示“本轮日志会在开始运行后显示”,历史日志查看作为后续低优先级入口,不自动混入当前运行界面。同步 GUI 测试覆盖新一轮运行不会混入旧日志 | DONE |
|
||||||
| T-524 | PyInstaller 打包为免安装 exe | T-521 | 依据 `docs/engineering-review.md` P1。新增 PyInstaller spec/脚本,产出 Windows 免安装 `.exe`;打包排除并绝不内置 `config.json`/`config/ai_models.json`/`cmshopee.db*`/`chrome_user_data_dir/`/`images/`/`logs/` 等本地数据与密钥;首次运行按现有默认值在本地生成配置;文档补打包与分发步骤 | TODO |
|
| T-524 | PyInstaller 打包为免安装 exe | T-521 | 依据 `docs/engineering-review.md` P1。新增 PyInstaller spec/脚本,产出 Windows onedir 免安装 `.exe`;打包排除并绝不内置 `config.json`/`config/ai_models.json`/`cmshopee.db*`/`chrome_user_data_dir/`/`images/`/`logs/`/`prompts/`/`title_prompt.txt` 等本地数据与密钥;打包版工作目录切到 exe 所在目录,首次运行按现有默认值在本地生成配置;`scripts/build_exe.ps1` 可构建并校验发布目录,`docs/packaging.md` 说明分发和用户手动覆盖更新步骤 | DONE |
|
||||||
| T-525 | 引入 ruff(lint + format)+ 可选 pre-commit | T-006 | 依据 `docs/engineering-review.md` P1。加 `ruff` 配置(lint + format),先以现状为基线不做大规模风格重排,只开启安全规则(未用 import/变量、明显错误);可选 `.pre-commit-config.yaml`;不改业务逻辑;CI(T-522)可串入 ruff 检查。数据模型渐进上 mypy 作为后续可选 | TODO |
|
| T-525 | 引入 ruff(lint + format)+ 可选 pre-commit | T-006 | 依据 `docs/engineering-review.md` P1。加 `ruff` 配置(lint + format),先以现状为基线不做大规模风格重排,只开启安全规则(未用 import/变量、明显错误);可选 `.pre-commit-config.yaml`;不改业务逻辑;CI(T-522)可串入 ruff 检查。数据模型渐进上 mypy 作为后续可选 | TODO |
|
||||||
|
|
||||||
## 里程碑
|
## 里程碑
|
||||||
|
|||||||
+4
-1
@@ -25,6 +25,7 @@ cmshopee 是一个给**电商运营**使用的 Windows PySide6 桌面自动化
|
|||||||
- [产品与 UI 评估](ux-review.md):以 PM + UI 设计视角评估 5 Tab 模块 / 组件合理性,含优化方案与优先级清单。
|
- [产品与 UI 评估](ux-review.md):以 PM + UI 设计视角评估 5 Tab 模块 / 组件合理性,含优化方案与优先级清单。
|
||||||
- [界面配色设计](ui-color-design.md):语义色板与组件配色映射规范,指导给状态 / 按钮 / 校验 / 登录状态上色。
|
- [界面配色设计](ui-color-design.md):语义色板与组件配色映射规范,指导给状态 / 按钮 / 校验 / 登录状态上色。
|
||||||
- [工程评估](engineering-review.md):全栈视角评估工程基础设施与可维护性(依赖清单 / CI / 打包 / gui.py 拆分 / lint),含 P0-P2 与优先级。
|
- [工程评估](engineering-review.md):全栈视角评估工程基础设施与可维护性(依赖清单 / CI / 打包 / gui.py 拆分 / lint),含 P0-P2 与优先级。
|
||||||
|
- [打包与分发](packaging.md):PyInstaller 免安装 exe 打包命令、排除本地数据规则和用户手动更新方式。
|
||||||
|
|
||||||
## 运行环境安装
|
## 运行环境安装
|
||||||
|
|
||||||
@@ -32,9 +33,11 @@ cmshopee 是一个给**电商运营**使用的 Windows PySide6 桌面自动化
|
|||||||
python -m pip install -r requirements.txt
|
python -m pip install -r requirements.txt
|
||||||
python -m compileall app main.py
|
python -m compileall app main.py
|
||||||
python -m unittest discover -s tests
|
python -m unittest discover -s tests
|
||||||
|
python -m pip install -r requirements-build.txt
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts\build_exe.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
`requirements.txt` 只锁定运行所需第三方依赖;`config.json`、`config/ai_models.json`、SQLite、图片、登录态和运营 Excel 仍是本地数据,不提交版本库。
|
`requirements.txt` 只锁定运行所需第三方依赖;打包环境使用 `requirements-build.txt` 安装 PyInstaller;`config.json`、`config/ai_models.json`、SQLite、图片、登录态和运营 Excel 仍是本地数据,不提交版本库。
|
||||||
|
|
||||||
GitHub Actions 工作流见 `.github/workflows/tests.yml`,在 push / pull_request 上使用 Windows + Python 3.11 安装 `requirements.txt`,并自动运行语法检查和全量单元/GUI 测试。
|
GitHub Actions 工作流见 `.github/workflows/tests.yml`,在 push / pull_request 上使用 Windows + Python 3.11 安装 `requirements.txt`,并自动运行语法检查和全量单元/GUI 测试。
|
||||||
|
|
||||||
|
|||||||
+17
-9
File diff suppressed because one or more lines are too long
@@ -0,0 +1,100 @@
|
|||||||
|
# 打包与分发
|
||||||
|
|
||||||
|
> T-524 目标:先把当前 Windows 桌面工具打成免安装 onedir `.exe`,方便第一版交付使用。暂不做自动更新器、安装器、增量补丁或在线升级。
|
||||||
|
|
||||||
|
## 一、打包前提
|
||||||
|
|
||||||
|
- 在 Windows 环境执行。
|
||||||
|
- Python 环境已能运行源码版 `python main.py`。
|
||||||
|
- 打包脚本使用当前 PATH 中的 `python`;正式出包前先确认 `python --version` 符合项目目标 Python 3.10+。
|
||||||
|
- 安装运行依赖和打包依赖:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m pip install -r requirements-build.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
`requirements-build.txt` 会先安装 `requirements.txt` 的运行依赖,再安装 PyInstaller。
|
||||||
|
|
||||||
|
## 二、打包命令
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts\build_exe.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本会执行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m PyInstaller --noconfirm --clean cmshopee.spec
|
||||||
|
```
|
||||||
|
|
||||||
|
成功后输出:
|
||||||
|
|
||||||
|
```text
|
||||||
|
dist\cmshopee\cmshopee.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
发布给用户时,以整个 `dist\cmshopee\` 文件夹为单位压缩分发,用户双击 `cmshopee.exe` 启动。
|
||||||
|
|
||||||
|
## 三、绝不打包的本地数据
|
||||||
|
|
||||||
|
发布包里不能包含以下本地数据、密钥、业务数据或登录态:
|
||||||
|
|
||||||
|
- `config.json`
|
||||||
|
- `config/ai_models.json`
|
||||||
|
- `cmshopee.db`、`cmshopee.db-wal`、`cmshopee.db-shm`
|
||||||
|
- `db.sqlite`
|
||||||
|
- `chrome_user_data_dir/`
|
||||||
|
- `images/`
|
||||||
|
- `logs/`
|
||||||
|
- `prompts/`
|
||||||
|
- `title_prompt.txt`
|
||||||
|
- 运营填写后的 Excel 文件
|
||||||
|
|
||||||
|
`cmshopee.spec` 不声明任何 `datas`;`scripts/build_exe.ps1` 会在打包后检查 `dist\cmshopee\`,如果发现上述路径会直接失败。
|
||||||
|
|
||||||
|
## 四、首次运行与本地数据位置
|
||||||
|
|
||||||
|
打包版启动时,`main.py` 会把工作目录切到 `cmshopee.exe` 所在目录。
|
||||||
|
|
||||||
|
因此首次运行时,现有代码会按默认值在 exe 同级目录生成或使用本地文件:
|
||||||
|
|
||||||
|
- `config.json`
|
||||||
|
- `config/ai_models.json`
|
||||||
|
- `cmshopee.db`
|
||||||
|
- `chrome_user_data_dir/`
|
||||||
|
- `images/`
|
||||||
|
- `logs/`
|
||||||
|
- `prompts/`、`title_prompt.txt`(用户维护提示词时生成)
|
||||||
|
|
||||||
|
这些文件属于用户本地数据,不随新版本程序包覆盖。
|
||||||
|
|
||||||
|
## 五、用户后续更新方式
|
||||||
|
|
||||||
|
第一版不做自动更新。给用户发新版本时:
|
||||||
|
|
||||||
|
1. 让用户先关闭 cmshopee。
|
||||||
|
2. 建议用户备份当前整个程序文件夹。
|
||||||
|
3. 解压新版 `dist\cmshopee\`。
|
||||||
|
4. 只覆盖程序文件,例如 `cmshopee.exe` 和 `_internal/`。
|
||||||
|
5. 不删除也不覆盖用户原有的 `config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/`、`logs/`、`prompts/`、`title_prompt.txt`。
|
||||||
|
|
||||||
|
如果用户把整个旧目录删除再放新版,账号、任务记录、图片和登录态也会一起丢失,只能从备份恢复。
|
||||||
|
|
||||||
|
## 六、验证清单
|
||||||
|
|
||||||
|
打包前后至少执行:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m compileall app main.py
|
||||||
|
python -m unittest discover -s tests
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts\build_exe.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
打包成功后确认:
|
||||||
|
|
||||||
|
- `dist\cmshopee\cmshopee.exe` 存在。
|
||||||
|
- `dist\cmshopee\` 中没有第三节列出的本地数据。
|
||||||
|
- 在干净目录首次启动时能生成默认配置并进入 GUI。
|
||||||
|
|
||||||
|
涉及 Shopee/CDP 的真实更新能力,仍按任务文档要求用测试商品做人工回归;打包任务本身不新增自动绕过登录、验证码或风控的能力。
|
||||||
|
|
||||||
@@ -5,9 +5,17 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
|
def _project_root():
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
return os.path.dirname(os.path.abspath(sys.executable))
|
||||||
|
return os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
PROJECT_ROOT = _project_root()
|
||||||
if PROJECT_ROOT not in sys.path:
|
if PROJECT_ROOT not in sys.path:
|
||||||
sys.path.insert(0, PROJECT_ROOT)
|
sys.path.insert(0, PROJECT_ROOT)
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
os.chdir(PROJECT_ROOT)
|
||||||
|
|
||||||
from app.gui import main
|
from app.gui import main
|
||||||
|
|
||||||
|
|||||||
@@ -1087,3 +1087,11 @@
|
|||||||
- 边界:不自动登录、不填密码、不绕过验证码;未改商品页、上传、提交逻辑。
|
- 边界:不自动登录、不填密码、不绕过验证码;未改商品页、上传、提交逻辑。
|
||||||
- 测试:`tests/test_editor_login.py` 增加真实 accounts 登录 URL 覆盖。
|
- 测试:`tests/test_editor_login.py` 增加真实 accounts 登录 URL 覆盖。
|
||||||
- 验证:`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(37 tests)。
|
- 验证:`python -m unittest discover -s tests -p "test_editor_login.py"` 通过(37 tests)。
|
||||||
|
|
||||||
|
## 【2026-07-03】T-524 完成 · PyInstaller 打包为免安装 exe
|
||||||
|
|
||||||
|
- 代码/配置:新增 `cmshopee.spec`、`scripts/build_exe.ps1`、`requirements-build.txt`;`main.py` 在 PyInstaller frozen 模式下把工作目录切到 `cmshopee.exe` 所在目录,确保配置、DB、图片、日志、登录态和提示词在 exe 同级目录生成/读取;`.gitignore` 新增 `build/`、`dist/`、`db.sqlite`、`prompts/`、`title_prompt.txt`。
|
||||||
|
- 打包安全:`cmshopee.spec` 不声明本地数据 `datas`;构建脚本打包后检查 `dist\cmshopee`,发现 `config.json`、`config/ai_models.json`、`cmshopee.db*`、`db.sqlite`、`chrome_user_data_dir/`、`images/`、`logs/`、`prompts/`、`title_prompt.txt` 会直接失败;脚本会打印当前 Python 版本,低于项目目标 3.10 时给出警告。
|
||||||
|
- 文档:新增 `docs/packaging.md`,同步 `docs/README.md`、`docs/03-tech-stack.md`、`docs/06-tasks.md`、`docs/current-state.md`;T-524 标为 DONE,下一个可领取任务更新为 T-525。
|
||||||
|
- 测试:新增 `tests/test_packaging.py` 覆盖 frozen 入口路径、spec 不打包本地数据、构建脚本排除列表。
|
||||||
|
- 验证:`python -m compileall app main.py` 通过;`python -m unittest discover -s tests -p "test_packaging.py"` 通过(3 tests);`python -m unittest discover -s tests` 通过(170 tests);`git diff --check` 无空白错误(仅 LF/CRLF 提示);`powershell -ExecutionPolicy Bypass -File scripts\build_exe.ps1` 成功产出 `dist\cmshopee\cmshopee.exe`,脚本确认未混入本地数据。本机当前 `python` 为 3.7.9,构建脚本已提示低于项目目标;正式发布建议切到 Python 3.10+ 环境后重新打包。
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-r requirements.txt
|
||||||
|
pyinstaller==5.13.2
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$repoRoot = Resolve-Path (Join-Path $scriptDir "..")
|
||||||
|
Set-Location $repoRoot
|
||||||
|
|
||||||
|
$pythonVersion = python -c "import sys; print('%d.%d.%d' % sys.version_info[:3])"
|
||||||
|
Write-Host "Using Python: $pythonVersion"
|
||||||
|
if ([version]$pythonVersion -lt [version]"3.10.0") {
|
||||||
|
Write-Warning "Project target is Python 3.10+. Switch to a 3.10+ environment before official release builds."
|
||||||
|
}
|
||||||
|
$spec = Join-Path $repoRoot "cmshopee.spec"
|
||||||
|
if (-not (Test-Path -LiteralPath $spec)) {
|
||||||
|
throw "Missing cmshopee.spec"
|
||||||
|
}
|
||||||
|
|
||||||
|
python -m PyInstaller --noconfirm --clean $spec
|
||||||
|
|
||||||
|
$distDir = Join-Path $repoRoot "dist\cmshopee"
|
||||||
|
$exePath = Join-Path $distDir "cmshopee.exe"
|
||||||
|
if (-not (Test-Path -LiteralPath $exePath)) {
|
||||||
|
throw "Build failed: $exePath not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
$excluded = @(
|
||||||
|
"config.json",
|
||||||
|
"config\ai_models.json",
|
||||||
|
"cmshopee.db",
|
||||||
|
"cmshopee.db-wal",
|
||||||
|
"cmshopee.db-shm",
|
||||||
|
"db.sqlite",
|
||||||
|
"chrome_user_data_dir",
|
||||||
|
"images",
|
||||||
|
"logs",
|
||||||
|
"prompts",
|
||||||
|
"title_prompt.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
$found = @()
|
||||||
|
foreach ($relative in $excluded) {
|
||||||
|
$candidate = Join-Path $distDir $relative
|
||||||
|
if (Test-Path -LiteralPath $candidate) {
|
||||||
|
$found += $relative
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($found.Count -gt 0) {
|
||||||
|
throw "Build output contains local user data: $($found -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Build complete: $exePath"
|
||||||
|
Write-Host "Do not copy local data into the release package."
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from _helpers import REPO_ROOT
|
||||||
|
|
||||||
|
|
||||||
|
class PackagingTests(unittest.TestCase):
|
||||||
|
def read_text(self, relative_path):
|
||||||
|
return (REPO_ROOT / relative_path).read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
def test_frozen_entrypoint_uses_exe_directory(self):
|
||||||
|
main_py = self.read_text("main.py")
|
||||||
|
|
||||||
|
self.assertIn('getattr(sys, "frozen", False)', main_py)
|
||||||
|
self.assertIn("sys.executable", main_py)
|
||||||
|
self.assertIn("os.chdir(PROJECT_ROOT)", main_py)
|
||||||
|
|
||||||
|
def test_pyinstaller_spec_uses_onedir_without_local_datas(self):
|
||||||
|
spec = self.read_text("cmshopee.spec")
|
||||||
|
normalized = "".join(spec.split())
|
||||||
|
|
||||||
|
self.assertIn("datas=[]", normalized)
|
||||||
|
self.assertIn("COLLECT(", spec)
|
||||||
|
self.assertIn('name="cmshopee"', normalized)
|
||||||
|
|
||||||
|
def test_build_script_blocks_user_data_in_release_output(self):
|
||||||
|
script = self.read_text("scripts/build_exe.ps1")
|
||||||
|
|
||||||
|
for token in (
|
||||||
|
"config.json",
|
||||||
|
"config\\ai_models.json",
|
||||||
|
"cmshopee.db",
|
||||||
|
"cmshopee.db-wal",
|
||||||
|
"cmshopee.db-shm",
|
||||||
|
"db.sqlite",
|
||||||
|
"chrome_user_data_dir",
|
||||||
|
"images",
|
||||||
|
"logs",
|
||||||
|
"prompts",
|
||||||
|
"title_prompt.txt",
|
||||||
|
):
|
||||||
|
self.assertIn(token, script)
|
||||||
|
|
||||||
|
self.assertIn("python -m PyInstaller --noconfirm --clean", script)
|
||||||
|
self.assertIn("cmshopee.spec", script)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user