Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions .github/workflows/action_bot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
# Bot command handler for CI permissions.
# 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
# @xllm-bot stop - Cancel all in-progress workflows

name: Action Bot

on:
issue_comment:
types: [created]

permissions:
contents: read
pull-requests: write
actions: write

jobs:
handle-command:
# Only run on PR comments mentioning @xllm-bot.
if: |
github.event.issue.pull_request &&
contains(github.event.comment.body, '@xllm-bot')
runs-on: ubuntu-latest

steps:
- name: Check collaborator permission
id: check-permission
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.event.comment.user.login }}
run: |
echo "Checking if $ACTOR is a collaborator on $REPO..."

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

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

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

# React with confused emoji.
gh api \
-X POST \
"/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \
-f content='confused'
89 changes: 6 additions & 83 deletions .github/workflows/build_x86_64_cuda.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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/**'
Expand All @@ -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 }}
Expand All @@ -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
Expand Down
Loading
Loading