name: 🔎 校验 on: workflow_run: workflows: ["🧰 准备环境"] types: - completed concurrency: group: checks-${{ github.repository }}-${{ github.event.workflow_run.head_sha }} cancel-in-progress: true # ========================================== # 🔧 配置区域 - 校验参数 # ========================================== env: # Prepare 完成后经 workflow_run 触发;Prepare 已装工具 + fetch 到共享 bare 仓库。 # 本 workflow 只做只读校验(lint + 测试),不推任何分支,故 token 仅用于 # 极端情况下补取缺失提交(正常路径 Prepare 已 fetch 到位)。 # # 安全:ACCESS_TOKEN 不放在 workflow 级 env。lint/test 步骤会执行上游提交 # (PR 场景为不受信输入)的脚本,其环境里不应出现 token;仅下放到 # prepare_worktree 步骤。 WORKSPACE_ROOT: "/data/workspace" SOURCE_BRANCH: ${{ github.event.workflow_run.head_branch }} SOURCE_SHA: ${{ github.event.workflow_run.head_sha }} SOURCE_EVENT: ${{ github.event.workflow_run.event }} COMMIT_LINT_REQUIRE_EMOJI: "1" jobs: checks: # 仅在 Prepare 成功后触发。Prepare 覆盖 push(main) / pull_request / # schedule / dispatch,故 checks 在这些场景均会运行。 # lint 与 test 合并为单 job:共用一个临时 worktree,准备一次,顺序执行 # (lint 在前 fail-fast,失败即不跑 test)。二者跑同一 SOURCE_SHA、皆只读、 # 无依赖,无需拆分为并行 job(那会重复整段 worktree 准备逻辑 + 多一个 worktree)。 if: ${{ github.event.workflow_run.conclusion == 'success' }} name: 🔎 规范检查 + 全量测试 runs-on: standard-ubuntu-22 permissions: contents: read steps: - name: 📥 准备临时 Worktree id: prepare_worktree shell: bash env: ACCESS_TOKEN: ${{ secrets.WORKFLOW }} run: | set -euo pipefail REPO_NAME="${{ github.event.repository.name }}" HEAD_SHA="${SOURCE_SHA}" REPOSITORY_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/repository.git" WORKTREE_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/worktrees/checks-${{ github.run_id }}-${{ github.run_attempt }}" WORKTREE_LOCK="${WORKSPACE_ROOT}/${REPO_NAME}/worktree-admin.lock" case "$HEAD_SHA" in ''|*[!0-9a-fA-F]*) echo "无效的上游提交 SHA: $HEAD_SHA" >&2 exit 1 ;; esac if [ ! -d "$REPOSITORY_DIR" ] || \ [ "$(git --git-dir="$REPOSITORY_DIR" rev-parse --is-bare-repository 2>/dev/null)" != "true" ]; then echo "Prepare 未创建有效共享 bare 仓库: $REPOSITORY_DIR" >&2 exit 1 fi # 正常路径 Prepare 已把上游提交 fetch 到位;极端情况下(bare 仓库被清理 # 重建、或 Prepare 与本 run 之间发生 prune)补取一次,token 仅用于此。 if ! git --git-dir="$REPOSITORY_DIR" cat-file -e "${HEAD_SHA}^{commit}" 2>/dev/null; then git \ -c credential.helper= \ -c 'credential.helper=!f() { test "$1" = get || exit 0; printf "%s\n" username=oauth2; printf "%s\n" "password=${ACCESS_TOKEN}"; }; f' \ -c credential.interactive=never \ --git-dir="$REPOSITORY_DIR" fetch --prune --force origin \ '+refs/heads/*:refs/remotes/origin/*' || true fi if ! git --git-dir="$REPOSITORY_DIR" cat-file -e "${HEAD_SHA}^{commit}"; then echo "共享仓库中不存在上游提交: $HEAD_SHA(事件: $SOURCE_EVENT)" >&2 exit 1 fi exec 9>"$WORKTREE_LOCK" flock 9 # 回收陈旧临时 worktree(超过 24h 的 checks-* 目录)。worktree prune 只清理 # 指向已消失目录的 metadata,不删仍存在的陈旧目录,故按 mtime 主动回收。 worktrees_root="$(dirname "$WORKTREE_DIR")" if [ -d "$worktrees_root" ]; then find "$worktrees_root" -mindepth 1 -maxdepth 1 -type d -name 'checks-*' \ -mmin +1440 -print 2>/dev/null | while IFS= read -r stale; do [ "$stale" = "$WORKTREE_DIR" ] && continue git --git-dir="$REPOSITORY_DIR" worktree remove --force "$stale" 2>/dev/null || rm -rf "$stale" done fi git --git-dir="$REPOSITORY_DIR" worktree prune if [ -e "$WORKTREE_DIR" ]; then git --git-dir="$REPOSITORY_DIR" worktree remove --force "$WORKTREE_DIR" 2>/dev/null || rm -rf "$WORKTREE_DIR" fi mkdir -p "$(dirname "$WORKTREE_DIR")" git --git-dir="$REPOSITORY_DIR" worktree add --detach "$WORKTREE_DIR" "$HEAD_SHA" flock -u 9 git -C "$WORKTREE_DIR" reset --hard "$HEAD_SHA" git -C "$WORKTREE_DIR" clean -ffdx echo "REPOSITORY_DIR=$REPOSITORY_DIR" >> "$GITHUB_ENV" echo "REPO_DIR=$WORKTREE_DIR" >> "$GITHUB_ENV" echo "✅ Worktree 就绪: $WORKTREE_DIR @ $HEAD_SHA" - name: 🔍 校验提交信息 shell: bash run: | set -euo pipefail cd "$REPO_DIR" # 注:经 workflow_run 触发,事件 payload 为 workflow_run,不含 PR title; # lint 脚本会回落到 head commit message 校验(PR 标题本身不再校验)。 echo "🔍 检查 commit message 规范(上游事件: $SOURCE_EVENT)..." python3 .gitea/ci/commit_message_lint.py echo "✅ Lint 检查通过" - name: 🧪 运行全量测试 shell: bash run: | set -euo pipefail echo "========================================" echo "🐍 Python 测试(上游事件: $SOURCE_EVENT)" echo "========================================" cd "$REPO_DIR" echo "📋 覆盖:CLI、subtree/snapshot 部署路线、模板同步、文档一致性" python3 -m unittest discover -s test -p "test_*.py" -v echo "✅ Python 测试通过" echo "========================================" echo "🔗 文档链接检查" echo "========================================" python3 test/integration/check_doc_links.py echo "✅ 文档链接检查通过" echo "🎉 所有测试完成" - name: 🧹 清理临时 Worktree if: always() shell: bash run: | set -uo pipefail [ -n "${REPOSITORY_DIR:-}" ] || exit 0 [ -n "${REPO_DIR:-}" ] || exit 0 WORKTREE_LOCK="$(dirname "$REPOSITORY_DIR")/worktree-admin.lock" exec 9>"$WORKTREE_LOCK" flock 9 git --git-dir="$REPOSITORY_DIR" worktree remove --force "$REPO_DIR" 2>/dev/null || rm -rf "$REPO_DIR" git --git-dir="$REPOSITORY_DIR" worktree prune flock -u 9