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//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"