Files
macos-tools/gitea/gitea-github-sync.sh
2025-12-24 13:42:39 +08:00

326 lines
8.8 KiB
Bash
Executable File

#!/bin/bash
#
# Gitea to GitHub Sync Script
# 自动同步 Gitea 仓库到 GitHub
#
# 使用方式:
# ./gitea-github-sync.sh sync # 执行同步
# ./gitea-github-sync.sh test # 测试配置
# ./gitea-github-sync.sh list # 列出配置的仓库
#
set -euo pipefail
# ==========================================
# 配置变量
# ==========================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/sync-config.yaml"
LOG_DIR="${HOME}/ops/logs"
LOG_FILE="${LOG_DIR}/gitea-github-sync.log"
TEMP_DIR="/tmp/gitea-github-sync"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# ==========================================
# 日志函数
# ==========================================
log() {
local level=$1
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[${timestamp}] [${level}] ${message}" | tee -a "${LOG_FILE}"
}
log_info() {
echo -e "${BLUE}[INFO]${NC} $*"
log "INFO" "$*"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $*"
log "SUCCESS" "$*"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $*"
log "WARNING" "$*"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $*"
log "ERROR" "$*"
}
# ==========================================
# 初始化
# ==========================================
init() {
# 创建日志目录
mkdir -p "${LOG_DIR}"
mkdir -p "${TEMP_DIR}"
# 检查配置文件
if [[ ! -f "${CONFIG_FILE}" ]]; then
log_error "配置文件不存在: ${CONFIG_FILE}"
log_info "请先创建配置文件,参考 sync-config.yaml.example"
exit 1
fi
# 检查必要的命令
local required_commands=("git" "yq")
for cmd in "${required_commands[@]}"; do
if ! command -v "${cmd}" &> /dev/null; then
log_error "缺少必要命令: ${cmd}"
if [[ "${cmd}" == "yq" ]]; then
log_info "安装 yq: brew install yq"
fi
exit 1
fi
done
}
# ==========================================
# 读取配置
# ==========================================
read_config() {
local key=$1
yq eval "${key}" "${CONFIG_FILE}"
}
# ==========================================
# 创建 GitHub 仓库
# ==========================================
create_github_repo() {
local repo_name=$1
local org_name=$2
local github_token=$3
# 检查仓库是否已存在
local check_url="https://api.github.com/repos/${org_name}/${repo_name}"
if curl -s -H "Authorization: token ${github_token}" "${check_url}" | grep -q '"id"'; then
log_info "GitHub 仓库已存在: ${repo_name}"
return 0
fi
# 创建仓库
log_info "创建 GitHub 仓库: ${repo_name}"
local create_url="https://api.github.com/orgs/${org_name}/repos"
local response=$(curl -s -X POST -H "Authorization: token ${github_token}" \
-H "Content-Type: application/json" \
-d "{\"name\":\"${repo_name}\",\"private\":true,\"auto_init\":false}" \
"${create_url}")
if echo "${response}" | grep -q '"id"'; then
log_success "成功创建 GitHub 仓库: ${repo_name}"
return 0
else
log_error "创建 GitHub 仓库失败: ${repo_name}"
echo "${response}" >> "${LOG_FILE}"
return 1
fi
}
# ==========================================
# 同步单个仓库
# ==========================================
sync_repo() {
local repo_name=$1
local gitea_url=$2
local github_url=$3
local repo_dir="${TEMP_DIR}/${repo_name}"
log_info "开始同步仓库: ${repo_name}"
# 清理旧的临时目录
if [[ -d "${repo_dir}" ]]; then
rm -rf "${repo_dir}"
fi
# 克隆或更新 Gitea 仓库
if git clone --mirror "${gitea_url}" "${repo_dir}" 2>&1 | tee -a "${LOG_FILE}"; then
log_success "成功克隆 Gitea 仓库: ${repo_name}"
else
log_error "克隆 Gitea 仓库失败: ${repo_name}"
return 1
fi
# 从配置读取 GitHub 信息
local github_org=$(yq eval '.github.org // "BrunoGaoSZ"' "${CONFIG_FILE}")
local github_token=$(yq eval '.github.token // ""' "${CONFIG_FILE}")
# 如果没有配置 token,尝试从环境变量读取
if [[ -z "${github_token}" ]]; then
github_token="${GITHUB_TOKEN:-}"
fi
# 创建 GitHub 仓库(如果需要)
if [[ -n "${github_token}" ]]; then
create_github_repo "${repo_name}" "${github_org}" "${github_token}" || true
fi
# 进入仓库目录
cd "${repo_dir}"
# 添加 GitHub remote
if git remote add github "${github_url}" 2>&1 | tee -a "${LOG_FILE}"; then
log_info "添加 GitHub remote"
else
log_info "GitHub remote 已存在"
fi
# 推送到 GitHub
if git push --mirror github 2>&1 | tee -a "${LOG_FILE}"; then
log_success "成功推送到 GitHub: ${repo_name}"
else
log_error "推送到 GitHub 失败: ${repo_name}"
return 1
fi
# 清理临时目录
cd "${SCRIPT_DIR}"
rm -rf "${repo_dir}"
log_success "仓库同步完成: ${repo_name}"
return 0
}
# ==========================================
# 同步所有仓库
# ==========================================
sync_all() {
log_info "========================================"
log_info "开始 Gitea → GitHub 同步任务"
log_info "时间: $(date '+%Y-%m-%d %H:%M:%S')"
log_info "========================================"
# 读取仓库数量
local repo_count=$(yq eval '.repositories | length' "${CONFIG_FILE}")
log_info "配置了 ${repo_count} 个仓库需要同步"
local success_count=0
local fail_count=0
# 遍历所有仓库
for ((i=0; i<repo_count; i++)); do
local repo_name=$(yq eval ".repositories[$i].name" "${CONFIG_FILE}")
local gitea_url=$(yq eval ".repositories[$i].gitea" "${CONFIG_FILE}")
local github_url=$(yq eval ".repositories[$i].github" "${CONFIG_FILE}")
log_info "----------------------------------------"
log_info "[$((i+1))/${repo_count}] 同步: ${repo_name}"
if sync_repo "${repo_name}" "${gitea_url}" "${github_url}"; then
((success_count++))
else
((fail_count++))
fi
done
log_info "========================================"
log_info "同步任务完成"
log_success "成功: ${success_count} 个仓库"
if [[ ${fail_count} -gt 0 ]]; then
log_error "失败: ${fail_count} 个仓库"
fi
log_info "========================================"
# 返回状态码
if [[ ${fail_count} -gt 0 ]]; then
return 1
else
return 0
fi
}
# ==========================================
# 测试配置
# ==========================================
test_config() {
log_info "测试配置文件..."
# 验证 YAML 格式
if ! yq eval '.' "${CONFIG_FILE}" > /dev/null 2>&1; then
log_error "配置文件格式错误"
exit 1
fi
log_success "配置文件格式正确"
# 读取仓库数量
local repo_count=$(yq eval '.repositories | length' "${CONFIG_FILE}")
log_info "找到 ${repo_count} 个仓库配置"
# 列出所有仓库
for ((i=0; i<repo_count; i++)); do
local repo_name=$(yq eval ".repositories[$i].name" "${CONFIG_FILE}")
local gitea_url=$(yq eval ".repositories[$i].gitea" "${CONFIG_FILE}")
local github_url=$(yq eval ".repositories[$i].github" "${CONFIG_FILE}")
echo ""
log_info "仓库 #$((i+1)): ${repo_name}"
echo " Gitea: ${gitea_url}"
echo " GitHub: ${github_url}"
done
log_success "配置测试完成"
}
# ==========================================
# 列出仓库
# ==========================================
list_repos() {
local repo_count=$(yq eval '.repositories | length' "${CONFIG_FILE}")
echo ""
echo "配置的同步仓库 (共 ${repo_count} 个):"
echo "========================================"
for ((i=0; i<repo_count; i++)); do
local repo_name=$(yq eval ".repositories[$i].name" "${CONFIG_FILE}")
echo "$((i+1)). ${repo_name}"
done
echo "========================================"
}
# ==========================================
# 主函数
# ==========================================
main() {
local command=${1:-sync}
case "${command}" in
sync)
init
sync_all
;;
test)
init
test_config
;;
list)
init
list_repos
;;
*)
echo "使用方式: $0 {sync|test|list}"
echo ""
echo "命令说明:"
echo " sync - 执行同步任务"
echo " test - 测试配置文件"
echo " list - 列出配置的仓库"
exit 1
;;
esac
}
# 执行主函数
main "$@"