name: 序列化结构检查 run-name: ${{ gitea.actor }}的序列化结构检查 on: push: workflow_dispatch: env: # serialization-schema-checker 私库坐标:com.codechecker:serialization-schema-checker:1.0.0 SERIALIZATION_SCHEMA_CHECKER_VERSION: "1.0.0" SERIALIZATION_SCHEMA_CHECKER_REPO_URL: "http://192.168.3.25:18081/nexus/repository/maven-releases" jobs: serialization-schema-check: # if: ${{ gitea.ref != 'refs/heads/pre' && gitea.ref != 'refs/heads/dev' && gitea.ref != 'refs/heads/master-2.0' }} if: ${{ gitea.ref != 'refs/heads/pre' && gitea.ref != 'refs/heads/dev' && gitea.ref != 'refs/heads/master-2.0' && !startsWith(gitea.ref, 'refs/heads/release/') }} runs-on: jdk11 steps: # 浅克隆当前 tip;对比基准用 push before(覆盖一次 push 的多 commit 累计 diff) # 不必全量历史:只需能 git show before / after 两边的文件内容 - name: 检出代码 run: | git config --global http.sslVerify false REPO_URL="https://${{ gitea.token }}@git.niujiekeji.com/${{ gitea.repository }}.git" BRANCH="${{ gitea.ref_name }}" NEW_SHA="${{ gitea.sha }}" git clone --depth 1 --single-branch --branch "${BRANCH}" "${REPO_URL}" . git checkout -B "${BRANCH}" "${NEW_SHA}" echo "${NEW_SHA}" > /tmp/serialization-schema-new-sha.txt - name: 检查配置文件 run: | if [ ! -f .gitea/config/serialization-schema-check-config.yaml ]; then echo "错误: 缺少 .gitea/config/serialization-schema-check-config.yaml" exit 1 fi # 顶层总开关 enabled: false 时跳过后续步骤(与 notify.enabled 区分,仅匹配行首) if grep -Eq '^enabled:[[:space:]]*false([[:space:]]|#|$)' .gitea/config/serialization-schema-check-config.yaml; then echo "总开关 enabled=false,跳过序列化结构检查" touch /tmp/serialization-schema-check.skip fi - name: 从 Nexus 私库下载 serialization-schema-checker run: | if [ -f /tmp/serialization-schema-check.skip ]; then echo "总开关已关闭,跳过下载" exit 0 fi GROUP_PATH="com/codechecker/serialization-schema-checker" JAR_NAME="serialization-schema-checker-${SERIALIZATION_SCHEMA_CHECKER_VERSION}.jar" JAR_URL="${SERIALIZATION_SCHEMA_CHECKER_REPO_URL}/${GROUP_PATH}/${SERIALIZATION_SCHEMA_CHECKER_VERSION}/${JAR_NAME}" JAR_PATH="/tmp/${JAR_NAME}" echo "下载: ${JAR_URL}" if command -v curl >/dev/null 2>&1; then curl -fsSL -o "${JAR_PATH}" "${JAR_URL}" elif command -v wget >/dev/null 2>&1; then wget -q -O "${JAR_PATH}" "${JAR_URL}" else echo "错误: Runner 缺少 curl 或 wget,无法从 Nexus 下载 jar" exit 1 fi if [ ! -s "${JAR_PATH}" ]; then echo "错误: 下载失败或文件为空: ${JAR_PATH}" exit 1 fi ls -lh "${JAR_PATH}" - name: 验证 JDK run: | if [ -f /tmp/serialization-schema-check.skip ]; then echo "总开关已关闭,跳过" exit 0 fi echo "Java: $(java -version 2>&1 | head -1)" - name: 执行序列化结构检测 env: # push 前 tip;新分支首次 push 时为全 0。workflow_dispatch 可能为空,下方会回退。 PUSH_BEFORE: ${{ gitea.event.before }} # push 事件携带的 commits 列表(JSON);用于准确统计 commit 数(不受浅克隆影响) PUSH_COMMITS_JSON: ${{ toJson(gitea.event.commits) }} run: | if [ -f /tmp/serialization-schema-check.skip ]; then echo "总开关已关闭,跳过检测" exit 0 fi NEW_SHA=$(cat /tmp/serialization-schema-new-sha.txt) OLD_SHA="${PUSH_BEFORE}" # 新分支首次 push(before 全 0)→ 跳过 if [ -z "$OLD_SHA" ] || echo "$OLD_SHA" | grep -Eq '^0+$'; then # 手动触发:无 before,回退为 HEAD~1(加深 1 层后取父提交) if [ "${{ gitea.event_name }}" = "workflow_dispatch" ]; then git fetch --deepen 1 || true OLD_SHA=$(git rev-parse HEAD~1 2>/dev/null || echo "") if [ -z "$OLD_SHA" ]; then echo "手动触发且无父提交,跳过检测" exit 0 fi echo "workflow_dispatch:无 push before,回退使用 HEAD~1=${OLD_SHA}" else echo "新分支首次 push(before 为空/全 0),跳过检测" exit 0 fi fi if [ "$OLD_SHA" = "$NEW_SHA" ]; then echo "before 与 after 相同,无需检测" exit 0 fi # 确保提交对象可读 ensure_commit() { local sha="$1" if git cat-file -e "${sha}^{commit}" 2>/dev/null; then return 0 fi echo "本地缺少 ${sha},尝试按 SHA 浅取…" if git fetch --depth 1 origin "${sha}"; then git cat-file -e "${sha}^{commit}" 2>/dev/null && return 0 fi for d in 10 30 50 100; do echo "deepen ${d}…" git fetch --deepen "${d}" || true if git cat-file -e "${sha}^{commit}" 2>/dev/null; then return 0 fi done return 1 } # 加深当前 tip 的历史,直到 before 成为 after 的祖先(否则 rev-list 在浅克隆下会少算) ensure_range() { local old="$1" local new="$2" local i=0 while [ "$i" -lt 15 ]; do if git merge-base --is-ancestor "${old}" "${new}" 2>/dev/null; then return 0 fi echo "区间历史不完整,deepen 20…(第 $((i + 1)) 次)" git fetch --deepen 20 || true i=$((i + 1)) done # force-push 等非祖先关系:对象在即可做 git show,但 rev-list 不可靠 if git cat-file -e "${old}^{commit}" 2>/dev/null \ && git cat-file -e "${new}^{commit}" 2>/dev/null; then echo "警告: ${old} 不是 ${new} 的祖先(可能 force-push),commit 计数将改用事件 payload" return 0 fi return 1 } # 优先用 Gitea push 事件的 commits 数组长度(准确且不依赖 shallow) count_from_payload() { if [ -z "${PUSH_COMMITS_JSON:-}" ] || [ "${PUSH_COMMITS_JSON}" = "null" ] || [ "${PUSH_COMMITS_JSON}" = "[]" ]; then return 1 fi # Gitea/GitHub push payload: [{"id":"<40位sha>", ...}, ...] local n n=$(printf '%s' "${PUSH_COMMITS_JSON}" | grep -oE '"id"[[:space:]]*:[[:space:]]*"[0-9a-fA-F]{7,40}"' | wc -l | tr -d ' ') if [ -n "$n" ] && [ "$n" -gt 0 ] 2>/dev/null; then echo "$n" return 0 fi return 1 } if ! ensure_commit "$OLD_SHA"; then echo "错误: 无法获取 push 前 tip ${OLD_SHA},请检查 shallow/权限" exit 2 fi if ! ensure_commit "$NEW_SHA"; then echo "错误: 无法解析当前 tip ${NEW_SHA}" exit 2 fi if ! ensure_range "$OLD_SHA" "$NEW_SHA"; then echo "错误: 无法补齐 ${OLD_SHA}..${NEW_SHA} 历史" exit 2 fi echo "对比区间: ${OLD_SHA} → ${NEW_SHA}" COMMIT_COUNT="" if COMMIT_COUNT=$(count_from_payload); then echo "本次 push 累计 commit 数: ${COMMIT_COUNT}(来自事件 payload)" elif git merge-base --is-ancestor "$OLD_SHA" "$NEW_SHA" 2>/dev/null; then COMMIT_COUNT=$(git rev-list --count "${OLD_SHA}..${NEW_SHA}" 2>/dev/null || echo "?") echo "本次 push 累计 commit 数: ${COMMIT_COUNT}(来自 git rev-list)" else echo "本次 push 累计 commit 数: ?(非祖先关系且无 payload)" fi COMMIT_TIME=$(git log -1 --format=%cd --date=format:'%Y-%m-%d %H:%M:%S' "${NEW_SHA}") # 对比区间仍为 before→after 全量;--responsible-author 只把推送人非 merge 触及的文件作触发集 # (他人改动应已在其各自 push 时告警;纯 merge 推送 → 空触发集 → 不告警) java -jar "/tmp/serialization-schema-checker-${SERIALIZATION_SCHEMA_CHECKER_VERSION}.jar" \ --config .gitea/config/serialization-schema-check-config.yaml \ --repo-root . \ --old-sha "$OLD_SHA" \ --new-sha "$NEW_SHA" \ --branch "${{ gitea.ref_name }}" \ --modifier "${{ gitea.actor }}" \ --responsible-author "${{ gitea.actor }}" \ --modify-time "$COMMIT_TIME"