326 lines
8.8 KiB
Bash
Executable File
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 "$@"
|