From 3a97615d233a05ec725fb880bda2fc0cae22a70e Mon Sep 17 00:00:00 2001 From: "zhangxu.709" Date: Fri, 24 Apr 2026 13:48:15 +0800 Subject: [PATCH 1/3] github: add github action bot to control ci manually. --- .github/workflows/action_bot.yaml | 246 +++++++++++++++++++++++ .github/workflows/build_x86_64_cuda.yaml | 89 +------- .github/workflows/build_x86_64_ilu.yaml | 89 +------- .github/workflows/build_x86_64_mlu.yaml | 89 +------- .github/workflows/build_x86_64_npu.yaml | 90 +-------- xllm/xllm.cpp | 7 + 6 files changed, 277 insertions(+), 333 deletions(-) create mode 100644 .github/workflows/action_bot.yaml diff --git a/.github/workflows/action_bot.yaml b/.github/workflows/action_bot.yaml new file mode 100644 index 0000000000..3539814b37 --- /dev/null +++ b/.github/workflows/action_bot.yaml @@ -0,0 +1,246 @@ +# Bot command handler for issue and PR maintenance. +# Authorized repository collaborators can comment: +# @xllm-bot help - Show available commands +# @xllm-bot triage - Add the needs-triage label to an issue or PR +# @xllm-bot run - Refresh the run-ci label to trigger manual PR CI +# @xllm-bot rerun - Cancel and rerun all workflows for a PR +# @xllm-bot rerun failed - Rerun failed and cancelled workflows for a PR +# @xllm-bot stop - Cancel in-progress workflows for a PR +# +# XLLM_BOT_TOKEN must be a PAT or GitHub App token. Label events created with +# the default GITHUB_TOKEN do not trigger downstream workflows. + +name: Action Bot + +on: + issue_comment: + types: [created] + +permissions: + actions: write + contents: read + issues: write + pull-requests: write + +jobs: + handle-command: + if: | + contains(github.event.comment.body, '@xllm-bot') && + github.event.comment.user.type != 'Bot' + runs-on: ubuntu-latest + + env: + BOT_NAME: '@xllm-bot' + COMMENT_BODY: ${{ github.event.comment.body }} + COMMENT_ID: ${{ github.event.comment.id }} + BOT_GH_TOKEN: ${{ secrets.XLLM_BOT_TOKEN }} + GH_TOKEN: ${{ github.token }} + IS_PR: ${{ github.event.issue.pull_request && 'true' || 'false' }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + REPO: ${{ github.repository }} + RUN_CI_LABEL: run-ci + TRIAGE_LABEL: needs-triage + + steps: + - name: Handle bot command + run: | + set -euo pipefail + + react() { + gh api \ + -X POST \ + "/repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \ + -f content="$1" >/dev/null || true + } + + ensure_label() { + local label="$1" + local color="$2" + local description="$3" + + if ! gh label list \ + --repo "$REPO" \ + --search "$label" \ + --json name \ + --jq '.[].name' | grep -Fx "$label" >/dev/null; then + gh label create "$label" \ + --repo "$REPO" \ + --color "$color" \ + --description "$description" >/dev/null || true + fi + } + + get_pr_sha() { + gh pr view "$ISSUE_NUMBER" \ + --repo "$REPO" \ + --json headRefOid \ + -q '.headRefOid' + } + + bot_pattern=$(printf '%s' "$BOT_NAME" | sed 's/[][\\.^$*+?{}|()]/\\&/g') + comment_lc=$(printf '%s' "$COMMENT_BODY" | tr '[:upper:]' '[:lower:]') + bot_pattern_lc=$(printf '%s' "$bot_pattern" | tr '[:upper:]' '[:lower:]') + + if printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+rerun[[:space:]]+failed"; then + command="rerun-failed" + elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+rerun"; then + command="rerun" + elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+stop"; then + command="stop" + elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+run"; then + command="run" + elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+triage"; then + command="triage" + elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+help"; then + command="help" + else + echo "Unknown bot command" + react "confused" + exit 0 + fi + + actor="${{ github.event.comment.user.login }}" + permission=$(gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/repos/${REPO}/collaborators/${actor}/permission" \ + --jq '.permission' 2>/dev/null || true) + + case "$permission" in + admin|maintain|write) + echo "$actor is authorized with $permission permission" + ;; + *) + echo "$actor is not authorized. Repository permission: ${permission:-none}" + react "confused" + exit 0 + ;; + esac + + if [[ -z "${BOT_GH_TOKEN}" ]]; then + echo "::error::XLLM_BOT_TOKEN secret is not set" + react "confused" + exit 1 + fi + + export GH_TOKEN="$BOT_GH_TOKEN" + + if [[ "$IS_PR" != "true" && "$command" =~ ^(run|rerun|rerun-failed|stop)$ ]]; then + echo "Command '$command' only applies to pull requests" + react "confused" + exit 0 + fi + + case "$command" in + help) + gh issue comment "$ISSUE_NUMBER" \ + --repo "$REPO" \ + --body "$(cat <<'EOF' + Supported commands: + - `@xllm-bot triage`: add `needs-triage` to an issue or PR. + - `@xllm-bot run`: refresh `run-ci` to trigger manual PR CI. + - `@xllm-bot rerun`: cancel queued/in-progress runs and rerun all workflows for a PR. + - `@xllm-bot rerun failed`: rerun failed and cancelled workflows for a PR. + - `@xllm-bot stop`: cancel queued/in-progress workflows for a PR. + EOF + )" + react "+1" + ;; + + triage) + ensure_label "$TRIAGE_LABEL" "fbca04" "Needs maintainer triage." + gh issue edit "$ISSUE_NUMBER" \ + --repo "$REPO" \ + --add-label "$TRIAGE_LABEL" + react "+1" + ;; + + run) + ensure_label "$RUN_CI_LABEL" "0e8a16" "Request CI for a pull request." + gh issue edit "$ISSUE_NUMBER" \ + --repo "$REPO" \ + --remove-label "$RUN_CI_LABEL" || true + gh issue edit "$ISSUE_NUMBER" \ + --repo "$REPO" \ + --add-label "$RUN_CI_LABEL" + react "+1" + ;; + + rerun) + pr_sha=$(get_pr_sha) + echo "PR HEAD SHA: $pr_sha" + + gh run list \ + --repo "$REPO" \ + --commit "$pr_sha" \ + --limit 100 \ + --json databaseId,status \ + -q '.[] | select(.status == "in_progress" or .status == "queued") | .databaseId' | \ + while read -r run_id; do + if [[ -n "$run_id" ]]; then + echo "Cancelling workflow run $run_id" + gh run cancel "$run_id" --repo "$REPO" || true + fi + done + + sleep 2 + + gh run list \ + --repo "$REPO" \ + --commit "$pr_sha" \ + --limit 100 \ + --json databaseId \ + -q '.[].databaseId' | \ + while read -r run_id; do + if [[ -n "$run_id" ]]; then + echo "Rerunning workflow run $run_id" + gh run rerun "$run_id" --repo "$REPO" || true + fi + done + + react "+1" + ;; + + rerun-failed) + pr_sha=$(get_pr_sha) + echo "PR HEAD SHA: $pr_sha" + + for status in failure cancelled; do + gh run list \ + --repo "$REPO" \ + --commit "$pr_sha" \ + --status "$status" \ + --limit 100 \ + --json databaseId \ + -q '.[].databaseId' | \ + while read -r run_id; do + if [[ -n "$run_id" ]]; then + echo "Rerunning $status workflow run $run_id" + gh run rerun "$run_id" --repo "$REPO" --failed || true + fi + done + done + + react "+1" + ;; + + stop) + pr_sha=$(get_pr_sha) + echo "PR HEAD SHA: $pr_sha" + + gh run list \ + --repo "$REPO" \ + --commit "$pr_sha" \ + --limit 100 \ + --json databaseId,status \ + -q '.[] | select(.status == "in_progress" or .status == "queued") | .databaseId' | \ + while read -r run_id; do + if [[ -n "$run_id" ]]; then + echo "Cancelling workflow run $run_id" + gh run cancel "$run_id" --repo "$REPO" || true + fi + done + + react "+1" + ;; + esac diff --git a/.github/workflows/build_x86_64_cuda.yaml b/.github/workflows/build_x86_64_cuda.yaml index ae6f69fa4c..a6aad6b832 100644 --- a/.github/workflows/build_x86_64_cuda.yaml +++ b/.github/workflows/build_x86_64_cuda.yaml @@ -2,21 +2,9 @@ name: xLLM Build x86_64 CUDA on: workflow_dispatch: - push: - branches: [main] - paths-ignore: - - '.github/**' - - 'cibuild/**' - - 'cmake/**' - - 'docs/**' - - 'third_party/**' - - 'tools/**' - - '*.md' - - '*.txt' - - '*.yml' pull_request: branches: [main] - types: [opened, synchronize, reopened] + types: [labeled] paths-ignore: - 'cmake/**' - 'docs/**' @@ -25,13 +13,6 @@ on: - '*.md' - '*.txt' - '*.yml' - pull_request_review: - types: [submitted] - paths: - - '.github/**.yaml' - - 'cibuild/**.sh' - - 'setup.py' - - 'examples/generate.py' env: JOBNAME: xllm-x86_64-cuda-cibuild-${{ github.run_id }} @@ -41,71 +22,13 @@ concurrency: cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: - # need to review code first when sensitive files are modified. - check-sensitive: - runs-on: [self-hosted] - outputs: - requires_approval: ${{ steps.check_sensitive.outputs.requires_approval }} - do_build: ${{ steps.decide.outputs.do_build }} - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Ensure we can compare commits - - - name: Install jq - run: yum install -y jq - - - name: Check if sensitive files were changed - id: check_sensitive - run: | - sensitive_files=( - ".github/**.yaml" - "cibuild/**.sh" - "setup.py" - ) - changed_files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) - requires_approval="false" - while IFS= read -r changed_file; do - [[ -z "$changed_file" ]] && continue - for pattern in "${sensitive_files[@]}"; do - if [[ "$changed_file" == $pattern ]]; then - requires_approval="true" - break 2 - fi - done - done < <(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}") - echo "requires_approval=$requires_approval" >> $GITHUB_OUTPUT - - - name: Decide whether to check build - id: decide - run: | - event="${{ github.event_name }}" - if [[ "$event" == "workflow_dispatch" || "$event" == "push" ]]; then - echo "do_build=true" >> $GITHUB_OUTPUT - elif [[ "$event" == "pull_request" ]]; then - if [ "${{ steps.check_sensitive.outputs.requires_approval }}" == "true" ]; then - echo "do_build=false" >> $GITHUB_OUTPUT - else - echo "do_build=true" >> $GITHUB_OUTPUT - fi - elif [[ "$event" == "pull_request_review" ]]; then - # Since pull_request_review now only triggers when sensitive files are modified, - # we only need to check if the review is approved - if [[ "${{ github.event.review.state }}" == "approved" ]]; then - echo "do_build=true" >> $GITHUB_OUTPUT - else - echo "do_build=false" >> $GITHUB_OUTPUT - fi - else - echo "do_build=false" >> $GITHUB_OUTPUT - fi - build: - needs: check-sensitive if: > - (github.event_name == 'workflow_dispatch' || github.event_name == 'push') || - needs.check-sensitive.outputs.do_build == 'true' + github.event_name == 'workflow_dispatch' || + ( + github.event_name == 'pull_request' && + github.event.label.name == 'run-ci' + ) runs-on: [self-hosted] steps: - name: Checkout Code diff --git a/.github/workflows/build_x86_64_ilu.yaml b/.github/workflows/build_x86_64_ilu.yaml index e046da7124..2248c50be2 100644 --- a/.github/workflows/build_x86_64_ilu.yaml +++ b/.github/workflows/build_x86_64_ilu.yaml @@ -2,21 +2,9 @@ name: xLLM Build x86_64 ILU on: workflow_dispatch: - push: - branches: [main] - paths-ignore: - - '.github/**' - - 'cibuild/**' - - 'cmake/**' - - 'docs/**' - - 'third_party/**' - - 'tools/**' - - '*.md' - - '*.txt' - - '*.yml' pull_request: branches: [main] - types: [opened, synchronize, reopened] + types: [labeled] paths-ignore: - 'cmake/**' - 'docs/**' @@ -25,13 +13,6 @@ on: - '*.md' - '*.txt' - '*.yml' - pull_request_review: - types: [submitted] - paths: - - '.github/**.yaml' - - 'cibuild/**.sh' - - 'setup.py' - - 'examples/generate.py' env: JOBNAME: xllm-x86_64-ilu-cibuild-${{ github.run_id }} @@ -41,71 +22,13 @@ concurrency: cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: - # need to review code first when sensitive files are modified. - check-sensitive: - runs-on: [self-hosted] - outputs: - requires_approval: ${{ steps.check_sensitive.outputs.requires_approval }} - do_build: ${{ steps.decide.outputs.do_build }} - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Ensure we can compare commits - - - name: Install jq - run: yum install -y jq - - - name: Check if sensitive files were changed - id: check_sensitive - run: | - sensitive_files=( - ".github/**.yaml" - "cibuild/**.sh" - "setup.py" - ) - changed_files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) - requires_approval="false" - while IFS= read -r changed_file; do - [[ -z "$changed_file" ]] && continue - for pattern in "${sensitive_files[@]}"; do - if [[ "$changed_file" == $pattern ]]; then - requires_approval="true" - break 2 - fi - done - done < <(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}") - echo "requires_approval=$requires_approval" >> $GITHUB_OUTPUT - - - name: Decide whether to check build - id: decide - run: | - event="${{ github.event_name }}" - if [[ "$event" == "workflow_dispatch" || "$event" == "push" ]]; then - echo "do_build=true" >> $GITHUB_OUTPUT - elif [[ "$event" == "pull_request" ]]; then - if [ "${{ steps.check_sensitive.outputs.requires_approval }}" == "true" ]; then - echo "do_build=false" >> $GITHUB_OUTPUT - else - echo "do_build=true" >> $GITHUB_OUTPUT - fi - elif [[ "$event" == "pull_request_review" ]]; then - # Since pull_request_review now only triggers when sensitive files are modified, - # we only need to check if the review is approved - if [[ "${{ github.event.review.state }}" == "approved" ]]; then - echo "do_build=true" >> $GITHUB_OUTPUT - else - echo "do_build=false" >> $GITHUB_OUTPUT - fi - else - echo "do_build=false" >> $GITHUB_OUTPUT - fi - build: - needs: check-sensitive if: > - (github.event_name == 'workflow_dispatch' || github.event_name == 'push') || - needs.check-sensitive.outputs.do_build == 'true' + github.event_name == 'workflow_dispatch' || + ( + github.event_name == 'pull_request' && + github.event.label.name == 'run-ci' + ) runs-on: [self-hosted] steps: - name: Checkout Code diff --git a/.github/workflows/build_x86_64_mlu.yaml b/.github/workflows/build_x86_64_mlu.yaml index be14b15189..a237a29c44 100644 --- a/.github/workflows/build_x86_64_mlu.yaml +++ b/.github/workflows/build_x86_64_mlu.yaml @@ -2,21 +2,9 @@ name: xLLM Build x86_64 MLU on: workflow_dispatch: - push: - branches: [main] - paths-ignore: - - '.github/**' - - 'cibuild/**' - - 'cmake/**' - - 'docs/**' - - 'third_party/**' - - 'tools/**' - - '*.md' - - '*.txt' - - '*.yml' pull_request: branches: [main] - types: [opened, synchronize, reopened] + types: [labeled] paths-ignore: - 'cmake/**' - 'docs/**' @@ -25,13 +13,6 @@ on: - '*.md' - '*.txt' - '*.yml' - pull_request_review: - types: [submitted] - paths: - - '.github/**.yaml' - - 'cibuild/**.sh' - - 'setup.py' - - 'examples/generate.py' env: JOBNAME: xllm-x86_64-mlu-cibuild-${{ github.run_id }} @@ -41,71 +22,13 @@ concurrency: cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: - # need to review code first when sensitive files are modified. - check-sensitive: - runs-on: [self-hosted] - outputs: - requires_approval: ${{ steps.check_sensitive.outputs.requires_approval }} - do_build: ${{ steps.decide.outputs.do_build }} - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Ensure we can compare commits - - - name: Install jq - run: yum install -y jq - - - name: Check if sensitive files were changed - id: check_sensitive - run: | - sensitive_files=( - ".github/**.yaml" - "cibuild/**.sh" - "setup.py" - ) - changed_files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) - requires_approval="false" - while IFS= read -r changed_file; do - [[ -z "$changed_file" ]] && continue - for pattern in "${sensitive_files[@]}"; do - if [[ "$changed_file" == $pattern ]]; then - requires_approval="true" - break 2 - fi - done - done < <(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}") - echo "requires_approval=$requires_approval" >> $GITHUB_OUTPUT - - - name: Decide whether to check build - id: decide - run: | - event="${{ github.event_name }}" - if [[ "$event" == "workflow_dispatch" || "$event" == "push" ]]; then - echo "do_build=true" >> $GITHUB_OUTPUT - elif [[ "$event" == "pull_request" ]]; then - if [ "${{ steps.check_sensitive.outputs.requires_approval }}" == "true" ]; then - echo "do_build=false" >> $GITHUB_OUTPUT - else - echo "do_build=true" >> $GITHUB_OUTPUT - fi - elif [[ "$event" == "pull_request_review" ]]; then - # Since pull_request_review now only triggers when sensitive files are modified, - # we only need to check if the review is approved - if [[ "${{ github.event.review.state }}" == "approved" ]]; then - echo "do_build=true" >> $GITHUB_OUTPUT - else - echo "do_build=false" >> $GITHUB_OUTPUT - fi - else - echo "do_build=false" >> $GITHUB_OUTPUT - fi - build: - needs: check-sensitive if: > - (github.event_name == 'workflow_dispatch' || github.event_name == 'push') || - needs.check-sensitive.outputs.do_build == 'true' + github.event_name == 'workflow_dispatch' || + ( + github.event_name == 'pull_request' && + github.event.label.name == 'run-ci' + ) runs-on: [self-hosted] steps: - name: Checkout Code diff --git a/.github/workflows/build_x86_64_npu.yaml b/.github/workflows/build_x86_64_npu.yaml index bc5c6b1b17..1a5c5c1b88 100644 --- a/.github/workflows/build_x86_64_npu.yaml +++ b/.github/workflows/build_x86_64_npu.yaml @@ -2,21 +2,9 @@ name: xLLM Build x86_64 NPU on: workflow_dispatch: - push: - branches: [main] - paths-ignore: - - '.github/**' - - 'cibuild/**' - - 'cmake/**' - - 'docs/**' - - 'third_party/**' - - 'tools/**' - - '*.md' - - '*.txt' - - '*.yml' pull_request: branches: [main] - types: [opened, synchronize, reopened] + types: [labeled] paths-ignore: - 'cmake/**' - 'docs/**' @@ -25,13 +13,6 @@ on: - '*.md' - '*.txt' - '*.yml' - pull_request_review: - types: [submitted] - paths: - - '.github/**.yaml' - - 'cibuild/**.sh' - - 'setup.py' - - 'examples/generate.py' env: JOBNAME: xllm-x86_64-npu-cibuild-${{ github.run_id }} @@ -41,72 +22,13 @@ concurrency: cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: - # need to review code first when sensitive files are modified. - check-sensitive: - runs-on: [self-hosted] - outputs: - requires_approval: ${{ steps.check_sensitive.outputs.requires_approval }} - do_build: ${{ steps.decide.outputs.do_build }} - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Ensure we can compare commits - - - name: Install jq - run: yum install -y jq - - - name: Check if sensitive files were changed - id: check_sensitive - run: | - sensitive_files=( - ".github/**.yaml" - "cibuild/**.sh" - "setup.py" - "examples/generate.py" - ) - changed_files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) - requires_approval="false" - while IFS= read -r changed_file; do - [[ -z "$changed_file" ]] && continue - for pattern in "${sensitive_files[@]}"; do - if [[ "$changed_file" == $pattern ]]; then - requires_approval="true" - break 2 - fi - done - done < <(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}") - echo "requires_approval=$requires_approval" >> $GITHUB_OUTPUT - - - name: Decide whether to check build - id: decide - run: | - event="${{ github.event_name }}" - if [[ "$event" == "workflow_dispatch" || "$event" == "push" ]]; then - echo "do_build=true" >> $GITHUB_OUTPUT - elif [[ "$event" == "pull_request" ]]; then - if [ "${{ steps.check_sensitive.outputs.requires_approval }}" == "true" ]; then - echo "do_build=false" >> $GITHUB_OUTPUT - else - echo "do_build=true" >> $GITHUB_OUTPUT - fi - elif [[ "$event" == "pull_request_review" ]]; then - # Since pull_request_review now only triggers when sensitive files are modified, - # we only need to check if the review is approved - if [[ "${{ github.event.review.state }}" == "approved" ]]; then - echo "do_build=true" >> $GITHUB_OUTPUT - else - echo "do_build=false" >> $GITHUB_OUTPUT - fi - else - echo "do_build=false" >> $GITHUB_OUTPUT - fi - build: - needs: check-sensitive if: > - (github.event_name == 'workflow_dispatch' || github.event_name == 'push') || - needs.check-sensitive.outputs.do_build == 'true' + github.event_name == 'workflow_dispatch' || + ( + github.event_name == 'pull_request' && + github.event.label.name == 'run-ci' + ) runs-on: [self-hosted] steps: - name: Prepare submodule checkout diff --git a/xllm/xllm.cpp b/xllm/xllm.cpp index 413fc456b6..3f10e78368 100644 --- a/xllm/xllm.cpp +++ b/xllm/xllm.cpp @@ -44,12 +44,19 @@ limitations under the License. #include "server/xllm_server_registry.h" using namespace xllm; + + static std::atomic signal_received{0}; + + + static const std::unordered_set prefill_sp_supported_model_set = { "deepseek_v32", "glm_moe_dsa"}; + + namespace { void fix_mlu_disagg_pd_flags() { From 97c689db599285ee414b5d86228ec133d56ae5f7 Mon Sep 17 00:00:00 2001 From: "zhangxu.709" Date: Fri, 24 Apr 2026 15:17:31 +0800 Subject: [PATCH 2/3] chore: update action bot to manage CI permissions for authorized users. --- .github/workflows/action_bot.yaml | 424 +++++++++++++++--------------- 1 file changed, 207 insertions(+), 217 deletions(-) diff --git a/.github/workflows/action_bot.yaml b/.github/workflows/action_bot.yaml index 3539814b37..18d84f6209 100644 --- a/.github/workflows/action_bot.yaml +++ b/.github/workflows/action_bot.yaml @@ -1,14 +1,9 @@ -# Bot command handler for issue and PR maintenance. -# Authorized repository collaborators can comment: -# @xllm-bot help - Show available commands -# @xllm-bot triage - Add the needs-triage label to an issue or PR -# @xllm-bot run - Refresh the run-ci label to trigger manual PR CI -# @xllm-bot rerun - Cancel and rerun all workflows for a PR -# @xllm-bot rerun failed - Rerun failed and cancelled workflows for a PR -# @xllm-bot stop - Cancel in-progress workflows for a PR -# -# XLLM_BOT_TOKEN must be a PAT or GitHub App token. Label events created with -# the default GITHUB_TOKEN do not trigger downstream workflows. +# Bot command handler for CI permissions. +# Authorized users (ci-users team) can comment to control CI: +# @xllm-bot run - Add run-ci label to trigger CI +# @xllm-bot rerun - Cancel and rerun all workflows +# @xllm-bot rerun failed - Rerun failed and cancelled jobs +# @xllm-bot stop - Cancel all in-progress workflows name: Action Bot @@ -17,230 +12,225 @@ on: types: [created] permissions: - actions: write contents: read - issues: write pull-requests: write + actions: write jobs: handle-command: + # Only run on PR comments mentioning @xllm-bot. if: | - contains(github.event.comment.body, '@xllm-bot') && - github.event.comment.user.type != 'Bot' + github.event.issue.pull_request && + contains(github.event.comment.body, '@xllm-bot') runs-on: ubuntu-latest - env: - BOT_NAME: '@xllm-bot' - COMMENT_BODY: ${{ github.event.comment.body }} - COMMENT_ID: ${{ github.event.comment.id }} - BOT_GH_TOKEN: ${{ secrets.XLLM_BOT_TOKEN }} - GH_TOKEN: ${{ github.token }} - IS_PR: ${{ github.event.issue.pull_request && 'true' || 'false' }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - REPO: ${{ github.repository }} - RUN_CI_LABEL: run-ci - TRIAGE_LABEL: needs-triage - steps: - - name: Handle bot command + - name: Check team membership + id: check-permission + env: + GH_TOKEN: ${{ secrets.XLLM_GITHUB_TOKEN || secrets.XLLM_BOT_TOKEN }} + ORG: ${{ github.repository_owner }} + TEAM: ci-users + ACTOR: ${{ github.event.comment.user.login }} run: | - set -euo pipefail - - react() { - gh api \ - -X POST \ - "/repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \ - -f content="$1" >/dev/null || true - } + echo "Checking if $ACTOR is a member of $ORG/$TEAM..." - ensure_label() { - local label="$1" - local color="$2" - local description="$3" - - if ! gh label list \ - --repo "$REPO" \ - --search "$label" \ - --json name \ - --jq '.[].name' | grep -Fx "$label" >/dev/null; then - gh label create "$label" \ - --repo "$REPO" \ - --color "$color" \ - --description "$description" >/dev/null || true - fi - } + # Verify token is set. + if [[ -z "$GH_TOKEN" ]]; then + echo "::error::XLLM_GITHUB_TOKEN or XLLM_BOT_TOKEN secret is not set" + echo "authorized=false" >> "$GITHUB_OUTPUT" + exit 0 + fi - get_pr_sha() { - gh pr view "$ISSUE_NUMBER" \ - --repo "$REPO" \ - --json headRefOid \ - -q '.headRefOid' + # List team members and check if commenter is in the list. + MEMBERS=$(gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + --paginate \ + "/orgs/${ORG}/teams/${TEAM}/members" \ + --jq '.[].login' 2>&1) || { + echo "::error::Failed to get team members: $MEMBERS" + echo "authorized=false" >> "$GITHUB_OUTPUT" + exit 0 } - bot_pattern=$(printf '%s' "$BOT_NAME" | sed 's/[][\\.^$*+?{}|()]/\\&/g') - comment_lc=$(printf '%s' "$COMMENT_BODY" | tr '[:upper:]' '[:lower:]') - bot_pattern_lc=$(printf '%s' "$bot_pattern" | tr '[:upper:]' '[:lower:]') - - if printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+rerun[[:space:]]+failed"; then - command="rerun-failed" - elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+rerun"; then - command="rerun" - elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+stop"; then - command="stop" - elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+run"; then - command="run" - elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+triage"; then - command="triage" - elif printf '%s\n' "$comment_lc" | grep -Eq "${bot_pattern_lc}[[:space:]]+help"; then - command="help" + if echo "$MEMBERS" | grep -qx "$ACTOR"; then + echo "$ACTOR is a member of $TEAM" + echo "authorized=true" >> "$GITHUB_OUTPUT" else - echo "Unknown bot command" - react "confused" - exit 0 + echo "$ACTOR is not a member of $TEAM" + echo "authorized=false" >> "$GITHUB_OUTPUT" fi - actor="${{ github.event.comment.user.login }}" - permission=$(gh api \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "/repos/${REPO}/collaborators/${actor}/permission" \ - --jq '.permission' 2>/dev/null || true) - - case "$permission" in - admin|maintain|write) - echo "$actor is authorized with $permission permission" - ;; - *) - echo "$actor is not authorized. Repository permission: ${permission:-none}" - react "confused" - exit 0 - ;; - esac - - if [[ -z "${BOT_GH_TOKEN}" ]]; then - echo "::error::XLLM_BOT_TOKEN secret is not set" - react "confused" - exit 1 + - name: Parse command + id: parse + env: + COMMENT_BODY: ${{ github.event.comment.body }} + run: | + if echo "$COMMENT_BODY" | grep -qi "@xllm-bot rerun failed"; then + echo "command=rerun-failed" >> "$GITHUB_OUTPUT" + elif echo "$COMMENT_BODY" | grep -qi "@xllm-bot rerun"; then + echo "command=rerun" >> "$GITHUB_OUTPUT" + elif echo "$COMMENT_BODY" | grep -qi "@xllm-bot stop"; then + echo "command=stop" >> "$GITHUB_OUTPUT" + elif echo "$COMMENT_BODY" | grep -qi "@xllm-bot run"; then + echo "command=run" >> "$GITHUB_OUTPUT" + else + echo "command=unknown" >> "$GITHUB_OUTPUT" fi - export GH_TOKEN="$BOT_GH_TOKEN" + - name: Handle @xllm-bot run + if: steps.check-permission.outputs.authorized == 'true' && steps.parse.outputs.command == 'run' + env: + GH_TOKEN: ${{ secrets.XLLM_BOT_TOKEN }} + run: | + echo "Adding run-ci label to PR #${{ github.event.issue.number }}" + + # Add run-ci label. + gh pr edit ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} \ + --add-label "run-ci" - if [[ "$IS_PR" != "true" && "$command" =~ ^(run|rerun|rerun-failed|stop)$ ]]; then - echo "Command '$command' only applies to pull requests" - react "confused" - exit 0 - fi + # React with thumbs up. + gh api \ + -X POST \ + "/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content='+1' + + echo "Label added successfully" + + - name: Handle @xllm-bot rerun + if: steps.check-permission.outputs.authorized == 'true' && steps.parse.outputs.command == 'rerun' + env: + GH_TOKEN: ${{ secrets.XLLM_BOT_TOKEN }} + run: | + echo "Rerunning all jobs for PR #${{ github.event.issue.number }}" + + # Get PR head SHA. + PR_SHA=$(gh pr view ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} \ + --json headRefOid -q '.headRefOid') + + echo "PR HEAD SHA: $PR_SHA" + + # Cancel in-progress and queued runs first. + echo "Cancelling in-progress runs..." + gh run list \ + --repo ${{ github.repository }} \ + --commit "$PR_SHA" \ + --json databaseId,status -q '.[] | select(.status == "in_progress" or .status == "queued") | .databaseId' | \ + while read -r run_id; do + if [ -n "$run_id" ]; then + echo "Cancelling workflow $run_id..." + gh run cancel "$run_id" --repo ${{ github.repository }} || true + fi + done + + # Wait for cancellations to complete. + sleep 2 + + # Rerun all workflow runs for this commit. + echo "Rerunning all workflows..." + gh run list \ + --repo ${{ github.repository }} \ + --commit "$PR_SHA" \ + --json databaseId -q '.[].databaseId' | \ + while read -r run_id; do + if [ -n "$run_id" ]; then + echo "Rerunning workflow $run_id..." + gh run rerun "$run_id" --repo ${{ github.repository }} || true + fi + done + + # React with thumbs up. + gh api \ + -X POST \ + "/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content='+1' + + echo "Rerun triggered successfully" + + - name: Handle @xllm-bot rerun failed + if: steps.check-permission.outputs.authorized == 'true' && steps.parse.outputs.command == 'rerun-failed' + env: + GH_TOKEN: ${{ secrets.XLLM_BOT_TOKEN }} + run: | + echo "Rerunning failed/cancelled jobs for PR #${{ github.event.issue.number }}" + + # Get PR head SHA. + PR_SHA=$(gh pr view ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} \ + --json headRefOid -q '.headRefOid') + + echo "PR HEAD SHA: $PR_SHA" + + # Rerun failed and cancelled workflow runs for this commit. + # Cancelled jobs are common with fail-fast when one job fails. + for STATUS in failure cancelled; do + gh run list \ + --repo ${{ github.repository }} \ + --commit "$PR_SHA" \ + --status "$STATUS" \ + --json databaseId -q '.[].databaseId' | \ + while read -r run_id; do + if [ -n "$run_id" ]; then + echo "Rerunning $STATUS workflow $run_id..." + gh run rerun "$run_id" --repo ${{ github.repository }} --failed || true + fi + done + done + + # React with thumbs up. + gh api \ + -X POST \ + "/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content='+1' + + echo "Rerun-failed triggered successfully" + + - name: Handle @xllm-bot stop + if: steps.check-permission.outputs.authorized == 'true' && steps.parse.outputs.command == 'stop' + env: + GH_TOKEN: ${{ secrets.XLLM_BOT_TOKEN }} + run: | + echo "Stopping all workflows for PR #${{ github.event.issue.number }}" + + # Get PR head SHA. + PR_SHA=$(gh pr view ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} \ + --json headRefOid -q '.headRefOid') + + echo "PR HEAD SHA: $PR_SHA" + + # Cancel all in-progress and queued runs. + gh run list \ + --repo ${{ github.repository }} \ + --commit "$PR_SHA" \ + --json databaseId,status -q '.[] | select(.status == "in_progress" or .status == "queued") | .databaseId' | \ + while read -r run_id; do + if [ -n "$run_id" ]; then + echo "Cancelling workflow $run_id..." + gh run cancel "$run_id" --repo ${{ github.repository }} || true + fi + done + + # React with thumbs up. + gh api \ + -X POST \ + "/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content='+1' + + echo "Stop triggered successfully" + + - name: Unauthorized user + if: steps.check-permission.outputs.authorized != 'true' && steps.parse.outputs.command != 'unknown' + env: + GH_TOKEN: ${{ secrets.XLLM_BOT_TOKEN }} + run: | + echo "User ${{ github.event.comment.user.login }} is not authorized" - case "$command" in - help) - gh issue comment "$ISSUE_NUMBER" \ - --repo "$REPO" \ - --body "$(cat <<'EOF' - Supported commands: - - `@xllm-bot triage`: add `needs-triage` to an issue or PR. - - `@xllm-bot run`: refresh `run-ci` to trigger manual PR CI. - - `@xllm-bot rerun`: cancel queued/in-progress runs and rerun all workflows for a PR. - - `@xllm-bot rerun failed`: rerun failed and cancelled workflows for a PR. - - `@xllm-bot stop`: cancel queued/in-progress workflows for a PR. - EOF - )" - react "+1" - ;; - - triage) - ensure_label "$TRIAGE_LABEL" "fbca04" "Needs maintainer triage." - gh issue edit "$ISSUE_NUMBER" \ - --repo "$REPO" \ - --add-label "$TRIAGE_LABEL" - react "+1" - ;; - - run) - ensure_label "$RUN_CI_LABEL" "0e8a16" "Request CI for a pull request." - gh issue edit "$ISSUE_NUMBER" \ - --repo "$REPO" \ - --remove-label "$RUN_CI_LABEL" || true - gh issue edit "$ISSUE_NUMBER" \ - --repo "$REPO" \ - --add-label "$RUN_CI_LABEL" - react "+1" - ;; - - rerun) - pr_sha=$(get_pr_sha) - echo "PR HEAD SHA: $pr_sha" - - gh run list \ - --repo "$REPO" \ - --commit "$pr_sha" \ - --limit 100 \ - --json databaseId,status \ - -q '.[] | select(.status == "in_progress" or .status == "queued") | .databaseId' | \ - while read -r run_id; do - if [[ -n "$run_id" ]]; then - echo "Cancelling workflow run $run_id" - gh run cancel "$run_id" --repo "$REPO" || true - fi - done - - sleep 2 - - gh run list \ - --repo "$REPO" \ - --commit "$pr_sha" \ - --limit 100 \ - --json databaseId \ - -q '.[].databaseId' | \ - while read -r run_id; do - if [[ -n "$run_id" ]]; then - echo "Rerunning workflow run $run_id" - gh run rerun "$run_id" --repo "$REPO" || true - fi - done - - react "+1" - ;; - - rerun-failed) - pr_sha=$(get_pr_sha) - echo "PR HEAD SHA: $pr_sha" - - for status in failure cancelled; do - gh run list \ - --repo "$REPO" \ - --commit "$pr_sha" \ - --status "$status" \ - --limit 100 \ - --json databaseId \ - -q '.[].databaseId' | \ - while read -r run_id; do - if [[ -n "$run_id" ]]; then - echo "Rerunning $status workflow run $run_id" - gh run rerun "$run_id" --repo "$REPO" --failed || true - fi - done - done - - react "+1" - ;; - - stop) - pr_sha=$(get_pr_sha) - echo "PR HEAD SHA: $pr_sha" - - gh run list \ - --repo "$REPO" \ - --commit "$pr_sha" \ - --limit 100 \ - --json databaseId,status \ - -q '.[] | select(.status == "in_progress" or .status == "queued") | .databaseId' | \ - while read -r run_id; do - if [[ -n "$run_id" ]]; then - echo "Cancelling workflow run $run_id" - gh run cancel "$run_id" --repo "$REPO" || true - fi - done - - react "+1" - ;; - esac + # React with confused emoji. + gh api \ + -X POST \ + "/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + -f content='confused' From 9c15a2624a02fb689d47b41f46a00380e4058cab Mon Sep 17 00:00:00 2001 From: "zhangxu.709" Date: Fri, 24 Apr 2026 15:28:14 +0800 Subject: [PATCH 3/3] chore: update action bot to check repository collaborator permissions instead of team membership. --- .github/workflows/action_bot.yaml | 49 ++++++++++++------------------- xllm/xllm.cpp | 7 ----- 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/.github/workflows/action_bot.yaml b/.github/workflows/action_bot.yaml index 18d84f6209..339fd803a2 100644 --- a/.github/workflows/action_bot.yaml +++ b/.github/workflows/action_bot.yaml @@ -1,5 +1,5 @@ # Bot command handler for CI permissions. -# Authorized users (ci-users team) can comment to control CI: +# Repository collaborators can comment to control CI: # @xllm-bot run - Add run-ci label to trigger CI # @xllm-bot rerun - Cancel and rerun all workflows # @xllm-bot rerun failed - Rerun failed and cancelled jobs @@ -25,42 +25,31 @@ jobs: runs-on: ubuntu-latest steps: - - name: Check team membership + - name: Check collaborator permission id: check-permission env: - GH_TOKEN: ${{ secrets.XLLM_GITHUB_TOKEN || secrets.XLLM_BOT_TOKEN }} - ORG: ${{ github.repository_owner }} - TEAM: ci-users + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} ACTOR: ${{ github.event.comment.user.login }} run: | - echo "Checking if $ACTOR is a member of $ORG/$TEAM..." + echo "Checking if $ACTOR is a collaborator on $REPO..." - # Verify token is set. - if [[ -z "$GH_TOKEN" ]]; then - echo "::error::XLLM_GITHUB_TOKEN or XLLM_BOT_TOKEN secret is not set" - echo "authorized=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # List team members and check if commenter is in the list. - MEMBERS=$(gh api \ + PERMISSION=$(gh api \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - --paginate \ - "/orgs/${ORG}/teams/${TEAM}/members" \ - --jq '.[].login' 2>&1) || { - echo "::error::Failed to get team members: $MEMBERS" - echo "authorized=false" >> "$GITHUB_OUTPUT" - exit 0 - } - - if echo "$MEMBERS" | grep -qx "$ACTOR"; then - echo "$ACTOR is a member of $TEAM" - echo "authorized=true" >> "$GITHUB_OUTPUT" - else - echo "$ACTOR is not a member of $TEAM" - echo "authorized=false" >> "$GITHUB_OUTPUT" - fi + "/repos/${REPO}/collaborators/${ACTOR}/permission" \ + --jq '.permission' 2>/dev/null || true) + + case "$PERMISSION" in + admin|maintain|write|triage|read) + echo "$ACTOR is authorized with $PERMISSION permission" + echo "authorized=true" >> "$GITHUB_OUTPUT" + ;; + *) + echo "$ACTOR is not a collaborator. Repository permission: ${PERMISSION:-none}" + echo "authorized=false" >> "$GITHUB_OUTPUT" + ;; + esac - name: Parse command id: parse diff --git a/xllm/xllm.cpp b/xllm/xllm.cpp index 3f10e78368..413fc456b6 100644 --- a/xllm/xllm.cpp +++ b/xllm/xllm.cpp @@ -44,19 +44,12 @@ limitations under the License. #include "server/xllm_server_registry.h" using namespace xllm; - - static std::atomic signal_received{0}; - - - static const std::unordered_set prefill_sp_supported_model_set = { "deepseek_v32", "glm_moe_dsa"}; - - namespace { void fix_mlu_disagg_pd_flags() {