ci: extract check-changes into reusable workflow (#25132)
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
name: Check Changes
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
pr_head_sha:
|
||||
type: string
|
||||
default: ''
|
||||
git_ref:
|
||||
type: string
|
||||
default: ''
|
||||
target_stage:
|
||||
type: string
|
||||
default: ''
|
||||
include_wheel_build:
|
||||
type: boolean
|
||||
default: false
|
||||
run_all_tests:
|
||||
type: boolean
|
||||
default: false
|
||||
force_continue_on_error:
|
||||
type: boolean
|
||||
default: false
|
||||
outputs:
|
||||
main_package:
|
||||
value: ${{ jobs.run.outputs.main_package }}
|
||||
sgl_kernel:
|
||||
value: ${{ jobs.run.outputs.sgl_kernel }}
|
||||
sgl_kernel_raw:
|
||||
value: ${{ jobs.run.outputs.sgl_kernel_raw }}
|
||||
jit_kernel:
|
||||
value: ${{ jobs.run.outputs.jit_kernel }}
|
||||
multimodal_gen:
|
||||
value: ${{ jobs.run.outputs.multimodal_gen }}
|
||||
max_parallel:
|
||||
value: ${{ jobs.run.outputs.max_parallel }}
|
||||
max_parallel_small:
|
||||
value: ${{ jobs.run.outputs.max_parallel_small }}
|
||||
max_parallel_2gpu:
|
||||
value: ${{ jobs.run.outputs.max_parallel_2gpu }}
|
||||
b200_runner:
|
||||
value: ${{ jobs.run.outputs.b200_runner }}
|
||||
enable_retry:
|
||||
value: ${{ jobs.run.outputs.enable_retry }}
|
||||
continue_on_error:
|
||||
value: ${{ jobs.run.outputs.continue_on_error }}
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
# Use API-based detection for target_stage mode (filter-api), otherwise use dorny/paths-filter (filter)
|
||||
main_package: ${{ steps.filter-api.outputs.main_package || steps.filter.outputs.main_package || steps.run-mode.outputs.run_all_tests }}
|
||||
# sgl_kernel is forced to false when target_stage is set AND include_wheel_build is NOT set,
|
||||
# since sgl-kernel-build-wheels normally skips in target_stage mode. When include_wheel_build
|
||||
# is true, keep the real value so the wheel build runs and the target stage downloads its
|
||||
# artifact (used by /rerun-stage on PRs that modify sgl-kernel/).
|
||||
# This prevents CUSTOM_BUILD_SGL_KERNEL=true when the wheel artifacts aren't available.
|
||||
# Note: If PR has kernel changes AND target_stage is set AND include_wheel_build is NOT set,
|
||||
# the validate-target-stage step will fail.
|
||||
sgl_kernel: ${{ (!inputs.target_stage || inputs.include_wheel_build) && (steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel) }}
|
||||
# Raw sgl_kernel value before target_stage override (used for validation)
|
||||
sgl_kernel_raw: ${{ steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel }}
|
||||
jit_kernel: ${{ steps.filter-api.outputs.jit_kernel || steps.filter.outputs.jit_kernel || steps.run-mode.outputs.run_all_tests }}
|
||||
multimodal_gen: ${{ steps.filter-api.outputs.multimodal_gen || steps.filter.outputs.multimodal_gen || steps.run-mode.outputs.run_all_tests }}
|
||||
max_parallel: ${{ steps.set-parallel.outputs.max_parallel }}
|
||||
max_parallel_small: ${{ steps.set-parallel.outputs.max_parallel_small }}
|
||||
max_parallel_2gpu: ${{ steps.set-parallel.outputs.max_parallel_2gpu }}
|
||||
b200_runner: ${{ steps.set-runner.outputs.b200_runner }}
|
||||
enable_retry: ${{ steps.set-retry.outputs.enable_retry }}
|
||||
continue_on_error: ${{ steps.set-continue-on-error.outputs.continue_on_error }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.pr_head_sha || inputs.git_ref || github.sha }}
|
||||
|
||||
- uses: ./.github/actions/check-maintenance
|
||||
|
||||
- name: Determine run mode
|
||||
id: run-mode
|
||||
run: |
|
||||
# Run all tests for scheduled runs and workflow_call (when ref input is provided)
|
||||
# Note: github.event_name is inherited from caller, so we detect workflow_call by checking inputs.git_ref
|
||||
if [[ "${{ github.event_name }}" == "schedule" || "${{ inputs.run_all_tests }}" == "true" ]]; then
|
||||
echo "run_all_tests=true" >> $GITHUB_OUTPUT
|
||||
echo "Run mode: ALL TESTS (schedule=${{ github.event_name == 'schedule' }}, run_all_tests=${{ inputs.run_all_tests }})"
|
||||
else
|
||||
echo "run_all_tests=false" >> $GITHUB_OUTPUT
|
||||
echo "Run mode: FILTERED (triggered by ${{ github.event_name }})"
|
||||
fi
|
||||
|
||||
- name: Detect file changes
|
||||
id: filter
|
||||
uses: dorny/paths-filter@v3
|
||||
# Only use paths-filter for pull_request events (where it works correctly)
|
||||
# For workflow_dispatch with target_stage, we use GitHub API in the next step
|
||||
if: steps.run-mode.outputs.run_all_tests != 'true' && !inputs.target_stage
|
||||
with:
|
||||
filters: |
|
||||
main_package:
|
||||
- ".github/workflows/pr-test.yml"
|
||||
- ".github/workflows/pr-gate.yml"
|
||||
- ".github/actions/**"
|
||||
- "python/pyproject.toml"
|
||||
- "python/sglang/!(multimodal_gen)/**/!(*.md)"
|
||||
- "scripts/ci/cuda/*"
|
||||
- "scripts/ci/utils/*"
|
||||
- "test/**/!(*.md)"
|
||||
multimodal_gen:
|
||||
- ".github/workflows/pr-test.yml"
|
||||
- ".github/workflows/pr-test-multimodal-gen.yml"
|
||||
- "python/pyproject.toml"
|
||||
- "python/sglang/multimodal_gen/**/!(*.md|*.ipynb)"
|
||||
- "python/sglang/jit_kernel/**"
|
||||
- "python/sglang/jit_kernel/tests/diffusion/**"
|
||||
- "python/sglang/jit_kernel/benchmark/diffusion/**"
|
||||
- "python/sglang/cli/**"
|
||||
jit_kernel:
|
||||
- ".github/workflows/pr-test.yml"
|
||||
- ".github/workflows/pr-test-jit-kernel.yml"
|
||||
- "python/pyproject.toml"
|
||||
- "python/sglang/jit_kernel/**"
|
||||
sgl_kernel:
|
||||
# Intentionally excludes ".github/workflows/pr-test-sgl-kernel.yml" —
|
||||
# see API-side detector below for rationale.
|
||||
- "sgl-kernel/**/!(*.md|THIRDPARTYNOTICES.txt|LICENSE)"
|
||||
|
||||
# For /rerun-stage (workflow_dispatch with target_stage), dorny/paths-filter doesn't work
|
||||
# correctly because it falls back to "last commit" detection which breaks for merge commits.
|
||||
# Instead, we use the GitHub API to compare the PR commit against main.
|
||||
- name: Detect file changes via API (for target_stage)
|
||||
id: filter-api
|
||||
if: inputs.target_stage && inputs.pr_head_sha
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
echo "Detecting file changes via GitHub API for target_stage mode..."
|
||||
echo "PR head SHA: ${{ inputs.pr_head_sha }}"
|
||||
|
||||
# Get the list of changed files by comparing PR commit against main
|
||||
# This correctly handles merge commits by looking at the actual PR diff
|
||||
CHANGED_FILES=$(gh api "repos/${{ github.repository }}/compare/main...${{ inputs.pr_head_sha }}" \
|
||||
--jq '[.files[].filename] | .[]' 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "Warning: Could not fetch changed files from API, assuming no changes"
|
||||
echo "sgl_kernel=false" >> $GITHUB_OUTPUT
|
||||
echo "main_package=false" >> $GITHUB_OUTPUT
|
||||
echo "jit_kernel=false" >> $GITHUB_OUTPUT
|
||||
echo "multimodal_gen=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Changed files:"
|
||||
echo "$CHANGED_FILES" | head -20
|
||||
echo "..."
|
||||
|
||||
# Check for sgl-kernel changes
|
||||
# Note: edits to .github/workflows/pr-test-sgl-kernel.yml are intentionally
|
||||
# NOT considered sgl-kernel changes. That filter line used to be included
|
||||
# so workflow refactors got retested, but in practice it only catches the
|
||||
# workflow's *consumers* (test job definitions), not the wheel build steps
|
||||
# themselves — and gating sgl_kernel=true on it forces a 20-30 min wheel
|
||||
# rebuild + the stage-a-test-1-gpu-small gate for pure CI-yaml edits that
|
||||
# can't actually affect kernel behavior. PRs that touch wheel-build logic
|
||||
# in scripts/ci/cuda/ or sgl-kernel/ still trigger correctly.
|
||||
if echo "$CHANGED_FILES" | grep -qE "^sgl-kernel/"; then
|
||||
echo "sgl_kernel=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected sgl-kernel changes"
|
||||
else
|
||||
echo "sgl_kernel=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check for main_package changes (excluding multimodal_gen, jit_kernel/diffusion, jit_kernel/tests/diffusion, jit_kernel/benchmark/diffusion, cli)
|
||||
# Note: Need to filter out multimodal_gen and diffusion-related paths before checking, not pipe grep -q output
|
||||
MAIN_PKG_FILES=$(echo "$CHANGED_FILES" | grep -E "^(python/sglang/|python/pyproject\.toml|scripts/ci/cuda/|scripts/ci/utils/|test/|\.github/workflows/pr-test\.yml|\.github/workflows/pr-gate\.yml|\.github/actions/)" | grep -v -E "^(python/sglang/multimodal_gen/|python/sglang/jit_kernel/diffusion/|python/sglang/jit_kernel/tests/diffusion/|python/sglang/jit_kernel/benchmark/diffusion/|python/sglang/cli/)" || true)
|
||||
if [ -n "$MAIN_PKG_FILES" ]; then
|
||||
echo "main_package=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected main_package changes"
|
||||
else
|
||||
echo "main_package=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check for jit_kernel changes
|
||||
if echo "$CHANGED_FILES" | grep -qE "^(python/sglang/jit_kernel/|python/pyproject\.toml|\.github/workflows/pr-test\.yml|\.github/workflows/pr-test-jit-kernel\.yml)"; then
|
||||
echo "jit_kernel=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected jit_kernel changes"
|
||||
else
|
||||
echo "jit_kernel=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check for multimodal_gen changes, including diffusion-specific jit_kernel coverage
|
||||
if echo "$CHANGED_FILES" | grep -qE "^(python/sglang/multimodal_gen/|python/sglang/cli/|python/sglang/jit_kernel/diffusion/|python/sglang/jit_kernel/tests/diffusion/|python/sglang/jit_kernel/benchmark/diffusion/|python/pyproject\.toml|\.github/workflows/pr-test\.yml|\.github/workflows/pr-test-multimodal-gen\.yml)"; then
|
||||
echo "multimodal_gen=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected multimodal_gen changes"
|
||||
else
|
||||
echo "multimodal_gen=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Set max-parallel based on run type
|
||||
id: set-parallel
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Determine if this run gets full parallelism (scheduled / high priority)
|
||||
FULL=false
|
||||
if [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||
FULL=true
|
||||
echo "Scheduled run detected, using full parallelism"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request" && "${{ contains(github.event.pull_request.labels.*.name, 'high priority') }}" == "true" ]]; then
|
||||
FULL=true
|
||||
echo "High priority PR detected, using full parallelism"
|
||||
elif [[ -n "${{ inputs.target_stage }}" ]]; then
|
||||
# /rerun-stage (workflow_dispatch): query PR labels via GitHub API
|
||||
# Try SHA lookup first (fork PRs), fallback to branch name (non-fork PRs)
|
||||
LABELS=""
|
||||
PR_HEAD_SHA="${{ inputs.pr_head_sha }}"
|
||||
if [[ -n "$PR_HEAD_SHA" ]]; then
|
||||
LABELS=$(gh api "repos/${{ github.repository }}/commits/${PR_HEAD_SHA}/pulls" \
|
||||
--jq '.[0].labels[].name' 2>/dev/null || true)
|
||||
fi
|
||||
if [[ -z "$LABELS" ]]; then
|
||||
LABELS=$(gh pr list --head "${{ github.ref_name }}" --repo "${{ github.repository }}" \
|
||||
--json labels --jq '.[0].labels[].name' 2>/dev/null || true)
|
||||
fi
|
||||
echo "PR labels: ${LABELS:-"(none)"}"
|
||||
if echo "$LABELS" | grep -Fxq "high priority"; then
|
||||
FULL=true
|
||||
echo "High priority PR detected via API (/rerun-stage), using full parallelism"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set max-parallel for each runner type
|
||||
# 1-gpu-h100: 14 partitions, 1-gpu-5090: 8 partitions, 2-gpu-h100: 4 partitions
|
||||
if [[ "$FULL" == "true" ]]; then
|
||||
LEVEL=full
|
||||
echo "max_parallel=14" >> $GITHUB_OUTPUT
|
||||
echo "max_parallel_small=8" >> $GITHUB_OUTPUT
|
||||
echo "max_parallel_2gpu=4" >> $GITHUB_OUTPUT
|
||||
else
|
||||
LEVEL=low
|
||||
echo "max_parallel=3" >> $GITHUB_OUTPUT
|
||||
echo "max_parallel_small=3" >> $GITHUB_OUTPUT
|
||||
echo "max_parallel_2gpu=2" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
echo "parallel_level=$LEVEL" >> $GITHUB_OUTPUT
|
||||
echo "Parallelism level: $LEVEL"
|
||||
|
||||
- name: Set B200 runner tag
|
||||
id: set-runner
|
||||
run: |
|
||||
# Use kernel-build runner only when sgl_kernel changes are detected AND we're not in target_stage mode
|
||||
# (target_stage skips wheel builds, so we can't use custom kernels)
|
||||
# Use API-based detection (filter-api) for target_stage mode, otherwise use dorny/paths-filter (filter)
|
||||
sgl_kernel="${{ steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel }}"
|
||||
target_stage="${{ inputs.target_stage }}"
|
||||
if [[ "$sgl_kernel" == "true" && -z "$target_stage" ]]; then
|
||||
echo "b200_runner=4-gpu-b200-kernel" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "b200_runner=4-gpu-b200" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Enable retry for CI
|
||||
id: set-retry
|
||||
run: |
|
||||
echo "enable_retry=true" >> $GITHUB_OUTPUT
|
||||
echo "Retry logic enabled for CI"
|
||||
|
||||
- name: Set continue-on-error for full test runs
|
||||
id: set-continue-on-error
|
||||
run: |
|
||||
if [[ "${{ steps.run-mode.outputs.run_all_tests }}" == "true" || "${{ inputs.force_continue_on_error }}" == "true" ]]; then
|
||||
echo "continue_on_error=true" >> $GITHUB_OUTPUT
|
||||
echo "Full test run or force flag detected, enabling continue-on-error to run all tests"
|
||||
else
|
||||
echo "continue_on_error=false" >> $GITHUB_OUTPUT
|
||||
echo "Filtered run, continue-on-error disabled"
|
||||
fi
|
||||
|
||||
- name: Validate target_stage with kernel changes
|
||||
# Fail only when PR has sgl-kernel changes AND the caller didn't opt into include_wheel_build.
|
||||
# include_wheel_build=true means sgl-kernel-build-wheels will run alongside the target stage
|
||||
# (see the sgl_kernel output and sgl-kernel-build-wheels if-conditions above/below), so it's
|
||||
# safe to proceed.
|
||||
if: inputs.target_stage && !inputs.include_wheel_build && (steps.filter-api.outputs.sgl_kernel == 'true' || steps.filter.outputs.sgl_kernel == 'true')
|
||||
run: |
|
||||
echo "::error::Cannot use /rerun-stage when PR has sgl-kernel changes without include_wheel_build."
|
||||
echo "::error::The sgl-kernel-build-wheels job is skipped in target_stage mode by default, but this PR modifies sgl-kernel/ files."
|
||||
echo "::error::The slash-command handler should have set include_wheel_build=true automatically; falling back to /tag-and-rerun-ci."
|
||||
echo ""
|
||||
echo "ERROR: Cannot use /rerun-stage when PR has sgl-kernel changes without include_wheel_build."
|
||||
echo ""
|
||||
echo "This PR modifies files in sgl-kernel/, which requires building custom kernel wheels."
|
||||
echo "Running the target stage without rebuilding the kernel would use the wrong (PyPI)"
|
||||
echo "version of sgl-kernel instead of your changes."
|
||||
echo ""
|
||||
echo "The /rerun-stage handler sets include_wheel_build=true automatically when it detects"
|
||||
echo "sgl-kernel/ changes on the PR. If you see this error, the handler may be outdated."
|
||||
echo ""
|
||||
echo "Alternatives:"
|
||||
echo " /tag-and-rerun-ci - Re-run the full workflow including kernel builds"
|
||||
echo " /rerun-ci - Re-run the full workflow"
|
||||
echo ""
|
||||
exit 1
|
||||
|
||||
- name: Show filter results in summary (table)
|
||||
run: |
|
||||
{
|
||||
echo "## Change Detection"
|
||||
echo ""
|
||||
echo "| Component | Changed |"
|
||||
echo "|-------------------|---------|"
|
||||
echo "| main_package | ${{ steps.filter-api.outputs.main_package || steps.filter.outputs.main_package || steps.run-mode.outputs.run_all_tests }} |"
|
||||
echo "| sgl_kernel (raw) | ${{ steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel }} |"
|
||||
echo "| sgl_kernel (used) | ${{ (!inputs.target_stage || inputs.include_wheel_build) && (steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel) }} |"
|
||||
echo "| jit_kernel | ${{ steps.filter-api.outputs.jit_kernel || steps.filter.outputs.jit_kernel || steps.run-mode.outputs.run_all_tests }} |"
|
||||
echo "| multimodal_gen | ${{ steps.filter-api.outputs.multimodal_gen || steps.filter.outputs.multimodal_gen || steps.run-mode.outputs.run_all_tests }} |"
|
||||
echo "| target_stage | ${{ inputs.target_stage || '(none)' }} |"
|
||||
echo "| detection_method | ${{ inputs.target_stage && 'GitHub API' || 'dorny/paths-filter' }} |"
|
||||
echo "| max_parallel | ${{ steps.set-parallel.outputs.parallel_level }} (h100=${{ steps.set-parallel.outputs.max_parallel }}, 5090=${{ steps.set-parallel.outputs.max_parallel_small }}, 2gpu=${{ steps.set-parallel.outputs.max_parallel_2gpu }}) |"
|
||||
echo "| b200_runner | ${{ steps.set-runner.outputs.b200_runner }} |"
|
||||
echo "| enable_retry | ${{ steps.set-retry.outputs.enable_retry }} |"
|
||||
echo "| continue_on_error | ${{ steps.set-continue-on-error.outputs.continue_on_error }} |"
|
||||
} >> $GITHUB_STEP_SUMMARY
|
||||
@@ -83,282 +83,15 @@ permissions:
|
||||
jobs:
|
||||
# =============================================== check changes ====================================================
|
||||
check-changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
# Use API-based detection for target_stage mode (filter-api), otherwise use dorny/paths-filter (filter)
|
||||
main_package: ${{ steps.filter-api.outputs.main_package || steps.filter.outputs.main_package || steps.run-mode.outputs.run_all_tests }}
|
||||
# sgl_kernel is forced to false when target_stage is set AND include_wheel_build is NOT set,
|
||||
# since sgl-kernel-build-wheels normally skips in target_stage mode. When include_wheel_build
|
||||
# is true, keep the real value so the wheel build runs and the target stage downloads its
|
||||
# artifact (used by /rerun-stage on PRs that modify sgl-kernel/).
|
||||
# This prevents CUSTOM_BUILD_SGL_KERNEL=true when the wheel artifacts aren't available.
|
||||
# Note: If PR has kernel changes AND target_stage is set AND include_wheel_build is NOT set,
|
||||
# the validate-target-stage step will fail.
|
||||
sgl_kernel: ${{ (!inputs.target_stage || inputs.include_wheel_build) && (steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel) }}
|
||||
# Raw sgl_kernel value before target_stage override (used for validation)
|
||||
sgl_kernel_raw: ${{ steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel }}
|
||||
jit_kernel: ${{ steps.filter-api.outputs.jit_kernel || steps.filter.outputs.jit_kernel || steps.run-mode.outputs.run_all_tests }}
|
||||
multimodal_gen: ${{ steps.filter-api.outputs.multimodal_gen || steps.filter.outputs.multimodal_gen || steps.run-mode.outputs.run_all_tests }}
|
||||
max_parallel: ${{ steps.set-parallel.outputs.max_parallel }}
|
||||
max_parallel_small: ${{ steps.set-parallel.outputs.max_parallel_small }}
|
||||
max_parallel_2gpu: ${{ steps.set-parallel.outputs.max_parallel_2gpu }}
|
||||
b200_runner: ${{ steps.set-runner.outputs.b200_runner }}
|
||||
enable_retry: ${{ steps.set-retry.outputs.enable_retry }}
|
||||
continue_on_error: ${{ steps.set-continue-on-error.outputs.continue_on_error }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.pr_head_sha || inputs.git_ref || github.sha }}
|
||||
|
||||
- uses: ./.github/actions/check-maintenance
|
||||
|
||||
- name: Determine run mode
|
||||
id: run-mode
|
||||
run: |
|
||||
# Run all tests for scheduled runs and workflow_call (when ref input is provided)
|
||||
# Note: github.event_name is inherited from caller, so we detect workflow_call by checking inputs.git_ref
|
||||
if [[ "${{ github.event_name }}" == "schedule" || "${{ inputs.run_all_tests }}" == "true" ]]; then
|
||||
echo "run_all_tests=true" >> $GITHUB_OUTPUT
|
||||
echo "Run mode: ALL TESTS (schedule=${{ github.event_name == 'schedule' }}, run_all_tests=${{ inputs.run_all_tests }})"
|
||||
else
|
||||
echo "run_all_tests=false" >> $GITHUB_OUTPUT
|
||||
echo "Run mode: FILTERED (triggered by ${{ github.event_name }})"
|
||||
fi
|
||||
|
||||
- name: Detect file changes
|
||||
id: filter
|
||||
uses: dorny/paths-filter@v3
|
||||
# Only use paths-filter for pull_request events (where it works correctly)
|
||||
# For workflow_dispatch with target_stage, we use GitHub API in the next step
|
||||
if: steps.run-mode.outputs.run_all_tests != 'true' && !inputs.target_stage
|
||||
with:
|
||||
filters: |
|
||||
main_package:
|
||||
- ".github/workflows/pr-test.yml"
|
||||
- ".github/workflows/pr-gate.yml"
|
||||
- ".github/actions/**"
|
||||
- "python/pyproject.toml"
|
||||
- "python/sglang/!(multimodal_gen)/**/!(*.md)"
|
||||
- "scripts/ci/cuda/*"
|
||||
- "scripts/ci/utils/*"
|
||||
- "test/**/!(*.md)"
|
||||
multimodal_gen:
|
||||
- ".github/workflows/pr-test.yml"
|
||||
- ".github/workflows/pr-test-multimodal-gen.yml"
|
||||
- "python/pyproject.toml"
|
||||
- "python/sglang/multimodal_gen/**/!(*.md|*.ipynb)"
|
||||
- "python/sglang/jit_kernel/**"
|
||||
- "python/sglang/jit_kernel/tests/diffusion/**"
|
||||
- "python/sglang/jit_kernel/benchmark/diffusion/**"
|
||||
- "python/sglang/cli/**"
|
||||
jit_kernel:
|
||||
- ".github/workflows/pr-test.yml"
|
||||
- ".github/workflows/pr-test-jit-kernel.yml"
|
||||
- "python/pyproject.toml"
|
||||
- "python/sglang/jit_kernel/**"
|
||||
sgl_kernel:
|
||||
# Intentionally excludes ".github/workflows/pr-test-sgl-kernel.yml" —
|
||||
# see API-side detector below for rationale.
|
||||
- "sgl-kernel/**/!(*.md|THIRDPARTYNOTICES.txt|LICENSE)"
|
||||
|
||||
# For /rerun-stage (workflow_dispatch with target_stage), dorny/paths-filter doesn't work
|
||||
# correctly because it falls back to "last commit" detection which breaks for merge commits.
|
||||
# Instead, we use the GitHub API to compare the PR commit against main.
|
||||
- name: Detect file changes via API (for target_stage)
|
||||
id: filter-api
|
||||
if: inputs.target_stage && inputs.pr_head_sha
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
echo "Detecting file changes via GitHub API for target_stage mode..."
|
||||
echo "PR head SHA: ${{ inputs.pr_head_sha }}"
|
||||
|
||||
# Get the list of changed files by comparing PR commit against main
|
||||
# This correctly handles merge commits by looking at the actual PR diff
|
||||
CHANGED_FILES=$(gh api "repos/${{ github.repository }}/compare/main...${{ inputs.pr_head_sha }}" \
|
||||
--jq '[.files[].filename] | .[]' 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "Warning: Could not fetch changed files from API, assuming no changes"
|
||||
echo "sgl_kernel=false" >> $GITHUB_OUTPUT
|
||||
echo "main_package=false" >> $GITHUB_OUTPUT
|
||||
echo "jit_kernel=false" >> $GITHUB_OUTPUT
|
||||
echo "multimodal_gen=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Changed files:"
|
||||
echo "$CHANGED_FILES" | head -20
|
||||
echo "..."
|
||||
|
||||
# Check for sgl-kernel changes
|
||||
# Note: edits to .github/workflows/pr-test-sgl-kernel.yml are intentionally
|
||||
# NOT considered sgl-kernel changes. That filter line used to be included
|
||||
# so workflow refactors got retested, but in practice it only catches the
|
||||
# workflow's *consumers* (test job definitions), not the wheel build steps
|
||||
# themselves — and gating sgl_kernel=true on it forces a 20-30 min wheel
|
||||
# rebuild + the stage-a-test-1-gpu-small gate for pure CI-yaml edits that
|
||||
# can't actually affect kernel behavior. PRs that touch wheel-build logic
|
||||
# in scripts/ci/cuda/ or sgl-kernel/ still trigger correctly.
|
||||
if echo "$CHANGED_FILES" | grep -qE "^sgl-kernel/"; then
|
||||
echo "sgl_kernel=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected sgl-kernel changes"
|
||||
else
|
||||
echo "sgl_kernel=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check for main_package changes (excluding multimodal_gen, jit_kernel/diffusion, jit_kernel/tests/diffusion, jit_kernel/benchmark/diffusion, cli)
|
||||
# Note: Need to filter out multimodal_gen and diffusion-related paths before checking, not pipe grep -q output
|
||||
MAIN_PKG_FILES=$(echo "$CHANGED_FILES" | grep -E "^(python/sglang/|python/pyproject\.toml|scripts/ci/cuda/|scripts/ci/utils/|test/|\.github/workflows/pr-test\.yml|\.github/workflows/pr-gate\.yml|\.github/actions/)" | grep -v -E "^(python/sglang/multimodal_gen/|python/sglang/jit_kernel/diffusion/|python/sglang/jit_kernel/tests/diffusion/|python/sglang/jit_kernel/benchmark/diffusion/|python/sglang/cli/)" || true)
|
||||
if [ -n "$MAIN_PKG_FILES" ]; then
|
||||
echo "main_package=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected main_package changes"
|
||||
else
|
||||
echo "main_package=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check for jit_kernel changes
|
||||
if echo "$CHANGED_FILES" | grep -qE "^(python/sglang/jit_kernel/|python/pyproject\.toml|\.github/workflows/pr-test\.yml|\.github/workflows/pr-test-jit-kernel\.yml)"; then
|
||||
echo "jit_kernel=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected jit_kernel changes"
|
||||
else
|
||||
echo "jit_kernel=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check for multimodal_gen changes, including diffusion-specific jit_kernel coverage
|
||||
if echo "$CHANGED_FILES" | grep -qE "^(python/sglang/multimodal_gen/|python/sglang/cli/|python/sglang/jit_kernel/diffusion/|python/sglang/jit_kernel/tests/diffusion/|python/sglang/jit_kernel/benchmark/diffusion/|python/pyproject\.toml|\.github/workflows/pr-test\.yml|\.github/workflows/pr-test-multimodal-gen\.yml)"; then
|
||||
echo "multimodal_gen=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected multimodal_gen changes"
|
||||
else
|
||||
echo "multimodal_gen=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Set max-parallel based on run type
|
||||
id: set-parallel
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Determine if this run gets full parallelism (scheduled / high priority)
|
||||
FULL=false
|
||||
if [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||
FULL=true
|
||||
echo "Scheduled run detected, using full parallelism"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request" && "${{ contains(github.event.pull_request.labels.*.name, 'high priority') }}" == "true" ]]; then
|
||||
FULL=true
|
||||
echo "High priority PR detected, using full parallelism"
|
||||
elif [[ -n "${{ inputs.target_stage }}" ]]; then
|
||||
# /rerun-stage (workflow_dispatch): query PR labels via GitHub API
|
||||
# Try SHA lookup first (fork PRs), fallback to branch name (non-fork PRs)
|
||||
LABELS=""
|
||||
PR_HEAD_SHA="${{ inputs.pr_head_sha }}"
|
||||
if [[ -n "$PR_HEAD_SHA" ]]; then
|
||||
LABELS=$(gh api "repos/${{ github.repository }}/commits/${PR_HEAD_SHA}/pulls" \
|
||||
--jq '.[0].labels[].name' 2>/dev/null || true)
|
||||
fi
|
||||
if [[ -z "$LABELS" ]]; then
|
||||
LABELS=$(gh pr list --head "${{ github.ref_name }}" --repo "${{ github.repository }}" \
|
||||
--json labels --jq '.[0].labels[].name' 2>/dev/null || true)
|
||||
fi
|
||||
echo "PR labels: ${LABELS:-"(none)"}"
|
||||
if echo "$LABELS" | grep -Fxq "high priority"; then
|
||||
FULL=true
|
||||
echo "High priority PR detected via API (/rerun-stage), using full parallelism"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set max-parallel for each runner type
|
||||
# 1-gpu-h100: 14 partitions, 1-gpu-5090: 8 partitions, 2-gpu-h100: 4 partitions
|
||||
if [[ "$FULL" == "true" ]]; then
|
||||
LEVEL=full
|
||||
echo "max_parallel=14" >> $GITHUB_OUTPUT
|
||||
echo "max_parallel_small=8" >> $GITHUB_OUTPUT
|
||||
echo "max_parallel_2gpu=4" >> $GITHUB_OUTPUT
|
||||
else
|
||||
LEVEL=low
|
||||
echo "max_parallel=3" >> $GITHUB_OUTPUT
|
||||
echo "max_parallel_small=3" >> $GITHUB_OUTPUT
|
||||
echo "max_parallel_2gpu=2" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
echo "parallel_level=$LEVEL" >> $GITHUB_OUTPUT
|
||||
echo "Parallelism level: $LEVEL"
|
||||
|
||||
- name: Set B200 runner tag
|
||||
id: set-runner
|
||||
run: |
|
||||
# Use kernel-build runner only when sgl_kernel changes are detected AND we're not in target_stage mode
|
||||
# (target_stage skips wheel builds, so we can't use custom kernels)
|
||||
# Use API-based detection (filter-api) for target_stage mode, otherwise use dorny/paths-filter (filter)
|
||||
sgl_kernel="${{ steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel }}"
|
||||
target_stage="${{ inputs.target_stage }}"
|
||||
if [[ "$sgl_kernel" == "true" && -z "$target_stage" ]]; then
|
||||
echo "b200_runner=4-gpu-b200-kernel" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "b200_runner=4-gpu-b200" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Enable retry for CI
|
||||
id: set-retry
|
||||
run: |
|
||||
echo "enable_retry=true" >> $GITHUB_OUTPUT
|
||||
echo "Retry logic enabled for CI"
|
||||
|
||||
- name: Set continue-on-error for full test runs
|
||||
id: set-continue-on-error
|
||||
run: |
|
||||
if [[ "${{ steps.run-mode.outputs.run_all_tests }}" == "true" || "${{ inputs.force_continue_on_error }}" == "true" ]]; then
|
||||
echo "continue_on_error=true" >> $GITHUB_OUTPUT
|
||||
echo "Full test run or force flag detected, enabling continue-on-error to run all tests"
|
||||
else
|
||||
echo "continue_on_error=false" >> $GITHUB_OUTPUT
|
||||
echo "Filtered run, continue-on-error disabled"
|
||||
fi
|
||||
|
||||
- name: Validate target_stage with kernel changes
|
||||
# Fail only when PR has sgl-kernel changes AND the caller didn't opt into include_wheel_build.
|
||||
# include_wheel_build=true means sgl-kernel-build-wheels will run alongside the target stage
|
||||
# (see the sgl_kernel output and sgl-kernel-build-wheels if-conditions above/below), so it's
|
||||
# safe to proceed.
|
||||
if: inputs.target_stage && !inputs.include_wheel_build && (steps.filter-api.outputs.sgl_kernel == 'true' || steps.filter.outputs.sgl_kernel == 'true')
|
||||
run: |
|
||||
echo "::error::Cannot use /rerun-stage when PR has sgl-kernel changes without include_wheel_build."
|
||||
echo "::error::The sgl-kernel-build-wheels job is skipped in target_stage mode by default, but this PR modifies sgl-kernel/ files."
|
||||
echo "::error::The slash-command handler should have set include_wheel_build=true automatically; falling back to /tag-and-rerun-ci."
|
||||
echo ""
|
||||
echo "ERROR: Cannot use /rerun-stage when PR has sgl-kernel changes without include_wheel_build."
|
||||
echo ""
|
||||
echo "This PR modifies files in sgl-kernel/, which requires building custom kernel wheels."
|
||||
echo "Running the target stage without rebuilding the kernel would use the wrong (PyPI)"
|
||||
echo "version of sgl-kernel instead of your changes."
|
||||
echo ""
|
||||
echo "The /rerun-stage handler sets include_wheel_build=true automatically when it detects"
|
||||
echo "sgl-kernel/ changes on the PR. If you see this error, the handler may be outdated."
|
||||
echo ""
|
||||
echo "Alternatives:"
|
||||
echo " /tag-and-rerun-ci - Re-run the full workflow including kernel builds"
|
||||
echo " /rerun-ci - Re-run the full workflow"
|
||||
echo ""
|
||||
exit 1
|
||||
|
||||
- name: Show filter results in summary (table)
|
||||
run: |
|
||||
{
|
||||
echo "## Change Detection"
|
||||
echo ""
|
||||
echo "| Component | Changed |"
|
||||
echo "|-------------------|---------|"
|
||||
echo "| main_package | ${{ steps.filter-api.outputs.main_package || steps.filter.outputs.main_package || steps.run-mode.outputs.run_all_tests }} |"
|
||||
echo "| sgl_kernel (raw) | ${{ steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel }} |"
|
||||
echo "| sgl_kernel (used) | ${{ (!inputs.target_stage || inputs.include_wheel_build) && (steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel) }} |"
|
||||
echo "| jit_kernel | ${{ steps.filter-api.outputs.jit_kernel || steps.filter.outputs.jit_kernel || steps.run-mode.outputs.run_all_tests }} |"
|
||||
echo "| multimodal_gen | ${{ steps.filter-api.outputs.multimodal_gen || steps.filter.outputs.multimodal_gen || steps.run-mode.outputs.run_all_tests }} |"
|
||||
echo "| target_stage | ${{ inputs.target_stage || '(none)' }} |"
|
||||
echo "| detection_method | ${{ inputs.target_stage && 'GitHub API' || 'dorny/paths-filter' }} |"
|
||||
echo "| max_parallel | ${{ steps.set-parallel.outputs.parallel_level }} (h100=${{ steps.set-parallel.outputs.max_parallel }}, 5090=${{ steps.set-parallel.outputs.max_parallel_small }}, 2gpu=${{ steps.set-parallel.outputs.max_parallel_2gpu }}) |"
|
||||
echo "| b200_runner | ${{ steps.set-runner.outputs.b200_runner }} |"
|
||||
echo "| enable_retry | ${{ steps.set-retry.outputs.enable_retry }} |"
|
||||
echo "| continue_on_error | ${{ steps.set-continue-on-error.outputs.continue_on_error }} |"
|
||||
} >> $GITHUB_STEP_SUMMARY
|
||||
uses: ./.github/workflows/_pr-test-check-changes.yml
|
||||
with:
|
||||
pr_head_sha: ${{ inputs.pr_head_sha || '' }}
|
||||
git_ref: ${{ inputs.git_ref || '' }}
|
||||
target_stage: ${{ inputs.target_stage || '' }}
|
||||
include_wheel_build: ${{ inputs.include_wheel_build == true }}
|
||||
run_all_tests: ${{ inputs.run_all_tests == true }}
|
||||
force_continue_on_error: ${{ inputs.force_continue_on_error == true }}
|
||||
secrets: inherit
|
||||
|
||||
# =============================================== Wait Jobs for Sequential PR Execution ====================================================
|
||||
# These jobs poll GitHub API to wait for previous stages to complete.
|
||||
|
||||
Reference in New Issue
Block a user