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
This commit is contained in:
shen
2026-06-06 17:42:27 +08:00
commit 6f4f625c56
51 changed files with 9968 additions and 0 deletions
+514
View File
@@ -0,0 +1,514 @@
# 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
```