wxs 6cc703ada2 feat: monorepo 重构 + 新增 5 个平台适配器
项目从单体结构重构为 pnpm monorepo (shared/backend/frontend),
新增 YouTube、Instagram、Twitter/X、哔哩哔哩、微博 5 个平台适配器,
包含完整的单元测试和 E2E 测试覆盖。

- 完成 T-031~T-044: 5 个适配器实现、注册、配置和测试
- 重构前后端分离: Hono 后端 + Next.js 前端
- 151 个单元测试 + 21 个 Mock E2E + 25 个真实 E2E
- 适配器基于真实 TikHub API 响应结构实现

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:43:25 +08:00

317 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: deploy
description: Drone CI + 服务器 CD 全流程引导:从基础设施检查到生成配置文件到验证部署,交互式完成。
---
# Deploy - CI/CD 全流程部署引导
> **定位**:基于公司 Drone CI + 私有 Docker Registry + Docker Compose 的自动化部署方案,交互式引导用户从零完成 CI/CD 接入。
当用户调用 `/deploy``/deploy <指令>` 时,执行以下步骤:
## 1. 收集项目信息
快速了解项目情况(已知的不重复问):
| 项目 | 说明 | 示例 |
|------|------|------|
| 项目名称 | 用于镜像命名 | `douyin`, `crm`, `blog` |
| 需构建的服务 | 每个服务对应一个镜像 | `backend`, `frontend` |
| 各服务的 Dockerfile 路径 | Docker build context | `./backend`, `./frontend` |
| 生产服务器 SSH 端口 | 默认 22 | `22`, `3141` |
| 部署目录 | 生产服务器上的路径 | `/opt/docker/myproject` |
| 数据库迁移命令 | 如有 | `alembic upgrade head`, `npx prisma migrate deploy` |
| 健康检查方式 | 三选一 | python / curl / host |
| 健康检查 URL | 容器内地址 | `http://127.0.0.1:8000/health` |
| 通知 Webhook | 可选,不配则跳过 | 企业微信/钉钉/飞书 |
确认后进入下一步。
## 2. 基础设施检查
输出 Checklist让用户逐项确认首次接入需全部完成后续项目可跳过
```
□ 基础设施(一次性,已完成则跳过)
□ Drone CI Server + Runner 已部署运行
□ 私有 Docker Registry 已运行(默认 :5000
□ insecure-registries 已配置Drone CI 服务器 + 生产服务器)
□ SSH 密钥已配置Drone CI 服务器 → 生产服务器免密登录)
□ 生产服务器用户已加入 docker 组
```
如果用户表示基础设施未就绪,输出对应的一次性搭建指引(参见下方「附录:基础设施搭建」)。
## 3. 生成配置文件
基于收集到的信息,生成以下文件:
### 3.1 `.drone.yml`
核心原则(踩坑总结,不可违反):
1. 使用 `docker:27-cli` + 宿主机 Docker socket**不用** `plugins/docker` DinD
2. 使用 `environment: { VAR: { from_secret: name } }` 注入密钥,**不用** `secrets:` 字段
3. 使用 `${DRONE_TAG:-latest}` 作为镜像 tag**不自定义中间变量**Drone 变量替换冲突)
4. 触发条件只用 `event: [tag, cron]`**不叠加** `cron: [name]`AND 运算陷阱)
生成内容包括:
- `trigger`: tag + cron
- `volumes`: 挂载宿主机 Docker socket
- 每个服务的 `build-<service>` step
- `deploy` step使用 `appleboy/drone-ssh`
- `notify-success` / `notify-failure` step如配置了 Webhook
### 3.2 `scripts/deploy-remote.sh`
部署脚本要点:
- `set -euo pipefail` 严格模式
- 部署锁PID 文件防并发)
- 同时 export `IMAGE_TAG``VERSION`(兼容不同 compose 变量命名)
- 按顺序pull → 停 beat → 更新核心服务 → 健康检查 → 数据库迁移 → 启动剩余服务 → 最终健康检查
- 健康检查根据用户选择的方式生成python / curl / host
### 3.3 生成后展示
```
已生成:
📄 .drone.yml — Drone CI 流水线配置
📄 scripts/deploy-remote.sh — 远程部署脚本
确认写入?[Y/n]
```
用户确认后写入文件。
## 4. Drone 面板配置引导
生成文件后,输出需要在 Drone 面板手动配置的清单:
### 4.1 仓库设置
```
在 Drone 面板完成以下配置:
1. 激活仓库SYNC → 找到仓库 → ACTIVATE
2. 开启 TrustedSettings → General → Project Settings → 勾选 Trusted
```
### 4.2 Secrets 配置
根据收集到的信息,输出具体的 Secret 列表:
```
在 Drone 面板 → 仓库 Settings → Secrets 添加:
| Secret 名称 | 填写内容 |
|-------------------|----------------------------------------------------|
| backend_repo | docker.internal.intelligrow.cn:5000/{project}-backend |
| frontend_repo | docker.internal.intelligrow.cn:5000/{project}-frontend |
| deploy_host | {生产服务器 IP} |
| deploy_user | {SSH 用户} |
| deploy_ssh_key | cat ~/.ssh/drone_deploy 的完整内容 |
| deploy_path | {部署目录} |
| wecom_webhook | {Webhook URL}(如已配置) |
```
### 4.3 Cron 配置(可选)
```
如需定时构建,在 Settings → Cron Jobs 添加:
| 字段 | 值 | 说明 |
|----------|------------------|-------------------------|
| Name | nightly-build | 任务名称 |
| Branch | main | 构建分支 |
| Schedule | 0 16 * * * | UTC 16:00 = 北京 00:00 |
```
## 5. 生产服务器配置引导
```
在生产服务器上确认:
1. 部署目录结构:
{deploy_path}/
├── docker-compose.prod.yml
├── .env
└── scripts/
└── deploy-remote.sh ← 需从代码仓库复制
2. .env 至少包含:
DOCKER_REGISTRY=docker.internal.intelligrow.cn:5000
(其他数据库密码等生产配置)
3. docker-compose.prod.yml 中镜像引用格式:
image: ${DOCKER_REGISTRY}/{project}-backend:${VERSION:-latest}
⚠️ 变量一致性Drone Secret 的镜像地址前缀 = .env 的 DOCKER_REGISTRY = compose 中的镜像名拼接结果
```
## 6. 验证
自己执行可执行的验证,不能远程执行的给出命令让用户确认结果:
### 6.1 本地验证(自己执行)
```bash
# 检查 .drone.yml 语法合法性YAML 解析)
# 检查 deploy-remote.sh 语法bash -n
# 检查文件是否已正确写入
```
### 6.2 远程验证引导(输出命令,让用户在服务器上执行并反馈结果)
```bash
# 推送 Tag 触发首次构建
git tag v{version}
git push origin v{version}
# 观察 Drone 面板 pipeline 状态
# 生产服务器检查
ssh -p {port} {user}@{host} "cd {deploy_path} && docker compose -f docker-compose.prod.yml ps"
```
## 7. 完成输出
```
✅ CI/CD 接入完成!
📄 生成的文件:
- .drone.yml
- scripts/deploy-remote.sh
🔧 Drone 面板配置(需手动):
- [x] 仓库已激活
- [x] Trusted 已开启
- [x] Secrets 已添加
- [ ] Cron Job可选
🖥️ 生产服务器:
- [ ] .env 已配置
- [ ] deploy-remote.sh 已复制
- [ ] 首次部署成功
📖 回滚方案:
方式1: ssh 到生产服务器执行 bash scripts/deploy-remote.sh {旧版本tag}
方式2: git tag {旧版本}-rollback {旧版本} && git push origin {旧版本}-rollback
主人,用不用我沉淀 or git 提交?
```
---
## 附录:基础设施搭建
当用户表示基础设施未就绪时,按需输出以下指引:
### A. Drone CI 部署
在 Drone CI 服务器创建 `~/drone/docker-compose.yml`
```yaml
services:
drone-server:
image: drone/drone:2
container_name: drone-server
restart: always
ports:
- "3080:80"
environment:
- DRONE_GITEA_SERVER=https://<your-gitea-domain>
- DRONE_GITEA_CLIENT_ID=<gitea-oauth-client-id>
- DRONE_GITEA_CLIENT_SECRET=<gitea-oauth-client-secret>
- DRONE_SERVER_HOST=<your-drone-domain>
- DRONE_SERVER_PROTO=https
- DRONE_RPC_SECRET=<openssl rand -hex 16 生成>
- DRONE_USER_CREATE=username:<gitea用户名>,admin:true
volumes:
- ./data:/data
drone-runner:
image: drone/drone-runner-docker:1
container_name: drone-runner
restart: always
depends_on:
- drone-server
environment:
- DRONE_RPC_PROTO=http
- DRONE_RPC_HOST=drone-server
- DRONE_RPC_SECRET=<与 server 相同>
- DRONE_RUNNER_CAPACITY=2
- DRONE_RUNNER_NAME=drone-runner-1
volumes:
- /var/run/docker.sock:/var/run/docker.sock
```
关键注意:
- `DRONE_RPC_PROTO=http`Runner 走 Docker 内网直连,不走 HTTPS
- `DRONE_USER_CREATE` 的 username 必须与 Gitea **登录用户名**完全一致(不是邮箱)
### B. 私有 Registry
```bash
docker run -d --name registry \
-p 5000:5000 \
-v /opt/registry-data:/var/lib/registry \
--restart always \
registry:2
```
### C. insecure-registries 配置
在 Drone CI 服务器和生产服务器的 `/etc/docker/daemon.json` 添加:
```json
{
"insecure-registries": ["<registry-host>:5000"]
}
```
**不要带 `http://` 前缀**,直接写 `host:port`。修改后 `sudo systemctl restart docker`
### D. SSH 免密
```bash
# Drone CI 服务器上生成密钥
ssh-keygen -t ed25519 -C "drone-ci-deploy" -f ~/.ssh/drone_deploy -N ""
# 将公钥添加到生产服务器
ssh-copy-id -i ~/.ssh/drone_deploy.pub -p <port> <user>@<production-ip>
# 验证
ssh -i ~/.ssh/drone_deploy -p <port> <user>@<production-ip> "echo ok"
```
---
## 踩坑清单(生成配置时必须规避)
| # | 坑 | 正确做法 |
|---|-----|---------|
| 1 | `insecure-registries``http://` 前缀 | 直接写 `host:port` |
| 2 | Drone `${VAR}` 与 shell 变量冲突 | 直接用 `${DRONE_TAG:-latest}`,不赋中间变量 |
| 3 | 用 `secrets:` 字段注入 secret | 用 `environment: { VAR: { from_secret: name } }` |
| 4 | `plugins/docker` DinD 启动失败 | 用 `docker:27-cli` + 挂载 Docker socket |
| 5 | `DRONE_USER_CREATE` 填邮箱 | 必须填 Gitea 登录用户名 |
| 6 | `event + cron` 触发条件互斥 | 只用 `event: [tag, cron]`,不加 `cron:` 过滤 |
| 7 | Registry 地址不一致IP vs 域名) | Drone Secret、`.env`、compose 三处统一 |
| 8 | SSH 端口不对 | `appleboy/drone-ssh` 显式指定 `port` |
| 9 | Docker 权限不足 | `sudo usermod -aG docker <user>` 后重新登录 |
| 10 | `daemon.json` 被覆盖 | 修改前先 cat 查看,合并内容 |
---
## 故障排查速查表
| 现象 | 检查方向 |
|------|---------|
| Pipeline 不触发 | Gitea Webhook 是否勾选"创建"事件;`.drone.yml` trigger |
| Step 一直 pending | Runner 是否连通 Server仓库是否 Trusted |
| 构建报 secret 为空 | `environment: from_secret` 而非 `secrets:` |
| Docker push 失败 (HTTPS) | 两台服务器 `insecure-registries` 配置 |
| SSH 部署超时 | 密钥是否正确端口是否匹配Docker 权限 |
| 镜像名 invalid reference | `.env``DOCKER_REGISTRY` 变量是否正确 |
| 数据库迁移失败 | `docker compose logs -f <service>` |
| 健康检查超时 | 增大 `MAX_ATTEMPTS`;检查服务启动日志 |