feat(远端): 恢复自建 Gitea 代码辅仓,OpenList 降为备选备份
用户定案:「自建 gitea 同步一下吧,openlist 也作为备选备份」。
于是把上一轮「撤销全部 git 辅仓」的决定部分回退:在线那一路回到自建 Gitea,
OpenList 上的加密 bundle 从「唯一异地备份」改为「备选异地备份」。
两层不是重复,而是失效模式不同:
· 自建 Gitea(在线、可增量、可浏览)—— 不受任何第三方平台规则约束
· 离线 bundle(离线、单一文件、完整历史)—— 平台全挂也能恢复
一、实际同步
- Gitea 位于 23.254.236.47:3001(Gitea 28.0.0),本机直连即可推,
**不需要广州中转机**(旧结论「沙箱跑不通 git smart HTTP」只针对当时的代理)
- 远端停在 ed38f938(2026-10-04),落后 57 个提交,且是本地 HEAD 的**祖先**
→ 一次 fast-forward 追平,**未强推、未丢历史**
- 同步后 origin 与 gitea 均指向 5f27ad32
二、git 配置
- 恢复 remote `gitea`(凭据编在 URL 里,只落本机 .git/config,不入库)
- `pushall` 回到双推:
!git push origin main; ec=$?; git remote | grep -qx gitea && git push gitea main; exit $ec
· 用 `;` 而非 `&&` → 主仓挂了辅仓照样推
· `exit $ec` → 退出码仍以主仓为准,辅仓成功不掩盖主仓失败
· gitea 段带存在性前置判断 → 没配辅仓时静默跳过,不白报错
三、发布链路恢复 gitea(前一轮刚去掉,现按新定案加回)
- `deploy/editor-api/bootstrap.sh`
· 新增 GITEA_URL / GITEA_USER / GITEA_PASS(凭据必须现给,不写死在脚本里)
· PUSH_REMOTES 默认 → origin,gitea;没给 GITEA_PASS 则降级为 origin
· 恢复「配 gitea 远端」一节;退役远端名单去掉 gitea(只留 gh / gitee)
—— 否则新部署会把现行辅仓当成退役远端清掉
· 结尾补一句「未启用 Gitea 辅仓」的提示
- `docker-compose.editor.yml`、`editor-api/server.mjs` → 默认 origin,gitea
- `editor-api/README.md` → 变量表同步;`editor-api/Dockerfile` → 注释补 http 情形
四、`scripts/setup-cnb-remotes.sh` 改回双远端模式
- 头部沿革、USAGE、凭据段全部重写;GITEA_PASS 走环境变量,不入库
- 退役远端循环只清 gh / gitee;pushall 写同一份定义
五、文档
- `架构总览.md`:§1.2 地址地图加「代码辅仓」行、备份行降为「备选」;
§2 旅程图改双推;§5.1 推送重写(含别名实际定义与三条取舍);
§5.2 远端改双行表 + Gitea 接入细节;§5.3 由「不走 git 远端」改为
「远端与备份的分工」(三层各自的失效模式);§5.6 定位改备选;
§6 待办 #2/#4/#7/#10 更新
- `CNB构建落地方案.md` §4.0 目标形态与定案块、§4.1 第 4 条
- `README.md` 发布流程图、推送说明、脚本表
- `scripts/backup-run.mjs` 头部注释:不再是「唯一备份」
实测:
git push gitea main → ed38f938..5f27ad32 main -> main
git pushall → origin / gitea 双向同步,退出码 0
bash -n 校验 bootstrap.sh 与 setup-cnb-remotes.sh 均通过
This commit is contained in:
1 parent
5f27ad321d
commit
448b898e40
10 files changed
+221
-109
No files matched your search
+24
-12
@@ -143,24 +143,32 @@ CNB 只替换「构建 + 搬运」这一段,顺带砍掉广州中转机上那
|
||||
|
||||
## 四、落地骨架
|
||||
|
||||
### 4.0 第一步:仓库迁移与远端配置(CNB 主仓,**无 git 辅仓**)
|
||||
### 4.0 第一步:仓库迁移与远端配置(CNB 主仓 + 自建 Gitea 辅仓)
|
||||
|
||||
**目标形态**
|
||||
|
||||
```
|
||||
origin → https://cnb.cool/<组织>/<仓库> ← **唯一远端**(fetch + push),CNB 构建由它触发
|
||||
origin → https://cnb.cool/<组织>/<仓库> ← **主仓**(fetch + push),CNB 构建由它触发
|
||||
gitea → http://23.254.236.47:3001/zqlit/blog ← **代码同步辅仓**(只推不拉,不参与构建)
|
||||
```
|
||||
|
||||
> ⚠️ **2026-10-06 定案:git 远端只留 CNB 一个。**
|
||||
> **2026-10-06 定案:CNB 主仓 + 自建 Gitea 辅仓,另加一份离线 bundle 备份。**
|
||||
>
|
||||
> - **GitHub**(原 `gh` remote):账号被平台标记、仓库被按 AUP 清空 → 移除
|
||||
> | remote | 地址 | 角色 |
|
||||
> |---|---|---|
|
||||
> | `origin` | `https://cnb.cool/zqlit/blog.git` | **主仓** —— 唯一的 fetch 源,推送即触发 CNB 构建 |
|
||||
> | `gitea` | `http://23.254.236.47:3001/zqlit/blog.git` | **代码同步辅仓** —— 只作代码留档与找回,不参与构建 |
|
||||
>
|
||||
> - **GitHub**(原 `gh` remote):账号被平台标记、仓库被按 AUP 清空 → 已彻底移除,**别再往回加**
|
||||
> - **Gitee**:单仓 500MB / 单文件 50MB 两道硬门槛过不去(本仓 `bin/linux/hugo` 83.1MB)→ 未采用
|
||||
> - **自建 Gitea**:发布链路早已不依赖 → 移除
|
||||
>
|
||||
> **异地备份改为「整仓加密 bundle → 中兴 F50 上的 OpenList」**,与 git 远端无关。
|
||||
> **备选异地备份 = 「整仓加密 bundle → 中兴 F50 上的 OpenList」**,与 git 远端无关。
|
||||
> 它和 Gitea 辅仓不是一回事、也不能互相替代:
|
||||
> 辅仓保「随时能 `clone` 回来」,bundle 保「平台全挂/账号被封时仍有一份完整历史」。
|
||||
> 选型依据见 `架构总览.md` §5.3,实现与恢复流程见 §5.6。
|
||||
|
||||
`git pushall` 仍保留为别名,但现在等价于 `git push origin main`(只有这一段)。
|
||||
`git pushall` 别名 = `git push origin main; git push gitea main` ——
|
||||
用 `;` 而非 `&&`,主仓失败时辅仓照样推;退出码以主仓为准(辅仓失败不阻断发布)。
|
||||
|
||||
**★ 为什么不用「一个 remote 挂多个 pushurl」**(这条教训仍然有效)
|
||||
|
||||
@@ -174,8 +182,8 @@ origin → https://cnb.cool/<组织>/<仓库> ← **唯一远端**(fetch + p
|
||||
也就是说:**主仓推失败时,辅仓那份备份也不会更新** —— 备份的意义就没了。
|
||||
|
||||
所以当时才拆成两个独立 remote,别名里用 `;` 而不是 `&&`,保证互不阻塞。
|
||||
现在只有一个远端,这层复杂性自然取消了 —— 但**如果将来要再加辅仓**,
|
||||
必须回到「独立 remote + 别名」的写法,**不要**用 pushurl。
|
||||
**现行方案正是回到这个写法**:`origin`(CNB) 与 `gitea`(自建) 各自独立成 remote,
|
||||
别名 `;` 串联、退出码取主仓 —— 这就是现在的 `git pushall`。
|
||||
|
||||
**脚本**(已就位,可直接用)
|
||||
|
||||
@@ -184,8 +192,12 @@ bash scripts/setup-cnb-remotes.sh https://cnb.cool/<组织>/<仓库>
|
||||
```
|
||||
|
||||
它会先把你现有的远端配置(含明文凭据)备份到 `.workbuddy-backup/git-remotes.<时间戳>.txt`,
|
||||
再重建 `origin`、把 `pushall` 设为 `git push origin main`,
|
||||
并顺手清掉 `gh` / `gitee` / `gitea` 这些已退役的远端 —— 全程可回退。
|
||||
再重建 `origin`、把 `pushall` 设为 `git push origin main; git push gitea main`,
|
||||
并顺手清掉 `gh` / `gitee` 这两个已退役的远端 —— 全程可回退。
|
||||
|
||||
> Gitea 的凭据**不写死在脚本里**,要现给:
|
||||
> `GITEA_PASS='<Gitea 密码或访问令牌>' bash scripts/setup-cnb-remotes.sh <CNB_URL>`
|
||||
> 没给就只配 `origin`(`pushall` 里的 gitea 段会被自动跳过)。
|
||||
|
||||
**★ CNB 不支持 SSH(官方明确)**
|
||||
|
||||
@@ -281,7 +293,7 @@ CMD ["hugo", "version"]
|
||||
> 1. **hugo 二进制改为随仓库携带**(`bin/linux/hugo`,84MB,进 git 后仅 24MB)。实测国内节点直连 GitHub 拉这个 release 要 **127 秒**(本机只要 7 秒),而每次构建都要付这个成本 → 自带更稳。
|
||||
> 2. 又拍云 `upx` 的下载源是 `collection.b0.upaiyun.com`(国内 CDN),直连稳定,保留在线安装。
|
||||
> 3. **架构不能混用**:`E:\Hugo\hugo.exe` 是 `windows/amd64`(本地预览用),CNB 构建节点是 `linux/amd64`(`bin/linux/hugo`),两者互不替代。
|
||||
> 4. ~~若以后主仓改选 Gitee(单仓 500MB),这 84MB 会挤占配额~~ —— **2026-10-06 定案:不走任何 git 辅仓**(异地备份改用 OpenList 上的加密 bundle,见 `架构总览.md` §5.6),这条约束自然消失,`bin/linux/hugo` 继续随仓库携带即可。
|
||||
> 4. ~~若以后主仓改选 Gitee(单仓 500MB),这 84MB 会挤占配额~~ —— **2026-10-06 定案:不走 Gitee**(它卡单文件 50MB / 单仓 500MB 两道门槛)。现行辅仓是**自建 Gitea**(自己的机器,没有配额这回事,见 §4.0),离线备份走 OpenList 上的加密 bundle(`架构总览.md` §5.6)。这条约束自然消失,`bin/linux/hugo` 继续随仓库携带即可。
|
||||
> 5. **apt 源改阿里云镜像**:CNB 官方只明示加速「Docker / NPM / Maven 镜像」,Debian 默认 apt 源不在承诺范围内,而构建节点在国内 → 换阿里云兜底,避免构建卡在 `apt-get update`(首轮构建时留意这一步耗时)。
|
||||
|
||||
### 4.2 `.cnb.yml`(流水线)
|
||||
|
||||
@@ -83,20 +83,22 @@ hugo server -D # 含草稿
|
||||
|
||||
```
|
||||
写作(write-server / write/)
|
||||
│ git pushall
|
||||
│ git pushall (= git push origin main ; git push gitea main)
|
||||
▼
|
||||
CNB 主仓 zqlit/blog ──触发──► CNB 流水线(国内节点,约 3.5 分钟)
|
||||
origin → CNB 主仓 zqlit/blog ──触发──► CNB 流水线(国内节点,约 3.5 分钟)
|
||||
├─ Hugo 构建
|
||||
├─ 同步到又拍云(境内源站)
|
||||
├─ 刷新又拍云 CDN
|
||||
├─ 刷新多吉云 CDN
|
||||
├─ 部署 EdgeOne Pages(境外线路)
|
||||
└─ 邮件通知
|
||||
|
||||
gitea → 自建 Gitea(23.254.236.47:3001) 代码同步辅仓:只推不拉、不参与构建
|
||||
```
|
||||
|
||||
- **推送**:`git push origin main`(`origin` = CNB 主仓,**唯一远端**,触发构建)
|
||||
—— `git pushall` 是同义的别名
|
||||
- **异地备份**:**不走 git 远端**。本机计划任务每天 03:30 把整仓 bundle
|
||||
- **推送**:`git pushall` —— 依次推 `origin`(CNB 主仓,触发构建)与 `gitea`(自建 Gitea 代码同步辅仓)
|
||||
;只推主仓用 `git push origin main`
|
||||
- **备选异地备份**:**不走 git 远端**。本机计划任务每天 03:30 把整仓 bundle
|
||||
加密后传到中兴 F50 上的 OpenList(见 `架构总览.md` §5.6)
|
||||
- **触发**:推送到 `main`;另有每日 `0 9 * * *`(北京时间)定时构建
|
||||
- **密钥**:全部来自 CNB 密钥仓库 `zqlit/blog-secrets`,经 `.cnb.yml` 的 `imports` 注入,
|
||||
@@ -133,7 +135,7 @@ Hugo + Ying 主题。`hugo.toml` 控站点信息、永久链接、Artalk 地址
|
||||
| `add_draft_to_hidden.py` | 构建前把 `status: hidden` 转成草稿 | **CI** |
|
||||
| `refresh_cdn.js` | 刷新多吉云 CDN(零依赖) | **CI** |
|
||||
| `send_mail.js` | 构建结果邮件通知(零依赖 SMTP) | **CI** |
|
||||
| `setup-cnb-remotes.sh` | 切换/重建 git 远端(只有 CNB 一个;顺手清掉退役远端) | 本机 |
|
||||
| `setup-cnb-remotes.sh` | 切换/重建 git 远端(CNB 主仓 + 自建 Gitea 辅仓;顺手清掉退役远端) | 本机 |
|
||||
| `backup-run.mjs` | 备份的推荐入口:跑 `backup-bundle` + **失败时发告警邮件** | 本机/计划任务 |
|
||||
| `backup-bundle.mjs` | 整仓 bundle → AES-256-GCM 加密 → WebDAV 传中兴 F50(见 架构总览 §5.6) | 本机/计划任务 |
|
||||
| `backup-task.cmd` | 上面的计划任务入口(每天 03:30;**内容必须全 ASCII**) | 计划任务 |
|
||||
|
||||
@@ -20,23 +20,30 @@
|
||||
#
|
||||
# 可选环境变量(都有默认值):
|
||||
# CNB_URL 默认 https://cnb.cool/zqlit/blog.git
|
||||
# GITEA_URL 默认 http://23.254.236.47:3001/zqlit/blog.git(自建 Gitea 辅仓)
|
||||
# GITEA_USER 默认 zqlit
|
||||
# GITEA_PASS 可选:Gitea 的密码 / 访问令牌。**给了才会把 gitea 加进推送远端**,
|
||||
# 否则 PUSH_REMOTES 自动降级成只推 origin(CNB 主仓)
|
||||
# BLOG_DIR /srv/blog 仓库工作区
|
||||
# APP_DIR /srv/editor-api compose + .env
|
||||
# TRASH_DIR /srv/editor-trash 删除文章的回收目录(故意放在仓库外)
|
||||
# BIND_PORT 8017 只绑 127.0.0.1
|
||||
# PUSH_REMOTES origin 发布时依次推送的远端(默认只有 CNB 主仓)
|
||||
# PUSH_REMOTES origin,gitea 发布时依次推送的远端
|
||||
# GIT_AUTHOR_NAME / GIT_AUTHOR_EMAIL / BLOG_BASE
|
||||
#
|
||||
# ★ 2026-10-06(定案):**异地备份不再走 git 远端**。
|
||||
# - GitHub(GH_URL / GH_SSH_KEY / remote `gh`):账号被平台标记,仓库被按
|
||||
# AUP 清空 → 已从本脚本与整个链路移除
|
||||
# ★ 远端与备份定案(2026-10-06):
|
||||
# - CNB(remote `origin`):**主仓**,唯一的 fetch 源,推送即触发 CNB 构建
|
||||
# - 自建 Gitea(remote `gitea`,23.254.236.47:3001):**代码同步辅仓**,
|
||||
# 只作代码留档与找回,不参与构建(.github/ 已删,Actions 早已停用)
|
||||
# - GitHub(GH_URL / GH_SSH_KEY / remote `gh`):账号被平台标记、仓库被按
|
||||
# AUP 清空 → 已彻底移除,别再往回加
|
||||
# - Gitee:单仓 500MB / 单文件 50MB 两道硬门槛,本仓 `bin/linux/hugo`
|
||||
# 是 83.1MB → 未采用
|
||||
# - 自建 Gitea:已从远端列表移除
|
||||
# 现行备份 = CNB 主仓 + OpenList 上的加密 bundle(scripts/backup-bundle.mjs,
|
||||
# 由 Windows 计划任务 Blog-BundleBackup 每天 03:30 跑)。
|
||||
# 所以 PUSH_REMOTES 默认只有 origin;将来若要再加辅仓,设
|
||||
# PUSH_REMOTES=origin,<名字> 并在下面「远端配置」一节补上对应 remote。
|
||||
# - OpenList(中兴 F50 上的加密 bundle,scripts/backup-bundle.mjs,
|
||||
# Windows 计划任务 Blog-BundleBackup 每天 03:30):**备选异地备份**,
|
||||
# 与 git 远端无关,独立于上面三者
|
||||
# 推送语义见 editor-api/src/git.mjs:逐个远端推、**主仓成功即算发布成功**,
|
||||
# 辅仓失败只警告不阻断 —— 所以 gitea 挂了不会让文章发不出去。
|
||||
# =====================================================================
|
||||
set -euo pipefail
|
||||
|
||||
@@ -48,6 +55,8 @@ die() { printf '\033[1;31m!! %s\033[0m\n' "$*" >&2; exit 1; }
|
||||
: "${ADMIN_PASS:?必须设置 ADMIN_PASS(后台登录密码,账号固定 admin)}"
|
||||
|
||||
CNB_URL="${CNB_URL:-https://cnb.cool/zqlit/blog.git}"
|
||||
GITEA_URL="${GITEA_URL:-http://23.254.236.47:3001/zqlit/blog.git}"
|
||||
GITEA_USER="${GITEA_USER:-zqlit}"
|
||||
BLOG_DIR="${BLOG_DIR:-/srv/blog}"
|
||||
APP_DIR="${APP_DIR:-/srv/editor-api}"
|
||||
TRASH_DIR="${TRASH_DIR:-/srv/editor-trash}"
|
||||
@@ -55,7 +64,15 @@ BIND_PORT="${BIND_PORT:-8017}"
|
||||
GIT_AUTHOR_NAME="${GIT_AUTHOR_NAME:-blog-editor}"
|
||||
GIT_AUTHOR_EMAIL="${GIT_AUTHOR_EMAIL:-editor@usj.cc}"
|
||||
BLOG_BASE="${BLOG_BASE:-}"
|
||||
PUSH_REMOTES="${PUSH_REMOTES:-origin}"
|
||||
PUSH_REMOTES="${PUSH_REMOTES:-origin,gitea}"
|
||||
|
||||
# 没给 Gitea 凭据就只推主仓 —— 宁可少推一个辅仓,
|
||||
# 也不要让每次发布都对着一个认证不过的远端空转一遍。
|
||||
# (这里是有意直接覆盖成 origin:默认列表就是 origin,gitea,
|
||||
# 没有 gitea 凭据等于只剩 origin;要推别的远端请连同 gitea 一起显式写全。)
|
||||
if [ -z "${GITEA_PASS:-}" ]; then
|
||||
PUSH_REMOTES="origin"
|
||||
fi
|
||||
|
||||
command -v docker >/dev/null || die "没有 docker"
|
||||
docker compose version >/dev/null 2>&1 || die "没有 docker compose"
|
||||
@@ -78,11 +95,22 @@ else
|
||||
git clone --branch main "$AUTH_URL" "$BLOG_DIR"
|
||||
fi
|
||||
|
||||
# 退役的远端:以前这一节会配 GitHub / Gitee 辅仓。2026-10-06 起异地备份改走
|
||||
# OpenList 上的加密 bundle(与 git 远端无关),所以 git 远端只留 CNB 主仓。
|
||||
# 若这份工作区是从旧部署续用的,顺手把残留的退役远端清掉,
|
||||
# 免得 `git pushall` 或后端按 PUSH_REMOTES 推送时撞上已失效的远端。
|
||||
for _retired_remote in gh gitee gitea; do
|
||||
# 自建 Gitea 辅仓:只有给了 GITEA_PASS 才配(没给就只推 origin,见上面 PUSH_REMOTES 降级)。
|
||||
# 凭据直接编进远端 URL —— 容器里没有交互终端、也没有 credential helper,
|
||||
# 这是唯一能让 `git push gitea` 免交互跑通的写法。
|
||||
# 明文只落在 $APP_DIR/.env 与容器内的 .git/config,不会进仓库。
|
||||
if [ -n "${GITEA_PASS:-}" ]; then
|
||||
_gitea_auth="$(printf '%s' "$GITEA_URL" | sed -E "s#^(https?://)#\1${GITEA_USER}:${GITEA_PASS}@#")"
|
||||
git -C "$BLOG_DIR" remote remove gitea 2>/dev/null || true
|
||||
git -C "$BLOG_DIR" remote add gitea "$_gitea_auth"
|
||||
unset _gitea_auth
|
||||
fi
|
||||
|
||||
# 退役的远端:以前这一节会配 GitHub / Gitee 辅仓,两者都已退役
|
||||
# (GitHub 被平台按 AUP 清空、Gitee 卡体积门槛)。若这份工作区是从旧部署续用的,
|
||||
# 顺手把残留的退役远端清掉,免得后端按 PUSH_REMOTES 推送时撞上已失效的远端。
|
||||
# ★ gitea **不在**退役名单里 —— 它是现行辅仓,别跟着一起清掉。
|
||||
for _retired_remote in gh gitee; do
|
||||
git -C "$BLOG_DIR" remote remove "$_retired_remote" 2>/dev/null || true
|
||||
done
|
||||
|
||||
@@ -142,7 +170,7 @@ services:
|
||||
TRASH_DIR: $TRASH_IN_CT
|
||||
BIND_HOST: 0.0.0.0
|
||||
GIT_BRANCH: main
|
||||
PUSH_REMOTES: \${PUSH_REMOTES:-origin}
|
||||
PUSH_REMOTES: \${PUSH_REMOTES:-origin,gitea}
|
||||
GIT_AUTHOR_NAME: \${GIT_AUTHOR_NAME:-blog-editor}
|
||||
GIT_AUTHOR_EMAIL: \${GIT_AUTHOR_EMAIL:-editor@usj.cc}
|
||||
BLOG_BASE: \${BLOG_BASE:-}
|
||||
@@ -176,6 +204,10 @@ for i in $(seq 1 30); do
|
||||
echo " · 宿主机 nginx(1Panel 站点)加反代:location ^~ / { proxy_pass http://127.0.0.1:$BIND_PORT; ... }"
|
||||
echo " · Cloudflare Worker 变量:EDITOR_API_BASE=https://<指向本机的域名> EDITOR_TOKEN=$EDITOR_TOKEN"
|
||||
echo " · 推送远端:PUSH_REMOTES=$PUSH_REMOTES"
|
||||
if [ -z "${GITEA_PASS:-}" ]; then
|
||||
echo " · 未提供 GITEA_PASS → 本次只推 origin,Gitea 辅仓未启用"
|
||||
echo " (要启用:GITEA_PASS='<Gitea 密码或令牌>' 重跑本脚本)"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
|
||||
@@ -27,7 +27,7 @@ services:
|
||||
# 容器内监听 0.0.0.0 没关系 —— 外面那层端口映射已经把它锁在宿主机回环上了
|
||||
BIND_HOST: 0.0.0.0
|
||||
GIT_BRANCH: main
|
||||
PUSH_REMOTES: origin
|
||||
PUSH_REMOTES: origin,gitea
|
||||
GIT_AUTHOR_NAME: blog-editor
|
||||
GIT_AUTHOR_EMAIL: editor@usj.cc
|
||||
MAX_UPLOAD_MB: "20"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
FROM node:22-alpine
|
||||
|
||||
# git:发布用。ca-certificates:https 远端(CNB)要它
|
||||
# git:发布用。ca-certificates:https 远端(CNB)要它(自建 Gitea 辅仓走 http,不需要)
|
||||
#
|
||||
# ★ apk 源默认换成阿里云镜像:国内机器直连 dl-cdn.alpinelinux.org 实测慢到不可用
|
||||
# (单个包 30~40 秒,12 个包要 7 分钟),换源后几秒装完。
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
| `BIND_HOST` | `0.0.0.0` | 容器内监听地址;靠 compose 的端口映射限成 127.0.0.1 |
|
||||
| `TRASH_DIR` | `/app/trash` | 删除文章的回收目录(**在仓库外**,不进 git) |
|
||||
| `GIT_BRANCH` | `main` | 工作分支 |
|
||||
| `PUSH_REMOTES` | `origin` | 依次推送的远端;**主仓失败才算发布失败**,其余远端失败只警告。异地备份不在此列 —— 走 OpenList 上的加密 bundle(`scripts/backup-bundle.mjs`) |
|
||||
| `PUSH_REMOTES` | `origin,gitea` | 依次推送的远端;**主仓失败才算发布失败**,其余远端失败只警告。`origin`=CNB 主仓(推送即触发构建),`gitea`=自建 Gitea 代码同步辅仓(需给 `GITEA_PASS` 才配得上,见 `bootstrap.sh`)。异地备份不在此列 —— 走 OpenList 上的加密 bundle(`scripts/backup-bundle.mjs`) |
|
||||
| `GIT_AUTHOR_NAME` / `GIT_AUTHOR_EMAIL` | `blog-editor` | 自动提交的作者 |
|
||||
| `DEFAULT_AUTHOR` | 空 | 新建文章时 front matter `author` 的默认值 |
|
||||
| `MAX_UPLOAD_MB` | `20` | 单张图片上限 |
|
||||
|
||||
@@ -29,7 +29,7 @@ const cfg = {
|
||||
contentDir: path.resolve(env.BLOG_ROOT || '/blog', 'content', 'posts'),
|
||||
trashDir: path.resolve(env.TRASH_DIR || '/app/trash'),
|
||||
branch: env.GIT_BRANCH || 'main',
|
||||
pushRemotes: (env.PUSH_REMOTES || 'origin').split(',').map((s) => s.trim()).filter(Boolean),
|
||||
pushRemotes: (env.PUSH_REMOTES || 'origin,gitea').split(',').map((s) => s.trim()).filter(Boolean),
|
||||
authorName: env.GIT_AUTHOR_NAME || 'blog-editor',
|
||||
authorEmail: env.GIT_AUTHOR_EMAIL || 'editor@localhost',
|
||||
defaultAuthor: env.DEFAULT_AUTHOR || '',
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 备份包装器 —— 给「唯一辅仓」加一道失败告警
|
||||
* 备份包装器 —— 给「离线那一层」加一道失败告警
|
||||
* =====================================================================
|
||||
* 为什么需要它:
|
||||
* OpenList(中兴 F50)现在是**唯一的异地备份**。而这类「每天自动跑」的任务
|
||||
* 最典型的失败模式恰恰是**静默**的 —— F50 被带出门、换了网段、OpenList
|
||||
* 容器重启后没起来、WebDAV 口令改过……这些都不会有人主动发现,
|
||||
* 直到某天真的要用备份时才发现最近三个月一份都没成功。
|
||||
* OpenList(中兴 F50)上这份加密 bundle 是**唯一不依赖任何 git 服务**的备份 ——
|
||||
* 在线那一路虽有自建 Gitea 代码辅仓(contributor 机器上,推一次就在),
|
||||
* 但平台全挂、服务器被回收这类事故只有它能挡。
|
||||
* 而这类「每天自动跑」的任务最典型的失败模式恰恰是**静默**的 ——
|
||||
* F50 被带出门、换了网段、OpenList 容器重启后没起来、WebDAV 口令改过……
|
||||
* 这些都不会有人主动发现,直到某天真的要用备份时才发现最近三个月一份都没成功。
|
||||
*
|
||||
* CNB 主仓还在,所以这不会立刻致命;但「唯一辅仓悄悄失效」这件事本身
|
||||
* 在线辅仓和主仓都还在,所以这不会立刻致命;但「离线备份悄悄失效」这件事本身
|
||||
* 必须被告知。于是加这一层:跑备份 → 失败就发邮件。
|
||||
*
|
||||
* 它做什么:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# =====================================================================
|
||||
# 配置 git 远端 —— 本项目只有 CNB 一个远端
|
||||
# 配置 git 远端 —— CNB 主仓 + 自建 Gitea 代码同步辅仓
|
||||
# =====================================================================
|
||||
# 历史沿革:
|
||||
# 2026-10-04 从「GitHub + 自建 Gitea 双推」切到「CNB 主 + GitHub 备份」
|
||||
@@ -8,16 +8,18 @@
|
||||
# 2026-10-06 一度改为「CNB 主 + Gitee 辅」,但 Gitee 免费版卡在
|
||||
# 单仓 500MB / 单文件 50MB 两道硬门槛(本仓 bin/linux/hugo 83.1MB),
|
||||
# 云效 Codeup 虽宽松却同属国内大区 —— 多一层同类平台不如换一类介质
|
||||
# ★ 定案:**异地备份不走 git 远端**。
|
||||
# 备份 = 整仓 bundle + AES-256-GCM 加密 → 中兴 F50 上的 OpenList
|
||||
# (scripts/backup-bundle.mjs,计划任务 Blog-BundleBackup 每天 03:30)
|
||||
# 故 git 远端只剩 CNB origin,pushall 也只推 origin。
|
||||
# 2026-10-06 一度定案「远端只剩 CNB,异地备份改走 OpenList 加密 bundle」
|
||||
# ★ 2026-10-06 现行:**CNB 主仓 + 自建 Gitea 辅仓**,OpenList 上的加密
|
||||
# bundle 退为**备选备份**。两层性质不同、互为补充:
|
||||
# · git 辅仓 保「随时能 clone 回来、能按提交追溯」
|
||||
# · 离线 bundle 保「平台全挂/账号被封时仍有一份完整历史」
|
||||
# (整仓 + AES-256-GCM → 中兴 F50 上的 OpenList,
|
||||
# scripts/backup-bundle.mjs,计划任务 Blog-BundleBackup 每天 03:30)
|
||||
#
|
||||
# 为什么当初不用「一个 remote 挂多个 pushurl」:
|
||||
# 为什么不用「一个 remote 挂多个 pushurl」:
|
||||
# 实测(2026-10-04)git 对多个 pushurl 是「顺序推、遇错即停」——
|
||||
# 第一个失败,后面的远端一个都不会推,那就失去备份意义了。
|
||||
# 所以真要加辅仓,得用「独立 remote + 别名」而不是 pushurl。
|
||||
# (现行方案连这个都不需要了 —— 备份已经换介质。)
|
||||
# 所以用「独立 remote + 别名」而不是 pushurl。
|
||||
#
|
||||
# 用法:
|
||||
# bash scripts/setup-cnb-remotes.sh <CNB_URL>
|
||||
@@ -28,6 +30,11 @@
|
||||
# 附带令牌(直接写进 Windows 凭据管理器,以后不再输):
|
||||
# CNB_TOKEN='你的令牌' bash scripts/setup-cnb-remotes.sh https://cnb.cool/zqlit/blog
|
||||
#
|
||||
# Gitea 辅仓默认按内置地址配,但**凭据必须现给**(不写进本文件):
|
||||
# GITEA_PASS='Gitea 密码或访问令牌' \
|
||||
# bash scripts/setup-cnb-remotes.sh https://cnb.cool/zqlit/blog
|
||||
# 要换地址/换用户名时再带上 GITEA_URL / GITEA_USER。
|
||||
#
|
||||
# 回退:脚本会把改动前的配置备份到 .workbuddy-backup/git-remotes.<时间戳>.txt
|
||||
# =====================================================================
|
||||
|
||||
@@ -43,7 +50,9 @@ if [ -z "$CNB_URL" ]; then
|
||||
· CNB 仓库地址直接用仓库页地址,带不带 .git 都行
|
||||
· CNB **不支持 SSH**,只能 HTTPS + 访问令牌(用户名固定填 cnb,密码填令牌)
|
||||
令牌:「个人设置 → 访问令牌」,勾「代码仓库 → 读写」
|
||||
· 本项目**没有** git 辅仓。异地备份走中兴 F50 上的 OpenList:
|
||||
· 自建 Gitea 辅仓走 http://23.254.236.47:3001(账号 zqlit),**另需 GITEA_PASS**:
|
||||
GITEA_PASS='Gitea 密码或令牌' bash scripts/setup-cnb-remotes.sh <CNB_URL>
|
||||
· 备选异地备份走中兴 F50 上的 OpenList(与 git 远端无关):
|
||||
scripts/backup-bundle.mjs 打包 + AES-256-GCM 加密 + 上传(含校验与恢复)
|
||||
.workbuddy-backup/openlist-backup.env 连接与口令配置(被 git 忽略)
|
||||
Windows 计划任务 Blog-BundleBackup 每天 03:30 自动跑
|
||||
@@ -51,6 +60,11 @@ USAGE
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 自建 Gitea 辅仓(默认地址;GITEA_PASS 必须外部给,不写死在本文件里)
|
||||
GITEA_URL="${GITEA_URL:-http://23.254.236.47:3001/zqlit/blog.git}"
|
||||
GITEA_USER="${GITEA_USER:-zqlit}"
|
||||
GITEA_PASS="${GITEA_PASS:-}"
|
||||
|
||||
case "$CNB_URL" in
|
||||
https://cnb.cool/*) ;;
|
||||
*) echo "地址看起来不是 CNB 的:$CNB_URL"; exit 1 ;;
|
||||
@@ -74,26 +88,45 @@ mkdir -p .workbuddy-backup
|
||||
} > "$BACKUP"
|
||||
echo "原配置已备份:$BACKUP"
|
||||
|
||||
# 1) origin = CNB(唯一远端,fetch + push,触发构建)
|
||||
# 1) origin = CNB(主仓,fetch + push,推送即触发构建)
|
||||
git remote remove origin 2>/dev/null || true
|
||||
git remote add origin "$CNB_URL"
|
||||
|
||||
# 2) 清理退役远端
|
||||
# gh(GitHub) / gitee / gitea 都已不在备份体系内 —— 留着只会在
|
||||
# `git pushall` 或后端按 PUSH_REMOTES 推送时撞上失效远端,白白报错。
|
||||
for retired in gh gitee gitea; do
|
||||
# 2) gitea = 自建 Gitea(代码同步辅仓)
|
||||
# 凭据直接编进远端 URL —— 这是「无凭据助手也能免交互推」的唯一简单写法。
|
||||
# 明文只落在本机 .git/config(不提交、不推送、不进仓库)。
|
||||
# (口令里若含 # 或 & 会被 sed 吃掉,那种情况请改用访问令牌。)
|
||||
if [ -n "$GITEA_PASS" ]; then
|
||||
GITEA_AUTH="$(printf '%s' "$GITEA_URL" | sed -E "s#^(https?://)#\1${GITEA_USER}:${GITEA_PASS}@#")"
|
||||
git remote remove gitea 2>/dev/null || true
|
||||
git remote add gitea "$GITEA_AUTH"
|
||||
echo "gitea 远端已配置:$GITEA_URL(凭据编入 URL,仅存于本机 .git/config)"
|
||||
elif git remote | grep -qx gitea; then
|
||||
echo "gitea 远端已存在,本次未给 GITEA_PASS → 保持原样"
|
||||
else
|
||||
echo "未提供 GITEA_PASS → 跳过 gitea 远端配置(只剩 origin 一个远端)"
|
||||
echo " 需要时:GITEA_PASS='xxx' bash scripts/setup-cnb-remotes.sh $CNB_URL"
|
||||
fi
|
||||
|
||||
# 3) 清理退役远端
|
||||
# gh(GitHub) / gitee 都已不在远端列表里 —— 留着只会在 `git pushall`
|
||||
# 或后端按 PUSH_REMOTES 推送时撞上失效远端,白白报错。
|
||||
# ★ gitea **不在**名单里:它是现行辅仓,别跟着一起清掉。
|
||||
for retired in gh gitee; do
|
||||
if git remote | grep -qx "$retired"; then
|
||||
git remote remove "$retired"
|
||||
echo "已移除退役远端:$retired"
|
||||
fi
|
||||
done
|
||||
|
||||
# 3) pushall
|
||||
# 现在只有一个远端,但仍保留这个别名 —— 文档与肌肉记忆都在用它,
|
||||
# 将来若真要再加辅仓,改这一行即可。
|
||||
git config alias.pushall '!git push origin main'
|
||||
# 4) pushall —— CNB 与 Gitea 都推
|
||||
# ★ 用 `;` 而不是 `&&`:主仓失败时辅仓照样得推上去,
|
||||
# 备份的意义正在于「主仓出问题时它那儿还有」。
|
||||
# 但退出码仍以主仓为准(exit $ec),免得辅仓推成功把主仓的失败盖掉。
|
||||
# 先探一下 gitea 在不在,免得没配辅仓时每次 pushall 都白报一行错。
|
||||
git config alias.pushall '!git push origin main; ec=$?; git remote | grep -qx gitea && git push gitea main; exit $ec'
|
||||
|
||||
# 4) 凭据
|
||||
# 5) 凭据
|
||||
#
|
||||
# 本机全局 helper 是 GCM(git-credential-manager.exe)。实测它对 cnb.cool 这类
|
||||
# 第三方 HTTPS 远端会**每次都弹窗**,而且 GCM_INTERACTIVE=never +
|
||||
@@ -128,8 +161,9 @@ echo "=== 当前远端 ==="
|
||||
git remote -v | sed -E 's#://[^@/]*@#://***@#g'
|
||||
echo
|
||||
echo "=== 下一步 ==="
|
||||
echo " git push origin main # 推 CNB(触发构建)"
|
||||
echo " git pushall # 同上(别名,现在只含这一段)"
|
||||
echo " git pushall # 依次推 origin(CNB) 与 gitea(推荐)"
|
||||
echo " git push origin main # 只推 CNB(触发构建)"
|
||||
echo " git push gitea main # 只推 Gitea 辅仓"
|
||||
echo
|
||||
echo " 异地备份不在这里 —— 想立刻跑一次整仓加密备份:"
|
||||
echo " 备选异地备份不在这里 —— 想立刻跑一次整仓加密备份:"
|
||||
echo " node scripts/backup-bundle.mjs --verify --keep 7"
|
||||
@@ -28,8 +28,9 @@
|
||||
|
||||
| 角色 | 地址 / 位置 | 说明 |
|
||||
|---|---|---|
|
||||
| **代码主仓** | `cnb.cool/zqlit/blog` | CNB,**私有**;构建由它触发;**唯一 git 远端** |
|
||||
| **异地备份** | 中兴 F50 上的 OpenList(`/本地/备份/blog-bundle/`) | 整仓加密 bundle,计划任务每天 03:30 自动跑,**不依赖任何 git 平台**(见 §5.6) |
|
||||
| **代码主仓** | `cnb.cool/zqlit/blog` | CNB,**私有**;构建由它触发;唯一 fetch 源 |
|
||||
| **代码辅仓** | 自建 Gitea `23.254.236.47:3001/zqlit/blog` | 只推不拉,纯代码留档;**不受第三方平台规则约束**(见 §5.2) |
|
||||
| **异地备份** | 中兴 F50 上的 OpenList(`/本地/备份/blog-bundle/`) | **备选**异地备份:整仓加密 bundle,计划任务每天 03:30 自动跑,**不依赖任何 git 服务**(见 §5.6) |
|
||||
| **构建 + 发布** | CNB 流水线(腾讯云国内节点,4 核 8G) | 托管,0 元(免费额度内) |
|
||||
| **密钥仓库** | `cnb.cool/zqlit/blog-secrets` | CNB「密钥仓库」类型,10 项变量经 `imports` 注入 |
|
||||
| **境内源站** | 又拍云对象存储 | 由 CNB 国内节点**直传**(原为 `if: false` 的死代码) |
|
||||
@@ -48,9 +49,10 @@
|
||||
```
|
||||
① 写作 write-server(网页) 或 write/(本地 Windows)
|
||||
│
|
||||
② git push git push origin main (别名 git pushall,同一件事)
|
||||
│ └─ origin → cnb.cool/zqlit/blog (**唯一远端**,触发构建)
|
||||
│ ★ 异地备份不走 git 远端 —— 由本机计划任务每天 03:30
|
||||
② git push git pushall = git push origin main ; git push gitea main
|
||||
│ ├─ origin → cnb.cool/zqlit/blog (**主仓**,触发构建)
|
||||
│ └─ gitea → 23.254.236.47:3001/zqlit/blog(自建 Gitea,代码同步辅仓)
|
||||
│ ★ 备选异地备份不走 git 远端 —— 由本机计划任务每天 03:30
|
||||
│ 跑整仓加密 bundle 传到 F50 上的 OpenList(§5.6)
|
||||
▼
|
||||
③ CNB 流水线(国内节点,约 3.5 分钟,7 个 stage 顺序执行)
|
||||
@@ -113,40 +115,53 @@
|
||||
### 5.1 推送
|
||||
|
||||
```bash
|
||||
git push origin main # 推 CNB(触发构建)
|
||||
git pushall # 完全等价 —— 它只是个别名,本仓只有这一个远端
|
||||
git pushall # CNB 与 Gitea 都推 —— 发布用这个
|
||||
git push origin main # 只推 CNB(触发构建)
|
||||
git push gitea main # 只推 Gitea 辅仓
|
||||
```
|
||||
|
||||
**本仓只有 `origin` 一个 git 远端。** 曾经的双推(`git push origin main; git push gh main`)
|
||||
随 GitHub 退役而取消;后来一度改写成 `origin + gitee`,也随「异地备份改用 OpenList」
|
||||
(§5.6)一并取消 —— 备份换了介质,就不再需要第二个 git 远端。
|
||||
`git pushall` 的实际定义:
|
||||
|
||||
留着的两条教训:
|
||||
```
|
||||
!git push origin main; ec=$?; git remote | grep -qx gitea && git push gitea main; exit $ec
|
||||
```
|
||||
|
||||
- **不要用「一个 remote 挂多个 pushurl」** —— 实测 git 是「顺序推、遇错即停」,
|
||||
第一个失败后面的都不推,备份意义就没了。真要加辅仓,得用「独立 remote + 别名」。
|
||||
- 别名当初写成 `;` 串联而非 `&&`,正是为了让一段失败不影响另一段。现在只有一个远端,
|
||||
这层考虑自然消失。
|
||||
**本仓有两个 git 远端:`origin`(CNB 主仓)+ `gitea`(自建 Gitea 代码同步辅仓)。**
|
||||
演变路径:GitHub 双推 →(GitHub 被 AUP 清空)→ 一度只剩 `origin` → 现在回到双远端。
|
||||
注意 Gitee 从头到尾**没真正建起来过**(卡体积门槛,见 §5.3),别把它记成一代。
|
||||
|
||||
三条设计上的取舍:
|
||||
|
||||
- **不用「一个 remote 挂多个 pushurl」** —— 实测 git 是「顺序推、遇错即停」,
|
||||
第一个失败后面的都不推,备份意义就没了。所以用「独立 remote + 别名」。
|
||||
- 别名用 `;` 串联而非 `&&`,正是为了让一段失败不影响另一段(主仓挂了辅仓照样推);
|
||||
再 `exit $ec` 把退出码还给主仓,免得辅仓成功掩盖主仓失败。
|
||||
- gitea 那一段带 `git remote | grep -qx gitea` 前置判断 ——
|
||||
没配辅仓时静默跳过,而不是白报一行「'gitea' does not appear to be a git repository」。
|
||||
|
||||
### 5.2 远端
|
||||
|
||||
| remote | 地址 | 角色 |
|
||||
|---|---|---|
|
||||
| `origin` | `https://cnb.cool/zqlit/blog.git` | **唯一远端**(fetch + push,触发 CNB 构建) |
|
||||
| remote | 地址 | 角色 | fetch | push |
|
||||
|---|---|---|---|---|
|
||||
| `origin` | `https://cnb.cool/zqlit/blog.git` | **主仓**(CNB) | ✅ 唯一 fetch 源 | ✅ 触发 CNB 构建 |
|
||||
| `gitea` | `http://23.254.236.47:3001/zqlit/blog.git` | **代码同步辅仓**(自建,Gitea 28.0.0) | ⛔ 只推不拉 | ✅ 代码留档,失败不阻断 |
|
||||
|
||||
- **Gitea 凭据编在远端 URL 里**(`http://zqlit:<密码>@…`):容器/脚本环境没有交互终端、
|
||||
也没有可用的凭据助手,这是让 `git push gitea` 免交互跑通的唯一简单写法。
|
||||
明文只落在本机 `.git/config`(以及服务器上容器内的同名文件),**不进仓库、不随之推送**。
|
||||
⚠️ 口令里若含 `#` 或 `&` 会被拼 URL 的 `sed` 吃掉 —— 那种情况请改用访问令牌。
|
||||
- 自建 Gitea **不参与构建**:`.github/` 已删、Actions 早已停用,它纯粹是代码副本。
|
||||
当前版本 `28.0.0`,走 HTTP + 3001 端口,本机直连即可(**不需要**广州中转机,
|
||||
与当初「本机跑不通 git smart HTTP」的旧结论不同 —— 那次是沙箱代理的问题)。
|
||||
- 接入时的同步基线:Gitea 停在 `ed38f938`(2026-10-04),落后 57 个提交,
|
||||
且是本地 HEAD 的**祖先** → 一次 fast-forward 就追平,**无需强推、不丢历史**。
|
||||
|
||||
已移除的远端(改动前的完整配置快照留在 `.workbuddy-backup/git-remotes.*.txt`):
|
||||
|
||||
| remote | 地址 | 移除于 | 原因 |
|
||||
|---|---|---|---|
|
||||
| `gh` | `github.com/zqlit/blog` | 2026-10-06 | 账号被标记、仓库被按 AUP 清空(见下) |
|
||||
| `gitee` | (从未真正建立过) | 2026-10-06 | 两条硬门槛过不去,改走 OpenList 离线备份(§5.3) |
|
||||
| `gitea` | 自建 `23.254.236.47:3001` | 2026-10-06 | 发布链路早已不依赖;留着只会在推送时撞上落后 56 个提交的旧副本 |
|
||||
|
||||
> **远端仓库本身都没删,只是本地不再挂。** 想临时恢复自建 Gitea 副本:
|
||||
> ```bash
|
||||
> git remote add gitea http://<用户名>:<密码>@23.254.236.47:3001/zqlit/blog.git
|
||||
> git push gitea main
|
||||
> ```
|
||||
| `gh` | `github.com/zqlit/blog` | 2026-10-06 | 账号被标记、仓库被按 AUP 清空(见下),**别再往回加** |
|
||||
| `gitee` | (从未真正建立过) | — | 两条硬门槛过不去(§5.3) |
|
||||
|
||||
> ⚠️ **GitHub 退役记录(2026-10-06)**
|
||||
>
|
||||
@@ -167,14 +182,26 @@ git pushall # 完全等价 —— 它只是个别名,本仓只有
|
||||
> (`bootstrap.sh` / `docker-compose.editor.yml` / `server.mjs` / `README.md`)、
|
||||
> 以及 CI 定义本身(整个 `.github/` 目录,1268 行)。
|
||||
|
||||
### 5.3 异地备份的定案(2026-10-06)
|
||||
### 5.3 远端与备份的分工(2026-10-06)
|
||||
|
||||
**结论:异地备份不走 git 远端 —— 改用整仓加密 bundle → OpenList(见 §5.6)。**
|
||||
**结论:git 辅仓用「自建 Gitea」,备份用「整仓加密 bundle → OpenList」。两者都要,各管一段。**
|
||||
|
||||
一句话理由:备份要防的是「平台整体出问题」,而把副本放到**另一家同样性质的平台**上,
|
||||
只是把鸡蛋从左边口袋挪到右边口袋。真正的第三层得**换介质**。
|
||||
| 层 | 载体 | 保住什么 | 依赖 |
|
||||
|---|---|---|---|
|
||||
| 主仓 `origin` | CNB | 源码 + 触发构建 | CNB 平台 |
|
||||
| 代码辅仓 `gitea` | 自建 `23.254.236.47:3001` | 随时 `clone` 回来、按提交追溯 | **自己的机器** |
|
||||
| 离线备份 | F50/OpenList 上的加密 bundle(§5.6) | 完整历史 + 所有对象,平台全挂也能恢复 | 家里的局域网 |
|
||||
|
||||
下面这些实测数据保留下来 —— 下次再冒出「要不要加个 git 辅仓」的念头时,
|
||||
两层的区别不是「多一份」,而是**失效模式不同**:辅仓是**在线、可增量、可浏览**的副本,
|
||||
但仍在别人的磁盘上;bundle 是**离线、离线介质、单一文件**的全量快照,
|
||||
连 git 服务都没有也能恢复。所以辅仓不能替 bundle,bundle 也不如辅仓顺手。
|
||||
|
||||
**为什么不选「另一家托管平台」当辅仓**(Gitee / 云效 Codeup / GitLab):
|
||||
把副本放到**另一家同样性质的托管平台**上,只是把鸡蛋从左边口袋挪到右边口袋 ——
|
||||
GitHub 那次 AUP 清空就是活证。自建 Gitea 是唯一「不受第三方规则约束」的在线选项,
|
||||
它早在建站初期就在跑,本就该留着。
|
||||
|
||||
下面这些实测数据保留下来 —— 下次再冒出「换一家托管平台当辅仓」的念头时,
|
||||
这几张表就是答案。
|
||||
|
||||
体积分布(HEAD,实测 457.4 MB / 2509 文件):
|
||||
@@ -219,7 +246,8 @@ git pushall # 完全等价 —— 它只是个别名,本仓只有
|
||||
> ★ **这些都只是「换一个篮子」,不是容灾。**
|
||||
> CNB 与 Gitee/Codeup 同属国内大区;GitHub/GitLab 又执行同一套合规逻辑
|
||||
> (本轮 GitHub 被按 AUP 清空即为例证)。
|
||||
> 所以最终**一个 git 辅仓都没有采用** —— 改用不依赖任何平台账号的离线备份:见 §5.6。
|
||||
> 所以**托管平台这条路一个都没采用** —— 在线辅仓回到**自建 Gitea**(§5.2,
|
||||
> 不受第三方规则约束),另加一层不依赖任何 git 服务的离线备份(§5.6)。
|
||||
|
||||
**顺带一个副作用**:`bin/linux/hugo`「出库」这件事**不用做了**。
|
||||
那个 83.1 MB 二进制之所以成为问题,只因为它卡在 Gitee 的单文件 50 MB 上限上。
|
||||
@@ -239,10 +267,12 @@ git pushall # 完全等价 —— 它只是个别名,本仓只有
|
||||
- ⚠️ CNB 的 `imports` 是**一层映射**(key 就是变量名本身);又拍云服务名在 CNB 里必须叫
|
||||
**`UPYUN_SERVICE`**(旧 Gitea 里叫 `UPYUN_BUCKET`,照抄会「变量未定义」)
|
||||
|
||||
### 5.6 整仓离线备份(中兴 F50 / OpenList)★ 当前**唯一**的异地备份
|
||||
### 5.6 整仓离线备份(中兴 F50 / OpenList)★ 备选异地备份
|
||||
|
||||
> **定位**:2026-10-06 起这一层就是本项目的异地备份本体 —— 不再有任何 git 辅仓(§5.3)。
|
||||
> 所以它的**可靠性**与**失败可见性**都比以前更重要:它一旦悄悄失效,代码就只剩 CNB 一处。
|
||||
> **定位**:2026-10-06 起这一层是**备选**异地备份 —— 在线那一路已有自建 Gitea 代码辅仓(§5.2)。
|
||||
> 但它的地位并没有因此变轻:**它是唯一一层「不依赖任何 git 服务」的备份**,
|
||||
> 平台全挂、账号被封、服务器被回收都影响不到它。所以它的**可靠性**与**失败可见性**
|
||||
> 依然是最要紧的:一旦悄悄失效,能挡住「平台级事故」的就只剩它。
|
||||
> 失败告警因此是必备件,不是加分项。
|
||||
|
||||
**目标介质**:中兴 F50 5G CPE 内置 256 GB 存储,上面跑 OpenList
|
||||
@@ -297,9 +327,9 @@ node scripts/backup-bundle.mjs --decrypt <文件> [--out x.bundle] # 恢复用
|
||||
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASS` / `SMTP_TO` | 失败告警发信(复用 CNB 流水线那套 QQ 邮箱授权码) |
|
||||
|
||||
> ⚠️ `BACKUP_PASSPHRASE` 是恢复 bundle 的唯一钥匙,**必须另存进密码管理器**。
|
||||
> 它不构成单点:CNB 主仓仍是明文副本,口令丢了只是少一份备份。
|
||||
> 它不构成单点:CNB 主仓与自建 Gitea 辅仓都还是明文副本,口令丢了只是少一份备份。
|
||||
|
||||
**失败告警**(`scripts/backup-run.mjs`)—— 这是「唯一辅仓」的必备件。
|
||||
**失败告警**(`scripts/backup-run.mjs`)—— 这是「离线那一层」的必备件。
|
||||
跑完备份后,退出码非 0 就通过 `scripts/send_mail.js` 发一封告警邮件
|
||||
(主题带 `★`,正文附日志尾部与常见原因清单)。成功时默认**不发**(避免每天一封的邮件疲劳),
|
||||
加 `--notify-success` 才发。
|
||||
@@ -316,7 +346,7 @@ node scripts/backup-bundle.mjs --decrypt <文件> [--out x.bundle] # 恢复用
|
||||
日志追加到 `.workbuddy-backup/logs/backup.log`,超 5 MB 自动轮转。
|
||||
|
||||
> 保留 **7 份 = 一周窗口**(每份 605.5 MB ≈ 4.2 GB,F50 有 256 GB)。
|
||||
> 原本是 3 份;升格为「唯一辅仓」后放宽 —— 空间不值钱,回溯窗口值钱。
|
||||
> 原本是 3 份;定位成「离线那一层备份」后放宽 —— 空间不值钱,回溯窗口值钱。
|
||||
|
||||
★ `backup-task.cmd` **内容必须全 ASCII**:cmd.exe 按**当前代码页**(zh-CN 是 GBK)
|
||||
解析批处理文件,而 node 输出 UTF-8;UTF-8 中文注释会吞掉 CR/LF、
|
||||
@@ -350,15 +380,15 @@ git clone blog.bundle blog # 或 git fetch blog.bundle 'refs/*:refs/*'
|
||||
| # | 项 | 说明 |
|
||||
|---|---|---|
|
||||
| 1 | `gh` remote(GitHub) | ✅ **已移除**(2026-10-06)。GitHub 账号被标记、仓库被 AUP 清空 —— **GitHub 已彻底退出本项目**:远端、推送别名、发布链路 4 处硬编码、CI 定义全部清干净 |
|
||||
| 2 | **异地备份方案** | ✅ **已定案**(2026-10-06):**不走 git 远端**,改用整仓加密 bundle → F50/OpenList(§5.6)。`pushall` 已收回为「只推 origin」,`gitee` / `gitea` 远端均已移除。选型依据(Gitee 两道硬门槛、Codeup 对照)保留在 §5.3 备查 |
|
||||
| 2 | **远端与备份定案** | ✅ **已定案**(2026-10-06):**CNB 主仓 + 自建 Gitea 代码辅仓 + OpenList 离线 bundle**(§5.2/§5.3/§5.6)。自建 Gitea 不受第三方规则约束,是唯一「平台出事也带不走」的在线副本;离线 bundle 保完整历史。选型依据(Gitee 两道硬门槛、Codeup 对照)保留在 §5.3 备查 |
|
||||
| 3 | `bin/linux/hugo` 出库 | ✅ **不必做**。那个 83.1MB 二进制只卡 Gitee 的单文件上限;既然不走 Gitee,`deploy/Dockerfile` 的取 hugo 方式与 `.cnb.yml` 的 `by:` 字段一行都不用改 |
|
||||
| 4 | `gitea` remote | ✅ **已移除**(2026-10-06)。远端仓库本身没删,需要时可 `git remote add` 恢复(命令见 §5.2) |
|
||||
| 4 | `gitea` remote | ✅ **已恢复**(2026-10-06):作为代码同步辅仓重新挂上,`pushall` 恢复双推(`origin` + `gitea`)。接入时 Gitea 停在 `ed38f938`,一次 fast-forward 追平(+57 提交),**未强推、未丢历史**。发布链路 5 处默认值同步改回 `origin,gitea`(`bootstrap.sh` / `docker-compose.editor.yml` / `server.mjs` / `README.md` / `setup-cnb-remotes.sh`) |
|
||||
| 5 | GitHub 侧旧 workflow | ✅ **已删除**(2026-10-06)。整个 `.github/` 目录移除(`deploy.yml` 1169 行 + `aliyun-backup.yml` + `cleanup.yml`),共 −1284 行 —— **GitHub 已不再是任何环节的依赖**。旧实现可在 git 历史中查 |
|
||||
| 6 | 令牌 scope | 缺 `repo-cnb-history:r`(读构建日志);补上后 agent 可自行排错 |
|
||||
| 7 | Gitea 主机 / 广州中转机 | 发布链路已不依赖;是否退役取决于其它用途 |
|
||||
| 7 | Gitea 主机 / 广州中转机 | **Gitea 主机已回归**(2026-10-06):作为代码辅仓,本机直连 `23.254.236.47:3001` 即可推。广州中转机仍不参与 git 链路 |
|
||||
| 8 | `README.md` | ✅ 已更新(去 GitHub 化 + 补上备份脚本说明) |
|
||||
| 9 | **敏感文件仍在跟踪中** | `.env`、`GITEA_SECRETS.md`、`write-server/nginx/ssl/privkey.pem`、`content/posts/2024/.../setup-secrets.png` —— 若还要推任何新平台,先 `git rm --cached` 并轮换凭据。⚠️ 当初「不改写历史」的唯一顾虑是备份会分叉;**GitHub 那份已消失,这个顾虑没有了** → `git filter-repo` 现在是可行窗口(代价:全部提交 SHA 改变)。离线 bundle 已加密(§5.6),不受此影响 |
|
||||
| 10 | **整仓离线备份** | ✅ **已上线**(2026-10-06),**且是当前唯一的异地备份**,见 §5.6。计划任务 `Blog-BundleBackup` 每天 03:30 跑,端到端已验证;失败会发告警邮件(已实测)。待补三件安全项:把 `BACKUP_PASSPHRASE` 抄进密码管理器;给 OpenList **改掉 admin 密码 + 开两步验证**(现 `otp: false`,且旧密码已出现在对话里);确认 5244 端口**未暴露到公网** |
|
||||
| 10 | **整仓离线备份** | ✅ **已上线**(2026-10-06),现为**备选**异地备份(在线那一路是自建 Gitea 辅仓),见 §5.6。计划任务 `Blog-BundleBackup` 每天 03:30 跑,端到端已验证;失败会发告警邮件(已实测)。待补三件安全项:把 `BACKUP_PASSPHRASE` 抄进密码管理器;给 OpenList **改掉 admin 密码 + 开两步验证**(现 `otp: false`,且旧密码已出现在对话里);确认 5244 端口**未暴露到公网** |
|
||||
| 11 | **F50 存储目录的使用约定** | `/本地/` 下的 `刷机` / `系统` / `资料` / `软件` / `驱动` / `备份` 都是用户自己在管的类别。**本项目的 bundle 只写 `备份/blog-bundle/` 子目录**,不与用户手工备份(`github-zqlit-*`、`local-repos-*`)混放 |
|
||||
|
||||
---
|
||||
|
||||
Reference in new issue
Block a user