From 4adfc6cf7e41c94a344b009ece1059f33092c893 Mon Sep 17 00:00:00 2001 From: Alison Shao <54658187+alisonshao@users.noreply.github.com> Date: Fri, 15 May 2026 01:01:23 -0700 Subject: [PATCH] ci: dispatch pr-test-extra.yml from pr-test.yml on the scheduled cron (#25320) Co-authored-by: hnyls2002 Co-authored-by: Liangsheng Yin --- .github/workflows/_pr-awareness-comment.yml | 124 +++++++++++++++++++ .github/workflows/_pr-test-check-changes.yml | 1 + .github/workflows/_pr-test-stage.yml | 1 + .github/workflows/pr-test-extra.yml | 15 ++- .github/workflows/pr-test.yml | 26 +++- 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/_pr-awareness-comment.yml diff --git a/.github/workflows/_pr-awareness-comment.yml b/.github/workflows/_pr-awareness-comment.yml new file mode 100644 index 000000000..65f7d00ea --- /dev/null +++ b/.github/workflows/_pr-awareness-comment.yml @@ -0,0 +1,124 @@ +name: PR Awareness Comment +# Reusable workflow maintaining an "awareness block" at the bottom of the PR +# body: extra-CI opt-in state + latest PR Test / PR Test Extra run URLs. +# Called from pr-test.yml + pr-test-extra.yml after their check-changes job so +# the block appears ~30s in, not after downstream stages. Each caller updates +# only its own slot; the other slot is preserved verbatim. +# +# pulls.update fires `pull_request: edited`, which is NOT in the default +# trigger types, so this does not loop back into the CI workflows. + +on: + workflow_call: + inputs: + workflow_kind: + description: "Which workflow is calling: 'pr-test' or 'pr-test-extra'" + required: true + type: string + +permissions: + pull-requests: write + +jobs: + update-pr-body: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + # Serialize concurrent writers from pr-test + pr-test-extra: both race on + # get/modify/update of the PR body, and the later `pulls.update` clobbers + # the earlier writer's slot. Group is per-PR so unrelated PRs don't block. + # cancel-in-progress MUST be false — cancelling would drop the earlier update. + concurrency: + group: pr-awareness-${{ github.event.pull_request.number }} + cancel-in-progress: false + steps: + - name: Update awareness block in PR body + uses: actions/github-script@v7 + env: + # Pass input via env (process.env) instead of `${{ }}` interpolation + # into the JS body, so future user-controlled values can't inject JS. + KIND: ${{ inputs.workflow_kind }} + with: + script: | + const kind = process.env.KIND; + const labels = context.payload.pull_request.labels.map(l => l.name); + const hasExtra = labels.includes('run-ci-extra'); + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + const outerStart = ''; + const outerEnd = ''; + const ptStart = ''; + const ptEnd = ''; + const peStart = ''; + const peEnd = ''; + + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + const body = pr.body || ''; + + // Extract existing block + slots so we preserve the other workflow's URL + const blockRe = new RegExp(`${outerStart}[\\s\\S]*?${outerEnd}`); + const existingBlock = (body.match(blockRe) || [''])[0]; + + function extractSlot(text, start, end) { + const re = new RegExp(`${start}([\\s\\S]*?)${end}`); + const m = text.match(re); + return m ? m[1] : null; + } + const prevPrTest = extractSlot(existingBlock, ptStart, ptEnd); + const prevPrExtra = extractSlot(existingBlock, peStart, peEnd); + + // Each caller updates only its own slot; the other slot is preserved. + // For pr-test-extra without the label: the rest of its graph is + // label-gate-skipped, so linking to that run would mislead — inline + // a warning notice in the slot instead. + // When kind=pr-test and hasExtra=false, force-reset the extra slot + // rather than carrying prevPrExtra forward — otherwise an old Run + // URL from a previous opt-in lingers after the label is removed, + // until pr-test-extra runs again and rewrites the slot. + const notEnabledText = ':warning: **Not enabled** — add `run-ci-extra` label to opt in.'; + const newPrTest = (kind === 'pr-test') + ? `[Run #${context.runId}](${runUrl})` + : (prevPrTest || '_Not run yet_'); + const newPrExtra = (kind === 'pr-test-extra') + ? (hasExtra + ? `[Run #${context.runId}](${runUrl})` + : notEnabledText) + : (hasExtra + ? (prevPrExtra || '_Not run yet_') + : notEnabledText); + + const newBlock = [ + outerStart, + '---', + '### CI Awareness', + '', + `Latest PR Test: ${ptStart}${newPrTest}${ptEnd}`, + `Latest PR Test (Extra): ${peStart}${newPrExtra}${peEnd}`, + outerEnd, + ].join('\n'); + + // Replace existing block in place, or append at body end. + let newBody; + if (blockRe.test(body)) { + newBody = body.replace(blockRe, newBlock); + } else { + const sep = body.length === 0 + ? '' + : (body.endsWith('\n') ? '\n---\n\n' : '\n\n---\n\n'); + newBody = `${body}${sep}${newBlock}`; + } + + if (newBody !== body) { + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + body: newBody, + }); + core.info(`Updated PR body awareness block (caller=${kind}).`); + } else { + core.info(`PR body awareness block already up-to-date (caller=${kind}).`); + } diff --git a/.github/workflows/_pr-test-check-changes.yml b/.github/workflows/_pr-test-check-changes.yml index eef3d88c8..26d63807b 100644 --- a/.github/workflows/_pr-test-check-changes.yml +++ b/.github/workflows/_pr-test-check-changes.yml @@ -49,6 +49,7 @@ on: jobs: run: + name: check-changes runs-on: ubuntu-latest outputs: # Use API-based detection for target_stage mode (filter-api), otherwise use dorny/paths-filter (filter) diff --git a/.github/workflows/_pr-test-stage.yml b/.github/workflows/_pr-test-stage.yml index b7f65f136..94cdcfe16 100644 --- a/.github/workflows/_pr-test-stage.yml +++ b/.github/workflows/_pr-test-stage.yml @@ -73,6 +73,7 @@ env: jobs: run: + name: ${{ inputs.self_name }} (${{ matrix.partition }}) # target_stage takes precedence; otherwise default per-commit gating # runs on schedule / parallel-dispatch / non-failed PR with main_package # or sgl_kernel changes. diff --git a/.github/workflows/pr-test-extra.yml b/.github/workflows/pr-test-extra.yml index 92602cfef..626b49711 100644 --- a/.github/workflows/pr-test-extra.yml +++ b/.github/workflows/pr-test-extra.yml @@ -67,7 +67,7 @@ env: SGLANG_IS_IN_CI: true SGLANG_CUDA_COREDUMP: "1" SGLANG_JIT_DEEPGEMM_FAST_WARMUP: true - SKIP_STAGE_HEALTH_CHECK: ${{ inputs.skip_stage_health_check == true && 'true' || 'false' }} + SKIP_STAGE_HEALTH_CHECK: ${{ (inputs.skip_stage_health_check == true || inputs.run_all_tests == true) && 'true' || 'false' }} FORCE_REBUILD_DEEPEP: '1' PR_TEST_BYPASS_MAINTENANCE_ON_MAIN: ${{ github.ref == 'refs/heads/main' && 'true' || 'false' }} USE_VENV: false @@ -105,6 +105,19 @@ jobs: pr_test_yml: '.github/workflows/pr-test-extra.yml' secrets: inherit + # always(): on a PR without `run-ci-extra` the rest of this workflow's graph + # is skipped, but we still want the awareness block updated so the author + # sees this run was a no-op. + call-pr-awareness: + needs: check-changes + if: always() && github.event_name == 'pull_request' + permissions: + pull-requests: write + uses: ./.github/workflows/_pr-awareness-comment.yml + with: + workflow_kind: pr-test-extra + secrets: inherit + # =============================================== sgl-kernel ==================================================== sgl-kernel-build-wheels: needs: check-changes diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index 928e9658a..7353f0218 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -35,6 +35,11 @@ on: required: false type: boolean default: false + run_all_tests: + description: "Run all tests (mirrors the scheduled cron's behavior — combine with test_parallel_dispatch for full sim)" + required: false + type: boolean + default: false workflow_call: inputs: git_ref: @@ -67,7 +72,7 @@ env: SGLANG_IS_IN_CI: true SGLANG_CUDA_COREDUMP: "1" SGLANG_JIT_DEEPGEMM_FAST_WARMUP: true - SKIP_STAGE_HEALTH_CHECK: ${{ inputs.skip_stage_health_check == true && 'true' || 'false' }} + SKIP_STAGE_HEALTH_CHECK: ${{ (inputs.skip_stage_health_check == true || inputs.test_parallel_dispatch == true || inputs.run_all_tests == true) && 'true' || 'false' }} # TEMP: rebuild deepep against the new torch for torch-211-merge PR only — revert before merging to main. FORCE_REBUILD_DEEPEP: '1' # Schedule / main-branch dispatch / workflow_call from main use refs/heads/main; PR events use refs/pull/*/merge @@ -93,6 +98,25 @@ jobs: force_continue_on_error: ${{ inputs.force_continue_on_error == true }} secrets: inherit + # Runs right after check-changes so the awareness block is injected ~30s in + # rather than waiting for downstream stages. + call-pr-awareness: + needs: check-changes + if: always() && github.event_name == 'pull_request' + permissions: + pull-requests: write + uses: ./.github/workflows/_pr-awareness-comment.yml + with: + workflow_kind: pr-test + secrets: inherit + + call-pr-test-extra: + if: github.event_name == 'schedule' || inputs.test_parallel_dispatch == true + uses: ./.github/workflows/pr-test-extra.yml + with: + run_all_tests: true + secrets: inherit + # =============================================== Wait Jobs for Sequential PR Execution ==================================================== # These jobs poll GitHub API to wait for previous stages to complete. # For PR runs: wait jobs run and enforce sequential execution via polling.