diff --git a/.gitea/workflows/mirror-to-gitee.yml b/.gitea/workflows/mirror-to-gitee.yml new file mode 100644 index 0000000..ec84ab7 --- /dev/null +++ b/.gitea/workflows/mirror-to-gitee.yml @@ -0,0 +1,101 @@ +name: mirror-to-gitee + +# 把本仓(开源项目门户站)镜像到 Gitee 企业仓 /<同名仓库>。 +# 触发:main 分支推送、v* tag 推送、手动;另有每日兜底重试。 +on: + push: + branches: + - main + tags: + - 'v*' + schedule: + # Gitea Actions 的 cron 用 UTC:18:30 UTC = 次日 02:30 北京时间 + - cron: '30 18 * * *' + workflow_dispatch: + +concurrency: + group: mirror-${{ github.ref }} + cancel-in-progress: true + +jobs: + mirror: + runs-on: host + steps: + - name: 同步到 Gitee + env: + SRC_TOKEN: ${{ secrets.SRC_GIT_TOKEN }} + DST_TOKEN: ${{ secrets.GITEE_TOKEN }} + DST_OWNER: ${{ secrets.GITEE_OWNER }} + DST_USER: ${{ secrets.GITEE_USER }} + run: | + set -u + + # 仓库名取当前仓;取不到时回退到单仓名 + REPO_NAME="${GITHUB_REPOSITORY#*/}" + if [ -z "${REPO_NAME:-}" ] || [ "$REPO_NAME" = "${GITHUB_REPOSITORY:-}" ]; then + REPO_NAME="iotlcm" + fi + + SRC="git.5iot.com/iotlcm-public/${REPO_NAME}.git" + DST="gitee.com/${DST_OWNER}/${REPO_NAME}.git" + echo "源:$SRC" + echo "目标:$DST" + + # Gitee 不支持 push 建仓,目标仓不存在时先按企业 API 创建(已存在会报 400,忽略) + if ! git ls-remote "https://${DST_USER}:${DST_TOKEN}@${DST}" >/dev/null 2>&1; then + echo "目标仓不存在,尝试创建 ${DST_OWNER}/${REPO_NAME}" + curl -sS -X POST \ + "https://gitee.com/api/v5/enterprises/${DST_OWNER}/repos?access_token=${DST_TOKEN}" \ + -H 'Content-Type: application/json' \ + -d "{\"name\":\"${REPO_NAME}\",\"auto_init\":false,\"description\":\"iotLCM 物联网卡全生命周期管理平台\"}" \ + -o /tmp/repo-create.json -w '建仓 HTTP %{http_code}\n' || true + head -c 300 /tmp/repo-create.json 2>/dev/null; echo + else + echo "目标仓已存在" + fi + + WORK=$(mktemp -d) + trap 'rm -rf "$WORK"' EXIT + + git clone --mirror "https://jenkins:${SRC_TOKEN}@${SRC%/}" "$WORK/repo.git" + cd "$WORK/repo.git" + echo "本地 ref:$(git for-each-ref --format='%(refname:short)' | tr '\n' ' ')" + + # Gitee 在推送密集时会返回 429 Pending queue overflowed / 408 Authorization server timeout, + # 属服务端限流,退避重试即可。 + attempt=0 + while [ "$attempt" -lt 5 ]; do + attempt=$((attempt + 1)) + echo "----- 推送尝试 $attempt/5" + if git push --mirror "https://${DST_USER}:${DST_TOKEN}@${DST}" 2>&1; then + echo "✅ 已同步到 Gitee" + + # 推送后校验:远端 main 与 tag 集合必须与本地一致 + # (避免“绿了但其实没推上去” —— Gitee 限流时容易发生) + LOCAL_MAIN=$(git rev-parse refs/heads/main 2>/dev/null || true) + REMOTE_MAIN=$(git ls-remote "https://${DST_USER}:${DST_TOKEN}@${DST}" refs/heads/main | awk '{print $1}') + LOCAL_TAGS=$(git for-each-ref --format='%(refname:short)' refs/tags | sort | tr '\n' ' ') + REMOTE_TAGS=$(git ls-remote --tags "https://${DST_USER}:${DST_TOKEN}@${DST}" \ + | awk '{print $2}' | sed 's#refs/tags/##' | grep -v '\^{}$' | sort | tr '\n' ' ') + echo "本地 main: ${LOCAL_MAIN:-<无>}" + echo "远端 main: ${REMOTE_MAIN:-<无>}" + echo "本地 tag: ${LOCAL_TAGS:-<无>}" + echo "远端 tag: ${REMOTE_TAGS:-<无>}" + if [ -n "$LOCAL_MAIN" ] && [ "$LOCAL_MAIN" != "$REMOTE_MAIN" ]; then + echo "❌ 校验失败:远端 main 与本地不一致" + exit 1 + fi + if [ "${LOCAL_TAGS% }" != "${REMOTE_TAGS% }" ]; then + echo "❌ 校验失败:远端 tag 与本地不一致" + exit 1 + fi + echo "✅ 校验通过:Gitee 端 main 与 tag 均已对齐" + exit 0 + fi + wait_s=$((attempt * 30)) + echo "推送失败,${wait_s}s 后重试" + sleep "$wait_s" + done + + echo "❌ 连续 5 次推送失败,请检查 Gitee 状态或令牌" + exit 1