206 lines
8.1 KiB
Bash
Executable File
206 lines
8.1 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================
|
||
# 卡若AI → Gitea 实时同步(局域网优先:192.168.110.101:3000;日志链接与 gitea_push.conf 一致)
|
||
# 规则:超过 1MB 的文件不上传;其余 Skill、功能、文档每日同步
|
||
# 推送成功后:1) 同步百科 2) 写入 gitea_push_log.md 3) 写入 代码管理.md
|
||
# ============================================
|
||
|
||
REPO_DIR="/Users/karuo/Documents/个人/卡若AI"
|
||
REMOTE="gitea"
|
||
BRANCH="main"
|
||
MAX_SIZE_MB=1
|
||
MAX_SIZE_BYTES=$((MAX_SIZE_MB * 1024 * 1024))
|
||
LOG_FILE="$REPO_DIR/01_卡资(金)/金仓_存储备份/Gitea管理/sync.log"
|
||
PUSH_LOG="$REPO_DIR/运营中枢/工作台/gitea_push_log.md"
|
||
CODE_MGMT="$REPO_DIR/运营中枢/工作台/代码管理.md"
|
||
GITEA_URL="http://192.168.110.101:3000/fnvtk/karuo-ai"
|
||
GITIGNORE="$REPO_DIR/.gitignore"
|
||
WIKI_SCRIPT="$REPO_DIR/01_卡资(金)/金仓_存储备份/Gitea管理/脚本/sync_wiki_to_gitea.sh"
|
||
|
||
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"; }
|
||
|
||
# 限制 sync.log 大小
|
||
if [ -f "$LOG_FILE" ] && [ $(stat -f%z "$LOG_FILE" 2>/dev/null || echo 0) -gt 512000 ]; then
|
||
tail -100 "$LOG_FILE" > "$LOG_FILE.tmp" && mv "$LOG_FILE.tmp" "$LOG_FILE"
|
||
fi
|
||
|
||
cd "$REPO_DIR" || { log "错误:无法进入目录 $REPO_DIR"; exit 1; }
|
||
|
||
# ============================================
|
||
# Step 0: 上传前检查公司 Gitea、仓库完整性和远端现状
|
||
# ============================================
|
||
if ! git fsck --connectivity-only --no-dangling >/dev/null 2>&1; then
|
||
log "错误:本地 Git 对象不完整,停止上传,避免把损坏历史推到公司 Gitea"
|
||
exit 2
|
||
fi
|
||
|
||
if ! GIT_TERMINAL_PROMPT=0 git -c http.connectTimeout=5 -c http.lowSpeedLimit=1 -c http.lowSpeedTime=8 \
|
||
ls-remote --exit-code --heads "$REMOTE" "$BRANCH" >/dev/null 2>&1; then
|
||
log "公司 Gitea 当前不可达或远端分支不存在,停止上传,等待下次重试"
|
||
exit 3
|
||
fi
|
||
|
||
if ! GIT_TERMINAL_PROMPT=0 git -c http.connectTimeout=5 -c http.lowSpeedLimit=1 -c http.lowSpeedTime=15 \
|
||
fetch --quiet "$REMOTE" "$BRANCH"; then
|
||
log "公司 Gitea 拉取检查失败,停止上传"
|
||
exit 4
|
||
fi
|
||
|
||
REMOTE_REF="refs/remotes/${REMOTE}/${BRANCH}"
|
||
if ! git show-ref --verify --quiet "$REMOTE_REF"; then
|
||
log "远端跟踪分支 ${REMOTE}/${BRANCH} 不存在,停止上传"
|
||
exit 5
|
||
fi
|
||
|
||
# ============================================
|
||
# Step 1: 排除 >1MB 文件(动态写入 .gitignore 区段)
|
||
# ============================================
|
||
MARKER_START="# === 自动排除:超过${MAX_SIZE_MB}MB的文件(脚本自动管理,勿手动修改)==="
|
||
MARKER_END="# === 自动排除结束 ==="
|
||
|
||
LARGE_FILES=$(find . -type f -size +${MAX_SIZE_MB}M -not -path "./.git/*" 2>/dev/null | sort)
|
||
NEW_SECTION="$MARKER_START"
|
||
if [ -n "$LARGE_FILES" ]; then
|
||
while IFS= read -r file; do
|
||
clean_path="${file#./}"
|
||
NEW_SECTION="$NEW_SECTION
|
||
$clean_path"
|
||
done <<< "$LARGE_FILES"
|
||
fi
|
||
NEW_SECTION="$NEW_SECTION
|
||
$MARKER_END"
|
||
|
||
if grep -q '# === 自动排除:超过.*MB的文件' "$GITIGNORE" 2>/dev/null; then
|
||
python3 -c "
|
||
import re
|
||
with open('$GITIGNORE', 'r') as f:
|
||
content = f.read()
|
||
pattern = r'(?m)^# === 自动排除:超过[^\n]*MB的文件[^\n]*===\n.*?^# === 自动排除结束 ==='
|
||
new_content = re.sub(pattern, '''$NEW_SECTION''', content, flags=re.DOTALL)
|
||
with open('$GITIGNORE', 'w') as f:
|
||
f.write(new_content)
|
||
" 2>/dev/null
|
||
else
|
||
echo "" >> "$GITIGNORE"
|
||
echo "$NEW_SECTION" >> "$GITIGNORE"
|
||
fi
|
||
|
||
LARGE_COUNT=$(echo "$LARGE_FILES" | grep -c '.' 2>/dev/null || echo 0)
|
||
log "大文件扫描:$LARGE_COUNT 个 >${MAX_SIZE_MB}MB 已排除"
|
||
|
||
# ============================================
|
||
# Step 2: 检查是否有变更
|
||
# ============================================
|
||
git add -A 2>/dev/null
|
||
|
||
# 已被Git跟踪的大文件即使写入.gitignore仍可能被暂存;逐项撤出本次提交。
|
||
while IFS= read -r -d '' file; do
|
||
size=0
|
||
if [ -f "$file" ]; then
|
||
size=$(stat -f%z "$file" 2>/dev/null || echo 0)
|
||
else
|
||
size=$(git cat-file -s "HEAD:$file" 2>/dev/null || echo 0)
|
||
fi
|
||
if [ "$size" -gt "$MAX_SIZE_BYTES" ]; then
|
||
git restore --staged -- "$file" 2>/dev/null || git reset -q HEAD -- "$file" 2>/dev/null || true
|
||
log "撤出暂存的大文件:$file (${size} bytes)"
|
||
fi
|
||
done < <(git diff --cached --name-only -z --diff-filter=ACMRD)
|
||
if git diff --cached --quiet 2>/dev/null; then
|
||
if [ "$(git rev-parse HEAD)" = "$(git rev-parse "$REMOTE_REF")" ]; then
|
||
log "本地与公司 Gitea 内容一致,无重复上传"
|
||
else
|
||
log "无新增文件变更,但本地与远端提交不同;停止并等待历史对账"
|
||
fi
|
||
exit 0
|
||
fi
|
||
|
||
# ============================================
|
||
# Step 3: 提交(提交说明写具体更新内容,不写「变更 N 个文件」)
|
||
# ============================================
|
||
CHANGED_COUNT=$(git diff --cached --numstat | wc -l | tr -d ' ')
|
||
TIMESTAMP=$(date '+%Y-%m-%d %H:%M')
|
||
SUMMARY_SCRIPT="$REPO_DIR/01_卡资(金)/金仓_存储备份/Gitea管理/脚本/生成同步说明.py"
|
||
if [ -f "$SUMMARY_SCRIPT" ]; then
|
||
SUMMARY=$(git diff --cached --name-only 2>/dev/null | python3 "$SUMMARY_SCRIPT" 2>/dev/null)
|
||
[ -z "$SUMMARY" ] && SUMMARY="多目录更新"
|
||
else
|
||
SUMMARY="多目录更新"
|
||
fi
|
||
COMMIT_MSG="🔄 卡若AI 同步 $TIMESTAMP | 更新:$SUMMARY | 排除 >${MAX_SIZE_MB}MB: ${LARGE_COUNT} 个"
|
||
|
||
git commit -m "$COMMIT_MSG" --quiet 2>/dev/null
|
||
if [ $? -ne 0 ]; then
|
||
log "错误:提交失败"
|
||
exit 1
|
||
fi
|
||
log "提交成功:$COMMIT_MSG"
|
||
|
||
# 提交后再次比较远端:只允许远端是本地祖先时做快进推送。
|
||
if [ "$(git rev-parse HEAD)" = "$(git rev-parse "$REMOTE_REF")" ]; then
|
||
log "提交已存在于公司 Gitea,跳过重复上传"
|
||
exit 0
|
||
fi
|
||
|
||
read -r AHEAD BEHIND <<<"$(git rev-list --left-right --count HEAD..."$REMOTE_REF")"
|
||
PUSH_MODE="normal"
|
||
if [ "${BEHIND:-0}" -gt 0 ]; then
|
||
# 用户指定本地为主:先保留远端快照分支,再以 force-with-lease 更新 main。
|
||
ARCHIVE_BRANCH="archive/remote-main-$(date '+%Y%m%d-%H%M%S')"
|
||
if ! git push "$REMOTE" "$REMOTE_REF:refs/heads/$ARCHIVE_BRANCH" --quiet; then
|
||
log "远端快照分支创建失败,停止上传,不直接覆盖"
|
||
exit 6
|
||
fi
|
||
log "已保留远端快照:$ARCHIVE_BRANCH;接下来以本地 main 为主同步"
|
||
PUSH_MODE="local-primary"
|
||
fi
|
||
if [ "${AHEAD:-0}" -eq 0 ]; then
|
||
log "没有需要上传的新提交"
|
||
exit 0
|
||
fi
|
||
|
||
# ============================================
|
||
# Step 4: 推送到 Gitea(智能:局域网用 IP,外网用域名+代理)
|
||
# ============================================
|
||
PUSH_SCRIPT="$REPO_DIR/01_卡资(金)/金仓_存储备份/Gitea管理/脚本/gitea_push_smart.sh"
|
||
if [ -x "$PUSH_SCRIPT" ]; then
|
||
bash "$PUSH_SCRIPT" "$REPO_DIR" "$REMOTE" "$BRANCH" "$PUSH_MODE" 2>&1 | tee -a "$LOG_FILE"
|
||
PUSH_RESULT=${PIPESTATUS[0]}
|
||
else
|
||
if [ "$PUSH_MODE" = "local-primary" ]; then
|
||
git push --force-with-lease="refs/heads/${BRANCH}:$(git rev-parse "$REMOTE_REF")" "$REMOTE" "${BRANCH}:${BRANCH}" --quiet 2>&1
|
||
else
|
||
git push "$REMOTE" "$BRANCH" --quiet 2>&1
|
||
fi
|
||
PUSH_RESULT=$?
|
||
fi
|
||
|
||
if [ $PUSH_RESULT -eq 0 ]; then
|
||
log "推送成功 → gitea/$BRANCH"
|
||
WIKI_STATUS="未执行"
|
||
if [ -x "$WIKI_SCRIPT" ]; then
|
||
if bash "$WIKI_SCRIPT" >> "$LOG_FILE" 2>&1; then
|
||
WIKI_STATUS="成功"
|
||
log "百科同步成功"
|
||
else
|
||
WIKI_STATUS="失败(百科未初始化或网络)"
|
||
log "百科同步失败"
|
||
fi
|
||
fi
|
||
# 建立推送记录
|
||
mkdir -p "$(dirname "$PUSH_LOG")"
|
||
if [ ! -f "$PUSH_LOG" ]; then
|
||
echo -e "# Gitea 推送记录\n\n> 卡若AI 有更新即同步到 ${GITEA_URL}\n\n| 时间 | 提交说明 |\n|:---|:---|" > "$PUSH_LOG"
|
||
fi
|
||
echo "| $(date '+%Y-%m-%d %H:%M:%S') | $COMMIT_MSG |" >> "$PUSH_LOG"
|
||
# 代码管理:写入本次上传(代码+百科+链接)
|
||
if [ -f "$CODE_MGMT" ]; then
|
||
echo "| $(date '+%Y-%m-%d %H:%M:%S') | 成功 | $WIKI_STATUS | $COMMIT_MSG | [仓库]($GITEA_URL) [百科]($GITEA_URL/wiki) |" >> "$CODE_MGMT"
|
||
fi
|
||
else
|
||
log "推送失败(code=$PUSH_RESULT);保留本地提交,等待下次自动重试,不执行强制推送"
|
||
exit 1
|
||
fi
|
||
|
||
exit 0
|