# 需求部署 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-production,IMAGE_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`**,否则默认构建最后一个 stage(testing),导致镜像过大 - 前端 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' # ✅ 正确的前端 Dockerfile(production 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 部署方案选择 #### 方案 A:Override 配置(推荐) 使用 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. 测试需求相关 API(token 从凭据文件获取) 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 ] [--env ]` 这是**项目级批量部署**命令,不是单需求操作。 执行步骤: 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= AI_PROJ_PASS= # Jenkins 配置 JENKINS_URL=https://jenkins.pipexerp.com JENKINS_USER= JENKINS_TOKEN= # 飞书部署通知 FEISHU_DEPLOY_WEBHOOK= ``` **⚠️ 安全提醒**:凭据文件权限必须为 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 --tail 100 # 2. 检查环境变量 docker inspect | jq '.[0].Config.Env' # 3. 检查端口冲突 netstat -tlnp | grep ``` ### 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@ "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' method app-store destination upload signingStyle automatic teamID YOUR_TEAM_ID 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 ] 环境选项: - 无参数或 --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 IdentityFile ~/.ssh/ 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 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 / ``` ### 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 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 |