[CI] Accept whitespace around /ci commands and reply instead of going silent (#60925)
Signed-off-by: khluu <khluu000@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ed7ae705a1
commit
783ac12071
@@ -18,22 +18,8 @@ jobs:
|
||||
if: >-
|
||||
github.repository_owner == 'vllm-project' &&
|
||||
github.event.issue.pull_request &&
|
||||
(github.event.comment.body == '/ci run' ||
|
||||
github.event.comment.body == '/ci run all' ||
|
||||
github.event.comment.body == '/ci run nightly' ||
|
||||
github.event.comment.body == '/ci run --allow-stale' ||
|
||||
github.event.comment.body == '/ci run all --allow-stale' ||
|
||||
github.event.comment.body == '/ci run nightly --allow-stale' ||
|
||||
github.event.comment.body == '/ci retry' ||
|
||||
github.event.comment.body == '/ci cancel' ||
|
||||
github.event.comment.body == '/amd-ci run' ||
|
||||
github.event.comment.body == '/amd-ci run all' ||
|
||||
github.event.comment.body == '/amd-ci run nightly' ||
|
||||
github.event.comment.body == '/amd-ci run --allow-stale' ||
|
||||
github.event.comment.body == '/amd-ci run all --allow-stale' ||
|
||||
github.event.comment.body == '/amd-ci run nightly --allow-stale' ||
|
||||
github.event.comment.body == '/amd-ci retry' ||
|
||||
github.event.comment.body == '/amd-ci cancel')
|
||||
(startsWith(github.event.comment.body, '/ci') ||
|
||||
startsWith(github.event.comment.body, '/amd-ci'))
|
||||
runs-on: [self-hosted, linux, x64, vllm-runners]
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
@@ -54,3 +40,14 @@ jobs:
|
||||
BUILDKITE_PIPELINE: ci
|
||||
CI_TRUSTED_USERS: ${{ vars.CI_TRUSTED_USERS }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
- name: Report failure on the PR
|
||||
if: failure()
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `❌ @${context.payload.comment.user.login}, your CI command failed with an unexpected error, so CI may not have started. See the [workflow run](${runUrl}) for details.`,
|
||||
});
|
||||
|
||||
@@ -58,6 +58,7 @@ AMD_CI_COMMANDS = frozenset(
|
||||
RETRY_COMMANDS = frozenset({COMMAND_RETRY_FAILED, COMMAND_RETRY_AMD_FAILED})
|
||||
CANCEL_COMMANDS = frozenset({COMMAND_CANCEL_CI, COMMAND_CANCEL_AMD_CI})
|
||||
ALL_CI_COMMANDS = UPSTREAM_CI_COMMANDS | AMD_CI_COMMANDS
|
||||
CI_COMMAND_PREFIXES = frozenset({"/ci", "/amd-ci"})
|
||||
CI_AUTHORIZED_COMMENT_MARKER = "<!-- vllm-ci-authorized -->"
|
||||
READY_LABELS = {"ready", "ready-run-all-tests"}
|
||||
TRUSTED_PERMISSIONS = {"admin", "maintain", "write"}
|
||||
@@ -461,11 +462,17 @@ class BuildkiteClient:
|
||||
|
||||
|
||||
def parse_command(body: str) -> str | None:
|
||||
body = body.strip()
|
||||
if body in ALL_CI_COMMANDS:
|
||||
return body
|
||||
return None
|
||||
|
||||
|
||||
def is_ci_command_attempt(body: str) -> bool:
|
||||
words = body.split(maxsplit=1)
|
||||
return bool(words) and words[0].casefold() in CI_COMMAND_PREFIXES
|
||||
|
||||
|
||||
def pipeline_for_command(
|
||||
command: str,
|
||||
*,
|
||||
@@ -1086,6 +1093,28 @@ def handle_cancel_ci(
|
||||
)
|
||||
|
||||
|
||||
def reply_unrecognized_command(
|
||||
event: Mapping[str, Any],
|
||||
github: GitHubClient,
|
||||
) -> None:
|
||||
if "pull_request" not in event["issue"]:
|
||||
return
|
||||
issue_number = event["issue"]["number"]
|
||||
comment_id = event["comment"]["id"]
|
||||
actor = event["comment"]["user"]["login"]
|
||||
if is_already_handled(github, issue_number, comment_id):
|
||||
print(f"Comment {comment_id} was already handled.")
|
||||
return
|
||||
add_reaction_safely(github, comment_id, "-1")
|
||||
commands = ", ".join(f"`{command}`" for command in sorted(ALL_CI_COMMANDS))
|
||||
github.add_comment(
|
||||
issue_number,
|
||||
f"❌ @{actor}, that is not a recognized CI command, so no CI was "
|
||||
"started. Post a new comment containing only one of: "
|
||||
f"{commands}.\n\n{command_comment_marker(comment_id)}",
|
||||
)
|
||||
|
||||
|
||||
def run(
|
||||
event: Mapping[str, Any],
|
||||
github: GitHubClient,
|
||||
@@ -1217,8 +1246,11 @@ def main() -> None:
|
||||
)
|
||||
return
|
||||
|
||||
command = parse_command(event["comment"]["body"])
|
||||
body = event["comment"]["body"]
|
||||
command = parse_command(body)
|
||||
if not command:
|
||||
if is_ci_command_attempt(body):
|
||||
reply_unrecognized_command(event, github)
|
||||
return
|
||||
|
||||
pipeline = pipeline_for_command(
|
||||
|
||||
@@ -32,10 +32,12 @@ from run_ci_command import (
|
||||
has_trusted_approval,
|
||||
is_active_build,
|
||||
is_build_for_pr,
|
||||
is_ci_command_attempt,
|
||||
notify_authorized,
|
||||
parse_command,
|
||||
parse_trusted_users,
|
||||
pipeline_for_command,
|
||||
reply_unrecognized_command,
|
||||
resolve_workflow_run_pr,
|
||||
run,
|
||||
select_latest_build,
|
||||
@@ -340,14 +342,46 @@ class RunCiCommandTest(unittest.TestCase):
|
||||
self.assertIsNone(parse_command("/ci run please"))
|
||||
self.assertIsNone(parse_command("/ci run all please"))
|
||||
self.assertIsNone(parse_command("/ci cancel please"))
|
||||
self.assertIsNone(parse_command(" /ci run"))
|
||||
self.assertIsNone(parse_command("/amd-ci run please"))
|
||||
self.assertIsNone(parse_command("/amd-ci retry "))
|
||||
self.assertIsNone(parse_command("/AMD-CI run"))
|
||||
self.assertIsNone(parse_command("/amdci run"))
|
||||
self.assertIsNone(parse_command("/ci run --allow-stale all"))
|
||||
self.assertIsNone(parse_command("/amd-ci run --allow-stale please"))
|
||||
|
||||
def test_surrounding_whitespace_is_ignored(self) -> None:
|
||||
self.assertEqual(parse_command("/ci run\r\n"), COMMAND_RUN_CI)
|
||||
self.assertEqual(parse_command(" /ci run "), COMMAND_RUN_CI)
|
||||
self.assertEqual(parse_command("/amd-ci retry\n"), COMMAND_RETRY_AMD_FAILED)
|
||||
|
||||
def test_ci_command_attempts_are_detected(self) -> None:
|
||||
for body in ("/ci", "/ci rn", "/CI run", "/amd-ci run please", " /ci\nrun"):
|
||||
with self.subTest(body=body):
|
||||
self.assertTrue(is_ci_command_attempt(body))
|
||||
for body in ("", "LGTM", "/cifoo", "/amdci run", "please /ci run"):
|
||||
with self.subTest(body=body):
|
||||
self.assertFalse(is_ci_command_attempt(body))
|
||||
|
||||
def test_unrecognized_command_gets_feedback(self) -> None:
|
||||
github = FakeGitHub()
|
||||
|
||||
reply_unrecognized_command(make_event("/ci rn", actor="author"), github)
|
||||
|
||||
self.assertEqual(github.reactions, ["-1"])
|
||||
self.assertEqual(len(github.comments), 1)
|
||||
self.assertIn(
|
||||
"@author, that is not a recognized CI command", github.comments[0]
|
||||
)
|
||||
self.assertIn(f"`{COMMAND_RUN_CI}`", github.comments[0])
|
||||
self.assertIn("<!-- vllm-ci-command:99 -->", github.comments[0])
|
||||
|
||||
def test_unrecognized_command_feedback_is_not_repeated(self) -> None:
|
||||
github = FakeGitHub(comments=["❌ earlier\n\n<!-- vllm-ci-command:99 -->"])
|
||||
|
||||
reply_unrecognized_command(make_event("/ci rn"), github)
|
||||
|
||||
self.assertEqual(len(github.comments), 1)
|
||||
self.assertEqual(github.reactions, [])
|
||||
|
||||
def test_commands_select_only_their_configured_pipeline(self) -> None:
|
||||
cases = (
|
||||
(COMMAND_RUN_CI, "ci"),
|
||||
|
||||
Reference in New Issue
Block a user