Skip to content

Commit f278bb5

Browse files
nieaoclaude
andcommitted
ci: GitHub Actions 自动部署 (push main → ssh VPS → git pull + build)
绕过 [meta-cc] 本机 SSH 网络障碍 (Azure Tencent 出口 IP 被 VPS fail2ban ban) GitHub runner 出口 IP 不在 ban 列表里, 能 SSH 通. 新增: - .github/workflows/deploy.yml: push main 触发, 也支持 workflow_dispatch 手动触发 - 只在源码改动时触发 (改 docs / gitignore 不触发) - 同名 deploy 串行 (concurrency) - 自动安装 SSH key, ssh 跑 deploy-on-vps.sh, https /canvas/ 验证 - cleanup step 删 SSH key (always run) - deploy/GITHUB-ACTIONS-SETUP.md: lichang333 + boss 一次性配置指引 - 一行 echo + tee 加公钥到 authorized_keys - 3 个 GitHub Secrets 配置 (DEPLOY_SSH_KEY/HOST/USER) - 手动触发首次部署 - Caddy 一次性追加 (无法自动化) - 故障排查 + deploy 用户最小权限设计 + 私钥安全说明 配置完成后任何 push 自动 1-2 分钟上线. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b98fd9e commit f278bb5

2 files changed

Lines changed: 385 additions & 0 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Deploy to VPS
2+
3+
# 触发条件: push 到 main 时自动部署
4+
# 也可以在 GitHub UI 手动触发 (workflow_dispatch)
5+
6+
on:
7+
push:
8+
branches: [ main ]
9+
paths:
10+
# 只有这些路径变化才触发部署 — 改 docs/.gitignore 之类不触发
11+
- 'src/**'
12+
- 'server/**'
13+
- 'deploy/**'
14+
- 'public/**'
15+
- 'package.json'
16+
- 'package-lock.json'
17+
- 'vite.config.js'
18+
- 'index.html'
19+
- '.github/workflows/deploy.yml'
20+
workflow_dispatch:
21+
# 手动触发: GitHub UI → Actions → Deploy to VPS → Run workflow
22+
inputs:
23+
reason:
24+
description: '部署原因 (可空)'
25+
required: false
26+
default: 'manual deploy'
27+
28+
concurrency:
29+
# 同时只允许一个部署在跑, 新的 push 会取消正在进行的
30+
group: deploy-vps
31+
cancel-in-progress: false
32+
33+
jobs:
34+
deploy:
35+
name: Deploy to ha2.digitalvio.shop
36+
runs-on: ubuntu-latest
37+
timeout-minutes: 15
38+
39+
steps:
40+
- name: Checkout repo (用于显示 commit info, 实际部署是 VPS git pull)
41+
uses: actions/checkout@v4
42+
with:
43+
fetch-depth: 1
44+
45+
- name: 显示部署信息
46+
run: |
47+
echo "=========================================="
48+
echo " Deploy reason: ${{ inputs.reason || github.event.head_commit.message }}"
49+
echo " Commit: ${{ github.sha }}"
50+
echo " Pusher: ${{ github.actor }}"
51+
echo " Target: ${{ secrets.DEPLOY_HOST }}"
52+
echo "=========================================="
53+
54+
- name: 安装 SSH 私钥
55+
env:
56+
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
57+
run: |
58+
mkdir -p ~/.ssh
59+
echo "$SSH_PRIVATE_KEY" > ~/.ssh/deploy_key
60+
chmod 600 ~/.ssh/deploy_key
61+
# 接受 host key (避免首次连接 prompt)
62+
ssh-keyscan -H -p ${{ secrets.DEPLOY_PORT || '22' }} ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
63+
echo " ✓ SSH key 已配置"
64+
65+
- name: SSH 部署 (git pull + build + restart)
66+
run: |
67+
ssh -i ~/.ssh/deploy_key \
68+
-o StrictHostKeyChecking=accept-new \
69+
-o ConnectTimeout=20 \
70+
-p ${{ secrets.DEPLOY_PORT || '22' }} \
71+
${{ secrets.DEPLOY_USER || 'root' }}@${{ secrets.DEPLOY_HOST }} \
72+
"set -e
73+
echo '==> 在 VPS 上 pull + 部署...'
74+
if [ ! -d /opt/know-canvas/.git ]; then
75+
sudo git clone https://github.com/${{ github.repository }}.git /opt/know-canvas
76+
sudo chown -R \$(whoami):\$(whoami) /opt/know-canvas
77+
fi
78+
cd /opt/know-canvas
79+
git fetch origin main
80+
git reset --hard origin/main
81+
sudo bash deploy/deploy-on-vps.sh
82+
echo '==> 部署脚本完成, 健康检查...'
83+
sleep 2
84+
curl -sf http://127.0.0.1:1234/health || (echo 'yws health 失败' && sudo journalctl -u know-canvas-yws -n 20 --no-pager && exit 1)
85+
echo '==> 部署成功 ✓'
86+
"
87+
88+
- name: 验证 HTTPS 可达
89+
run: |
90+
# 在 GitHub runner (公网) 验证 https 入口
91+
STATUS=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 https://ha2.digitalvio.shop/canvas/ || echo '000')
92+
echo " HTTPS /canvas/ status: $STATUS"
93+
if [ "$STATUS" = "200" ] || [ "$STATUS" = "301" ] || [ "$STATUS" = "302" ]; then
94+
echo " ✓ Canvas 已上线 https://ha2.digitalvio.shop/canvas/"
95+
elif [ "$STATUS" = "404" ]; then
96+
echo " ⚠ 404 — Caddy 还没追加 /canvas/ 配置 (一次性手动操作, 见 deploy/ONESHOT.md)"
97+
else
98+
echo " ⚠ 异常 status $STATUS — 但 yws health 已 OK, 通常 Caddy 配置问题"
99+
fi
100+
101+
- name: 清理 SSH key
102+
if: always()
103+
run: rm -f ~/.ssh/deploy_key

deploy/GITHUB-ACTIONS-SETUP.md

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
# GitHub Actions 自动部署 — 一次性配置指南
2+
3+
> **配完之后**: 任何人 push 到 `main` 分支 → GitHub Actions 自动 SSH 到 VPS → git pull + build + 重启 → 1-2 分钟内上线
4+
> **谁配**: lichang333 配 VPS 一次 + boss 配 GitHub Secrets 一次, **总共 5 分钟**
5+
6+
---
7+
8+
## 第一步: 在 VPS 上加 deploy 用的 SSH 公钥
9+
10+
ssh 上 VPS (lichang333 自己的方式, 我们 [meta-cc] 上不去):
11+
12+
```bash
13+
ssh root@64.176.62.85
14+
```
15+
16+
把下面整段公钥追加到 `/root/.ssh/authorized_keys`:
17+
18+
```
19+
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINm+ZFZc99HRra9uR84nwwkZdr4h2Ax8oO5L/E8QaTNh nieao@hermes-agent-hackathon-2026-05-02
20+
```
21+
22+
一行命令搞定:
23+
24+
```bash
25+
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINm+ZFZc99HRra9uR84nwwkZdr4h2Ax8oO5L/E8QaTNh nieao@hermes-agent-hackathon-2026-05-02" | sudo tee -a /root/.ssh/authorized_keys
26+
sudo chmod 600 /root/.ssh/authorized_keys
27+
sudo chmod 700 /root/.ssh
28+
```
29+
30+
(可选) 验证 GitHub Actions 出口 IP 不被 fail2ban ban:
31+
32+
```bash
33+
sudo fail2ban-client status sshd
34+
```
35+
36+
如果有大量 banned IP, 暂时禁用 fail2ban 让首次部署通过:
37+
38+
```bash
39+
sudo systemctl stop fail2ban
40+
# 部署成功后再开
41+
sudo systemctl start fail2ban
42+
```
43+
44+
---
45+
46+
## 第二步: 把 SSH 私钥配成 GitHub Secret
47+
48+
### 2.1 拿到私钥内容
49+
50+
[meta-cc] 的本地机 (boss 你想猫的电脑) 拿:
51+
52+
```bash
53+
cat ~/.ssh/hermes_agent_vps
54+
```
55+
56+
会输出类似:
57+
58+
```
59+
-----BEGIN OPENSSH PRIVATE KEY-----
60+
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQ...
61+
... 多行 base64 ...
62+
-----END OPENSSH PRIVATE KEY-----
63+
```
64+
65+
**整段复制** (包括 BEGIN / END 行).
66+
67+
### 2.2 配 GitHub Secret
68+
69+
打开浏览器:
70+
71+
`https://github.com/nieao/know-canvas/settings/secrets/actions`
72+
73+
(如果没看到 settings, 说明账号不是仓库 owner — 需要 nieao 自己操作)
74+
75+
**"New repository secret"**, 加 3 个:
76+
77+
| Secret 名 || 说明 |
78+
|----------|---|------|
79+
| `DEPLOY_SSH_KEY` | 上面 cat 的私钥**整段** (含 BEGIN/END 行) | SSH 私钥 |
80+
| `DEPLOY_HOST` | `64.176.62.85` | VPS IP (或域名 `ha2.digitalvio.shop` 也行) |
81+
| `DEPLOY_USER` | `root` | SSH 用户名 (新建 deploy 用户的话改这个) |
82+
83+
(可选) 第 4 个:
84+
85+
| `DEPLOY_PORT` | `22` | SSH 端口 — 默认 22, 不用配 |
86+
87+
---
88+
89+
## 第三步: 触发首次部署
90+
91+
### 方式 A: 手动触发 (推荐第一次)
92+
93+
`https://github.com/nieao/know-canvas/actions/workflows/deploy.yml`
94+
95+
**"Run workflow"** → Branch `main`**"Run workflow"** 按钮.
96+
97+
约 1-3 分钟跑完, 看绿色勾就是成功. 如果红色叉, 点进去看哪一步失败.
98+
99+
### 方式 B: 推一个 commit 测试
100+
101+
```bash
102+
cd "/e/claude code/know-canvas"
103+
git commit --allow-empty -m "trigger first deploy"
104+
git push origin main
105+
```
106+
107+
GitHub Actions 自动跑.
108+
109+
---
110+
111+
## 第四步: 一次性 Caddy 配置 (无法自动化)
112+
113+
**重要**: 部署脚本不会自动改 `/etc/caddy/Caddyfile` (避免破坏 Hermes 现有配置). 第一次部署后必须手动追加.
114+
115+
ssh 上 VPS:
116+
117+
```bash
118+
sudo nano /etc/caddy/Caddyfile
119+
```
120+
121+
找到 `ha2.digitalvio.shop {` 这一段, 在它的 `}` 之前插入:
122+
123+
```caddy
124+
# Know Canvas 前端 (静态文件)
125+
handle_path /canvas/* {
126+
root * /var/www/know-canvas
127+
try_files {path} /index.html
128+
file_server
129+
}
130+
131+
# Yjs WebSocket 反代
132+
handle_path /yws/* {
133+
reverse_proxy localhost:1234 {
134+
header_up Host {host}
135+
header_up X-Real-IP {remote}
136+
}
137+
}
138+
```
139+
140+
应用:
141+
142+
```bash
143+
sudo caddy validate /etc/caddy/Caddyfile
144+
sudo systemctl reload caddy
145+
```
146+
147+
验证:
148+
149+
```bash
150+
curl -s -o /dev/null -w '%{http_code}\n' https://ha2.digitalvio.shop/canvas/
151+
# 期望: 200
152+
```
153+
154+
---
155+
156+
## 配置完成后的工作流
157+
158+
**[meta-cc] / [ui-cc] / boss 任何人**:
159+
160+
```bash
161+
git push origin main # → GitHub Actions 自动部署
162+
```
163+
164+
**1-2 分钟内**: VPS 上的 `/var/www/know-canvas/` 自动更新, 浏览器刷新 https://ha2.digitalvio.shop/canvas/ 看到最新版本.
165+
166+
**手动触发** (不 push 也能部署, 比如想 redeploy 一次):
167+
168+
`https://github.com/nieao/know-canvas/actions/workflows/deploy.yml` → Run workflow.
169+
170+
---
171+
172+
## 故障排查
173+
174+
### Actions 显示 "Permission denied (publickey)"
175+
176+
公钥没加到 VPS authorized_keys, 或路径错了. 在 VPS 上:
177+
178+
```bash
179+
sudo cat /root/.ssh/authorized_keys | grep nieao
180+
# 应该看到完整的一行 ssh-ed25519 AAAA... nieao@hermes-agent-hackathon-2026-05-02
181+
```
182+
183+
### Actions 显示 "Connection closed by remote host"
184+
185+
VPS 的 fail2ban 把 GitHub runner IP ban 了. 解决:
186+
187+
```bash
188+
sudo fail2ban-client status sshd # 看 banned IP
189+
sudo fail2ban-client set sshd unbanip <IP>
190+
```
191+
192+
或者临时禁用:
193+
194+
```bash
195+
sudo systemctl stop fail2ban
196+
# 跑完部署再
197+
sudo systemctl start fail2ban
198+
```
199+
200+
### Actions 显示 "git pull 失败" (合并冲突)
201+
202+
VPS 上 /opt/know-canvas 有未提交改动:
203+
204+
```bash
205+
cd /opt/know-canvas
206+
git status # 看哪些文件改了
207+
git stash # 暂存掉
208+
# 或 git reset --hard origin/main (慎用, 丢失本地改动)
209+
```
210+
211+
### Actions 部署成功但 https://ha2.digitalvio.shop/canvas/ 返回 404
212+
213+
Caddy 还没追加 `/canvas/*` 段. 见上面"第四步".
214+
215+
---
216+
217+
## 高级: 用专门的 deploy 用户 (更安全)
218+
219+
避免给 GitHub Actions root 权限, 在 VPS 上:
220+
221+
```bash
222+
sudo useradd -m -s /bin/bash deploy
223+
sudo mkdir -p /home/deploy/.ssh
224+
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINm+ZFZc99HRra9uR84nwwkZdr4h2Ax8oO5L/E8QaTNh nieao@hermes-agent-hackathon-2026-05-02" | sudo tee /home/deploy/.ssh/authorized_keys
225+
sudo chmod 600 /home/deploy/.ssh/authorized_keys
226+
sudo chmod 700 /home/deploy/.ssh
227+
sudo chown -R deploy:deploy /home/deploy/.ssh
228+
229+
# deploy 用户能 sudo 跑哪些命令 (最小权限原则)
230+
sudo tee /etc/sudoers.d/deploy-know-canvas <<'EOF'
231+
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart know-canvas-yws
232+
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl reload caddy
233+
deploy ALL=(ALL) NOPASSWD: /bin/bash /opt/know-canvas/deploy/deploy-on-vps.sh
234+
deploy ALL=(ALL) NOPASSWD: /usr/bin/cp -r /opt/know-canvas/dist /var/www/
235+
EOF
236+
sudo chmod 440 /etc/sudoers.d/deploy-know-canvas
237+
238+
# /opt/know-canvas 给 deploy 写权限
239+
sudo chown -R deploy:deploy /opt/know-canvas
240+
sudo chown -R deploy:deploy /var/www/know-canvas 2>/dev/null || true
241+
```
242+
243+
然后把 GitHub Secret `DEPLOY_USER` 改成 `deploy`.
244+
245+
---
246+
247+
## 关于私钥安全
248+
249+
私钥 `~/.ssh/hermes_agent_vps` **永远不入 git**:
250+
251+
- ✅ 它****作为 GitHub Secret 存在 (加密存储, GitHub 内部都看不到)
252+
- ✅ 用完会自动 rm (workflow 末尾 `cleanup` step)
253+
- ❌ 不写到任何文件 / 注释 / 日志里
254+
- ❌ 不发到 Slack / 邮件 / 群聊
255+
256+
如果担心泄露, 任何时候都可以重新生成一对 key:
257+
258+
```bash
259+
# 本机
260+
ssh-keygen -t ed25519 -f ~/.ssh/hermes_agent_vps_v2 -N ""
261+
# 把 .pub 加到 VPS authorized_keys, .pub 旧的删掉
262+
# GitHub Secret DEPLOY_SSH_KEY 换成新私钥内容
263+
```
264+
265+
---
266+
267+
## 配置好后效果预览
268+
269+
```
270+
boss / cc 任何人:
271+
git push origin main
272+
↓ (1 秒内)
273+
GitHub Actions 触发
274+
↓ (10 秒装 SSH key)
275+
ssh root@64.176.62.85
276+
↓ (~30 秒 git pull + npm install + build)
277+
systemctl restart know-canvas-yws
278+
↓ (5 秒 health check)
279+
完成 ✓ 通知 boss
280+
```
281+
282+
总耗时约 **1-2 分钟**. 期间 yws 重启会让所有协作用户**短暂断开** (Yjs 自动重连, 不丢数据).

0 commit comments

Comments
 (0)