218 lines
6.6 KiB
Bash
218 lines
6.6 KiB
Bash
#!/bin/bash
|
||
set -euo pipefail
|
||
|
||
# ============================================================
|
||
# OCIFLAB 仓库备份脚本
|
||
# 每天从 whitelist.md 拉取仓库列表,克隆/更新仓库,压缩备份
|
||
# 需要使用 Access Token 进行认证
|
||
# ============================================================
|
||
|
||
# ---------- 配置区 ----------
|
||
GIT_ACCESS_TOKEN="" # 填写你的 Git 访问令牌
|
||
WHITELIST_REPO_URL="" # 白名单所在仓库地址 (例: https://git.ociflab.icu/user/repo.git)
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
BACKUP_DIR="${SCRIPT_DIR}/backup"
|
||
WORK_DIR="${SCRIPT_DIR}/work"
|
||
ARCHIVE_DIR="${SCRIPT_DIR}/archives"
|
||
WHITELIST_CACHE="${SCRIPT_DIR}/whitelist_cache.md"
|
||
RETENTION_DAYS=7
|
||
DATE_STAMP="$(date +%Y%m%d)"
|
||
|
||
# server酱3 配置
|
||
SERVERCHAN_UID="" # 填写你的 UID
|
||
SERVERCHAN_SENDKEY="" # 填写你的 SendKey
|
||
|
||
# ---------- 函数区 ----------
|
||
|
||
log() {
|
||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||
}
|
||
|
||
send_warning() {
|
||
local title="$1"
|
||
local desp="$2"
|
||
if [[ -z "${SERVERCHAN_UID}" ]] || [[ -z "${SERVERCHAN_SENDKEY}" ]]; then
|
||
log "server酱未配置,跳过推送: ${title}"
|
||
return 0
|
||
fi
|
||
local resp
|
||
resp=$(curl -s -o /dev/null -w "%{http_code}" --data-urlencode "title=${title}" --data-urlencode "desp=${desp}" "https://${SERVERCHAN_UID}.push.ft07.com/send/${SERVERCHAN_SENDKEY}.send")
|
||
if [[ "${resp}" == "200" ]]; then
|
||
log "警告推送成功: ${title}"
|
||
else
|
||
log "警告推送失败(HTTP ${resp}): ${title}"
|
||
fi
|
||
}
|
||
|
||
git_with_auth() {
|
||
if [[ -n "${GIT_ACCESS_TOKEN}" ]]; then
|
||
git -c "http.https://git.ociflab.icu/.extraHeader=Authorization: Bearer ${GIT_ACCESS_TOKEN}" "$@"
|
||
else
|
||
git "$@"
|
||
fi
|
||
}
|
||
|
||
fetch_whitelist() {
|
||
local whitelist_repo_dir="${SCRIPT_DIR}/whitelist_repo"
|
||
|
||
log "正在更新白名单仓库..."
|
||
|
||
if [[ -d "${whitelist_repo_dir}/.git" ]]; then
|
||
# 更新
|
||
if ! git_with_auth -C "${whitelist_repo_dir}" pull --ff-only --quiet 2>&1; then
|
||
log "更新白名单仓库失败,尝试重新克隆..."
|
||
rm -rf "${whitelist_repo_dir}"
|
||
fi
|
||
fi
|
||
|
||
if [[ ! -d "${whitelist_repo_dir}/.git" ]]; then
|
||
if ! git_with_auth clone --depth 1 --quiet "${WHITELIST_REPO_URL}" "${whitelist_repo_dir}" 2>&1; then
|
||
log "克隆白名单仓库失败"
|
||
fi
|
||
fi
|
||
|
||
# 读取 whitelist.md 到缓存
|
||
if [[ -f "${whitelist_repo_dir}/whitelist.md" ]]; then
|
||
cp "${whitelist_repo_dir}/whitelist.md" "${WHITELIST_CACHE}"
|
||
log "whitelist.md 已更新"
|
||
return 0
|
||
fi
|
||
|
||
log "whitelist.md 获取失败,尝试使用前一天缓存..."
|
||
if [[ -f "${SCRIPT_DIR}/whitelist_cache.md.yesterday" ]]; then
|
||
cp "${SCRIPT_DIR}/whitelist_cache.md.yesterday" "${WHITELIST_CACHE}"
|
||
log "已使用前一天缓存"
|
||
return 0
|
||
fi
|
||
|
||
log "前一天缓存也不存在,无法继续"
|
||
return 1
|
||
}
|
||
|
||
parse_whitelist() {
|
||
grep -Eo 'https://git\.ociflab\.icu/[^[:space:]]+' "${WHITELIST_CACHE}" | sed 's/[[:space:]]*$//'
|
||
}
|
||
|
||
clone_or_pull_repo() {
|
||
local repo_url="$1"
|
||
local repo_name
|
||
repo_name=$(echo "${repo_url}" | sed 's|https://git\.ociflab\.icu/||' | tr '/' '_')
|
||
local repo_path="${WORK_DIR}/${repo_name}"
|
||
|
||
if [[ -d "${repo_path}/.git" ]]; then
|
||
log " 更新: ${repo_url}"
|
||
if ! git_with_auth -C "${repo_path}" pull --ff-only --quiet 2>&1; then
|
||
log " 更新失败: ${repo_url},尝试重新克隆..."
|
||
rm -rf "${repo_path}"
|
||
if ! git_with_auth clone --depth 1 --quiet "${repo_url}" "${repo_path}" 2>&1; then
|
||
log " 重新克隆也失败: ${repo_url}"
|
||
return 1
|
||
fi
|
||
fi
|
||
else
|
||
log " 克隆: ${repo_url}"
|
||
rm -rf "${repo_path}"
|
||
if ! git_with_auth clone --depth 1 --quiet "${repo_url}" "${repo_path}" 2>&1; then
|
||
log " 克隆失败: ${repo_url}"
|
||
return 1
|
||
fi
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
backup_repos() {
|
||
local whitelist=("$@")
|
||
local total=${#whitelist[@]}
|
||
local failed=0
|
||
local failed_list=""
|
||
|
||
log "共 ${total} 个仓库待备份"
|
||
|
||
for repo_url in "${whitelist[@]}"; do
|
||
[[ -z "${repo_url}" ]] && continue
|
||
if ! clone_or_pull_repo "${repo_url}"; then
|
||
failed=$((failed + 1))
|
||
failed_list="${failed_list}${repo_url}\n"
|
||
fi
|
||
done
|
||
|
||
# 检查错误率:超过三分之一则发送警告
|
||
if [[ ${total} -gt 0 ]]; then
|
||
local threshold=$((total / 3))
|
||
if [[ ${failed} -gt ${threshold} ]]; then
|
||
local warn_desp="备份失败 ${failed}/${total} 个仓库,超过三分之一。\n失败列表:\n${failed_list}"
|
||
send_warning "OCIFLAB备份异常 - ${failed}/${total} 仓库失败" "${warn_desp}"
|
||
fi
|
||
fi
|
||
|
||
log "备份完成: 成功 $((total - failed)) 个, 失败 ${failed} 个"
|
||
}
|
||
|
||
create_archive() {
|
||
log "正在压缩备份..."
|
||
mkdir -p "${ARCHIVE_DIR}"
|
||
|
||
local archive_name="ociflab-backup-${DATE_STAMP}.zip"
|
||
local archive_path="${ARCHIVE_DIR}/${archive_name}"
|
||
|
||
if [[ -d "${WORK_DIR}" ]] && [[ -n "$(ls -A "${WORK_DIR}" 2>/dev/null)" ]]; then
|
||
cd "${WORK_DIR}"
|
||
zip -r "${archive_path}" . -q
|
||
cd "${SCRIPT_DIR}"
|
||
log "压缩完成: ${archive_path}"
|
||
else
|
||
log "工作目录为空,跳过压缩"
|
||
fi
|
||
}
|
||
|
||
cleanup_old_archives() {
|
||
log "清理 ${RETENTION_DAYS} 天前的备份..."
|
||
find "${ARCHIVE_DIR}" -name "ociflab-backup-*.zip" -type f -mtime +${RETENTION_DAYS} -delete 2>/dev/null || true
|
||
log "清理完成"
|
||
}
|
||
|
||
persist_yesterday_whitelist() {
|
||
if [[ -f "${WHITELIST_CACHE}" ]]; then
|
||
cp "${WHITELIST_CACHE}" "${SCRIPT_DIR}/whitelist_cache.md.yesterday"
|
||
fi
|
||
}
|
||
|
||
# ---------- 主流程 ----------
|
||
|
||
main() {
|
||
log "========== OCIFLAB 备份开始 =========="
|
||
|
||
mkdir -p "${WORK_DIR}" "${ARCHIVE_DIR}"
|
||
|
||
# 1. 拉取白名单
|
||
if ! fetch_whitelist; then
|
||
log "无法获取白名单,退出"
|
||
send_warning "OCIFLAB备份失败" "无法拉取 whitelist.md 且无前一天缓存"
|
||
exit 1
|
||
fi
|
||
|
||
# 2. 解析仓库列表
|
||
mapfile -t repos < <(parse_whitelist)
|
||
|
||
if [[ ${#repos[@]} -eq 0 ]]; then
|
||
log "白名单为空,退出"
|
||
send_warning "OCIFLAB备份失败" "whitelist.md 中无有效仓库地址"
|
||
exit 1
|
||
fi
|
||
|
||
# 3. 克隆/更新仓库
|
||
backup_repos "${repos[@]}"
|
||
|
||
# 4. 压缩归档
|
||
create_archive
|
||
|
||
# 5. 清理旧备份
|
||
cleanup_old_archives
|
||
|
||
# 6. 保存今天的白名单供明天备用
|
||
persist_yesterday_whitelist
|
||
|
||
log "========== OCIFLAB 备份完成 =========="
|
||
}
|
||
|
||
main "$@" |