🔧 chore(workflows): migrate to prepare + worktree model and merge checks
- add prepare.yml: install tools + maintain shared bare repo, anchors the workflow_run chain - add checks.yml: merge lint + full test suite into a single workflow_run consumer job - remove standards-check.yml and test.yml (superseded by checks.yml) - convert sync-tsl-playbook and update-thirdparty-skills to worktree-model consumers - unify workflow/job/step display names and emoji
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
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
|
||||
@@ -0,0 +1,212 @@
|
||||
name: 🧰 准备环境
|
||||
|
||||
on:
|
||||
push:
|
||||
# 主分支:下游 sync / thirdparty 消费者只关心 main。
|
||||
# 其它分支(含 ci bot 推送的 tsl-playbook / thirdparty/skill 分支)跑 Prepare 纯属空耗,故在此过滤。
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
# PR:为下游 checks 消费者(standards + tests)预备 bare 仓库。
|
||||
# PR 的 github.sha 是 Gitea 生成的 merge commit,不在 refs/heads/*,
|
||||
# 故下面的 fetch 额外取 PR ref 并按事件类型选校验目标。
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# thirdparty 快照每日轮询:Prepare 定时成功后,update-thirdparty-skills 通过 workflow_run 触发。
|
||||
- cron: "17 3 * * *"
|
||||
|
||||
concurrency:
|
||||
group: prepare-${{ github.repository }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
ACCESS_TOKEN: ${{ secrets.WORKFLOW }}
|
||||
WORKSPACE_ROOT: "/data/workspace"
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: standard-ubuntu-22
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: 🔧 检查并安装共享工具
|
||||
id: tools
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
declare -A package_for=(
|
||||
[git]="git"
|
||||
[python3]="python3"
|
||||
[curl]="curl"
|
||||
[flock]="util-linux"
|
||||
[realpath]="coreutils"
|
||||
)
|
||||
required_commands=(git python3 curl flock realpath)
|
||||
missing_commands=()
|
||||
missing_packages=()
|
||||
declare -A seen_packages=()
|
||||
|
||||
for command in "${required_commands[@]}"; do
|
||||
if ! command -v "$command" >/dev/null 2>&1; then
|
||||
package="${package_for[$command]}"
|
||||
missing_commands+=("$command")
|
||||
if [[ -z "${seen_packages[$package]+x}" ]]; then
|
||||
missing_packages+=("$package")
|
||||
seen_packages[$package]=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
installed=false
|
||||
if [ "${#missing_packages[@]}" -gt 0 ]; then
|
||||
echo "缺失命令: ${missing_commands[*]}"
|
||||
echo "准备安装: ${missing_packages[*]}"
|
||||
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
APT=(apt-get)
|
||||
elif command -v sudo >/dev/null 2>&1; then
|
||||
APT=(sudo apt-get)
|
||||
else
|
||||
echo "无法安装缺失命令;当前用户不是 root 且没有 sudo" >&2
|
||||
echo "命令: ${missing_commands[*]}" >&2
|
||||
echo "包: ${missing_packages[*]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"${APT[@]}" update -qq
|
||||
"${APT[@]}" install -y -qq "${missing_packages[@]}"
|
||||
installed=true
|
||||
fi
|
||||
|
||||
for command in "${required_commands[@]}"; do
|
||||
if ! command -v "$command" >/dev/null 2>&1; then
|
||||
echo "安装后仍找不到命令: $command (${package_for[$command]})" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
{
|
||||
echo "## Prepare 环境"
|
||||
echo ""
|
||||
echo "| 命令 | 版本 |"
|
||||
echo "| --- | --- |"
|
||||
for command in "${required_commands[@]}"; do
|
||||
version_output=$("$command" --version 2>&1 || true)
|
||||
version_line=${version_output%%$'\n'*}
|
||||
version_line=${version_line//|/\\|}
|
||||
printf '| `%s` | `%s` |\n' "$command" "$version_line"
|
||||
done
|
||||
echo ""
|
||||
if [ "$installed" = true ]; then
|
||||
echo "安装的包:\`${missing_packages[*]}\`"
|
||||
else
|
||||
echo "安装的包:无,所有命令已存在。"
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
echo "installed=$installed" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 📥 准备共享 bare 仓库
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
REPO_NAME="${{ github.event.repository.name }}"
|
||||
REMOTE_URL="${{ github.server_url }}/${{ github.repository }}.git"
|
||||
EVENT_NAME="${{ github.event_name }}"
|
||||
REPOSITORY_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/repository.git"
|
||||
|
||||
# 校验目标 SHA 因事件而异:
|
||||
# - pull_request:github.sha 是 Gitea 生成的 merge commit,不在
|
||||
# refs/heads/* 里,直接校验会失败;改用 PR head SHA,并在下方额外
|
||||
# fetch PR ref 把 head 提交拉进 bare 仓库。下游 workflow_run 消费者
|
||||
# 读到的 head_sha 也正是这个 PR head,两者一致。
|
||||
# - 其它事件(push / schedule / dispatch):沿用 github.sha。
|
||||
if [ "$EVENT_NAME" = "pull_request" ]; then
|
||||
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
else
|
||||
HEAD_SHA="${{ github.sha }}"
|
||||
PR_NUMBER=""
|
||||
fi
|
||||
|
||||
case "$HEAD_SHA" in
|
||||
''|*[!0-9a-fA-F]*)
|
||||
echo "无效的触发提交 SHA: $HEAD_SHA" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
case "$REMOTE_URL" in
|
||||
https://*) ;;
|
||||
*)
|
||||
echo "拒绝通过非 HTTPS remote 使用 WORKFLOW token: $REMOTE_URL" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
if [ -z "$ACCESS_TOKEN" ]; then
|
||||
echo "未配置 WORKFLOW secret,无法读取仓库" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$REPOSITORY_DIR")"
|
||||
if [ ! -e "$REPOSITORY_DIR" ]; then
|
||||
git init --bare "$REPOSITORY_DIR"
|
||||
else
|
||||
if [ ! -d "$REPOSITORY_DIR" ]; then
|
||||
echo "共享仓库路径存在但不是目录: $REPOSITORY_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! is_bare=$(git --git-dir="$REPOSITORY_DIR" rev-parse --is-bare-repository 2>/dev/null) || \
|
||||
[ "$is_bare" != "true" ]; then
|
||||
echo "共享仓库路径不是有效 bare 仓库: $REPOSITORY_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if git --git-dir="$REPOSITORY_DIR" remote get-url origin >/dev/null 2>&1; then
|
||||
git --git-dir="$REPOSITORY_DIR" remote set-url origin "$REMOTE_URL"
|
||||
else
|
||||
git --git-dir="$REPOSITORY_DIR" remote add origin "$REMOTE_URL"
|
||||
fi
|
||||
|
||||
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 --prune-tags --tags --force origin \
|
||||
'+refs/heads/*:refs/remotes/origin/*'
|
||||
|
||||
# PR head 不在 refs/heads/*,按需补取 PR ref 把 head 提交拉进 bare 仓库。
|
||||
# 先试 PR head SHA 是否已在库(同仓库分支 PR 的 head 常已随 refs/heads/*
|
||||
# 到位),不在再按 PR 号 fetch Gitea 的 refs/pull/<n>/head。
|
||||
if [ -n "$PR_NUMBER" ] && \
|
||||
! 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 --force origin \
|
||||
"+refs/pull/${PR_NUMBER}/head:refs/remotes/origin/pull/${PR_NUMBER}/head" || true
|
||||
fi
|
||||
|
||||
if ! git --git-dir="$REPOSITORY_DIR" cat-file -e "${HEAD_SHA}^{commit}"; then
|
||||
echo "共享仓库中不存在触发提交: $HEAD_SHA(事件: $EVENT_NAME)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
resolved_sha=$(git --git-dir="$REPOSITORY_DIR" rev-parse "${HEAD_SHA}^{commit}")
|
||||
{
|
||||
echo ""
|
||||
echo "## Prepare 仓库"
|
||||
echo ""
|
||||
echo "- bare 仓库:\`$REPOSITORY_DIR\`"
|
||||
echo "- 触发提交:\`$resolved_sha\`"
|
||||
echo "- 远端:\`$REMOTE_URL\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
echo "Prepare 完成: $REPOSITORY_DIR @ $resolved_sha"
|
||||
@@ -1,75 +0,0 @@
|
||||
name: ✅ Standards Check
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch: # 允许手动触发
|
||||
|
||||
concurrency:
|
||||
group: standards-${{ github.repository }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# ==========================================
|
||||
# 🔧 配置区域 - 标准校验参数
|
||||
# ==========================================
|
||||
env:
|
||||
COMMIT_LINT_REQUIRE_EMOJI: "1"
|
||||
WORKSPACE_DIR: "/home/workspace"
|
||||
|
||||
jobs:
|
||||
commit-message:
|
||||
name: 🔍 Commit message lint
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: 📥 准备仓库
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "========================================"
|
||||
echo "📦 准备仓库到 WORKSPACE_DIR"
|
||||
echo "========================================"
|
||||
|
||||
REPO_NAME="${{ github.event.repository.name }}"
|
||||
TOKEN="${{ secrets.WORKFLOW }}"
|
||||
TARGET_SHA="${{ github.sha }}"
|
||||
TARGET_REF="${{ github.ref }}"
|
||||
TARGET_REF_NAME="${{ github.ref_name }}"
|
||||
|
||||
mkdir -p "$WORKSPACE_DIR"
|
||||
REPO_DIR="$(mktemp -d "$WORKSPACE_DIR/${REPO_NAME}.XXXXXX")"
|
||||
|
||||
if [ -n "$TOKEN" ]; then
|
||||
REPO_URL="https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git"
|
||||
else
|
||||
REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||
fi
|
||||
|
||||
git clone "$REPO_URL" "$REPO_DIR"
|
||||
|
||||
if git -C "$REPO_DIR" cat-file -e "${TARGET_SHA}^{commit}" 2>/dev/null; then
|
||||
git -C "$REPO_DIR" checkout -f "$TARGET_SHA"
|
||||
else
|
||||
if [ -n "$TARGET_REF" ]; then
|
||||
git -C "$REPO_DIR" fetch origin "$TARGET_REF"
|
||||
git -C "$REPO_DIR" checkout -f FETCH_HEAD
|
||||
else
|
||||
git -C "$REPO_DIR" checkout -f "$TARGET_REF_NAME"
|
||||
fi
|
||||
fi
|
||||
|
||||
git config --global --add safe.directory "$REPO_DIR"
|
||||
echo "REPO_DIR=$REPO_DIR" >> "$GITHUB_ENV"
|
||||
echo "✅ 仓库准备完成"
|
||||
|
||||
- name: 🔍 Lint commit message / PR title
|
||||
run: |
|
||||
cd "$REPO_DIR"
|
||||
echo "🔍 检查 commit message 和 PR title 规范..."
|
||||
python3 .gitea/ci/commit_message_lint.py
|
||||
echo "✅ Lint 检查通过"
|
||||
|
||||
- name: 🧹 清理临时仓库
|
||||
if: always()
|
||||
run: |
|
||||
rm -rf "$REPO_DIR"
|
||||
@@ -1,78 +1,130 @@
|
||||
name: 📦 Sync TSL Playbook
|
||||
name: 📦 发布 TSL Playbook
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ["🧰 准备环境"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
concurrency:
|
||||
group: sync-tsl-playbook-${{ github.repository }}
|
||||
cancel-in-progress: true
|
||||
cancel-in-progress: false
|
||||
|
||||
# ==========================================
|
||||
# 🔧 配置区域
|
||||
# ==========================================
|
||||
env:
|
||||
WORKSPACE_DIR: "/home/workspace"
|
||||
# Prepare 维护共享仓库并读取代码;此处 token 仅用于推送。
|
||||
ACCESS_TOKEN: ${{ secrets.WORKFLOW }}
|
||||
|
||||
# ===== 工作区配置 =====
|
||||
WORKSPACE_ROOT: "/data/workspace"
|
||||
WORKSPACE_SLOT: "tsl-playbook"
|
||||
SOURCE_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
SOURCE_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||
|
||||
# ===== 分支与构建配置 =====
|
||||
TARGET_BRANCH: "tsl-playbook"
|
||||
BUILD_SCRIPT: "scripts/build_tsl_playbook.py"
|
||||
|
||||
# ===== Git 配置 =====
|
||||
GIT_USER_NAME: "ci[bot]"
|
||||
GIT_USER_EMAIL: "ci[bot]@tinysoft.com.cn"
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: 📦 Build and publish tsl-playbook
|
||||
runs-on: ubuntu-22.04
|
||||
# 仅在 Prepare 于主分支成功后触发。
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' && (github.event.workflow_run.head_branch == 'main' || github.event.workflow_run.head_branch == 'master') }}
|
||||
name: 🔨 构建并发布 tsl-playbook
|
||||
runs-on: standard-ubuntu-22
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: 📥 准备仓库
|
||||
- name: 🔐 验证 Token 配置
|
||||
shell: bash
|
||||
env:
|
||||
WORKFLOW: ${{ secrets.WORKFLOW }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "$ACCESS_TOKEN" ]; then
|
||||
echo "❌ 未配置 WORKFLOW secret,无法推送 $TARGET_BRANCH" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Token 已配置"
|
||||
echo "🌿 上游分支: ${{ env.SOURCE_BRANCH }}"
|
||||
echo "📝 上游提交: ${{ env.SOURCE_SHA }}"
|
||||
|
||||
echo "========================================"
|
||||
echo "📦 准备仓库到 WORKSPACE_DIR"
|
||||
echo "========================================"
|
||||
- name: 📥 对齐 Worktree
|
||||
id: prepare_worktree
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
REPO_NAME="${{ github.event.repository.name }}"
|
||||
mkdir -p "$WORKSPACE_DIR"
|
||||
REPO_DIR="$(mktemp -d "$WORKSPACE_DIR/${REPO_NAME}.XXXXXX")"
|
||||
export GIT_TERMINAL_PROMPT=0
|
||||
echo "GIT_TERMINAL_PROMPT=0" >> "$GITHUB_ENV"
|
||||
HEAD_SHA="${{ env.SOURCE_SHA }}"
|
||||
REPOSITORY_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/repository.git"
|
||||
WORKTREE_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/worktrees/${WORKSPACE_SLOT}"
|
||||
WORKTREE_LOCK="${WORKSPACE_ROOT}/${REPO_NAME}/worktree-admin.lock"
|
||||
|
||||
if [ -n "${WORKFLOW:-}" ]; then
|
||||
ASKPASS_SCRIPT="$(mktemp "$WORKSPACE_DIR/git-askpass.XXXXXX")"
|
||||
cat > "$ASKPASS_SCRIPT" <<'EOF'
|
||||
#!/usr/bin/env sh
|
||||
case "$1" in
|
||||
*Username*) printf '%s\n' oauth2 ;;
|
||||
*Password*) printf '%s\n' "$WORKFLOW" ;;
|
||||
*) printf '\n' ;;
|
||||
case "$HEAD_SHA" in
|
||||
''|*[!0-9a-fA-F]*)
|
||||
echo "无效的上游提交 SHA: $HEAD_SHA" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod 700 "$ASKPASS_SCRIPT"
|
||||
export GIT_ASKPASS="$ASKPASS_SCRIPT"
|
||||
echo "GIT_ASKPASS=$ASKPASS_SCRIPT" >> "$GITHUB_ENV"
|
||||
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
|
||||
if ! git --git-dir="$REPOSITORY_DIR" cat-file -e "${HEAD_SHA}^{commit}"; then
|
||||
echo "共享仓库中不存在上游提交: $HEAD_SHA" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||
if [ ! -f "$WORKTREE_DIR/.git" ]; then
|
||||
exec 9>"$WORKTREE_LOCK"
|
||||
flock 9
|
||||
|
||||
git clone "$REPO_URL" "$REPO_DIR"
|
||||
if [ ! -f "$WORKTREE_DIR/.git" ]; then
|
||||
if [ -e "$WORKTREE_DIR" ]; then
|
||||
echo "发布 Worktree 路径已存在但不是 linked worktree: $WORKTREE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$(dirname "$WORKTREE_DIR")"
|
||||
git --git-dir="$REPOSITORY_DIR" worktree add --detach "$WORKTREE_DIR" "$HEAD_SHA"
|
||||
fi
|
||||
|
||||
git -C "$REPO_DIR" fetch origin main
|
||||
git -C "$REPO_DIR" checkout -B main origin/main
|
||||
flock -u 9
|
||||
fi
|
||||
|
||||
git config --global --add safe.directory "$REPO_DIR"
|
||||
echo "REPO_DIR=$REPO_DIR" >> "$GITHUB_ENV"
|
||||
echo "✅ 仓库准备完成"
|
||||
common_dir=$(git -C "$WORKTREE_DIR" rev-parse --path-format=absolute --git-common-dir)
|
||||
if [ "$common_dir" != "$(realpath "$REPOSITORY_DIR")" ]; then
|
||||
echo "发布 Worktree 不属于共享仓库: $WORKTREE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 📦 Build and publish tsl-playbook
|
||||
git -C "$WORKTREE_DIR" checkout --detach --force "$HEAD_SHA"
|
||||
git -C "$WORKTREE_DIR" reset --hard "$HEAD_SHA"
|
||||
git -C "$WORKTREE_DIR" clean -ffdx
|
||||
|
||||
actual_sha=$(git -C "$WORKTREE_DIR" rev-parse HEAD)
|
||||
if [ "$actual_sha" != "$HEAD_SHA" ]; then
|
||||
echo "发布 Worktree 提交不匹配: 期望 $HEAD_SHA,实际 $actual_sha" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "REPOSITORY_DIR=$REPOSITORY_DIR" >> "$GITHUB_ENV"
|
||||
echo "REPO_DIR=$WORKTREE_DIR" >> "$GITHUB_ENV"
|
||||
echo "REPO_NAME=$REPO_NAME" >> "$GITHUB_ENV"
|
||||
echo "发布 Worktree: $WORKTREE_DIR @ $actual_sha"
|
||||
|
||||
- name: 📦 构建并发布 tsl-playbook
|
||||
shell: bash
|
||||
env:
|
||||
WORKFLOW: ${{ secrets.WORKFLOW }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR="${REPO_DIR:-$(pwd)}"
|
||||
REPO_DIR="${REPO_DIR}"
|
||||
REPOSITORY_DIR="${REPOSITORY_DIR}"
|
||||
TARGET_BRANCH="${TARGET_BRANCH:-tsl-playbook}"
|
||||
BUILD_SCRIPT="${BUILD_SCRIPT:-scripts/build_tsl_playbook.py}"
|
||||
|
||||
@@ -85,10 +137,15 @@ jobs:
|
||||
git config user.name "$GIT_USER_NAME"
|
||||
git config user.email "$GIT_USER_EMAIL"
|
||||
|
||||
source_sha="$(git rev-parse HEAD)"
|
||||
# 校验仍在上游提交,构建的是正确源码。
|
||||
actual_sha="$(git rev-parse HEAD)"
|
||||
if [ "$actual_sha" != "${{ env.SOURCE_SHA }}" ]; then
|
||||
echo "源码 Worktree 不在上游提交: 期望 ${{ env.SOURCE_SHA }},实际 $actual_sha" >&2
|
||||
exit 1
|
||||
fi
|
||||
source_short="$(git rev-parse --short HEAD)"
|
||||
|
||||
# Build the bundle outside the repo so branch checkout cannot clobber it.
|
||||
# 在仓库外构建 bundle,切换分支时不会被清理。
|
||||
build_dir="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "$build_dir"
|
||||
@@ -98,7 +155,7 @@ jobs:
|
||||
bundle="$build_dir/tsl-playbook"
|
||||
python3 "$BUILD_SCRIPT" --output "$bundle"
|
||||
|
||||
# These are the only paths this workflow owns on the target branch.
|
||||
# 本 workflow 在目标分支上仅拥有以下路径。
|
||||
managed_paths=(
|
||||
"AGENTS.md"
|
||||
"docs/tsl"
|
||||
@@ -113,11 +170,11 @@ jobs:
|
||||
fi
|
||||
done
|
||||
|
||||
if git show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then
|
||||
git fetch origin "$TARGET_BRANCH"
|
||||
git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH"
|
||||
# 目标分支的引用由 Prepare fetch 到共享仓库;此处无需再 fetch。
|
||||
if git --git-dir="$REPOSITORY_DIR" show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then
|
||||
git checkout --force -B "$TARGET_BRANCH" "refs/remotes/origin/$TARGET_BRANCH"
|
||||
else
|
||||
# A new orphan branch inherits main's index; clear it without deleting files.
|
||||
# 目标分支尚不存在:创建孤儿分支,清空索引但保留工作树文件。
|
||||
git checkout --orphan "$TARGET_BRANCH"
|
||||
git rm -rf --cached --quiet . >/dev/null 2>&1 || true
|
||||
fi
|
||||
@@ -137,15 +194,20 @@ jobs:
|
||||
|
||||
git commit -m ":package: deps(tsl): sync tsl-playbook from ${source_short}
|
||||
|
||||
Source-Commit: ${source_sha}"
|
||||
Source-Commit: ${{ env.SOURCE_SHA }}"
|
||||
|
||||
git push origin "$TARGET_BRANCH"
|
||||
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 \
|
||||
push origin "$TARGET_BRANCH"
|
||||
echo "✅ Published tsl-playbook @ ${source_short}"
|
||||
|
||||
- name: 🧹 清理临时仓库
|
||||
- name: 🧹 重置 Worktree
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "${GIT_ASKPASS:-}" ]; then
|
||||
rm -f "$GIT_ASKPASS"
|
||||
# 持久化 worktree 由下次运行前的 detached checkout + reset + clean 复用,
|
||||
# 此处仅在结束时 detach,避免留下已检出的分支阻塞其它 worktree。
|
||||
if [ -n "${REPO_DIR:-}" ] && [ -f "$REPO_DIR/.git" ]; then
|
||||
git -C "$REPO_DIR" checkout --detach --force "${SOURCE_SHA}" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$REPO_DIR"
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
---
|
||||
name: 🧪 Playbook 测试套件
|
||||
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch: # 允许手动触发
|
||||
|
||||
concurrency:
|
||||
group: test-${{ github.repository }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# ==========================================
|
||||
# 🔧 配置区域 - 测试参数
|
||||
# ==========================================
|
||||
env:
|
||||
# 测试工作目录
|
||||
WORKSPACE_DIR: "/home/workspace"
|
||||
|
||||
jobs:
|
||||
# ==========================================
|
||||
# Job: 全量测试
|
||||
# ==========================================
|
||||
test:
|
||||
name: 🧪 全量测试
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: 📥 准备仓库
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "========================================"
|
||||
echo "📦 准备仓库到 WORKSPACE_DIR"
|
||||
echo "========================================"
|
||||
|
||||
REPO_NAME="${{ github.event.repository.name }}"
|
||||
TOKEN="${{ secrets.WORKFLOW }}"
|
||||
TARGET_SHA="${{ github.sha }}"
|
||||
TARGET_REF="${{ github.ref }}"
|
||||
TARGET_REF_NAME="${{ github.ref_name }}"
|
||||
|
||||
mkdir -p "$WORKSPACE_DIR"
|
||||
REPO_DIR="$(mktemp -d "$WORKSPACE_DIR/${REPO_NAME}.XXXXXX")"
|
||||
|
||||
if [ -n "$TOKEN" ]; then
|
||||
REPO_URL="https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git"
|
||||
else
|
||||
REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||
fi
|
||||
|
||||
git clone "$REPO_URL" "$REPO_DIR"
|
||||
|
||||
if git -C "$REPO_DIR" cat-file -e \
|
||||
"${TARGET_SHA}^{commit}" 2>/dev/null; then
|
||||
git -C "$REPO_DIR" checkout -f "$TARGET_SHA"
|
||||
else
|
||||
if [ -n "$TARGET_REF" ]; then
|
||||
git -C "$REPO_DIR" fetch origin "$TARGET_REF"
|
||||
git -C "$REPO_DIR" checkout -f FETCH_HEAD
|
||||
else
|
||||
git -C "$REPO_DIR" checkout -f "$TARGET_REF_NAME"
|
||||
fi
|
||||
fi
|
||||
|
||||
git config --global --add safe.directory "$REPO_DIR"
|
||||
echo "REPO_DIR=$REPO_DIR" >> "$GITHUB_ENV"
|
||||
echo "✅ 仓库准备完成"
|
||||
|
||||
- name: 🧪 运行全量测试
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "========================================"
|
||||
echo "🐍 Python 测试"
|
||||
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: 🧹 清理临时仓库
|
||||
if: always()
|
||||
run: |
|
||||
rm -rf "$REPO_DIR"
|
||||
@@ -1,19 +1,22 @@
|
||||
name: 🪄 Update Third-Party Skills
|
||||
name: ♻️ 更新第三方 Skills
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "@daily"
|
||||
workflow_run:
|
||||
workflows: ["🧰 准备环境"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
concurrency:
|
||||
group: update-thirdparty-${{ github.repository }}
|
||||
cancel-in-progress: true
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
WORKSPACE_DIR: "/home/workspace"
|
||||
# Prepare 维护共享仓库并读取代码;此处 token 用于 worktree 内的 fetch/push。
|
||||
ACCESS_TOKEN: ${{ secrets.WORKFLOW }}
|
||||
WORKSPACE_ROOT: "/data/workspace"
|
||||
WORKSPACE_SLOT: "thirdparty"
|
||||
SOURCE_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
SOURCE_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||
THIRDPARTY_BRANCH: "thirdparty/skill"
|
||||
MANIFEST_PATH: ".gitea/ci/thirdparty_skills.json"
|
||||
GIT_USER_NAME: "ci[bot]"
|
||||
@@ -21,39 +24,107 @@ env:
|
||||
|
||||
jobs:
|
||||
update_and_sync:
|
||||
name: ♻️ Update thirdparty and sync main
|
||||
runs-on: ubuntu-22.04
|
||||
# 仅在 Prepare 于主分支成功后触发(含每日 schedule 触发的 Prepare)。
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' && (github.event.workflow_run.head_branch == 'main' || github.event.workflow_run.head_branch == 'master') }}
|
||||
name: 📥 更新快照并同步 main
|
||||
runs-on: standard-ubuntu-22
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: 🧰 Prepare repo
|
||||
- name: 🔐 验证 Token 配置
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "$ACCESS_TOKEN" ]; then
|
||||
echo "❌ 未配置 WORKFLOW secret,无法 fetch/push" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Token 已配置"
|
||||
echo "🌿 上游分支: ${{ env.SOURCE_BRANCH }}"
|
||||
echo "📝 上游提交: ${{ env.SOURCE_SHA }}"
|
||||
|
||||
- name: 📥 对齐 Worktree
|
||||
id: prepare_worktree
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "========================================"
|
||||
echo "📦 Prepare repo in WORKSPACE_DIR"
|
||||
echo "========================================"
|
||||
|
||||
REPO_NAME="${{ github.event.repository.name }}"
|
||||
TOKEN="${{ secrets.WORKFLOW }}"
|
||||
mkdir -p "${{ env.WORKSPACE_DIR }}"
|
||||
REPO_DIR="$(mktemp -d "${{ env.WORKSPACE_DIR }}/${REPO_NAME}.XXXXXX")"
|
||||
if [ -n "$TOKEN" ]; then
|
||||
REPO_URL="https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git"
|
||||
else
|
||||
REPO_URL="${GITHUB_SERVER_URL}/${{ github.repository }}.git"
|
||||
HEAD_SHA="${{ env.SOURCE_SHA }}"
|
||||
REPOSITORY_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/repository.git"
|
||||
WORKTREE_DIR="${WORKSPACE_ROOT}/${REPO_NAME}/worktrees/${WORKSPACE_SLOT}"
|
||||
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
|
||||
if ! git --git-dir="$REPOSITORY_DIR" cat-file -e "${HEAD_SHA}^{commit}"; then
|
||||
echo "共享仓库中不存在上游提交: $HEAD_SHA" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git clone "$REPO_URL" "$REPO_DIR"
|
||||
# 在共享仓库上配置读取 ACCESS_TOKEN 的凭证助手,供 worktree 内后续
|
||||
# fetch/push 透明鉴权。token 不落盘,助手在 git 调用时从环境读取。
|
||||
# 幂等:同一 runner 复用共享仓库时,credential.helper 已是多值(空值 +
|
||||
# helper),单值写入会报 "cannot overwrite multiple values";故先 unset
|
||||
# 再用两个 --add 重建(空值清空继承链,再追加读环境的助手)。
|
||||
git --git-dir="$REPOSITORY_DIR" config credential.interactive never
|
||||
git --git-dir="$REPOSITORY_DIR" config --unset-all credential.helper 2>/dev/null || true
|
||||
git --git-dir="$REPOSITORY_DIR" config --add credential.helper ''
|
||||
git --git-dir="$REPOSITORY_DIR" config --add credential.helper '!f() { test "$1" = get || exit 0; printf "%s\n" username=oauth2; printf "%s\n" "password=${ACCESS_TOKEN}"; }; f'
|
||||
# 固化 origin 到无 token 的 HTTPS URL:区域脚本内保留的历史
|
||||
# `git remote set-url origin https://oauth2:$TOKEN@...` 仅在设置了
|
||||
# WORKFLOW env 时才触发;本 job 严禁设置 WORKFLOW,否则 token 会落盘到
|
||||
# 共享 bare 仓库的 config 并污染所有其它 slot。此处显式重置以固化约束。
|
||||
git --git-dir="$REPOSITORY_DIR" remote set-url origin "${{ github.server_url }}/${{ github.repository }}.git"
|
||||
|
||||
git -C "$REPO_DIR" fetch origin main
|
||||
git -C "$REPO_DIR" checkout -B main origin/main
|
||||
if [ ! -f "$WORKTREE_DIR/.git" ]; then
|
||||
exec 9>"$WORKTREE_LOCK"
|
||||
flock 9
|
||||
|
||||
git config --global --add safe.directory "$REPO_DIR"
|
||||
echo "REPO_DIR=$REPO_DIR" >> "$GITHUB_ENV"
|
||||
echo "✅ Repo prepared"
|
||||
if [ ! -f "$WORKTREE_DIR/.git" ]; then
|
||||
if [ -e "$WORKTREE_DIR" ]; then
|
||||
echo "Worktree 路径已存在但不是 linked worktree: $WORKTREE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$(dirname "$WORKTREE_DIR")"
|
||||
git --git-dir="$REPOSITORY_DIR" worktree add --detach "$WORKTREE_DIR" "$HEAD_SHA"
|
||||
fi
|
||||
|
||||
- name: ♻️ Update thirdparty and sync main
|
||||
flock -u 9
|
||||
fi
|
||||
|
||||
common_dir=$(git -C "$WORKTREE_DIR" rev-parse --path-format=absolute --git-common-dir)
|
||||
if [ "$common_dir" != "$(realpath "$REPOSITORY_DIR")" ]; then
|
||||
echo "Worktree 不属于共享仓库: $WORKTREE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git -C "$WORKTREE_DIR" checkout --detach --force "$HEAD_SHA"
|
||||
git -C "$WORKTREE_DIR" reset --hard "$HEAD_SHA"
|
||||
git -C "$WORKTREE_DIR" clean -ffdx
|
||||
|
||||
actual_sha=$(git -C "$WORKTREE_DIR" rev-parse HEAD)
|
||||
if [ "$actual_sha" != "$HEAD_SHA" ]; then
|
||||
echo "Worktree 提交不匹配: 期望 $HEAD_SHA,实际 $actual_sha" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "REPOSITORY_DIR=$REPOSITORY_DIR" >> "$GITHUB_ENV"
|
||||
echo "REPO_DIR=$WORKTREE_DIR" >> "$GITHUB_ENV"
|
||||
echo "REPO_NAME=$REPO_NAME" >> "$GITHUB_ENV"
|
||||
echo "Worktree: $WORKTREE_DIR @ $actual_sha"
|
||||
|
||||
- name: 📥 更新快照并同步 main
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -562,7 +633,12 @@ jobs:
|
||||
|
||||
echo "✅ Update and sync finished."
|
||||
|
||||
- name: 🧹 Clean temporary repo
|
||||
- name: 🧹 重置 Worktree
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
rm -rf "$REPO_DIR"
|
||||
# 持久化 worktree 由下次运行前的 detached checkout + reset + clean 复用,
|
||||
# 此处仅在结束时 detach,避免留下已检出的分支阻塞其它 worktree。
|
||||
if [ -n "${REPO_DIR:-}" ] && [ -f "$REPO_DIR/.git" ]; then
|
||||
git -C "$REPO_DIR" checkout --detach --force "${SOURCE_SHA}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
+6
-6
@@ -31,13 +31,13 @@ python test/integration/check_doc_links.py
|
||||
|
||||
## 🧭 CI 自动化测试
|
||||
|
||||
测试套件通过 Gitea Actions 自动运行(见 `.gitea/workflows/test.yml`):
|
||||
测试套件通过 Gitea Actions 自动运行(见 `.gitea/workflows/checks.yml` 的 `tests` job):
|
||||
|
||||
- **触发时机**:
|
||||
- 推送到 `main` 分支
|
||||
- Pull Request 到 `main` 分支
|
||||
- 手动触发(workflow_dispatch)
|
||||
- **运行平台**:ubuntu-22.04
|
||||
- **触发方式**:`workflow_run`——「🧰 准备环境」(`prepare.yml`)完成后触发。
|
||||
Prepare 监听 `push:main`、`pull_request`、`workflow_dispatch` 与每日 `schedule`,
|
||||
故推送到 main、对 main 开 PR 或手动运行 Prepare 都会经 Prepare 传导到测试。
|
||||
- **运行平台**:standard-ubuntu-22
|
||||
- **注**:`checks.yml` 同时包含 commit message 规范校验(`standards` job)。
|
||||
|
||||
## 📚 测试详解
|
||||
|
||||
|
||||
Reference in New Issue
Block a user