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

52 KiB
Raw Blame History

需求部署 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判断哪些服务需要重新构建

# 获取 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

# 查看最近变更涉及的目录
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,否则下次部署无法对比:

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 需添加(一次性):

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 标签)

# 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 镜像大小检查

# 获取镜像大小(字节)
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 镜像过大排查流程

# 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

# 推送镜像到 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 阈值)

正在推送镜像...

获取镜像大小的方法

# 方法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 提交,识别关键功能变更:

# 1. 查看最近提交(识别关键功能)
git log --oneline -20

# 2. 重点关注以下关键词的提交:
#    - feat: 新功能(特别是导航菜单、新页面)
#    - migration: 数据库迁移
#    - route: 路由变更
#    - menu/nav: 导航菜单变更

2.0.2 导航菜单入口检查

如果提交中包含导航菜单变更,必须:

  1. 检查 Layout.tsx 中的菜单配置

    # 查看导航菜单相关变更
    git diff HEAD~10 -- frontend/src/components/Layout.tsx | grep -A5 -B5 "menuItems\|SubMenu\|Menu.Item"
    
  2. 确认新菜单路由已配置

    # 检查路由文件
    git diff HEAD~10 -- frontend/src/routes/*.tsx
    
  3. 验证构建产物包含新代码

    # 构建后检查(在部署前验证)
    docker run --rm saltthing123/ai-proj-frontend:test sh -c \
      "grep -r '<关键路由关键词>' /usr/share/nginx/html/static/js/ | wc -l"
    

2.0.3 数据库迁移检查

如果提交中包含数据库迁移,必须在部署前应用迁移:

# 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 构建的正确流程

# 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

验证环境变量正确注入

# 检查构建产物中的环境变量
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 避免使用缓存的旧代码层。

# 需要 --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.147Singapore新加坡腾讯云
  • 项目路径:/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 文件覆盖镜像配置:

# 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 标签:

# 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 部署验证清单

部署完成后,必须检查以下项目:

# 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 健康检查:

# 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返回手册列表数据

验证结果记录

## Staging API 验证

| 接口 | 方法 | 状态码 | 响应 | 结果 |
|------|------|--------|------|------|
| /health | GET | 200 | {"status":"ok"} | ✅ |
| /api/v1/manuals | GET | 401 | 需要认证 | ✅ |
| /api/v1/manuals (with token) | GET | 200 | 返回数据 | ✅ |

3.2 Production API 验证

Production 部署后,执行相同的验证流程:

# 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 调用验证。

# 示例:验证 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

验证结果记录格式

### 变更定向验证
| 变更 | 验证方式 | 期望 | 实际 | 结果 |
|------|---------|------|------|------|
| delivery_stage API 修复 | GET /requirements/897 检查字段 | delivery_stage 有值 | backlog | ✅ |
| sync 工具新增 | MCP dry run | 返回 success | success | ✅ |

4. 前端截图验证

4.1 本机截图验证阶段1

本机测试阶段,使用 chrome-dev MCP 工具截图验证:

// 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 部署后,截图验证:

// 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 部署后,必须进行截图验证:

// 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 标签转换(避免重新构建):

# 将已验证的 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 触发部署:

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
# 通过 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 构建失败处理

如果构建失败,执行以下排查步骤:

# 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

# 部署成功时
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 批量部署文档模板

部署完成后,必须创建部署文档并附加到部署批次任务:

# 批量部署文档 - {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 创建批量部署文档

// 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

### `/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 读取)

{
  "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

统一存储所有部署相关凭据:

# 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>

⚠️ 安全提醒:凭据文件权限必须为 600chmod 600 ~/.config/devops/credentials.env),防止其他用户读取。

REST API 标准用法(替代硬编码 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'])")
# 后续使用 -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: 镜像构建失败

# 1. 查看构建日志
docker build ... 2>&1 | tee build.log

# 2. 检查 Dockerfile 语法
docker build --no-cache ...

# 3. 检查网络问题npm install 超时)
docker build --network=host ...

Q2: 容器启动失败

# 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

# 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 构建失败

# 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: 确保本机代码已提交

# 检查是否有未提交的更改
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 并更新代码

# 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 上构建并安装

# 在 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 一键执行部署流程:

# 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 或远程桌面访问:

# 通过 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 连接超时

# 检查 Tailscale 状态
tailscale status

# 如果 cn-dev 离线,需要在 cn-dev 本地启动 Tailscale

Q2: Xcode 构建失败

# 清理构建缓存
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 未识别

# 检查设备连接
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 上构建发布版本:

# 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 命令行

# 导出 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 部署脚本

#!/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 部署命令:

/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 命令应执行:

/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 构建失败 - 签名问题

# 检查签名配置
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 失败

# 验证 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: 确保本机代码已提交

# 检查是否有未提交的更改
cd /path/to/android-project
git status

# 如有更改,提交并推送
git add -A
git commit -m "feat(Android): 功能描述"
git push origin main

步骤 2: SSH 到 fuxing 并更新代码

# SSH 连接到 fuxing
ssh fuxing

# 进入 Android 项目目录
cd /path/to/android-project

# 拉取最新代码
git pull origin main

# 确认代码已更新
git log -1 --oneline

步骤 3: 构建 APK

# 在 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 设备

# 检查已连接的 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 一键部署命令

从本机一键执行部署流程:

# 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 连接超时

# 检查 Tailscale 状态
tailscale status

# 确认 fuxing 电脑在线 (100.90.190.119)
ping 100.90.190.119

Q2: ADB 设备未识别

# 检查 USB 调试是否已开启
# Android 设备上:设置 → 开发者选项 → USB 调试

# 重启 ADB 服务
adb kill-server
adb start-server

# 重新检查设备
adb devices

# 如果显示 unauthorized需要在手机上允许调试

Q3: Gradle 构建失败

# 清理构建缓存
./gradlew clean

# 重新构建
./gradlew assembleDebug --stacktrace

Q4: APK 安装失败

# 卸载旧版本后重新安装
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 命令应执行:

/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