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>
1958 lines
52 KiB
Markdown
1958 lines
52 KiB
Markdown
# 需求部署 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 <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 |
|