#!/bin/bash # sync-gitea.sh - 自动同步到 Gitea 仓库 # # 用法: # ./sync-gitea.sh # 正常提交和推送 # ./sync-gitea.sh --init # 初始化仓库 # ./sync-gitea.sh --commit # 仅提交 # ./sync-gitea.sh --push # 仅推送 set -euo pipefail # 配置 REPO_DIR="/Users/x/websafe" GITEA_URL="https://git.hk.hao.work" GITEA_API="${GITEA_URL}/api/v1" REPO_NAME="${REPO_NAME:-websafe-kb}" REPO_DESC="${REPO_DESC:-授权攻防实验与研究知识库}" GITEA_TOKEN="${GITEA_TOKEN:-}" GIT_USER="${GIT_USER:-hao}" GIT_EMAIL="${GIT_EMAIL:-hao@users.noreply.git.hk.hao.work}" AUTO_PUSH_MAIN="${AUTO_PUSH_MAIN:-1}" LOCK_DIR="${REPO_DIR}/.git/.sync-gitea.lock" cd "$REPO_DIR" # 颜色定义 RED='\033[91m' GREEN='\033[92m' YELLOW='\033[93m' BLUE='\033[94m' END='\033[0m' log_info() { echo -e "${BLUE}[INFO]${END} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${END} $1" } log_warning() { echo -e "${YELLOW}[WARNING]${END} $1" } log_error() { echo -e "${RED}[ERROR]${END} $1" } repo_api_url() { echo "${GITEA_API}/repos/${GIT_USER}/${REPO_NAME}" } repo_git_url() { echo "${GITEA_URL}/${GIT_USER}/${REPO_NAME}.git" } remote_repo_reachable() { git ls-remote --exit-code origin HEAD >/dev/null 2>&1 } current_branch_name() { local branch branch=$(git branch --show-current 2>/dev/null || true) if [ -z "$branch" ]; then branch="main" fi echo "$branch" } current_branch_needs_push() { if ! git rev-parse --verify "@{upstream}" >/dev/null 2>&1; then return 0 fi [ "$(git rev-list --count @{upstream}..HEAD)" -gt 0 ] } main_ref_needs_push() { if [ "$AUTO_PUSH_MAIN" != "1" ]; then return 1 fi if ! git rev-parse --verify "refs/remotes/origin/main" >/dev/null 2>&1; then return 0 fi ! git merge-base --is-ancestor "refs/remotes/origin/main" HEAD } needs_push() { current_branch_needs_push || main_ref_needs_push } ensure_remote_repo() { if remote_repo_reachable; then log_info "远程仓库可访问: ${GIT_USER}/${REPO_NAME}" return 0 fi if curl --connect-timeout 5 --max-time 15 -fsS ${GITEA_TOKEN:+-H} ${GITEA_TOKEN:+"Authorization: token ${GITEA_TOKEN}"} "$(repo_api_url)" >/dev/null 2>&1; then log_info "远程仓库已存在: ${GIT_USER}/${REPO_NAME}" return 0 fi if [ -z "$GITEA_TOKEN" ]; then log_error "远程仓库不存在,且未提供 GITEA_TOKEN,无法自动创建" return 1 fi log_info "创建远程仓库: ${GIT_USER}/${REPO_NAME}" local payload payload=$(cat </dev/null log_success "远程仓库创建完成" } run_validations() { log_info "运行校验: lab validate" python3 "$REPO_DIR/scripts/lab/main.py" validate log_info "运行校验: intel validate" python3 "$REPO_DIR/scripts/intel/main.py" validate log_success "校验通过" } acquire_lock() { if mkdir "$LOCK_DIR" 2>/dev/null; then trap 'rm -rf "$LOCK_DIR"' EXIT return 0 fi log_warning "检测到另一个同步任务正在运行,跳过本次执行" return 1 } # 初始化仓库 init_repo() { log_info "初始化 Git 仓库..." if [ -d ".git" ]; then log_warning "Git 仓库已存在" else git init git config user.name "$GIT_USER" git config user.email "$GIT_EMAIL" log_success "Git 仓库初始化完成" fi # 添加远程仓库 if git remote | grep -q "origin"; then git remote set-url origin "$(repo_git_url)" log_info "远程仓库 URL 已更新" else git remote add origin "$(repo_git_url)" log_success "远程仓库已添加" fi ensure_remote_repo # 凭证处理: # 默认不在仓库脚本中写入真实凭证。 # 如需使用 token,请在运行时通过环境变量 GITEA_TOKEN 注入, # 推送时通过临时 HTTP Header 使用,不写入仓库或全局凭证文件。 if [ -n "$GITEA_TOKEN" ]; then log_info "检测到 GITEA_TOKEN 环境变量,将在推送时临时注入 HTTP Header" else log_warning "未提供 GITEA_TOKEN;推送时将使用本机已有认证方式" fi log_success "初始化完成" } # 提交更改 commit_changes() { log_info "检查更改..." if git status --porcelain | grep -q .; then if [ "${SKIP_VALIDATE:-0}" != "1" ]; then run_validations else log_warning "已跳过 validate" fi else log_info "没有需要提交的更改" return 0 fi # 添加所有文件 git add -A # 检查是否有更改 if git diff --staged --quiet; then log_info "没有需要提交的更改" return 0 fi # 生成提交信息 local timestamp=$(date '+%Y-%m-%d %H:%M:%S') local changed_files=$(git diff --staged --name-only | wc -l | tr -d ' ') local commit_msg="更新: ${changed_files} 个文件 - ${timestamp}" # 如果提供了自定义提交信息 local custom_msg="${1:-}" if [ -n "$custom_msg" ]; then commit_msg="$custom_msg" fi git commit -m "$commit_msg" log_success "提交完成: $commit_msg" } # 推送到远程 push_changes() { log_info "推送到远程仓库..." # 获取当前分支 local branch branch=$(current_branch_name) ensure_remote_repo # 推送 if [ -n "$GITEA_TOKEN" ]; then git -c http.extraHeader="Authorization: token ${GITEA_TOKEN}" push -u origin "$branch" else git push -u origin "$branch" fi if [ $? -eq 0 ]; then log_success "推送完成: $branch" else log_error "推送失败" return 1 fi if [ "$AUTO_PUSH_MAIN" = "1" ]; then if git ls-remote --exit-code --heads origin main >/dev/null 2>&1; then git fetch origin main >/dev/null 2>&1 || true if git merge-base --is-ancestor FETCH_HEAD HEAD; then if [ -n "$GITEA_TOKEN" ]; then git -c http.extraHeader="Authorization: token ${GITEA_TOKEN}" push origin HEAD:main else git push origin HEAD:main fi git branch -f main HEAD >/dev/null 2>&1 || true log_success "main 已快进到当前提交" else log_warning "origin/main 不是当前 HEAD 的祖先,跳过 main 快进推送" fi else if [ -n "$GITEA_TOKEN" ]; then git -c http.extraHeader="Authorization: token ${GITEA_TOKEN}" push origin HEAD:main else git push origin HEAD:main fi git branch -f main HEAD >/dev/null 2>&1 || true log_success "main 已创建并指向当前提交" fi fi } # 完整同步 full_sync() { init_repo commit_changes if needs_push; then push_changes else log_info "没有需要推送的提交" fi } auto_sync() { acquire_lock || return 0 full_sync } # 显示帮助 show_help() { echo "用法: $0 [选项]" echo "" echo "选项:" echo " --init 初始化 Git 仓库" echo " --commit 仅提交更改" echo " --push 仅推送到远程" echo " --autosync 定时任务模式: 无并发锁 + 校验 + 提交 + 推送" echo " --ensure 检查远程仓库;不存在则创建" echo " --status 显示仓库状态" echo " --help 显示此帮助" echo "" echo "环境变量:" echo " GITEA_TOKEN 可选;脚本不会自动写入 ~/.git-credentials" echo " GIT_USER 可选;默认 hao" echo " GIT_EMAIL 可选;默认 hao@users.noreply.git.hk.hao.work" echo " REPO_NAME 可选;默认 websafe-kb" echo " REPO_DESC 可选;默认 授权攻防实验与研究知识库" echo "" echo "无参数运行时执行完整同步 (提交 + 推送)" } # 显示状态 show_status() { log_info "仓库状态:" echo "" git status -s echo "" local ahead=$(git rev-list --count @{upstream}..HEAD 2>/dev/null || echo "0") local behind=$(git rev-list --count HEAD..@{upstream} 2>/dev/null || echo "0") log_info "领先远程 $ahead 个提交, 落后 $behind 个提交" } # 主程序 case "${1:-}" in --init) init_repo ;; --commit) commit_changes "$2" ;; --push) push_changes ;; --autosync) auto_sync ;; --ensure) init_repo ;; --status) show_status ;; --help|-h) show_help ;; *) full_sync ;; esac