diff --git a/README.md b/README.md index 0efb208..e1fbe0a 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,22 @@ ## 功能 每天从https://git.ociflab.icu/Laplace/backup-list拉取whitelist.md +如果没拉取到,则使用前一天拉取并保存的whitelist文件。 +如果超过三分之一的仓库都拉取错误,则给脚本中设置的server酱3 Pushkey推送警告信息。 然后根据whitelist.md拉取对应的仓库到backup文件夹进行备份 压缩成zip,每天定时备份一次,保留7天。 -使用Python。 +使用bash。 ## whitelist.md文件结构: ~~~ https://git.ociflab.icu/userA/projectA https://git.ociflab.icu/userB/projectB +~~~ + + +## server酱3参考 +~~~bash +# 一行代码即可调用,中文参数需要UrlEncode,长内容建议使用POST +curl "https://.push.ft07.com/send/.send?title=&desp=<desp>" ~~~ \ No newline at end of file diff --git a/ociflab-backup.sh b/ociflab-backup.sh new file mode 100644 index 0000000..14b0020 --- /dev/null +++ b/ociflab-backup.sh @@ -0,0 +1,192 @@ +#!/bin/bash +set -euo pipefail + +# ============================================================ +# OCIFLAB 仓库备份脚本 +# 每天从 whitelist.md 拉取仓库列表,克隆/更新仓库,压缩备份 +# ============================================================ + +# ---------- 配置区 ---------- +WHITELIST_URL="" +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 +} + +fetch_whitelist() { + log "正在拉取 whitelist.md ..." + local http_code + http_code=$(curl -s -o "${WHITELIST_CACHE}" -w "%{http_code}" --connect-timeout 10 --max-time 30 "${WHITELIST_URL}") + + if [[ "${http_code}" == "200" ]] && [[ -s "${WHITELIST_CACHE}" ]]; then + log "whitelist.md 拉取成功 (HTTP ${http_code})" + return 0 + fi + + log "whitelist.md 拉取失败 (HTTP ${http_code}),尝试使用前一天缓存..." + 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 -C "${repo_path}" pull --ff-only 2>&1; then + log " 更新失败: ${repo_url},尝试重新克隆..." + rm -rf "${repo_path}" + if ! git clone --depth 1 "${repo_url}" "${repo_path}" 2>&1; then + log " 重新克隆也失败: ${repo_url}" + return 1 + fi + fi + else + log " 克隆: ${repo_url}" + rm -rf "${repo_path}" + if ! git clone --depth 1 "${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 "$@"