[CI] /rerun-stage: auto-include wheel build when PR modifies sgl-kernel/ (#23492)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
de962f3274
commit
14ac14287c
@@ -25,6 +25,11 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: ""
|
default: ""
|
||||||
|
include_wheel_build:
|
||||||
|
description: "When set with target_stage, also run sgl-kernel-build-wheels so the target stage uses the freshly-built kernel (for /rerun-stage on PRs that modify sgl-kernel/)"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
test_parallel_dispatch:
|
test_parallel_dispatch:
|
||||||
description: "Test parallel dispatch behavior (simulates scheduled run)"
|
description: "Test parallel dispatch behavior (simulates scheduled run)"
|
||||||
required: false
|
required: false
|
||||||
@@ -80,10 +85,14 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
# Use API-based detection for target_stage mode (filter-api), otherwise use dorny/paths-filter (filter)
|
# 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 }}
|
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, since sgl-kernel-build-wheels won't run
|
# sgl_kernel is forced to false when target_stage is set AND include_wheel_build is NOT set,
|
||||||
# This prevents CUSTOM_BUILD_SGL_KERNEL=true when the wheel artifacts aren't available
|
# since sgl-kernel-build-wheels normally skips in target_stage mode. When include_wheel_build
|
||||||
# Note: If PR has kernel changes AND target_stage is set, the validate-target-stage step will fail
|
# is true, keep the real value so the wheel build runs and the target stage downloads its
|
||||||
sgl_kernel: ${{ !inputs.target_stage && (steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel) }}
|
# 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)
|
# 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 }}
|
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 }}
|
jit_kernel: ${{ steps.filter-api.outputs.jit_kernel || steps.filter.outputs.jit_kernel || steps.run-mode.outputs.run_all_tests }}
|
||||||
@@ -298,20 +307,26 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Validate target_stage with kernel changes
|
- name: Validate target_stage with kernel changes
|
||||||
# Use API-based detection (filter-api) for target_stage mode, otherwise use dorny/paths-filter (filter)
|
# Fail only when PR has sgl-kernel changes AND the caller didn't opt into include_wheel_build.
|
||||||
if: inputs.target_stage && (steps.filter-api.outputs.sgl_kernel == 'true' || steps.filter.outputs.sgl_kernel == 'true')
|
# 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: |
|
run: |
|
||||||
echo "::error::Cannot use /rerun-stage when PR has sgl-kernel changes."
|
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, but this PR modifies sgl-kernel/ files."
|
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::Please use /tag-and-rerun-ci to run the full workflow including kernel builds."
|
echo "::error::The slash-command handler should have set include_wheel_build=true automatically; falling back to /tag-and-rerun-ci."
|
||||||
echo ""
|
echo ""
|
||||||
echo "ERROR: Cannot use /rerun-stage when PR has sgl-kernel changes."
|
echo "ERROR: Cannot use /rerun-stage when PR has sgl-kernel changes without include_wheel_build."
|
||||||
echo ""
|
echo ""
|
||||||
echo "This PR modifies files in sgl-kernel/, which requires building custom kernel wheels."
|
echo "This PR modifies files in sgl-kernel/, which requires building custom kernel wheels."
|
||||||
echo "The /rerun-stage command skips the wheel build job, so the test would run against"
|
echo "Running the target stage without rebuilding the kernel would use the wrong (PyPI)"
|
||||||
echo "the wrong (PyPI) version of sgl-kernel instead of your changes."
|
echo "version of sgl-kernel instead of your changes."
|
||||||
echo ""
|
echo ""
|
||||||
echo "To properly test your kernel changes, use one of these commands instead:"
|
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 " /tag-and-rerun-ci - Re-run the full workflow including kernel builds"
|
||||||
echo " /rerun-ci - Re-run the full workflow"
|
echo " /rerun-ci - Re-run the full workflow"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -326,7 +341,7 @@ jobs:
|
|||||||
echo "|-------------------|---------|"
|
echo "|-------------------|---------|"
|
||||||
echo "| main_package | ${{ steps.filter-api.outputs.main_package || steps.filter.outputs.main_package || steps.run-mode.outputs.run_all_tests }} |"
|
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 (raw) | ${{ steps.filter-api.outputs.sgl_kernel || steps.filter.outputs.sgl_kernel }} |"
|
||||||
echo "| sgl_kernel (used) | ${{ !inputs.target_stage && (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 "| 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 "| 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 "| target_stage | ${{ inputs.target_stage || '(none)' }} |"
|
||||||
@@ -421,8 +436,22 @@ jobs:
|
|||||||
|
|
||||||
sgl-kernel-build-wheels:
|
sgl-kernel-build-wheels:
|
||||||
needs: [check-changes, call-gate]
|
needs: [check-changes, call-gate]
|
||||||
# Skip for scheduled runs (they run stages independently) and when target_stage is set
|
# Skip for scheduled runs (they run stages independently). Runs in target_stage mode only when
|
||||||
if: github.event_name != 'schedule' && inputs.test_parallel_dispatch != true && !inputs.target_stage && needs.check-changes.outputs.sgl_kernel == 'true'
|
# include_wheel_build is true (i.e. /rerun-stage on a PR with sgl-kernel changes), so the
|
||||||
|
# target stage can download the freshly-built wheel.
|
||||||
|
#
|
||||||
|
# `always()` lets us run when call-gate is skipped (which it always is in target_stage mode by
|
||||||
|
# design). The explicit needs.<x>.result checks preserve old gating for the normal PR path.
|
||||||
|
if: |
|
||||||
|
always() &&
|
||||||
|
github.event_name != 'schedule' &&
|
||||||
|
inputs.test_parallel_dispatch != true &&
|
||||||
|
needs.check-changes.result == 'success' &&
|
||||||
|
needs.check-changes.outputs.sgl_kernel == 'true' &&
|
||||||
|
(
|
||||||
|
(!inputs.target_stage && needs.call-gate.result == 'success') ||
|
||||||
|
(inputs.target_stage && inputs.include_wheel_build)
|
||||||
|
)
|
||||||
runs-on: x64-kernel-build-node
|
runs-on: x64-kernel-build-node
|
||||||
timeout-minutes: 240
|
timeout-minutes: 240
|
||||||
strategy:
|
strategy:
|
||||||
@@ -469,8 +498,20 @@ jobs:
|
|||||||
|
|
||||||
sgl-kernel-build-wheels-arm:
|
sgl-kernel-build-wheels-arm:
|
||||||
needs: [check-changes, call-gate]
|
needs: [check-changes, call-gate]
|
||||||
# Skip for scheduled runs (they run stages independently) and when target_stage is set
|
# Skip for scheduled runs (they run stages independently). Runs in target_stage mode only when
|
||||||
if: github.event_name != 'schedule' && inputs.test_parallel_dispatch != true && !inputs.target_stage && needs.check-changes.outputs.sgl_kernel == 'true'
|
# include_wheel_build is true (i.e. /rerun-stage on a PR with sgl-kernel changes).
|
||||||
|
#
|
||||||
|
# See sgl-kernel-build-wheels above for the always() + result-check rationale.
|
||||||
|
if: |
|
||||||
|
always() &&
|
||||||
|
github.event_name != 'schedule' &&
|
||||||
|
inputs.test_parallel_dispatch != true &&
|
||||||
|
needs.check-changes.result == 'success' &&
|
||||||
|
needs.check-changes.outputs.sgl_kernel == 'true' &&
|
||||||
|
(
|
||||||
|
(!inputs.target_stage && needs.call-gate.result == 'success') ||
|
||||||
|
(inputs.target_stage && inputs.include_wheel_build)
|
||||||
|
)
|
||||||
runs-on: arm-kernel-build-node
|
runs-on: arm-kernel-build-node
|
||||||
timeout-minutes: 240
|
timeout-minutes: 240
|
||||||
strategy:
|
strategy:
|
||||||
@@ -946,7 +987,7 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
stage-c-test-4-gpu-h100:
|
stage-c-test-4-gpu-h100:
|
||||||
needs: [check-changes, call-gate, wait-for-stage-b]
|
needs: [check-changes, call-gate, wait-for-stage-b, sgl-kernel-build-wheels]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
(
|
(
|
||||||
@@ -1004,7 +1045,7 @@ jobs:
|
|||||||
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
||||||
|
|
||||||
stage-c-test-8-gpu-h200:
|
stage-c-test-8-gpu-h200:
|
||||||
needs: [check-changes, call-gate, wait-for-stage-b]
|
needs: [check-changes, call-gate, wait-for-stage-b, sgl-kernel-build-wheels]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
(
|
(
|
||||||
@@ -1081,7 +1122,7 @@ jobs:
|
|||||||
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
||||||
|
|
||||||
stage-c-test-8-gpu-h20:
|
stage-c-test-8-gpu-h20:
|
||||||
needs: [check-changes, call-gate, wait-for-stage-b]
|
needs: [check-changes, call-gate, wait-for-stage-b, sgl-kernel-build-wheels]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
(
|
(
|
||||||
@@ -1142,7 +1183,7 @@ jobs:
|
|||||||
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
||||||
|
|
||||||
stage-c-test-deepep-4-gpu-h100:
|
stage-c-test-deepep-4-gpu-h100:
|
||||||
needs: [check-changes, call-gate, wait-for-stage-b]
|
needs: [check-changes, call-gate, wait-for-stage-b, sgl-kernel-build-wheels]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
(
|
(
|
||||||
@@ -1211,7 +1252,7 @@ jobs:
|
|||||||
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
||||||
|
|
||||||
stage-c-test-deepep-8-gpu-h200:
|
stage-c-test-deepep-8-gpu-h200:
|
||||||
needs: [check-changes, call-gate, wait-for-stage-b]
|
needs: [check-changes, call-gate, wait-for-stage-b, sgl-kernel-build-wheels]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
(
|
(
|
||||||
@@ -1281,7 +1322,7 @@ jobs:
|
|||||||
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
||||||
|
|
||||||
stage-c-test-4-gpu-b200:
|
stage-c-test-4-gpu-b200:
|
||||||
needs: [check-changes, call-gate, wait-for-stage-b]
|
needs: [check-changes, call-gate, wait-for-stage-b, sgl-kernel-build-wheels]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
(
|
(
|
||||||
@@ -1340,7 +1381,7 @@ jobs:
|
|||||||
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
run: bash scripts/ci/cuda/ci_cleanup_venv.sh
|
||||||
|
|
||||||
stage-c-test-4-gpu-b200-small:
|
stage-c-test-4-gpu-b200-small:
|
||||||
needs: [check-changes, call-gate, wait-for-stage-b]
|
needs: [check-changes, call-gate, wait-for-stage-b, sgl-kernel-build-wheels]
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -367,6 +367,18 @@ def handle_rerun_stage(
|
|||||||
)
|
)
|
||||||
print(f"PR is from fork: {is_fork}")
|
print(f"PR is from fork: {is_fork}")
|
||||||
|
|
||||||
|
# If the PR modifies sgl-kernel/, the target stage would otherwise use the
|
||||||
|
# PyPI sgl-kernel wheel instead of the PR's changes (sgl-kernel-build-wheels
|
||||||
|
# skips in target_stage mode by default). Set include_wheel_build=true so the
|
||||||
|
# workflow runs sgl-kernel-build-wheels alongside the target stage; the target
|
||||||
|
# stage waits for the build via its needs list.
|
||||||
|
kernel_changes = has_sgl_kernel_changes(pr)
|
||||||
|
if kernel_changes:
|
||||||
|
print(
|
||||||
|
"PR modifies sgl-kernel/ - setting include_wheel_build=true so the "
|
||||||
|
"target stage gets the freshly-built wheel instead of the PyPI one."
|
||||||
|
)
|
||||||
|
|
||||||
# pr_head_sha is used for fork PRs (passed to workflow and used for URL lookup)
|
# pr_head_sha is used for fork PRs (passed to workflow and used for URL lookup)
|
||||||
pr_head_sha = None
|
pr_head_sha = None
|
||||||
|
|
||||||
@@ -378,25 +390,25 @@ def handle_rerun_stage(
|
|||||||
print(
|
print(
|
||||||
f"Triggering {workflow_name} workflow on ref: {ref}, PR head SHA: {pr_head_sha}"
|
f"Triggering {workflow_name} workflow on ref: {ref}, PR head SHA: {pr_head_sha}"
|
||||||
)
|
)
|
||||||
if is_amd_stage:
|
inputs = {
|
||||||
inputs = {
|
"target_stage": stage_name,
|
||||||
"target_stage": stage_name,
|
"pr_head_sha": pr_head_sha,
|
||||||
"pr_head_sha": pr_head_sha,
|
}
|
||||||
}
|
|
||||||
else:
|
|
||||||
inputs = {
|
|
||||||
"target_stage": stage_name,
|
|
||||||
"pr_head_sha": pr_head_sha,
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
# For non-fork PRs: dispatch on the PR branch directly
|
# For non-fork PRs: dispatch on the PR branch directly
|
||||||
# This allows testing workflow changes before merge
|
# This allows testing workflow changes before merge
|
||||||
ref = pr.head.ref
|
ref = pr.head.ref
|
||||||
print(f"Triggering {workflow_name} workflow on branch: {ref}")
|
print(f"Triggering {workflow_name} workflow on branch: {ref}")
|
||||||
if is_amd_stage:
|
inputs = {"target_stage": stage_name}
|
||||||
inputs = {"target_stage": stage_name}
|
|
||||||
else:
|
# For NVIDIA stages, honor the sgl-kernel / include_wheel_build flow. AMD is
|
||||||
inputs = {"target_stage": stage_name}
|
# a separate workflow that doesn't share the same wheel-build pipeline.
|
||||||
|
if kernel_changes and not is_amd_stage:
|
||||||
|
inputs["include_wheel_build"] = "true"
|
||||||
|
# include_wheel_build relies on filter-api detecting kernel changes, which
|
||||||
|
# requires pr_head_sha. Ensure it's set even for non-fork PRs.
|
||||||
|
if not is_fork:
|
||||||
|
inputs["pr_head_sha"] = pr.head.sha
|
||||||
|
|
||||||
# Record dispatch time before triggering
|
# Record dispatch time before triggering
|
||||||
dispatch_time = time.time()
|
dispatch_time = time.time()
|
||||||
|
|||||||
Reference in New Issue
Block a user