Files
blog/砍COS改造步骤.md
T

187 lines
7.8 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.
# 砍掉腾讯云 COS 中转层 —— 完整改造步骤
> 目标:把「build → COS → 中转机 → 又拍云 → 多吉云」这条绕路,
> 改成「build → Gitea artifact →(中转机拉取)→ 又拍云 → 多吉云」,
> 砍掉腾讯云 COS 这一层,省下 COS 存储/流量账单。
> 状态(2026-10-04):
> - ✅ `deploy.yml` 已改完(本地,未推送)
> - ⏸️ `sync.sh`(国内中转机)待改,**卡在需要一个 Gitea 只读 token**
> - ⚠️ 两者必须一起改一起推,否则国内博客会停更(见下方「为什么不能只推一半」)
---
## 一、为什么不能只推一半(重要)
deploy.yml 改完后,build 产物**不再上传 COS**,只进 Gitea artifact。
而国内中转机的 sync.sh 如果**还从 COS 拉**,就会:
- build 传 artifact → COS 没有新产物
- 中转机从 COS 拉 → 拿到的是旧产物 / 拉不到
- **国内线路(又拍云 + 多吉云)停更**
所以 `deploy.yml` 和 `sync.sh` 必须**同步改、同步推**,不能只推 deploy.yml。
---
## 二、已完成的改动(deploy.yml)
文件:`.github/workflows/deploy.yml`
### build job
- ❌ 删掉:「Setup Python」「Install/Configure coscmd」「Get last build hash from COS」「Check if upload is needed」「Upload to Tencent COS」「Upload hash to COS」
- ✅ 新增:「Upload build artifact (public/)」→ `actions/upload-artifact@v4`,name=`hugo-public`,path=`public/`,retention-days=7
- 保留:「Report deploy status (built)」上报 hash 到 `api.200181.xyz`
### deploy-edgeone job
- ❌ 删掉:「Check if deployment is needed」(last_hash 判断)、「Setup Python」「Install/Configure coscmd」「Download files from COS」
- ✅ 改成:`actions/download-artifact@v4`(name=`hugo-public`,path=`public/`)→ 直接 `npx edgeone pages deploy ./public`
- `deployed` 恒为 true(push 才触发,本就该部署)
### 清理
- 删掉所有 `download_duration` / `last_hash` / `need_upload` / `coscmd` 引用(通知、表格里的「下载 COS」等)
---
## 三、剩下的改动:sync.sh(国内中转机)
文件:`/opt/upyun-sync/sync.sh`(国内机 119.29.215.187,通过 1Panel API 或 SSH 操作)
### 要改的核心逻辑
原流程(第 1-3 步):
```bash
# 1. 取远端 hash:从 COS 读 /__build_hash(cos_sign.py 签名)
# 2. 比对本地 hash,无变化退出
# 3. 从 COS 下载 public.tar.zst
```
新流程:
```bash
# 1. 调 Gitea API 列出最新 artifact
# GET https://gitea.usj.cc/api/v1/repos/zqlit/blog/actions/artifacts?name=hugo-public
# (Header: Authorization: token <GITEA_TOKEN>)
# 2. 拿最新一个 artifact 的 id,下载 zip
# GET .../actions/artifacts/{id}/zip (302 重定向,curl -L 跟随)
# 3. 解压 zip → 读 public/.build_hash → 比对本地 hash
# 有变化才继续走「upx sync 又拍云 → purge → 刷多吉云」
```
### 需要的准备
1. **Gitea 只读 token**:
- Gitea 后台(gitea.usj.cc)→ 头像 → 设置 → 应用 → 生成令牌
- 名称随意(如 `upyun-sync`),勾选 `read:repository` 即可(只读够用)
- 生成后把 token 存到中转机 `/opt/upyun-sync/.gitea_token`(chmod 600)
2. 中转机访问 Gitea 的网络:已实测 0.56s,稳定,无问题。
### sync.sh 改动示例(第 1-3 步替换成如下)
```bash
# ---------- 取最新构建(从 Gitea artifact,替代原 COS)----------
GITEA="https://gitea.usj.cc"
REPO="zqlit/blog"
TOKEN=$(cat "$ROOT/.gitea_token" 2>/dev/null)
# 1. 列出 hugo-public artifact,拿最新 id + created_at
ART_LIST=$(curl -fsS --max-time 40 -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/actions/artifacts?name=hugo-public" 2>/dev/null)
ART_ID=$(echo "$ART_LIST" | python3 -c "import sys,json; a=json.load(sys.stdin); print(a[-1]['id'] if a else '')")
[ -n "$ART_ID" ] || { say "❌ 没有找到 hugo-public artifact"; exit 1; }
# 2. 下载 artifact zip
rm -f "$ROOT/artifact.zip"
curl -fSL --max-time 600 -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/actions/artifacts/$ART_ID/zip" \
-o "$ROOT/artifact.zip" || { say "❌ 下载 artifact 失败"; exit 1; }
# 3. 解压(artifact zip 里是 public/ 的内容)
rm -rf "$ROOT/public.new"; mkdir -p "$ROOT/public.new"
unzip -q "$ROOT/artifact.zip" -d "$ROOT/public.new" || { say "❌ 解压失败"; exit 1; }
# 4. 读 hash 比对(.build_hash 在 zip 里 public/.build_hash)
REMOTE=$(cat "$ROOT/public.new/.build_hash" 2>/dev/null | tr -d ' \r\n')
LOCAL=$(cat "$STAMP" 2>/dev/null | tr -d ' \r\n')
if [ "$REMOTE" = "$LOCAL" ] && [ -f "$PUBLIC/index.html" ]; then
say "无变化 ${REMOTE:0:12}…"; exit 0
fi
say "发现新构建: ${LOCAL:0:12}… -> ${REMOTE:0:12}…"
# 后续:原子切换 public.new → public,然后照旧 upx sync / purge / 刷多吉云
# (这部分逻辑不变,只是"下载产物"和"取 hash"的来源从 COS 换成了 artifact)
```
> ⚠️ 注意:artifact 解压出来的目录结构取决于 `upload-artifact` 的 path。
> deploy.yml 里 `path: public/`,所以 zip 里是 `public/...`,解压后要取 `public.new/public/`
> 或调整 path。实测一次 build 看 zip 结构最稳。
---
## 四、执行顺序(照着做)
### 第 1 步:生成 Gitea token
Gitea 后台 → 设置 → 应用 → 生成令牌 → 只读 → 复制 token
### 第 2 步:token 放到中转机
```bash
# 通过 1Panel API 或 SSH(国内机)
echo "你的token" > /opt/upyun-sync/.gitea_token
chmod 600 /opt/upyun-sync/.gitea_token
```
### 第 3 步:改 sync.sh
按上面「三」的示例,把「取 hash + 下载」两段从 COS 换成 Gitea artifact。
### 第 4 步:推 deploy.yml
```bash
cd /e/GitHub/blog
git add .github/workflows/deploy.yml
git commit -m "chore: 砍掉 COS 中转,build 产物改用 Gitea artifact 传递"
git push gitea main
```
### 第 5 步:验证
1. 看 Gitea Actions 这次 build 是否成功(重点看 `upload-artifact` 有没有报错——这是 Gitea 28 兼容性风险点)
2. 看中转机 sync.log 有没有「✅ 同步完成」
3. 打开 `https://api.200181.xyz/api/deploy-status` 看国内线路是否 synced
4. 访问 usj.cc 看国内是否更新
### 第 6 步:确认稳定后,清理 COS
- 删 Gitea secrets 里的 COS_SECRET_ID / COS_SECRET_KEY / COS_BUCKET / COS_REGION(先留着观察几天再删)
- 腾讯云 COS 桶 `hugo-1303964578` 可以清空或删桶(确认不再被引用后)
- 中转机的 `cos_sign.py` 可以删
---
## 五、风险点与回退
| 风险 | 说明 | 对策 |
|---|---|---|
| `upload-artifact@v4` 在 Gitea 28 兼容性 | v4 是新版 action,Gitea 兼容层可能只支持 v3 | 若报错,降级到 `actions/upload-artifact@v3` |
| artifact 保留期 7 天 | 若长时间不部署,artifact 过期 | retention-days 设大,或确认 deploy-status 兜底 |
| zip 目录结构 | `path: public/` 解压后是 `public/...` 还是直接文件 | 实测一次 build 看 zip 结构再定解压路径 |
| 回退 | 万一 artifact 链路不通 | 原 deploy.yml 备份在 `.workbuddy-backup/deploy.yml.20261004-085652`,`git revert` 即可回 COS |
---
## 六、原始备份位置
- deploy.yml 原始版本:`E:/GitHub/blog/.workbuddy-backup/deploy.yml.20261004-085652`
- 已改版本:`E:/GitHub/blog/.github/workflows/deploy.yml`(未推送)
---
## 七、关键信息速查
| 项 | 值 |
|---|---|
| 境外 Gitea | `23.254.236.47:3001`(域名 gitea.usj.cc),SSH root / `5I3fXqbV5Aco9aW9K9` |
| Gitea 数据 | docker `gitea/gitea:latest`,`/opt/gitea/data`,SQLite |
| 国内中转机 | `119.29.215.187:3721`(1Panel),又拍云同步任务 ID=15 |
| 中转机 sync.sh | `/opt/upyun-sync/sync.sh` |
| Gitea artifact API | `GET /api/v1/repos/zqlit/blog/actions/artifacts` 和 `/{id}/zip` |
| 又拍云 | bucket=`imzql`,operator=`1770186415` |
| 多吉云 | AK=`4fa23701981899fb`(sync.sh 内已硬编码) |