Files
ai-proj-helper/skills-req/req-deploy-plugin/skills/SKILL.md
John Qiu 712063071c refactor: 通用技能按类别拆分为独立目录
skills/ → skills-dev(9), skills-req(10), skills-ops(4),
skills-integration(8), skills-biz(4), skills-workflow(7)

generate-marketplace.py 改为自动扫描所有 skills-* 目录。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 11:31:58 +10:30

1958 lines
52 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.
# 需求部署 Skill (req-deploy)
## 概述
本技能专门负责需求的部署发布工作,包括:
- **Docker 镜像构建与推送**
- **镜像大小检查与优化**
- **Staging 环境部署**SSH 手动部署)
- **Production 环境部署**Jenkins API 自动触发)
- **API 验证与健康检查**
- **前端截图验证**本机、Staging、Production
- **部署任务文档创建**
- **iOS 应用部署**(通过 cn-dev 安装到 iPhone
- **Android 应用部署**(通过 fuxing 电脑安装到 Android 设备)
---
## 部署流程概览
### Web 应用部署流程
```
阶段1: 本机测试
阶段2: Staging 部署
├── 构建镜像 (:test 标签)
├── 镜像大小检查
├── 推送 DockerHub
├── SSH 手动部署
├── API 验证
└── 前端截图验证
阶段3: Production 部署
├── 重新标记 :test → :prod无需重新构建
├── 推送 :prod 标签到 DockerHub
├── Jenkins API 触发部署Job: ai-proj-productionIMAGE_TAG=prod
├── API 健康检查
├── 生产截图验证
└── 创建部署任务文档
```
### iOS 应用部署流程
```
阶段1: 本机代码提交
├── git add -A
├── git commit
└── git push origin main
阶段2: cn-dev 部署
├── SSH 到 cn-dev
├── git pull 更新代码
├── Xcode 构建
└── 安装到 iPhone
阶段3: 功能验证
├── App 启动检查
├── 登录功能测试
└── 新功能验证
```
### Android 应用部署流程
```
阶段1: 本机代码提交
├── git add -A
├── git commit
└── git push origin main
阶段2: fuxing 电脑部署 (100.90.190.119)
├── SSH 到 fuxing
├── git pull 更新代码
├── Gradle 构建 APK
└── ADB 安装到 Android 设备
阶段3: 功能验证
├── App 启动检查
├── 登录功能测试
└── 新功能验证
```
---
## 0. 部署前变更检测(必须)
**⚠️ 每次部署前必须执行变更检测**,防止漏部署服务。
### 0.1 Prod 镜像 Commit 对比
对比本地最新 commit 与 prod 镜像内嵌的 commit判断哪些服务需要重新构建
```bash
# 获取 prod 镜像内嵌的 commit需要先 pull
PROD_BE_COMMIT=$(docker inspect saltthing123/ai-proj-backend:prod --format '{{index .Config.Labels "git.commit"}}' 2>/dev/null || echo "unknown")
PROD_FE_COMMIT=$(docker inspect saltthing123/ai-proj-frontend:prod --format '{{index .Config.Labels "git.commit"}}' 2>/dev/null || echo "unknown")
LOCAL_COMMIT=$(git rev-parse HEAD)
echo "本地 HEAD: $LOCAL_COMMIT"
echo "Prod 后端: $PROD_BE_COMMIT"
echo "Prod 前端: $PROD_FE_COMMIT"
# 检查各服务是否有变更
if [ "$PROD_BE_COMMIT" != "unknown" ] && [ "$PROD_BE_COMMIT" != "$LOCAL_COMMIT" ]; then
BE_CHANGES=$(git diff "$PROD_BE_COMMIT"..HEAD --name-only -- backend/ | wc -l | tr -d ' ')
echo "后端变更: $BE_CHANGES 个文件"
else
BE_CHANGES="?"
echo "后端: 无法对比(镜像无 commit label使用 git diff 兜底"
fi
if [ "$PROD_FE_COMMIT" != "unknown" ] && [ "$PROD_FE_COMMIT" != "$LOCAL_COMMIT" ]; then
FE_CHANGES=$(git diff "$PROD_FE_COMMIT"..HEAD --name-only -- frontend/ | wc -l | tr -d ' ')
echo "前端变更: $FE_CHANGES 个文件"
else
FE_CHANGES="?"
echo "前端: 无法对比(镜像无 commit label使用 git diff 兜底"
fi
```
### 0.2 Git Diff 兜底(镜像无 label 时)
当镜像没有 `git.commit` label 时,用 git log 查找上次部署的 commit
```bash
# 查看最近变更涉及的目录
git diff HEAD~N --name-only | cut -d/ -f1 | sort -u
```
### 0.3 决策树
```
变更检测结果?
├── backend/ 有变更 → 必须构建 backend 镜像
├── frontend/ 有变更 → 必须构建 frontend 镜像
├── 两者都有变更 → 构建全部
├── mcp-task-bridge/ → 本地 npm run build不走 Docker
└── 仅 deploy/ → 检查 docker-compose 配置变更
```
| 变更范围 | 构建什么 | 跳过什么 | 时间节省 |
|---------|---------|---------|---------|
| 仅 backend/ | `ai-proj-backend:test` | frontend 构建 + 推送 | ~5 min |
| 仅 frontend/ | `ai-proj-frontend:test` | backend 构建 + 推送 | ~3 min |
| 两者都有 | 全部构建 | 无 | 0 |
| 仅 mcp-task-bridge/ | 无 Docker 构建 | 全部跳过,本地 `npm run build` 即可 | ~8 min |
**Jenkins 只拉取有新 digest 的镜像**,未更新的镜像不会重新部署容器。因此只推送变更的服务镜像即可。
### 0.4 Dockerfile 嵌入 Commit Label
**⚠️ 构建时必须传入 GIT_COMMIT**,否则下次部署无法对比:
```bash
docker build \
--build-arg GIT_COMMIT=$(git rev-parse HEAD) \
--target production --platform linux/amd64 \
-t saltthing123/ai-proj-backend:test \
-f backend/Dockerfile backend/
```
Dockerfile 需添加(一次性):
```dockerfile
ARG GIT_COMMIT=unknown
LABEL git.commit=$GIT_COMMIT
```
---
## 1. 镜像构建与推送
### 1.1 镜像大小阈值
| 服务类型 | 正常大小 | ⚠️ 警告阈值 | ❌ 错误阈值 | 说明 |
|----------|----------|-------------|-------------|------|
| **Go 后端** | 50-100MB | > 200MB | > 500MB | 使用多阶段构建 + alpine |
| **Node 前端(生产)** | 100-300MB | > 500MB | > 1GB | Nginx + 静态文件 |
| **Node 前端(测试)** | 500MB-1GB | > 1.5GB | > 2GB | 包含测试工具 |
| **iOS 应用** | 50-150MB | > 300MB | > 500MB | 二进制 + 资源文件 |
| **Android 应用** | 30-80MB | > 150MB | > 300MB | APK 文件 |
### 1.2 Staging 镜像构建(:test 标签)
```bash
# 1. 进入项目根目录
cd /Users/donglinlai/coding/qiudl/new-ai-proj
# 2. 构建后端镜像Go + Alpine使用 production target嵌入 commit
docker build \
--build-arg GIT_COMMIT=$(git rev-parse HEAD) \
--target production \
--platform linux/amd64 \
-t saltthing123/ai-proj-backend:test \
-f backend/Dockerfile \
backend/
# 3. 构建前端镜像React + Nginx使用 production target嵌入 commit
docker build \
--build-arg GIT_COMMIT=$(git rev-parse HEAD) \
--target production \
--platform linux/amd64 \
-t saltthing123/ai-proj-frontend:test \
-f frontend/Dockerfile \
frontend/
# 4. 检查镜像大小
docker images saltthing123/ai-proj-backend:test --format "{{.Repository}}:{{.Tag}} {{.Size}}"
docker images saltthing123/ai-proj-frontend:test --format "{{.Repository}}:{{.Tag}} {{.Size}}"
```
**⚠️ 关键注意事项**
- **必须使用 `--target production`**,否则默认构建最后一个 stagetesting导致镜像过大
- 前端 Dockerfile 的 production stage 使用 Nginx 提供静态文件服务
- 后端 Dockerfile 的 production stage 使用 Alpine Linux 基础镜像
### 1.3 镜像大小检查
```bash
# 获取镜像大小(字节)
BACKEND_SIZE=$(docker images saltthing123/ai-proj-backend:test --format "{{.Size}}")
FRONTEND_SIZE=$(docker images saltthing123/ai-proj-frontend:test --format "{{.Size}}")
echo "Backend: $BACKEND_SIZE"
echo "Frontend: $FRONTEND_SIZE"
```
**自动检查逻辑**
```
IF 后端 > 200MB THEN
⚠️ 警告:后端镜像过大,执行排查流程
ELSE IF 后端 > 500MB THEN
❌ 错误:后端镜像异常,暂停部署
END IF
IF 前端 > 500MB THEN
⚠️ 警告:前端镜像过大,执行排查流程
ELSE IF 前端 > 1GB THEN
❌ 错误:前端镜像异常,暂停部署
END IF
```
### 1.4 镜像过大排查流程
```bash
# 1. 查看镜像层级(找出哪层占用空间大)
docker history saltthing123/ai-proj-frontend:test --human=false --no-trunc
# 2. 检查 Dockerfile 配置
cat frontend/Dockerfile | grep -E "^FROM|^RUN|^COPY"
# 3. 常见问题检查清单
问题1: ❌ 未指定 --target构建了 testing stage
解决:添加 --target production
问题2: ❌ COPY 了 node_modules 或源代码到生产镜像
解决:确保 production stage 只 COPY 构建产物(/app/build
问题3: ❌ 安装了 devDependencies
解决:确保只运行 npm ci --only=production或使用 builder stage
问题4: ❌ 包含了测试文件或工具
解决:使用 .dockerignore 排除测试文件
# 4. 进入镜像检查文件内容(调试用)
docker run --rm -it --entrypoint /bin/sh saltthing123/ai-proj-frontend:test
# 在容器内执行:
# ls -lh /app
# du -sh /app/*
# exit
# 5. 对比正确的 Dockerfile 示例
cat << 'EOF'
# ✅ 正确的前端 Dockerfileproduction stage
FROM nginx:alpine AS production
RUN apk --no-cache add curl && rm -rf /var/cache/apk/*
COPY --from=builder /app/build /usr/share/nginx/html # 只拷贝构建产物
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
EOF
```
### 1.5 推送到 DockerHub
```bash
# 推送镜像到 DockerHub
docker push saltthing123/ai-proj-backend:test
docker push saltthing123/ai-proj-frontend:test
# 验证推送成功(可选)
docker pull saltthing123/ai-proj-backend:test
docker pull saltthing123/ai-proj-frontend:test
```
### 1.6 镜像推送时显示大小(必须)
**⚠️ 关键要求**: 在推送镜像过程中或推送完成后,**必须**向用户显示镜像大小信息。
**显示格式**
```
=== 镜像构建完成 ===
后端镜像: saltthing123/ai-proj-backend:test
大小: 96MB
状态: ✅ 正常 (< 200MB 阈值)
前端镜像: saltthing123/ai-proj-frontend:test
大小: 89.4MB
状态: ✅ 正常 (< 500MB 阈值)
正在推送镜像...
```
**获取镜像大小的方法**
```bash
# 方法1: 本地 Docker 镜像(构建后)
docker images saltthing123/ai-proj-backend:test --format "{{.Size}}"
# 输出: 96MB
# 方法2: 使用 buildx 构建时,从构建日志中提取
# 构建日志最后会显示: "exporting manifest list sha256:xxx done"
# 然后可以通过 docker manifest inspect 获取
# 方法3: 推送后从 DockerHub API 获取
curl -s "https://hub.docker.com/v2/repositories/saltthing123/ai-proj-backend/tags/test" | jq '.full_size'
```
**异常处理**
- 如果镜像大小超过警告阈值,显示 ⚠️ 并提示排查
- 如果镜像大小超过错误阈值,显示 ❌ 并暂停部署,要求用户确认
- 如果无法获取镜像大小,显示 "⚠️ 无法获取镜像大小" 并继续
---
## 2. Staging 环境部署
### 2.0 ⚠️ Staging 部署前检查清单(必须)
> **重要**: 在构建和部署 Staging 环境之前,**必须**完成以下检查清单,确保不遗漏关键功能。
#### 2.0.1 Git 提交日志检查
在部署前,检查最近的 git 提交,识别关键功能变更:
```bash
# 1. 查看最近提交(识别关键功能)
git log --oneline -20
# 2. 重点关注以下关键词的提交:
# - feat: 新功能(特别是导航菜单、新页面)
# - migration: 数据库迁移
# - route: 路由变更
# - menu/nav: 导航菜单变更
```
#### 2.0.2 导航菜单入口检查
如果提交中包含**导航菜单变更**,必须:
1. **检查 Layout.tsx 中的菜单配置**
```bash
# 查看导航菜单相关变更
git diff HEAD~10 -- frontend/src/components/Layout.tsx | grep -A5 -B5 "menuItems\|SubMenu\|Menu.Item"
```
2. **确认新菜单路由已配置**
```bash
# 检查路由文件
git diff HEAD~10 -- frontend/src/routes/*.tsx
```
3. **验证构建产物包含新代码**
```bash
# 构建后检查(在部署前验证)
docker run --rm saltthing123/ai-proj-frontend:test sh -c \
"grep -r '<关键路由关键词>' /usr/share/nginx/html/static/js/ | wc -l"
```
#### 2.0.3 数据库迁移检查
如果提交中包含**数据库迁移**,必须在部署前应用迁移:
```bash
# 1. 检查新增迁移文件
git diff HEAD~10 --name-only | grep -E "migrations/.*\.sql$"
# 2. 列出待执行的迁移
ls -la backend/migrations/*.sql | tail -10
# 3. 在 Staging 数据库执行迁移(以 Singapore 服务器为例)
ssh singapore "docker exec ai_postgres_staging psql -U ai_staging_user ai_project_staging -f /path/to/migration.sql"
# 4. 验证迁移成功
ssh singapore "docker exec ai_postgres_staging psql -U ai_staging_user ai_project_staging -c '\dt'"
```
**⚠️ 迁移执行顺序**
- 数据库迁移必须在后端部署**之前**完成
- 先执行 `_up.sql`,回滚时执行 `_down.sql`
#### 2.0.4 前端环境变量检查CRA 特殊处理)
> **重要**: CRA (Create React App) 在构建时只读取 `.env.production` 文件,**忽略 Docker ENV/ARG 传入的环境变量**。
**Staging 构建的正确流程**
```bash
# 1. 备份原 .env.production
cp frontend/.env.production frontend/.env.production.bak
# 2. 用 .env.staging 替换 .env.production
cp frontend/.env.staging frontend/.env.production
# 3. 构建镜像(必须使用 --no-cache 确保最新代码)
docker build --no-cache --target production --platform linux/amd64 \
-t saltthing123/ai-proj-frontend:test \
-f frontend/Dockerfile \
frontend/
# 4. 恢复原 .env.production
mv frontend/.env.production.bak frontend/.env.production
# 5. 推送镜像
docker push saltthing123/ai-proj-frontend:test
```
**验证环境变量正确注入**
```bash
# 检查构建产物中的环境变量
docker run --rm saltthing123/ai-proj-frontend:test sh -c \
"grep -o 'REACT_APP_ENVIRONMENT=[^,]*' /usr/share/nginx/html/static/js/*.js | head -1"
# 期望输出: REACT_APP_ENVIRONMENT=staging
```
#### 2.0.5 Docker 缓存清理
> **关键**: 当有重要代码变更时,必须使用 `--no-cache` 避免使用缓存的旧代码层。
```bash
# 需要 --no-cache 的场景:
# - 导航菜单变更
# - 路由配置变更
# - 环境变量变更
# - 依赖包更新
docker build --no-cache --target production --platform linux/amd64 \
-t saltthing123/ai-proj-frontend:test \
-f frontend/Dockerfile \
frontend/
```
#### 2.0.6 检查清单汇总表
| 检查项 | 命令/操作 | 必须 |
|--------|----------|------|
| Git 提交日志 | `git log --oneline -20` | ✅ |
| 识别导航菜单变更 | 检查 Layout.tsx 差异 | 如有变更 |
| 识别数据库迁移 | 检查 migrations/ 目录 | 如有变更 |
| 执行数据库迁移 | SSH 到 staging 执行 SQL | 如有变更 |
| 替换 .env.production | 用 .env.staging 替换 | ✅ |
| 使用 --no-cache 构建 | `docker build --no-cache` | 推荐 |
| 验证构建产物 | 检查关键代码存在 | ✅ |
| 恢复 .env.production | 还原原文件 | ✅ |
---
### 2.1 部署前准备
**环境信息**(从项目配置读取):
- Staging 服务器:`43.134.28.147`Singapore新加坡腾讯云
- 项目路径:`/opt/ai-project-staging`
- Docker Compose 文件:`docker-compose.yml` + `docker-compose.override.yml`
- **Docker Compose**: 使用 `docker compose` (v2)v1 已卸载
### 2.2 部署方案选择
#### 方案 AOverride 配置(推荐)
使用 docker-compose override 文件覆盖镜像配置:
```bash
# 1. SSH 到 staging 服务器 (Singapore)
ssh singapore
# 或: ssh ubuntu@43.134.28.147
# 2. 进入项目目录
cd /opt/ai-project-staging
# 3. 创建 override 文件(使用 DockerHub 镜像代替本地构建)
cat > docker-compose.staging-test.yml <<'EOF'
version: '3.8'
services:
backend-staging:
image: saltthing123/ai-proj-backend:test
build: null # 覆盖 build 配置,不再本地构建
frontend-staging:
image: saltthing123/ai-proj-frontend:test
build: null # 覆盖 build 配置,不再本地构建
EOF
# 4. 拉取最新镜像
docker pull saltthing123/ai-proj-backend:test
docker pull saltthing123/ai-proj-frontend:test
# 5. 停止旧服务
docker compose -f deploy/docker-compose.staging.yml \
-f docker-compose.staging-test.yml \
down
# 6. 启动新服务
docker compose -f deploy/docker-compose.staging.yml \
-f docker-compose.staging-test.yml \
up -d backend-staging frontend-staging
# 7. 查看服务状态
docker compose -f deploy/docker-compose.staging.yml \
-f docker-compose.staging-test.yml \
ps
# 8. 查看启动日志(确认无错误)
docker logs -f ai_backend_staging --tail 50
```
#### 方案 B标签重映射更简单
直接拉取镜像并重新标记为 staging 标签:
```bash
# 1. SSH 到 staging 服务器 (Singapore)
ssh singapore
# 或: ssh ubuntu@43.134.28.147
# 2. 拉取最新镜像
docker pull saltthing123/ai-proj-backend:test
docker pull saltthing123/ai-proj-frontend:test
# 3. 重新标记为 staging 标签(如果现有配置使用 staging 标签)
docker tag saltthing123/ai-proj-backend:test saltthing123/ai-proj-backend:staging
docker tag saltthing123/ai-proj-frontend:test saltthing123/ai-proj-frontend:staging
# 4. 重启服务
cd /opt/ai-project-staging
docker compose -f deploy/docker-compose.staging.yml up -d --force-recreate backend-staging frontend-staging
# 5. 查看服务状态
docker compose -f deploy/docker-compose.staging.yml ps
# 6. 查看日志
docker logs -f ai_backend_staging --tail 50
```
### 2.3 部署验证清单
部署完成后,**必须**检查以下项目:
```bash
# 1. 容器状态检查
docker ps | grep "ai_backend_staging\|ai_frontend_staging"
# 期望Status 为 "Up"Health 为 "healthy"
# 2. 后端日志检查(无严重错误)
docker logs ai_backend_staging --tail 100 | grep -iE "error|panic|fatal"
# 期望:无 FATAL 或 PANIC 错误INFO 级别错误可忽略)
# 3. 前端日志检查Nginx 正常启动)
docker logs ai_frontend_staging --tail 50
# 期望:看到 "nginx: configuration file ... test is successful"
# 4. 端口监听检查
netstat -tlnp | grep -E "8080|3000"
# 期望8080后端和 3000前端端口被 Docker 监听
# 5. 重启 MCP bridge后端部署后必须执行
docker compose restart mcp-task-bridge-staging 2>/dev/null || echo "⚠️ mcp-task-bridge-staging 不存在,跳过"
sleep 5
docker logs mcp-task-bridge-staging --tail 5 2>/dev/null
# 期望bridge 正常启动,无连接错误
```
**⚠️ MCP bridge 重启说明**
- 后端新增 API 端点后mcp-task-bridge 需要重启才能发现新端点
- 如果 staging 未部署 mcp-task-bridge此步骤自动跳过
- 重启后等待 5 秒确保 bridge 完成初始化
---
## 3. API 验证
### 3.1 Staging API 验证
部署完成后,执行 API 健康检查:
```bash
# 1. 健康检查接口(内部访问)
curl -f http://localhost:8080/health
# 期望200 OK返回 {"status":"ok"}
# 2. 健康检查接口(外部访问,如有 Nginx 反向代理)
curl -f https://staging.pipexerp.com/api/v1/health
# 期望200 OK
# 3. 测试需求相关 API项目手册列表
curl -X GET "http://localhost:8080/api/v1/manuals?page=1&pageSize=20" \
-H "Content-Type: application/json"
# 期望:返回 401需要认证或 200返回数据
# 4. 如需认证,从凭据文件获取 token
source ~/.config/devops/credentials.env
TOKEN=$(curl -s -X POST "$AI_PROJ_API_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$AI_PROJ_USER\",\"password\":\"$AI_PROJ_PASS\"}" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['access_token'])")
curl -X GET "http://localhost:8080/api/v1/manuals?page=1&pageSize=20" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN"
# 期望200 OK返回手册列表数据
```
**验证结果记录**
```markdown
## Staging API 验证
| 接口 | 方法 | 状态码 | 响应 | 结果 |
|------|------|--------|------|------|
| /health | GET | 200 | {"status":"ok"} | ✅ |
| /api/v1/manuals | GET | 401 | 需要认证 | ✅ |
| /api/v1/manuals (with token) | GET | 200 | 返回数据 | ✅ |
```
### 3.2 Production API 验证
Production 部署后,执行相同的验证流程:
```bash
# 1. 健康检查接口
curl -f https://ai.pipexerp.com/api/v1/health
# 期望200 OK
# 2. 测试需求相关 APItoken 从凭据文件获取)
source ~/.config/devops/credentials.env
PROD_TOKEN=$(curl -s -X POST "$AI_PROJ_API_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$AI_PROJ_USER\",\"password\":\"$AI_PROJ_PASS\"}" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['access_token'])")
curl -X GET "https://ai.pipexerp.com/api/v1/manuals?page=1&pageSize=20" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PROD_TOKEN"
# 期望200 OK返回生产数据
```
### 3.3 变更定向验证(部署后必做)
**健康检查只能证明服务活着,不能证明修复生效。** 部署后必须针对本次变更做定向验证。
**执行方式**:根据 commit 内容设计验证用例,通过 API 或 MCP CLI 调用验证。
```bash
# 示例:验证 delivery_stage 字段修复
# 1. 找一个有 delivery_stage 的需求
echo '{"jsonrpc":"2.0","id":1,...}' | \
TASK_API_BASE="https://ai.pipexerp.com/api/v1" ... \
node dist/index.js 2>/dev/null | python3 -c "..."
# 2. 确认 delivery_stage 字段有值(修复前为 null
# 示例:验证新增 API 端点
curl -sf "https://ai.pipexerp.com/api/v1/NEW_ENDPOINT" -H "Authorization: ..." | head -1
# 确认返回 200 而非 404
```
**验证结果记录格式**
```markdown
### 变更定向验证
| 变更 | 验证方式 | 期望 | 实际 | 结果 |
|------|---------|------|------|------|
| delivery_stage API 修复 | GET /requirements/897 检查字段 | delivery_stage 有值 | backlog | ✅ |
| sync 工具新增 | MCP dry run | 返回 success | success | ✅ |
```
---
## 4. 前端截图验证
### 4.1 本机截图验证阶段1
本机测试阶段,使用 chrome-dev MCP 工具截图验证:
```typescript
// 1. 打开本机前端页面
mcp__chrome-dev__navigate_page({
url: "http://localhost:3000/project-manuals"
})
// 2. 等待页面加载完成
mcp__chrome-dev__wait_for({
selector: "table", // 等待表格加载
timeout: 10000
})
// 3. 截图验证
mcp__chrome-dev__take_screenshot({
name: "local-project-manuals-page"
})
```
**验证要点**
- [ ] 页面正常渲染,无白屏或错误
- [ ] 表格显示手册列表数据
- [ ] 搜索框、筛选器正常显示
- [ ] 操作按钮(查看、编辑)正常显示
### 4.2 Staging 截图验证阶段2
Staging 部署后,截图验证:
```typescript
// 1. 打开 Staging 环境页面
mcp__chrome-dev__navigate_page({
url: "https://staging.pipexerp.com/project-manuals"
})
// 2. 等待页面加载
mcp__chrome-dev__wait_for({
selector: "table",
timeout: 10000
})
// 3. 截图验证
mcp__chrome-dev__take_screenshot({
name: "staging-project-manuals-page"
})
```
**验证要点**
- [ ] 页面正常渲染
- [ ] API 数据加载成功
- [ ] 功能点正确显示(与本机测试一致)
### 4.3 Production 截图验证阶段3
Production 部署后,**必须**进行截图验证:
```typescript
// 1. 等待 Jenkins 部署完成(约 30 秒)
// 2. 打开生产环境页面
mcp__chrome-dev__navigate_page({
url: "https://ai.pipexerp.com/project-manuals"
})
// 3. 等待页面加载
mcp__chrome-dev__wait_for({
selector: "table",
timeout: 10000
})
// 4. 截图验证
mcp__chrome-dev__take_screenshot({
name: "production-project-manuals-page"
})
```
**验证要点**
- [ ] 页面正常渲染
- [ ] 生产数据正确显示
- [ ] 核心功能点正确渲染
- [ ] 无 JavaScript 错误
**⚠️ 如截图显示异常,立即回滚并排查问题。**
---
## 5. Production 部署Jenkins
### 5.0 镜像标签策略
| 标签 | 用途 | 说明 |
|------|------|------|
| `:test` | Staging | 本地构建后推送 |
| `:prod` | Production 语义标签 | 由 `:test` 重新标记,无需重建 |
| `:latest` | Production 实际拉取 | **docker-compose 默认使用此标签** |
**⚠️ 必须同时推送 `:prod` 和 `:latest`**。生产 docker-compose 使用 `${IMAGE_TAG:-latest}`,缺省拉取 `:latest`。只推 `:prod` 不推 `:latest` 会导致部署不生效。
**Staging → Production 标签转换**(避免重新构建):
```bash
# 将已验证的 staging 镜像标记为 prod + latest 并推送
for svc in backend frontend; do
docker tag saltthing123/ai-proj-$svc:test saltthing123/ai-proj-$svc:prod
docker tag saltthing123/ai-proj-$svc:test saltthing123/ai-proj-$svc:latest
docker push saltthing123/ai-proj-$svc:prod
docker push saltthing123/ai-proj-$svc:latest
done
```
### 5.1 Jenkins 部署配置
**Jenkins 服务器信息**
- URL: `https://jenkins.pipexerp.com`(凭据在 `~/.config/devops/credentials.env`
- Job: `ai-proj-production`(生产部署专用)
- 参数:`IMAGE_TAG`(默认 `latest`)、`SKIP_HEALTH_CHECK`(默认 false
- 认证HTTP Basic Auth`$JENKINS_USER:$JENKINS_TOKEN`
**生产服务器部署路径**`/opt/ai-project/docker-compose.dockerhub.yml`
- 镜像标签通过 `${IMAGE_TAG:-prod}` 动态注入
### 5.2 触发 Jenkins 构建
使用 Jenkins API 触发部署:
```bash
source ~/.config/devops/credentials.env
# 触发构建IMAGE_TAG 默认已是 prod可省略参数
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
-u "$JENKINS_USER:$JENKINS_TOKEN" \
"$JENKINS_URL/job/ai-proj-production/buildWithParameters?IMAGE_TAG=prod&SKIP_HEALTH_CHECK=false")
echo "HTTP: $HTTP_CODE" # 期望 201
# 获取最新构建编号
sleep 5
BUILD_NUMBER=$(curl -s -u "$JENKINS_USER:$JENKINS_TOKEN" \
"$JENKINS_URL/job/ai-proj-production/api/json" | \
python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('lastBuild',{}).get('number',''))")
echo "Build #$BUILD_NUMBER"
# 轮询构建状态
for i in $(seq 1 30); do
sleep 10
RESULT=$(curl -s -u "$JENKINS_USER:$JENKINS_TOKEN" \
"$JENKINS_URL/job/ai-proj-production/$BUILD_NUMBER/api/json" | \
python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result','null'), d.get('building', False))")
echo "[$i] $RESULT"
if echo "$RESULT" | grep -qE "^(SUCCESS|FAILURE|ABORTED)"; then break; fi
done
```
**⚠️ Jenkins CSRF 注意事项**
- 修改 Job 配置时,`config.xml` POST 接口容易报 500
- **推荐方案**:改用 Script Console`/scriptText`
```bash
# 通过 Script Console 修改 Job 参数默认值
CRUMB=$(curl -s -c /tmp/jc.txt -u "$JENKINS_USER:$JENKINS_TOKEN" \
"$JENKINS_URL/crumbIssuer/api/json" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['crumb'])")
curl -s -X POST \
-u "$JENKINS_USER:$JENKINS_TOKEN" \
-b /tmp/jc.txt \
-H "Jenkins-Crumb: $CRUMB" \
--data-urlencode 'script=
def job = Jenkins.instance.getItem("ai-proj-production")
def params = job.getProperty(hudson.model.ParametersDefinitionProperty.class)
params.getParameterDefinitions().each { p ->
if (p.name == "IMAGE_TAG") { p.defaultValue = "prod" }
}
job.save()
println "Done"
' "$JENKINS_URL/scriptText"
```
### 5.3 Jenkins 构建失败处理
如果构建失败,执行以下排查步骤:
```bash
# 1. 获取构建日志
curl -s -u "$JENKINS_USER:$JENKINS_TOKEN" \
"$JENKINS_URL/job/$JENKINS_JOB/$BUILD_NUMBER/consoleText" > jenkins-build.log
# 2. 查看错误信息
cat jenkins-build.log | grep -iE "error|failed|exception"
# 3. 常见问题排查
问题1: 镜像拉取失败
- 检查 DockerHub 推送是否成功
- 检查生产服务器网络是否正常
问题2: 容器启动失败
- 查看容器日志
- 检查环境变量配置
问题3: 健康检查失败
- 检查服务端口是否正常
- 检查数据库连接
# 4. 提供解决方案并通知用户
```
### 5.4 部署结果写入需求历史
**⚠️ 关键步骤**Jenkins 部署完成后(无论成功或失败),**必须**将部署摘要写入需求的操作历史。
**前置条件**:部署必须关联到某个需求(有 REQ-ID
**写入方式**:通过 ai-proj MCP 的需求历史 API
```bash
# 部署成功时
curl -X POST "https://ai.pipexerp.com/api/v1/requirements/${REQ_ID}/history" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"action": "deployed",
"field_name": "environment",
"new_value": "production",
"comment": "Jenkins Build #${BUILD_NUMBER} SUCCESS\n耗时: ${DURATION}\n镜像: ${IMAGE_NAME}:${IMAGE_TAG} (${IMAGE_SIZE})\nCommit: ${COMMIT_HASH}"
}'
# 部署失败时
curl -X POST "https://ai.pipexerp.com/api/v1/requirements/${REQ_ID}/history" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"action": "deployed",
"field_name": "environment",
"new_value": "production",
"comment": "Jenkins Build #${BUILD_NUMBER} FAILURE\n错误: ${ERROR_SUMMARY}"
}'
```
**在 req-deploy 技能流程中的位置**
```
Jenkins 构建完成
获取构建结果BUILD_NUMBER, RESULT, DURATION
API 健康检查
★ 调用 POST /requirements/{id}/history 记录部署结果
前端截图验证
创建部署文档
```
**comment 字段模板**
```
Jenkins Build #{BUILD_NUMBER} {RESULT}
环境: {staging|production}
耗时: {DURATION}
镜像: {IMAGE_NAME}:{TAG} ({SIZE})
Commit: {HASH}
健康检查: {PASS|FAIL}
```
**Staging 部署也需记录**`new_value` 设为 `"staging"`)。
---
## 6. 部署任务文档创建
### 6.1 批量部署文档模板
部署完成后,**必须**创建部署文档并附加到部署批次任务:
```markdown
# 批量部署文档 - {project} {date}
## 部署概览
| 项目 | 日期 | 包含需求数 | 环境 | 状态 |
|------|------|-----------|------|------|
| ai-proj | 2026-02-18 | 3 | staging → production | ✅ 完成 |
### 包含需求
| REQ ID | 标题 | delivery_stage 变更 |
|--------|------|-------------------|
| REQ-20260218-0013 | 用户认证功能 | testing → released |
| REQ-20260218-0015 | 手册管理功能 | testing → released |
| REQ-20260218-0016 | 阶段化管理优化 | testing → released |
---
## 镜像信息
| 服务 | 镜像 | 大小 | 阈值 | 状态 |
|------|------|------|------|------|
| 后端 | saltthing123/ai-proj-backend:test/:prod | 96MB | < 200MB | ✅ |
| 前端 | saltthing123/ai-proj-frontend:test/:prod | 89MB | < 500MB | ✅ |
---
## Staging 部署
- **部署方式**: SSH 手动部署
- **服务器**: singapore (43.134.28.147)
- **时间**: {timestamp}
### API 验证
| 接口 | 状态码 | 结果 |
|------|--------|------|
| /health | 200 | ✅ |
---
## Production 部署
- **部署方式**: Jenkins API
- **构建号**: #{BUILD_NUMBER}
- **状态**: {SUCCESS/FAILURE}
- **时间**: {timestamp}
### API 验证
| 接口 | 状态码 | 结果 |
|------|--------|------|
| /health | 200 | ✅ |
---
## 问题记录
(如无问题可省略)
## 总结
✅ {N} 个需求批量部署完成
✅ 所有需求已推进到 released
```
### 6.2 创建批量部署文档
```typescript
// 1. 创建部署批次任务(由 /req deploy 流程创建)
const deployTask = create_task({
title: "【部署】ai-proj 2026-02-18 (3 requirements)"
})
// 2. 关联到所有选中需求
for (const req of selectedRequirements) {
link_tasks_to_requirement({
requirementId: req.id,
taskIds: [deployTask.id],
linkRole: "deploy"
})
}
// 3. 执行部署流程...
// 4. 部署成功后,附加文档
create-and-attach({
taskId: deployTask.id,
title: "批量部署文档 - ai-proj 2026-02-18",
content: batchDeployDocContent
})
// 5. 批量推进所有需求到 released
for (const req of selectedRequirements) {
advance_delivery_stage(req.id, "released")
}
// 6. 完成部署任务
complete_task(deployTask.id)
```
---
## 7. 与 req skill 集成
### 7.1 两层分离模型
**需求维度**:各需求独立走到 testing 完成 → 进入「待部署池」
**部署维度**`/req deploy` → 收集所有待部署需求 → 一次构建部署 → 批量推进到 released
### 7.2 调用方式
在 req skill 中,`/req deploy` 命令调用本 skill
```markdown
### `/req deploy [--project <name>] [--env <environment>]`
这是**项目级批量部署**命令,不是单需求操作。
执行步骤:
1. 收集待部署需求delivery_stage=testing, test 任务完成, 有 implementation 任务)
2. 展示待部署列表 → AskUserQuestion 确认范围
3. 创建部署批次任务,关联所有选中需求
4. **调用 req-deploy skill** 执行部署:
- 构建 Docker 镜像 + 镜像大小检查
- 推送到 DockerHub
- Staging: SSH 手动部署 + API 验证 + 截图验证
- Production: Jenkins API 触发 + API 验证 + 截图验证
5. 部署成功后批量推进需求到 released
6. 创建批量部署文档
```
### 7.3 标准工作流(两层分离)
```
需求维度(各需求独立):
/req test REQ-A → testing 完成 → 进入待部署池
/req test REQ-B → testing 完成 → 进入待部署池
/req test REQ-C → testing 完成 → 进入待部署池
部署维度(项目级批量):
/req deploy --env staging
收集 REQ-A, REQ-B, REQ-C待部署池
(req-deploy skill 执行 Staging 部署)
Staging 验证通过
/req deploy --env production
(req-deploy skill 执行 Production 部署)
批量推进 REQ-A/B/C → released
/req done REQ-A, /req done REQ-B, /req done REQ-C
```
---
## 8. 环境配置管理
### 8.1 项目环境配置(从 .claude/settings.local.json 读取)
```json
{
"REQUIREMENT_PROJECT": "ai-proj",
"STAGING_SERVER": "43.134.28.147",
"STAGING_SERVER_ALIAS": "singapore",
"STAGING_USER": "ubuntu",
"STAGING_PATH": "/opt/ai-project-staging",
"STAGING_NETWORK": "ai-project-staging_ai_staging_network",
"PRODUCTION_DOMAIN": "ai.pipexerp.com",
"DOCKERHUB_REPO": "saltthing123",
"JENKINS_URL": "http://116.62.148.151:8888",
"JENKINS_JOB": "ai-proj-deploy"
}
```
### 8.2 凭据文件(`~/.config/devops/credentials.env`
统一存储所有部署相关凭据:
```bash
# AI-Proj API 配置
AI_PROJ_API_URL=https://ai.pipexerp.com
AI_PROJ_USER=<username>
AI_PROJ_PASS=<password>
# Jenkins 配置
JENKINS_URL=https://jenkins.pipexerp.com
JENKINS_USER=<username>
JENKINS_TOKEN=<token>
# 飞书部署通知
FEISHU_DEPLOY_WEBHOOK=<webhook_url>
```
**⚠️ 安全提醒**:凭据文件权限必须为 600`chmod 600 ~/.config/devops/credentials.env`),防止其他用户读取。
**REST API 标准用法**(替代硬编码 token
```bash
source ~/.config/devops/credentials.env
TOKEN=$(curl -s -X POST "$AI_PROJ_API_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$AI_PROJ_USER\",\"password\":\"$AI_PROJ_PASS\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['data']['access_token'])")
# 后续使用 -H "Authorization: Bearer $TOKEN"
```
### 8.3 服务配置
| 服务 | Staging | Production |
|------|---------|------------|
| 后端 | localhost:8080 | https://ai.pipexerp.com/api/v1 |
| 前端 | localhost:3000 | https://ai.pipexerp.com |
| 数据库 | postgres-staging:5432 | postgres-prod:5432 |
| Redis | redis-staging:6379 | redis-prod:6379 |
---
## 9. 安全约束
⚠️ **关键安全规则**
1. **禁止自动 SSH 部署到生产服务器**
- Staging 部署命令只显示,由用户手动执行
- Production 必须通过 Jenkins API 触发
2. **构建失败必须显示错误原因**
- 不能隐藏错误信息
- 必须提供解决方案
3. **生产部署后必须进行截图验证**
- 不能跳过此步骤
- 截图保存到部署任务文档中
4. **部署完成后必须创建部署任务文档**
- 记录部署步骤、问题、验证结果
- 关联到需求
---
## 10. 常见问题排查
### Q1: 镜像构建失败
```bash
# 1. 查看构建日志
docker build ... 2>&1 | tee build.log
# 2. 检查 Dockerfile 语法
docker build --no-cache ...
# 3. 检查网络问题npm install 超时)
docker build --network=host ...
```
### Q2: 容器启动失败
```bash
# 1. 查看容器日志
docker logs <container-name> --tail 100
# 2. 检查环境变量
docker inspect <container-name> | jq '.[0].Config.Env'
# 3. 检查端口冲突
netstat -tlnp | grep <port>
```
### Q3: API 返回 502/503
```bash
# 1. 检查后端容器状态
docker ps | grep backend
# 2. 检查后端日志
docker logs ai_backend_staging --tail 100
# 3. 检查数据库连接
docker exec ai_backend_staging curl http://localhost:8080/health
```
### Q4: Jenkins 构建失败
```bash
# 1. 获取构建日志
curl -s -u "$USER:$TOKEN" "$JENKINS_URL/job/$JOB/$BUILD_NUMBER/consoleText"
# 2. 检查 Jenkins 服务状态
curl -s "$JENKINS_URL/login"
# 3. 检查生产服务器磁盘空间
ssh root@<prod-server> "df -h"
```
---
## 11. iOS 应用部署
### 11.1 部署概述
iOS 应用部署与 Web 应用不同,**不使用 Docker 和 Jenkins**,而是通过连接到 cn-dev (中国开发电脑) 进行真机安装。
**部署流程**
```
本机代码提交
SSH 到 cn-dev
git pull 更新代码
Xcode 构建
安装到 iPhone
```
### 11.2 服务器信息
| 配置项 | 值 |
|--------|-----|
| 服务器别名 | cn-dev / coolbuy-dev |
| IP 地址 | 100.112.161.79 (Tailscale VPN) |
| 用户名 | coolbuy-dev |
| SSH 密钥 | ~/.ssh/id_ed25519_coolbuy_dev |
| 项目路径 | ~/coding/qiudl/new-ai-proj/AI-Proj-iOS |
### 11.3 SSH 配置
确保 `~/.ssh/config` 包含以下配置:
```
Host cn-dev coolbuy-dev
HostName 100.112.161.79
User coolbuy-dev
IdentityFile ~/.ssh/id_ed25519_coolbuy_dev
IdentitiesOnly yes
ServerAliveInterval 30
ServerAliveCountMax 6
```
### 11.4 部署步骤
#### 步骤 1: 确保本机代码已提交
```bash
# 检查是否有未提交的更改
cd /Users/donglinlai/coding/qiudl/new-ai-proj/AI-Proj-iOS
git status
# 如有更改,提交并推送
git add -A
git commit -m "feat(iOS): 功能描述"
git push origin main
```
#### 步骤 2: SSH 到 cn-dev 并更新代码
```bash
# SSH 连接到 cn-dev
ssh cn-dev
# 进入 iOS 项目目录
cd ~/coding/qiudl/new-ai-proj/AI-Proj-iOS
# 拉取最新代码
git pull origin main
# 确认代码已更新
git log -1 --oneline
```
#### 步骤 3: 在 cn-dev 上构建并安装
```bash
# 在 cn-dev 上打开 Xcode 项目
open AI-Proj-iOS.xcodeproj
# 或者使用命令行构建(需要连接 iPhone
xcodebuild -project AI-Proj-iOS.xcodeproj \
-scheme AI-Proj-iOS \
-destination 'platform=iOS,id=<设备UDID>' \
-configuration Debug \
build
```
**通过 Xcode GUI 安装**
1. 打开 Xcode 项目
2. 选择目标设备(已连接的 iPhone
3. 点击 Run (⌘R) 构建并安装
### 11.5 一键部署命令
从 au-dev 一键执行部署流程:
```bash
# 1. 推送本机代码
cd /Users/donglinlai/coding/qiudl/new-ai-proj/AI-Proj-iOS
git add -A && git commit -m "feat(iOS): 更新" && git push origin main
# 2. SSH 到 cn-dev 更新代码
ssh cn-dev "cd ~/coding/qiudl/new-ai-proj/AI-Proj-iOS && git pull origin main && echo '✅ 代码已更新'"
# 3. 提示在 cn-dev 上打开 Xcode 安装
echo "请在 cn-dev 上执行: open ~/coding/qiudl/new-ai-proj/AI-Proj-iOS/AI-Proj-iOS.xcodeproj"
```
### 11.6 远程打开 Xcode可选
如果 cn-dev 上有 VNC 或远程桌面访问:
```bash
# 通过 SSH 在 cn-dev 上打开 Xcode需要 GUI 环境)
ssh cn-dev "open ~/coding/qiudl/new-ai-proj/AI-Proj-iOS/AI-Proj-iOS.xcodeproj"
```
### 11.7 验证安装
安装完成后验证:
1. **App 启动检查**:确保 App 能正常启动
2. **登录功能**:测试登录是否正常
3. **新功能验证**:测试本次部署的新功能
4. **API 连接**:确保 App 能正确连接到后端 API
### 11.8 常见问题
#### Q1: SSH 连接超时
```bash
# 检查 Tailscale 状态
tailscale status
# 如果 cn-dev 离线,需要在 cn-dev 本地启动 Tailscale
```
#### Q2: Xcode 构建失败
```bash
# 清理构建缓存
cd ~/coding/qiudl/new-ai-proj/AI-Proj-iOS
rm -rf ~/Library/Developer/Xcode/DerivedData/AI-Proj-iOS-*
# 重新构建
xcodebuild clean -project AI-Proj-iOS.xcodeproj -scheme AI-Proj-iOS
```
#### Q3: iPhone 未识别
```bash
# 检查设备连接
xcrun xctrace list devices
# 信任开发者证书(在 iPhone 上)
# 设置 → 通用 → VPN与设备管理 → 信任开发者
```
### 11.9 iOS TestFlight 发布
#### 11.9.1 概述
TestFlight 是 Apple 官方的内测分发平台,用于在正式发布到 App Store 之前进行内测。
**TestFlight vs 直接安装对比**
| 特性 | 直接安装 (cn-dev) | TestFlight |
|------|-------------------|------------|
| 安装方式 | USB 连接 + Xcode | OTA 无线安装 |
| 设备数量 | 单台设备 | 最多 10,000 测试用户 |
| 需要 UDID | 是 | 否 |
| 审核 | 无 | Apple 自动审核 (约 1-2 天) |
| 有效期 | 无限制 | 90 天 |
| 适用场景 | 开发调试 | 内测分发、用户验收 |
#### 11.9.2 前置条件
1. **Apple Developer 账号**:需要有效的 Apple Developer Program 会员资格
2. **App Store Connect 配置**
- App 已在 App Store Connect 创建
- Bundle ID 已注册
- 签名证书和 Provisioning Profile 已配置
3. **Xcode 配置**
- 已登录 Apple Developer 账号
- 自动签名或手动签名已配置
#### 11.9.3 构建 Archive
在 cn-dev 上构建发布版本:
```bash
# SSH 到 cn-dev
ssh cn-dev
# 进入项目目录
cd ~/coding/qiudl/new-ai-proj/AI-Proj-iOS
# 拉取最新代码
git pull origin main
# 方法 1: 通过 Xcode GUI
open AI-Proj-iOS.xcodeproj
# 菜单: Product → Archive
# 方法 2: 通过命令行
xcodebuild -project AI-Proj-iOS.xcodeproj \
-scheme AI-Proj-iOS \
-configuration Release \
-archivePath ~/Desktop/AI-Proj-iOS.xcarchive \
archive
# Archive 输出路径
# ~/Library/Developer/Xcode/Archives/YYYY-MM-DD/AI-Proj-iOS.xcarchive
```
#### 11.9.4 上传到 App Store Connect
**方法 1: 通过 Xcode Organizer (推荐)**
```
1. 打开 Xcode → Window → Organizer
2. 选择刚构建的 Archive
3. 点击 "Distribute App"
4. 选择 "App Store Connect" → "Upload"
5. 选择分发选项:
- Include bitcode: 否 (可选)
- Upload your app's symbols: 是
- Manage Version and Build Number: 是
6. 选择签名证书和 Profile
7. 点击 "Upload"
8. 等待上传完成 (约 5-10 分钟)
```
**方法 2: 通过 altool 命令行**
```bash
# 导出 IPA
xcodebuild -exportArchive \
-archivePath ~/Desktop/AI-Proj-iOS.xcarchive \
-exportPath ~/Desktop/AI-Proj-iOS-Export \
-exportOptionsPlist ExportOptions.plist
# ExportOptions.plist 内容
cat > ExportOptions.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>destination</key>
<string>upload</string>
<key>signingStyle</key>
<string>automatic</string>
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
</dict>
</plist>
EOF
# 使用 altool 上传 (已废弃,建议使用 xcrun)
xcrun altool --upload-app \
-f ~/Desktop/AI-Proj-iOS-Export/AI-Proj-iOS.ipa \
-t ios \
-u "your-apple-id@example.com" \
-p "@keychain:AC_PASSWORD"
# 或使用 xcrun notarytool (Xcode 13+)
xcrun notarytool submit \
~/Desktop/AI-Proj-iOS-Export/AI-Proj-iOS.ipa \
--apple-id "your-apple-id@example.com" \
--password "@keychain:AC_PASSWORD" \
--team-id "YOUR_TEAM_ID"
```
#### 11.9.5 TestFlight 内测分发
**步骤 1: 等待处理**
上传完成后App Store Connect 会自动处理:
- 处理时间:约 15-30 分钟
- 状态变化Processing → Ready to Submit
**步骤 2: 添加测试信息**
```
1. 登录 App Store Connect (https://appstoreconnect.apple.com)
2. 选择 App → TestFlight 标签页
3. 选择新上传的构建版本
4. 填写测试信息:
- What to Test: 测试重点说明
- App Description: 应用描述
- Feedback Email: 反馈邮箱
5. 提交内部测试 (Internal Testing)
```
**步骤 3: 邀请测试用户**
```
内部测试 (最多 100 人):
1. TestFlight → Internal Testing → 添加测试人员
2. 测试人员必须是 App Store Connect 用户
3. 无需 Apple 审核,立即可用
外部测试 (最多 10,000 人):
1. TestFlight → External Testing → 添加测试组
2. 输入测试用户邮箱
3. 需要 Apple 审核 (约 1-2 天)
4. 审核通过后,用户收到 TestFlight 邀请邮件
```
**步骤 4: 测试用户安装**
```
1. 测试用户从 App Store 下载 TestFlight App
2. 打开邀请邮件中的链接,或输入邀请码
3. 在 TestFlight 中安装测试版本
4. 应用旁会显示橙色圆点,表示是测试版本
```
#### 11.9.6 一键 TestFlight 部署脚本
```bash
#!/bin/bash
# deploy-testflight.sh - iOS TestFlight 一键部署脚本
set -e
# 配置
PROJECT_PATH="$HOME/coding/qiudl/new-ai-proj/AI-Proj-iOS"
PROJECT_NAME="AI-Proj-iOS"
SCHEME="AI-Proj-iOS"
ARCHIVE_PATH="$HOME/Desktop/${PROJECT_NAME}.xcarchive"
EXPORT_PATH="$HOME/Desktop/${PROJECT_NAME}-Export"
echo "=== iOS TestFlight 部署 ==="
# 1. 更新代码
echo "📥 更新代码..."
cd "$PROJECT_PATH"
git pull origin main
# 2. 获取版本号
VERSION=$(xcodebuild -showBuildSettings -project "${PROJECT_NAME}.xcodeproj" -scheme "$SCHEME" 2>/dev/null | grep "MARKETING_VERSION" | head -1 | awk '{print $3}')
BUILD=$(xcodebuild -showBuildSettings -project "${PROJECT_NAME}.xcodeproj" -scheme "$SCHEME" 2>/dev/null | grep "CURRENT_PROJECT_VERSION" | head -1 | awk '{print $3}')
echo "📦 版本: $VERSION (Build $BUILD)"
# 3. 构建 Archive
echo "🔨 构建 Archive..."
xcodebuild -project "${PROJECT_NAME}.xcodeproj" \
-scheme "$SCHEME" \
-configuration Release \
-archivePath "$ARCHIVE_PATH" \
archive
# 4. 导出 IPA
echo "📤 导出 IPA..."
xcodebuild -exportArchive \
-archivePath "$ARCHIVE_PATH" \
-exportPath "$EXPORT_PATH" \
-exportOptionsPlist "$PROJECT_PATH/ExportOptions.plist"
echo ""
echo "✅ 构建完成!"
echo ""
echo "下一步:"
echo "1. 打开 Xcode Organizer: xed -b com.apple.dt.Xcode"
echo "2. 选择 Archive → Distribute App → App Store Connect"
echo "3. 或使用命令行上传:"
echo " xcrun altool --upload-app -f $EXPORT_PATH/${PROJECT_NAME}.ipa -t ios -u YOUR_APPLE_ID -p @keychain:AC_PASSWORD"
```
#### 11.9.7 TestFlight 部署与 req skill 集成
在 req skill 中iOS TestFlight 部署命令:
```markdown
/req deploy REQ-ID --platform ios --env testflight
执行步骤:
1. SSH 到 cn-dev 更新代码
2. 执行 Archive 构建
3. 上传到 App Store Connect
4. 在 TestFlight 添加测试信息
5. 邀请测试用户
6. 确认测试用户可安装后,标记部署完成
```
### 11.10 iOS 部署与 req skill 集成
在 req skill 中iOS 项目的 `/req deploy` 命令应执行:
```markdown
/req deploy REQ-ID --platform ios [--env <environment>]
环境选项:
- 无参数或 --env dev: 直接安装到开发设备 (cn-dev)
- --env testflight: 发布到 TestFlight 内测
执行步骤 (开发安装)
1. 检查本机代码是否已提交
2. SSH 到 cn-dev 执行 git pull
3. 提示用户在 cn-dev 上打开 Xcode 构建并安装
4. 用户确认安装完成后,标记部署任务完成
执行步骤 (TestFlight)
1. 检查本机代码是否已提交
2. SSH 到 cn-dev 执行 git pull
3. 构建 Archive 并导出 IPA
4. 上传到 App Store Connect
5. 配置 TestFlight 测试信息
6. 邀请测试用户
7. 确认测试用户可安装后,标记部署完成
```
**iOS 项目标准工作流**
```
开发测试流程:
/req test REQ-ID
本机代码验证通过
/req deploy REQ-ID --platform ios
(SSH 到 cn-dev 更新代码)
(在 cn-dev 上 Xcode 安装到 iPhone)
用户验证 App 功能
/req done REQ-ID
内测分发流程:
/req test REQ-ID
本机代码验证通过
/req deploy REQ-ID --platform ios --env testflight
(构建 Archive + 上传 App Store Connect)
(TestFlight 配置 + 邀请测试用户)
测试用户验证 App 功能
/req done REQ-ID
```
### 11.11 iOS 常见问题补充
#### Q5: Archive 构建失败 - 签名问题
```bash
# 检查签名配置
security find-identity -v -p codesigning
# 解锁钥匙串
security unlock-keychain -p "your-password" ~/Library/Keychains/login.keychain-db
# 检查 Provisioning Profile
ls ~/Library/MobileDevice/Provisioning\ Profiles/
```
#### Q6: 上传到 App Store Connect 失败
```bash
# 验证 IPA 格式
xcrun altool --validate-app -f /path/to/app.ipa -t ios -u YOUR_APPLE_ID -p @keychain:AC_PASSWORD
# 常见错误:
# - Invalid Binary: 检查 Info.plist 配置
# - Missing Icon: 确保所有尺寸的 App Icon 都存在
# - Provisioning Profile: 确保使用 App Store 类型的 Profile
```
#### Q7: TestFlight 处理时间过长
```
正常处理时间: 15-30 分钟
如果超过 1 小时:
1. 检查 App Store Connect 状态
2. 可能正在进行额外审核
3. 联系 Apple Developer Support
```
---
## 12. Android 应用部署
### 12.1 部署概述
Android 应用部署通过连接到 **fuxing 电脑** 进行真机安装,使用 ADB (Android Debug Bridge) 将 APK 安装到连接的 Android 设备。
**部署流程**
```
本机代码提交
SSH 到 fuxing 电脑
git pull 更新代码
构建 APK
通过 ADB 安装到 Android 设备
```
### 12.2 服务器信息
| 配置项 | 值 |
|--------|-----|
| 服务器别名 | fuxing |
| IP 地址 | 100.90.190.119 (Tailscale VPN) |
| 用户名 | (根据实际配置) |
| 项目路径 | (根据实际项目位置) |
### 12.3 SSH 配置
确保 `~/.ssh/config` 包含以下配置:
```
Host fuxing
HostName 100.90.190.119
User <username>
IdentityFile ~/.ssh/<your-key>
IdentitiesOnly yes
ServerAliveInterval 30
ServerAliveCountMax 6
```
### 12.4 部署步骤
#### 步骤 1: 确保本机代码已提交
```bash
# 检查是否有未提交的更改
cd /path/to/android-project
git status
# 如有更改,提交并推送
git add -A
git commit -m "feat(Android): 功能描述"
git push origin main
```
#### 步骤 2: SSH 到 fuxing 并更新代码
```bash
# SSH 连接到 fuxing
ssh fuxing
# 进入 Android 项目目录
cd /path/to/android-project
# 拉取最新代码
git pull origin main
# 确认代码已更新
git log -1 --oneline
```
#### 步骤 3: 构建 APK
```bash
# 在 fuxing 上构建 APK
cd /path/to/android-project
# 使用 Gradle 构建 debug APK
./gradlew assembleDebug
# 或构建 release APK
./gradlew assembleRelease
# APK 输出路径
# Debug: app/build/outputs/apk/debug/app-debug.apk
# Release: app/build/outputs/apk/release/app-release.apk
```
#### 步骤 4: 通过 ADB 安装到 Android 设备
```bash
# 检查已连接的 Android 设备
adb devices
# 如果有多个设备,指定设备 ID
adb -s <device-id> install -r app/build/outputs/apk/debug/app-debug.apk
# 单设备直接安装(-r 表示替换已有应用)
adb install -r app/build/outputs/apk/debug/app-debug.apk
# 安装成功后启动应用
adb shell am start -n <package-name>/<activity-name>
```
### 12.5 一键部署命令
从本机一键执行部署流程:
```bash
# 1. 推送本机代码
cd /path/to/android-project
git add -A && git commit -m "feat(Android): 更新" && git push origin main
# 2. SSH 到 fuxing 执行部署
ssh fuxing "cd /path/to/android-project && git pull origin main && ./gradlew assembleDebug && adb install -r app/build/outputs/apk/debug/app-debug.apk && echo '✅ Android 应用已安装'"
```
### 12.6 验证安装
安装完成后验证:
1. **App 启动检查**:确保 App 能正常启动
2. **登录功能**:测试登录是否正常
3. **新功能验证**:测试本次部署的新功能
4. **API 连接**:确保 App 能正确连接到后端 API
### 12.7 常见问题
#### Q1: SSH 连接超时
```bash
# 检查 Tailscale 状态
tailscale status
# 确认 fuxing 电脑在线 (100.90.190.119)
ping 100.90.190.119
```
#### Q2: ADB 设备未识别
```bash
# 检查 USB 调试是否已开启
# Android 设备上:设置 → 开发者选项 → USB 调试
# 重启 ADB 服务
adb kill-server
adb start-server
# 重新检查设备
adb devices
# 如果显示 unauthorized需要在手机上允许调试
```
#### Q3: Gradle 构建失败
```bash
# 清理构建缓存
./gradlew clean
# 重新构建
./gradlew assembleDebug --stacktrace
```
#### Q4: APK 安装失败
```bash
# 卸载旧版本后重新安装
adb uninstall <package-name>
adb install app/build/outputs/apk/debug/app-debug.apk
# 检查签名问题
# 确保 debug/release APK 使用正确的签名
```
### 12.8 Android 部署与 req skill 集成
在 req skill 中Android 项目的 `/req deploy` 命令应执行:
```markdown
/req deploy REQ-ID --platform android
执行步骤:
1. 检查本机代码是否已提交
2. SSH 到 fuxing (100.90.190.119) 执行 git pull
3. 构建 APK (./gradlew assembleDebug)
4. 通过 ADB 安装到连接的 Android 设备
5. 用户确认安装完成后,标记部署任务完成
```
**Android 项目标准工作流**
```
/req test REQ-ID
本机代码验证通过
/req deploy REQ-ID --platform android
(SSH 到 fuxing 100.90.190.119)
(构建 APK 并通过 ADB 安装)
用户验证 App 功能
/req done REQ-ID
```
---
## 变更记录
| 版本 | 日期 | 变更内容 | 变更人 |
|------|------|----------|--------|
| V1.2 | 2026-01-30 | 新增 Android 应用部署章节Section 12支持通过 fuxing 电脑 (100.90.190.119) 部署到 Android 设备 | Claude + qiudl |
| V1.1 | 2026-01-29 | 新增 iOS 应用部署章节Section 11支持通过 cn-dev 部署到 iPhone | Claude + qiudl |
| V1.0 | 2026-01-26 | 从 req skill 拆分,创建独立的 req-deploy 技能 | Claude + qiudl |