iOSBuildServer/README.md
shen 6f4f625c56 Initial commit: iOS Build Server
- FastAPI backend with build queue, WebSocket logs, task management
- Vue 3 frontend with build/config/history views
- Xcode project build automation with IPA export
- Fix: initialize build_dir before try block to ensure cleanup on early failure
2026-06-06 17:42:27 +08:00

515 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# iOS 自动打包服务
基于 FastAPI + Vue 3 的 iOS 应用自动打包服务,支持 Ad_Hoc 和 App_Store 两种打包类型,提供 Web 界面操作、实时日志流、构建历史管理和健康监测。
## 目录
- [环境要求](#环境要求)
- [快速开始](#快速开始)
- [配置说明](#配置说明)
- [服务管理](#服务管理)
- [Watchdog 健康监测](#watchdog-健康监测)
- [macOS 开机自启](#macos-开机自启)
- [开发模式](#开发模式)
- [自动化测试](#自动化测试)
- [项目结构](#项目结构)
- [常见问题](#常见问题)
---
## 环境要求
| 依赖 | 版本要求 | 说明 |
|------|----------|------|
| macOS | 11+ | 必须xcodebuild 依赖 |
| Xcode | 14+ | 必须,含 Command Line Tools |
| Python | 3.9+ | 后端运行 |
| Node.js | 18+ | 前端构建 |
| CocoaPods | - | 项目已有 Podfile 时需要 |
## 快速开始
### 1. 克隆项目
```bash
git clone <repo-url> BuildServer
cd BuildServer
```
### 2. 配置环境变量
```bash
cp .env.example .env
vim .env
```
**必须修改的配置项:**
```bash
# iOS 源码项目路径(包含 readoor.xcworkspace 的目录)
PROJECT_ROOT=/path/to/your/Readoor
# 打包输出目录
BUILD_BASE_DIR=/path/to/build/output
# 管理员密码(建议修改)
ADMIN_PASSWORD=your_secure_password
```
### 3. 构建与启动
```bash
# 一键构建(安装依赖 + 构建前端)
./deploy.sh build
# 启动服务
./deploy.sh start
```
启动后访问 `http://<服务器IP>:8000` 即可使用。
### 4.(可选)初始化分支源码目录
如果需要按分支打包,先准备分支源码目录:
```bash
# 创建分支源码根目录
mkdir -p /path/to/ReadoorBranches
# 手动 clone 主分支(后续自动 pull 更新)
git clone -b main git@github.com:org/repo.git /path/to/ReadoorBranches/main
```
其他分支无需手动 clone首次打包时会自动从远程仓库 clone。
### 5.(可选)启用健康监测
```bash
./deploy.sh watchdog
```
### 6.(可选)设为开机自启
```bash
./deploy/install-service.sh install
```
---
## 配置说明
所有配置通过项目根目录的 `.env` 文件管理,修改后重启服务生效。
### 项目路径
| 变量 | 示例 | 说明 |
|------|------|------|
| `PROJECT_ROOT` | `/Users/shen/Work/Code/Readoor` | iOS 源码项目根目录AutoPacking 脚本路径) |
| `BUILD_BASE_DIR` | `/Users/shen/Documents` | 打包产物输出基础目录 |
| `GIT_SOURCE_BASE` | `/path/to/ReadoorBranches` | 分支源码根目录,每个分支一个子目录 |
| `GIT_REMOTE_URL` | `git@github.com:org/repo.git` | 远程仓库地址,分支目录不存在时自动 clone |
### 服务配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `BACKEND_PORT` | `8000` | 后端服务端口 |
| `ADMIN_USERNAME` | `admin` | 管理员用户名 |
| `ADMIN_PASSWORD` | `admin123` | 管理员密码 |
### 打包配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `MAX_CONCURRENT_BUILDS` | `2` | 最大并行打包数 |
| `BUILD_DIR_RETENTION_HOURS` | `24` | 打包目录保留时间(小时),超期自动清理 |
### Watchdog 配置
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `WATCHDOG_INTERVAL` | `30` | 健康检查间隔(秒) |
| `WATCHDOG_TIMEOUT` | `10` | HTTP 健康检查超时(秒) |
| `MAX_RESTART_ATTEMPTS` | `3` | 连续重启上限,超过后进入冷却 |
| `COOLDOWN_SECONDS` | `300` | 冷却时间(秒) |
---
## 服务管理
所有服务管理通过 `deploy.sh` 完成:
```bash
./deploy.sh <command>
```
### 命令一览
| 命令 | 说明 |
|------|------|
| `build` | 构建前端、创建 Python 虚拟环境、安装所有依赖 |
| `start` | 后台启动服务 |
| `stop` | 停止服务和 watchdog |
| `restart` | 重启服务 |
| `status` | 查看服务和 watchdog 运行状态 |
| `test` | 一键运行全部测试(后端 + 前端) |
| `watchdog` | 启动健康监测(后台守护) |
| `watchdog-stop` | 停止健康监测 |
| `watchdog-fg` | 前台运行健康监测(调试用) |
### 常用操作
```bash
# 首次部署
./deploy.sh build && ./deploy.sh start
# 查看状态
./deploy.sh status
# 更新代码后重新部署
git pull
./deploy.sh build
./deploy.sh restart
# 启用健康监测
./deploy.sh watchdog
# 停止所有服务
./deploy.sh stop
```
### 日志
日志文件位于 `logs/` 目录:
```
logs/
├── server.log # 服务运行日志
├── watchdog.log # Watchdog 操作日志(重启记录等)
├── server-stdout.log # launchd 模式下的标准输出
├── server-stderr.log # launchd 模式下的标准错误
├── watchdog-stdout.log # launchd watchdog 标准输出
└── watchdog-stderr.log # launchd watchdog 标准错误
```
---
## Watchdog 健康监测
Watchdog 定期检查服务健康状态,发现异常自动重启。
### 检测逻辑
```
每 N 秒执行一次
├─ 进程是否存在? (kill -0)
│ └─ 否 → 重启
└─ HTTP /api/health 返回 200
├─ 是 → 正常,继续轮询
└─ 否 → 重启
```
### 重启保护
- 连续失败次数 <= `MAX_RESTART_ATTEMPTS`:立即重启
- 连续失败次数 > `MAX_RESTART_ATTEMPTS`:进入冷却期(`COOLDOWN_SECONDS` 秒),冷却结束后重置计数器重新尝试
### 运行方式
```bash
# 后台守护(推荐)
./deploy.sh watchdog
# 前台运行(调试,可看到实时输出)
./deploy.sh watchdog-fg
# 查看 watchdog 日志
tail -f logs/watchdog.log
# 停止 watchdog
./deploy.sh watchdog-stop
```
---
## macOS 开机自启
通过 macOS 原生的 launchd 实现开机自启和进程守护。
### 安装
```bash
# 前置条件:先完成构建
./deploy.sh build
# 安装服务
./deploy/install-service.sh install
```
安装后会注册两个 launchd 服务:
| 服务 | 说明 |
|------|------|
| `com.readoor.buildserver` | 主服务,进程崩溃自动拉起 |
| `com.readoor.buildserver.watchdog` | 健康监测,检测 HTTP 卡死并重启 |
### 卸载
```bash
./deploy/install-service.sh uninstall
```
### 手动管理 launchd 服务
```bash
# 查看服务状态
launchctl list | grep readoor
# 手动停止
launchctl unload ~/Library/LaunchAgents/com.readoor.buildserver.plist
launchctl unload ~/Library/LaunchAgents/com.readoor.buildserver.watchdog.plist
# 手动启动
launchctl load ~/Library/LaunchAgents/com.readoor.buildserver.plist
launchctl load ~/Library/LaunchAgents/com.readoor.buildserver.watchdog.plist
```
---
## 开发模式
一键启动本地开发环境(后端 + 前端同时运行):
```bash
./start.sh
```
- 前端界面:`http://localhost:3000`(自动代理 API 请求到后端)
- 后端 API`http://localhost:8000`
- 前端修改自动热更新,后端修改自动 reload
- `Ctrl+C` 同时停止所有服务
---
## 自动化测试
### 运行测试
```bash
# 一键运行全部测试(后端 89 + 前端 35 = 124 个)
./deploy.sh test
# 只运行匹配的测试
./deploy.sh test -k test_login
# 单独运行后端测试
.venv/bin/python -m pytest tests/ -v
# 单独运行前端测试
cd frontend && npx vitest run
```
### 测试覆盖
| 测试文件 | 覆盖内容 | 数量 |
|----------|----------|------|
| `test_api_health.py` | 健康检查 | 1 |
| `test_api_auth.py` | 登录认证 | 3 |
| `test_api_config.py` | App/Scheme/Branch/Server/Build 配置 CRUD | 20 |
| `test_api_tasks.py` | 任务创建/列表/详情/取消 | 9 |
| `test_api_apps.py` | 打包选择接口 | 3 |
| `test_websocket.py` | 日志流订阅/发送/完成/清理 | 10 |
| `test_downloads.py` | dSYM/混淆映射/二维码下载 | 11 |
| `test_build_service.py` | 源码更新/拷贝/配置生成/目录清理 | 11 |
| `test_build_queue.py` | 并发控制/任务执行/取消 | 7 |
| `test_edge_cases.py` | 边界情况/无效参数/配置备份 | 14 |
| `App.test.js` | 登录/退出/导航 | 8 |
| `BuildView.vue.test.js` | 打包表单/任务列表/提交 | 8 |
| `ConfigView.vue.test.js` | 配置管理/分支增删/设置保存 | 8 |
| `HistoryView.vue.test.js` | 历史列表/过滤/下载/二维码 | 11 |
---
## 项目结构
```
BuildServer/
├── .env.example # 环境变量模板
├── .env # 实际配置(不入库)
├── .gitignore
├── README.md # 项目文档
├── deploy.sh # 部署与管理脚本
├── start.sh # 开发模式启动脚本(一键启动前后端)
├── requirements.txt # Python 依赖
├── requirements-dev.txt # 测试依赖pytest, httpx
├── pytest.ini # pytest 配置
├── deploy/ # 部署相关
│ └── install-service.sh # macOS launchd 服务安装
├── tests/ # 后端测试pytest
│ ├── conftest.py # 测试 fixtures
│ ├── test_api_*.py # API 接口测试
│ ├── test_websocket.py # WebSocket 测试
│ ├── test_downloads.py # 文件下载测试
│ ├── test_build_service.py # 打包服务测试
│ ├── test_build_queue.py # 队列测试
│ └── test_edge_cases.py # 边界情况测试
├── backend/ # 后端FastAPI
│ ├── main.py # 入口,路由注册,静态文件服务
│ ├── config.py # 配置加载(读取 .env
│ ├── database.py # SQLAlchemy 数据库初始化
│ ├── models.py # 数据库模型Task, BuildConfig
│ ├── schemas.py # Pydantic 请求/响应模型
│ ├── routers/
│ │ ├── auth.py # 登录认证
│ │ ├── config.py # 配置管理App/Scheme/Server CRUD
│ │ ├── apps.py # 打包选择(只读)
│ │ └── tasks.py # 任务管理(创建/列表/取消/下载)
│ └── services/
│ ├── build_service.py # 打包核心流程
│ ├── build_queue.py # 异步任务队列
│ └── log_streamer.py # WebSocket 日志流
└── frontend/ # 前端Vue 3 + Vite
├── vite.config.js # 构建配置,开发代理
├── vitest.config.js # 前端测试配置
├── index.html
└── src/
├── App.vue # 根组件(导航/登录)
├── router/index.js # 路由
├── composables/
│ └── useWebSocket.js
├── views/
│ ├── BuildView.vue # 打包页面
│ ├── HistoryView.vue # 历史记录
│ └── ConfigView.vue # 管理后台
└── __tests__/ # 前端测试vitest
├── App.test.js
├── BuildView.test.js
├── ConfigView.test.js
└── HistoryView.test.js
```
---
## 常见问题
### Q: 打包时提示 "未找到 xcodebuild"
确保安装了 Xcode 和 Command Line Tools
```bash
xcode-select --install
xcodebuild -version
```
### Q: 前端页面打不开
检查前端是否已构建:
```bash
ls backend/static/index.html # 应该存在
# 如果不存在:
./deploy.sh build
./deploy.sh restart
```
### Q: 端口被占用
修改 `.env` 中的 `BACKEND_PORT`,然后重启:
```bash
vim .env
./deploy.sh restart
```
### Q: 打包目录占满磁盘
调整保留时间或手动清理:
```bash
# 缩短保留时间
vim .env # BUILD_DIR_RETENTION_HOURS=12
./deploy.sh restart
# 手动清理所有打包目录
rm -rf /path/to/build/output/build_readoor_*
```
### Q: Watchdog 频繁重启
查看 watchdog 日志定位原因:
```bash
tail -50 logs/watchdog.log
tail -50 logs/server.log
```
常见原因:
- `PROJECT_ROOT` 路径不存在
- 磁盘空间不足
- Python 依赖缺失(重新执行 `./deploy.sh build`
### Q: 如何修改管理员密码
编辑 `.env`
```bash
ADMIN_PASSWORD=new_password
./deploy.sh restart
```
### Q: 多台 Mac 部署
每台 Mac 上:
```bash
git clone <repo>
cd BuildServer
cp .env.example .env
# 编辑 .env 配置路径和密码
./deploy.sh build
./deploy.sh start
./deploy.sh watchdog
```
各机器独立运行,配置互不影响。
### Q: 如何按分支打包
1.`.env` 中配置 `GIT_SOURCE_BASE``GIT_REMOTE_URL`
2. 在管理页面「分支管理」中添加需要打包的分支(默认已有 `main`
3. 打包时在「代码分支」下拉框中选择分支
4. 首次打包某个分支时会自动从远程 clone后续打包会自动 `git pull` 更新
5. 每个分支有独立的源码目录,支持并行打包不同分支
### Q: 分支目录占满磁盘怎么办
```bash
# 查看各分支目录大小
du -sh /path/to/ReadoorBranches/*
# 删除不用的分支目录
rm -rf /path/to/ReadoorBranches/old-branch
```
### Q: 如何运行测试
```bash
# 一键运行全部测试(后端 + 前端)
./deploy.sh test
# 只运行后端测试
.venv/bin/python -m pytest tests/ -v
# 只运行前端测试
cd frontend && npx vitest run
# 运行指定测试
./deploy.sh test -k test_login
```