TimeTrace 一次性部署清单(手把手)¶
这份文档不会进 git(
.git/info/exclude已配置)。 看完做完照着上面打勾,然后rm DEPLOY-CHECKLIST.md即可。仅你自己在自己机器上操作;不要把里面的 token / 私钥贴给别人,包括我。
0. 一分钟科普 SSH 公私钥(看一遍就够了)¶
每个 SSH 用户都有一对 key:
- 私钥 (private key):文件名常见
id_ed25519、id_rsa。永远不要给任何人看,不要传到公网,不要进 git。这是"你的身份"。 - 公钥 (public key):文件名加
.pub后缀,如id_ed25519.pub。可以随便贴在哪里,包括 GitHub Settings、远端的~/.ssh/authorized_keys、Stack Overflow。它只能用来验证带着对应私钥的人,不能反推私钥。
登录远端流程是这样的:
你本机: 有 私钥 id_ed25519
小主机: ~/.ssh/authorized_keys 里有 你的 id_ed25519.pub
→ ssh 小主机
→ 远端发挑战:"你拿私钥签个数字给我看"
→ 本机用私钥签
→ 远端用授权过的公钥验签 → 通过 → 放你进去
"为什么要两把 key"(你的开发 key + CI 专用 key):
- 开发 key(已存在的
~/.ssh/id_ed25519之类):你本人用,带口令保护 - CI 专用 key(这次新生成的
timetrace_deploy):GitHub Actions 用,无口令(runner 自动跑不可能输密码)
CI key 单独一把的好处:
- 万一 GitHub Secrets 泄露 / 你想吊销,只 revoke 这一把,不影响你日常 SSH
- GitHub Actions 拿不到带口令的 key(unlock 需要你输密码)
- 审计清晰:"哪次部署用的哪把 key"一目了然
1. 本机准备(PowerShell)¶
1.1 确认 gh 已可用¶
打开新的 PowerShell 窗口(你之前装完 gh 没重启终端 → 旧窗口看不到):
期望看到:
如果 gh: command not found:重启 PowerShell;还不行就 winget install --id GitHub.cli 再试。
1.2 看你的 ~/.ssh 里有哪些 key¶
期望看到至少一对开发 key,比如 id_ed25519 + id_ed25519.pub。
如果连开发 key 都没有:
ssh-keygen -t ed25519 -f $HOME\.ssh\id_ed25519 -C "Yuki dev key"
# 提示 passphrase 时建议输一个,回车两次也可(开发 key 无口令风险中等)
2. 用密码登录小主机一次(最后一次用密码)¶
你之前一直密码登录小主机,所以下面假设 ~/.ssh/config 里已经有 tt-rb4g 这类条目,可以直接:
如果 ~/.ssh/config 里没配过 tt-rb4g,可以临时直连:
ssh Yuki@121.x.x.x -p <FRP 映射的小主机 SSH 端口>
# 第一次会问 "Are you sure you want to continue connecting (yes/no)?" → yes
记下三个数字(后面 GitHub Secrets 会用到):
| 名称 | 取值方法 |
|---|---|
| 跳板 IP | tt-rb4g 在 ~/.ssh/config 里指向的那个 121.x.x.x;或 gci $HOME\.ssh\config 看 HostName |
| 跳板 SSH 端口 | 通常 22。如果你 FRP 改过就用你改的 |
| FRP 映射到小主机的端口 | 你在 frpc.toml 里给小主机 22 端口配的 remote_port,例如 22001 |
如果忘了 FRP 端口,登录到云服务器上看 frps.toml 或 ss -tlnp | grep frps 查找。
3. 把你的开发公钥装到小主机(一次性,告别密码)¶
Windows PowerShell 没有内置 ssh-copy-id。两种办法:
办法 A:Git Bash 里跑(推荐)¶
办法 B:PowerShell 一行命令模拟¶
Get-Content $HOME\.ssh\id_ed25519.pub | ssh tt-rb4g "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
验证:
4. 生成 CI 专用 keypair(无口令)¶
参数说明:
- -t ed25519:算法(比 RSA 更短更快更安全)
- -f $HOME\.ssh\timetrace_deploy:输出文件名(会得到两个文件:timetrace_deploy 私钥 + timetrace_deploy.pub 公钥)
- -C "timetrace-deploy":注释,方便日后辨认这把 key 是哪用的
- -N '""':空口令(CI runner 无法输密码)。PowerShell 语法注意是 '""',外单引号 + 内双引号
生成后看一眼:
5. 把 CI 公钥装到小主机的 authorized_keys¶
验证 CI key 也能登:
如果 ~/.ssh/config 里 tt-rb4g 锁定了 IdentityFile 到你的开发 key,-i 会被忽略。改成直连测:
6. 生成 known_hosts(让 CI 不弹"信任此主机吗")¶
CI runner 每次跑都是干净环境,没有缓存的 host key。我们提前抓两个 host 的 SSH 指纹,作为 secret 喂进去。
在本机 PowerShell:
# 跳板的 host key(121.x.x.x 是真实 IP,22 是 SSH 端口)
ssh-keyscan -p 22 121.x.x.x > $HOME\timetrace_known_hosts.txt
# 通过跳板看小主机的 host key(注意 127.0.0.1 + FRP 端口)
# 这条会在跳板上执行 ssh-keyscan
ssh tt-rb4g "ssh-keyscan -p <FRP端口> 127.0.0.1" >> $HOME\timetrace_known_hosts.txt
cat $HOME\timetrace_known_hosts.txt
期望看到 2-6 行(每台主机可能有 rsa/ecdsa/ed25519 多种算法的 key):
保留这个文件,第 7 步会用。
7. 在 GitHub 仓库配 6 个 Secrets¶
打开浏览器:
或者用 gh CLI 一条条加(推荐,下面给精确命令)。
7.1 DEPLOY_SSH_KEY — CI 私钥全文¶
Web 方式:复制
Get-Content $HOME\.ssh\timetrace_deploy -Raw输出,包括头尾的-----BEGIN OPENSSH PRIVATE KEY-----/-----END OPENSSH PRIVATE KEY-----,粘进去。
7.2 DEPLOY_KNOWN_HOSTS — 第 6 步生成的文件全文¶
7.3 DEPLOY_JUMP_USER_HOST — user@跳板IP¶
把
Yuki换成你的云服务器实际用户名(可能是root/ubuntu/yuki,登一次ssh tt-rb4g whoami确认);121.x.x.x换成你云服务器真实 IP。
7.4 DEPLOY_JUMP_PORT — 跳板 SSH 端口¶
通常 22。如果你云服务器把 sshd 改到别的端口(比如 22022),填那个。
7.5 DEPLOY_TARGET_USER_HOST — 从跳板视角看小主机¶
这里
Yuki是小主机用户名,不是云服务器用户名。127.0.0.1是因为 FRP 在跳板的 loopback 监听,从跳板视角看就是本机。
7.6 DEPLOY_TARGET_PORT — 跳板上 FRP 监听端口¶
替换成你
frpc.toml里remote_port配的值。
7.7 验证¶
应该看到这 6 个,每个右侧显示更新时间。Secret 值不可读回(GH 安全设计),上面命令只能"存"和"列"。
8. 小主机一次性配 systemd lingering¶
让 server 在你登出后继续跑(默认 user systemd 在用户登出时停服务):
Yuki换成小主机的真实用户名。
验证:
9. 首次部署 — 手动触发 + 紧盯日志¶
9.1 触发 workflow¶
第一次部署前确认你想部署的代码已经在
main分支。当前在feature/refactor-split分支,需要先 merge /--ref feature/refactor-split测试:
9.2 看跑得怎么样¶
期望流程:
- Checkout — 1s
- Set up SSH key — 1s
- Trust jumphost + target host keys — <1s
- Deploy via SSH (jump → FRP → target) — 这是关键步,~20s ~ 数分钟(首次要
git clone+uv sync全量下载) - Summary — <1s
9.3 失败诊断速查表¶
| 报错关键字 | 原因 | 处理 |
|---|---|---|
Permission denied (publickey) |
DEPLOY_SSH_KEY 没装上小主机的 authorized_keys |
重做第 5 步 |
Host key verification failed |
DEPLOY_KNOWN_HOSTS 漏了某台主机或抓错端口 |
重做第 6 步,确保两台都抓到 |
Bad configuration option: proxyjumpport |
你拉了 e82cba9 之前的旧 deploy.yml | 拉最新代码 |
Could not resolve hostname |
DEPLOY_JUMP_USER_HOST 写错 IP |
改 secret |
Connection refused 在 127.0.0.1:22XXX |
FRP 在跳板没在跑 / 端口写错 | 登跳板 ss -tlnp 看 frps 监听的端口对不对 |
跑到 bash -s 后才挂 |
SSH 通了,是 deploy.sh 内部错。看 Action 日志最下面 deploy.sh 自己 echo 的"FATAL" 行 |
9.4 小主机这边看部署效果¶
ssh tt-rb4g "systemctl --user status timetrace-server"
# 期望 active (running)
ssh tt-rb4g "curl -s http://127.0.0.1:8765/healthz"
# 期望 {"status":"ok"} 或类似
ssh tt-rb4g "journalctl --user -u timetrace-server -n 20 --no-pager"
# 看启动日志,里面有 "auth.token_generated" 一行带 tt_live_... 这就是首启 token
记下首启 token:复制 tt_live_... 这一串 —— 这是默认设备的 bearer token,下一步配 client.toml 用。
10. 配置一台 client 接进来(本机 Windows)¶
按提示:
- Server URL:写你能从本机访问到 server 的 URL。两种典型:
- 本机和小主机不在一个网段:起
ssh -L 8765:127.0.0.1:8765 tt-rb4g(保持终端开着)→ Server URL 填http://127.0.0.1:8765 - 直接公网暴露(不推荐):需要先在小主机前面挂 Caddy + TLS
- Auth token:粘贴第 9.4 拿到的
tt_live_... - 其余按需
然后:
托盘出现 TimeTrace 图标 → 切几个窗口 → 看 server 端日志:
11. 善后¶
跑通之后:
# 清理 known_hosts 临时文件
Remove-Item $HOME\timetrace_known_hosts.txt
# 删除这份 checklist(不需要保留)
Remove-Item DEPLOY-CHECKLIST.md
保留这些不要删:
- $HOME\.ssh\timetrace_deploy + .pub(CI key,下次轮换 deploy 时还要用)
- $HOME\.ssh\id_ed25519 + .pub(你日常 SSH 用)
12. 常用后续命令速记¶
# 部署最新 main
gh workflow run deploy.yml
# 部署某个分支
gh workflow run deploy.yml --ref feature/some-branch
# 跟踪最近一次部署
gh run watch
# 看历史
gh run list --workflow=deploy.yml --limit 10
# 部署失败要看完整日志
gh run view --log
# 在小主机加一个新设备的 token
ssh tt-rb4g "cd ~/TimeTrace && uv run timetrace-server tokens add Mac-Laptop"
# 列所有 token
ssh tt-rb4g "cd ~/TimeTrace && uv run timetrace-server tokens list"
# 吊销
ssh tt-rb4g "cd ~/TimeTrace && uv run timetrace-server tokens revoke Mac-Laptop"
ssh tt-rb4g "systemctl --user restart timetrace-server" # 改完 token 后重启
# 看前端 / OpenAPI(按需 SSH 隧道,看完关掉)
ssh -L 5173:127.0.0.1:5173 tt-rb4g # 前端
ssh -L 8765:127.0.0.1:8765 tt-rb4g # API
13. 出了大问题怎么回滚¶
# 找上一个跑过的 commit
ssh tt-rb4g "cd ~/TimeTrace && git log --oneline -5"
# 临时回滚(不走 CI,直接 ssh 改)
ssh tt-rb4g "cd ~/TimeTrace && git reset --hard <好的-commit> && systemctl --user restart timetrace-server"
# 或者:通过 deploy.yml 指定 ref 回滚
gh workflow run deploy.yml --ref <好的-commit-或-tag>
完成所有步骤后即可删除本文件。任何 step 卡住截图给我看。