From 7cba56e9fdc3d1a087c2e4dc3aaf3e231439f2ec Mon Sep 17 00:00:00 2001 From: tqchen Date: Tue, 16 Jun 2026 13:49:42 +0000 Subject: [PATCH 1/2] [CI] Remove Jenkins PR linter step The Jenkins PR title/body linter is comparatively heavy and can report false positives before the normal CI signal is available. This removes the check_pr step from the Jenkins prepare flow and drops the now-unused script-level test coverage. --- ci/jenkins/generated/arm_jenkinsfile.groovy | 21 +-- ci/jenkins/generated/cpu_jenkinsfile.groovy | 21 +-- .../generated/docker_jenkinsfile.groovy | 21 +-- ci/jenkins/generated/gpu_jenkinsfile.groovy | 21 +-- ci/jenkins/generated/wasm_jenkinsfile.groovy | 21 +-- ci/jenkins/templates/utils/Prepare.groovy.j2 | 19 --- ci/scripts/jenkins/check_pr.py | 143 ------------------ tests/python/ci/test_ci.py | 43 ------ 8 files changed, 5 insertions(+), 305 deletions(-) delete mode 100755 ci/scripts/jenkins/check_pr.py diff --git a/ci/jenkins/generated/arm_jenkinsfile.groovy b/ci/jenkins/generated/arm_jenkinsfile.groovy index 0cbed4cb2805..99a13e85fc97 100644 --- a/ci/jenkins/generated/arm_jenkinsfile.groovy +++ b/ci/jenkins/generated/arm_jenkinsfile.groovy @@ -60,7 +60,7 @@ // 'python3 jenkins/generate.py' // Note: This timestamp is here to ensure that updates to the Jenkinsfile are // always rebased on main before merging: -// Generated at 2026-06-09T19:52:01.246622 +// Generated at 2026-06-16T13:42:33.948016 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils // These are set at runtime from data in ci/jenkins/docker-images.yml, update @@ -339,31 +339,12 @@ def should_skip_ci(pr_number) { return git_skip_ci_code == 0 } -def check_pr(pr_number) { - if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) { - // never skip CI on build sourced from a branch - return false - } - withCredentials([string( - credentialsId: 'tvm-bot-jenkins-reader', - variable: 'GITHUB_TOKEN', - )]) { - sh ( - script: "python3 ${jenkins_scripts_root}/check_pr.py --pr ${pr_number}", - label: 'Check PR title and body', - ) - } - -} - def prepare(node_type) { stage('Prepare') { node(node_type) { ws("workspace/exec_${env.EXECUTOR_NUMBER}/tvm/prepare") { init_git() - check_pr(env.CHANGE_ID) - if (env.DETERMINE_DOCKER_IMAGES == 'yes') { sh( script: "./${jenkins_scripts_root}/determine_docker_images.py ci_arm ci_cpu ci_gpu ci_lint ci_wasm ", diff --git a/ci/jenkins/generated/cpu_jenkinsfile.groovy b/ci/jenkins/generated/cpu_jenkinsfile.groovy index 584bb8db92f3..a65edd3f7da8 100644 --- a/ci/jenkins/generated/cpu_jenkinsfile.groovy +++ b/ci/jenkins/generated/cpu_jenkinsfile.groovy @@ -60,7 +60,7 @@ // 'python3 jenkins/generate.py' // Note: This timestamp is here to ensure that updates to the Jenkinsfile are // always rebased on main before merging: -// Generated at 2026-06-09T19:52:01.232631 +// Generated at 2026-06-16T13:42:33.935741 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils // These are set at runtime from data in ci/jenkins/docker-images.yml, update @@ -339,31 +339,12 @@ def should_skip_ci(pr_number) { return git_skip_ci_code == 0 } -def check_pr(pr_number) { - if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) { - // never skip CI on build sourced from a branch - return false - } - withCredentials([string( - credentialsId: 'tvm-bot-jenkins-reader', - variable: 'GITHUB_TOKEN', - )]) { - sh ( - script: "python3 ${jenkins_scripts_root}/check_pr.py --pr ${pr_number}", - label: 'Check PR title and body', - ) - } - -} - def prepare(node_type) { stage('Prepare') { node(node_type) { ws("workspace/exec_${env.EXECUTOR_NUMBER}/tvm/prepare") { init_git() - check_pr(env.CHANGE_ID) - if (env.DETERMINE_DOCKER_IMAGES == 'yes') { sh( script: "./${jenkins_scripts_root}/determine_docker_images.py ci_arm ci_cpu ci_gpu ci_lint ci_wasm ", diff --git a/ci/jenkins/generated/docker_jenkinsfile.groovy b/ci/jenkins/generated/docker_jenkinsfile.groovy index b64fd4bb018c..19b785fd2aa1 100644 --- a/ci/jenkins/generated/docker_jenkinsfile.groovy +++ b/ci/jenkins/generated/docker_jenkinsfile.groovy @@ -60,7 +60,7 @@ // 'python3 jenkins/generate.py' // Note: This timestamp is here to ensure that updates to the Jenkinsfile are // always rebased on main before merging: -// Generated at 2026-06-09T19:52:01.257919 +// Generated at 2026-06-16T13:42:33.958277 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils // These are set at runtime from data in ci/jenkins/docker-images.yml, update @@ -339,31 +339,12 @@ def should_skip_ci(pr_number) { return git_skip_ci_code == 0 } -def check_pr(pr_number) { - if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) { - // never skip CI on build sourced from a branch - return false - } - withCredentials([string( - credentialsId: 'tvm-bot-jenkins-reader', - variable: 'GITHUB_TOKEN', - )]) { - sh ( - script: "python3 ${jenkins_scripts_root}/check_pr.py --pr ${pr_number}", - label: 'Check PR title and body', - ) - } - -} - def prepare(node_type) { stage('Prepare') { node(node_type) { ws("workspace/exec_${env.EXECUTOR_NUMBER}/tvm/prepare") { init_git() - check_pr(env.CHANGE_ID) - if (env.DETERMINE_DOCKER_IMAGES == 'yes') { sh( script: "./${jenkins_scripts_root}/determine_docker_images.py ci_arm ci_cpu ci_gpu ci_lint ci_wasm ", diff --git a/ci/jenkins/generated/gpu_jenkinsfile.groovy b/ci/jenkins/generated/gpu_jenkinsfile.groovy index 772639ee1ef1..8c04f811db36 100644 --- a/ci/jenkins/generated/gpu_jenkinsfile.groovy +++ b/ci/jenkins/generated/gpu_jenkinsfile.groovy @@ -60,7 +60,7 @@ // 'python3 jenkins/generate.py' // Note: This timestamp is here to ensure that updates to the Jenkinsfile are // always rebased on main before merging: -// Generated at 2026-06-09T19:52:01.271485 +// Generated at 2026-06-16T13:42:33.970141 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils // These are set at runtime from data in ci/jenkins/docker-images.yml, update @@ -339,31 +339,12 @@ def should_skip_ci(pr_number) { return git_skip_ci_code == 0 } -def check_pr(pr_number) { - if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) { - // never skip CI on build sourced from a branch - return false - } - withCredentials([string( - credentialsId: 'tvm-bot-jenkins-reader', - variable: 'GITHUB_TOKEN', - )]) { - sh ( - script: "python3 ${jenkins_scripts_root}/check_pr.py --pr ${pr_number}", - label: 'Check PR title and body', - ) - } - -} - def prepare(node_type) { stage('Prepare') { node(node_type) { ws("workspace/exec_${env.EXECUTOR_NUMBER}/tvm/prepare") { init_git() - check_pr(env.CHANGE_ID) - if (env.DETERMINE_DOCKER_IMAGES == 'yes') { sh( script: "./${jenkins_scripts_root}/determine_docker_images.py ci_arm ci_cpu ci_gpu ci_lint ci_wasm ", diff --git a/ci/jenkins/generated/wasm_jenkinsfile.groovy b/ci/jenkins/generated/wasm_jenkinsfile.groovy index 28e4462ae9cf..c7ab9a2cc974 100644 --- a/ci/jenkins/generated/wasm_jenkinsfile.groovy +++ b/ci/jenkins/generated/wasm_jenkinsfile.groovy @@ -60,7 +60,7 @@ // 'python3 jenkins/generate.py' // Note: This timestamp is here to ensure that updates to the Jenkinsfile are // always rebased on main before merging: -// Generated at 2026-06-09T19:52:01.285310 +// Generated at 2026-06-16T13:42:33.982453 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils // These are set at runtime from data in ci/jenkins/docker-images.yml, update @@ -339,31 +339,12 @@ def should_skip_ci(pr_number) { return git_skip_ci_code == 0 } -def check_pr(pr_number) { - if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) { - // never skip CI on build sourced from a branch - return false - } - withCredentials([string( - credentialsId: 'tvm-bot-jenkins-reader', - variable: 'GITHUB_TOKEN', - )]) { - sh ( - script: "python3 ${jenkins_scripts_root}/check_pr.py --pr ${pr_number}", - label: 'Check PR title and body', - ) - } - -} - def prepare(node_type) { stage('Prepare') { node(node_type) { ws("workspace/exec_${env.EXECUTOR_NUMBER}/tvm/prepare") { init_git() - check_pr(env.CHANGE_ID) - if (env.DETERMINE_DOCKER_IMAGES == 'yes') { sh( script: "./${jenkins_scripts_root}/determine_docker_images.py ci_arm ci_cpu ci_gpu ci_lint ci_wasm ", diff --git a/ci/jenkins/templates/utils/Prepare.groovy.j2 b/ci/jenkins/templates/utils/Prepare.groovy.j2 index 6770fab24850..b74e7f4ba514 100644 --- a/ci/jenkins/templates/utils/Prepare.groovy.j2 +++ b/ci/jenkins/templates/utils/Prepare.groovy.j2 @@ -221,31 +221,12 @@ def should_skip_ci(pr_number) { return git_skip_ci_code == 0 } -def check_pr(pr_number) { - if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) { - // never skip CI on build sourced from a branch - return false - } - withCredentials([string( - credentialsId: 'tvm-bot-jenkins-reader', - variable: 'GITHUB_TOKEN', - )]) { - sh ( - script: "python3 ${jenkins_scripts_root}/check_pr.py --pr ${pr_number}", - label: 'Check PR title and body', - ) - } - -} - def prepare(node_type) { stage('Prepare') { node(node_type) { ws("workspace/exec_${env.EXECUTOR_NUMBER}/tvm/prepare") { init_git() - check_pr(env.CHANGE_ID) - if (env.DETERMINE_DOCKER_IMAGES == 'yes') { sh( script: "./${jenkins_scripts_root}/determine_docker_images.py {% for image in images %}{{ image.name }} {% endfor %}", diff --git a/ci/scripts/jenkins/check_pr.py b/ci/scripts/jenkins/check_pr.py deleted file mode 100755 index 683c3fdd6ddb..000000000000 --- a/ci/scripts/jenkins/check_pr.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python3 -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ruff: noqa: E501 -import argparse -import json -import os -import re -import textwrap -from collections.abc import Callable -from dataclasses import dataclass -from typing import Any - -from cmd_utils import init_log, tags_from_title -from git_utils import GitHubRepo, git, parse_remote - -GITHUB_USERNAME_REGEX = re.compile(r"(@[a-zA-Z0-9-]+)", flags=re.MULTILINE) -OK = object() -FAIL = object() - - -@dataclass -class Check: - # check to run, returning OK means it passed, anything else means it failed - check: Callable[[str], Any] - - # function to call to generate the error message - error_fn: Callable[[Any], str] - - -def non_empty(s: str): - if len(s) == 0: - return FAIL - return OK - - -def usernames(s: str): - m = GITHUB_USERNAME_REGEX.findall(s) - return m if m else OK - - -def tags(s: str): - items = tags_from_title(s) - if len(items) == 0: - return FAIL - return OK - - -def trailing_period(s: str): - if s.endswith("."): - return FAIL - return OK - - -title_checks = [ - Check(check=non_empty, error_fn=lambda d: "PR must have a title but title was empty"), - Check(check=trailing_period, error_fn=lambda d: "PR must not end in a tailing '.'"), - Check( - check=usernames, - error_fn=lambda d: f"PR title must not tag anyone but found these usernames: {d}", - ), -] -body_checks = [ - Check(check=non_empty, error_fn=lambda d: "PR must have a body but body was empty"), - Check( - check=usernames, - error_fn=lambda d: f"PR body must not tag anyone but found these usernames: {d}", - ), -] - - -def run_checks(checks: list[Check], s: str, name: str) -> bool: - print(f"Running checks for {name}") - print(textwrap.indent(s, prefix=" ")) - passed = True - print(" Checks:") - for i, check in enumerate(checks): - result = check.check(s) - if result == OK: - print(f" [{i + 1}] {check.check.__name__}: PASSED") - else: - passed = False - msg = check.error_fn(result) - print(f" [{i + 1}] {check.check.__name__}: FAILED: {msg}") - - return passed - - -if __name__ == "__main__": - init_log() - help = "Check a PR's title and body for conformance to guidelines" - parser = argparse.ArgumentParser(description=help) - parser.add_argument("--pr", required=True) - parser.add_argument("--remote", default="origin", help="ssh remote to parse") - parser.add_argument( - "--pr-data", help="(testing) PR data to use instead of fetching from GitHub" - ) - args = parser.parse_args() - - try: - pr = int(args.pr) - except ValueError: - print(f"PR was not a number: {args.pr}") - exit(0) - - if args.pr_data: - pr = json.loads(args.pr_data) - else: - remote = git(["config", "--get", f"remote.{args.remote}.url"]) - user, repo = parse_remote(remote) - - github = GitHubRepo(token=os.environ["GITHUB_TOKEN"], user=user, repo=repo) - pr = github.get(f"pulls/{args.pr}") - - body = "" if pr["body"] is None else pr["body"].strip() - title = "" if pr["title"] is None else pr["title"].strip() - - title_passed = run_checks(checks=title_checks, s=title, name="PR title") - print("") - body_passed = run_checks(checks=body_checks, s=body, name="PR body") - - if title_passed and body_passed: - print("All checks passed!") - exit(0) - else: - print( - "Some checks failed, please review the logs above and edit your PR on GitHub accordingly" - ) - exit(1) diff --git a/tests/python/ci/test_ci.py b/tests/python/ci/test_ci.py index 251143c4306f..4ff8be49d2bd 100644 --- a/tests/python/ci/test_ci.py +++ b/tests/python/ci/test_ci.py @@ -1403,48 +1403,5 @@ def test_should_rebuild_docker(tmpdir_factory, changed_files, name, check, expec assert proc.returncode == expected_code -@parameterize_named( - passing=dict( - title="[something] a change", - body="something", - expected="All checks passed", - expected_code=0, - ), - period=dict( - title="[something] a change.", - body="something", - expected="trailing_period: FAILED", - expected_code=1, - ), - empty_body=dict( - title="[something] a change", - body=None, - expected="non_empty: FAILED", - expected_code=1, - ), -) -def test_pr_linter(title, body, expected, expected_code): - """ - Test the PR linter - """ - tag_script = JENKINS_SCRIPT_ROOT / "check_pr.py" - pr_data = { - "title": title, - "body": body, - } - proc = run_script( - [ - tag_script, - "--pr", - 1234, - "--pr-data", - json.dumps(pr_data), - ], - check=False, - ) - assert proc.returncode == expected_code - assert_in(expected, proc.stdout) - - if __name__ == "__main__": tvm.testing.main() From 5f8c3acbaef1332865fca5dac40bdd36217569bf Mon Sep 17 00:00:00 2001 From: tqchen Date: Tue, 16 Jun 2026 17:00:31 +0000 Subject: [PATCH 2/2] [CUDA][FIX] Use public uint32_t spelling in FP8 helpers NVRTC does not reliably define the private __uint32_t typedef used by the FP8 vector helper casts. This updates the generated CUDA helper sources to use the public uint32_t spelling already used elsewhere in TVM CUDA code. --- python/tvm/backend/cuda/operator/intrinsics/header.py | 6 +++--- src/backend/cuda/codegen/literal/cuda_half_t.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/tvm/backend/cuda/operator/intrinsics/header.py b/python/tvm/backend/cuda/operator/intrinsics/header.py index 848c3bd0ecf5..2330f75a4f30 100644 --- a/python/tvm/backend/cuda/operator/intrinsics/header.py +++ b/python/tvm/backend/cuda/operator/intrinsics/header.py @@ -343,7 +343,7 @@ def header_generator(tags): TVec2 hi_half2 = *reinterpret_cast(&z); __nv_fp8x2_e4m3 lo_part(lo_half2), hi_part(hi_half2); result.__x = - (static_cast<__uint32_t>(lo_part.__x) | (static_cast<__uint32_t>(hi_part.__x) << 16)); + (static_cast(lo_part.__x) | (static_cast(hi_part.__x) << 16)); return result; } __host__ __device__ explicit half4_bfloat164(const __nv_fp8x4_e5m2& fp8x4) { @@ -363,7 +363,7 @@ def header_generator(tags): TVec2 hi_half2 = *reinterpret_cast(&z); __nv_fp8x2_e5m2 lo_part(lo_half2), hi_part(hi_half2); result.__x = - (static_cast<__uint32_t>(lo_part.__x) | (static_cast<__uint32_t>(hi_part.__x) << 16)); + (static_cast(lo_part.__x) | (static_cast(hi_part.__x) << 16)); return result; } __host__ __device__ explicit half4_bfloat164(const __nv_fp8x4_e8m0& fp8x4) { @@ -383,7 +383,7 @@ def header_generator(tags): TVec2 hi_half2 = *reinterpret_cast(&z); __nv_fp8x2_e8m0 lo_part(lo_half2), hi_part(hi_half2); result.__x = - (static_cast<__uint32_t>(lo_part.__x) | (static_cast<__uint32_t>(hi_part.__x) << 16)); + (static_cast(lo_part.__x) | (static_cast(hi_part.__x) << 16)); return result; } """ diff --git a/src/backend/cuda/codegen/literal/cuda_half_t.h b/src/backend/cuda/codegen/literal/cuda_half_t.h index 78ee0298be24..d55453cba468 100644 --- a/src/backend/cuda/codegen/literal/cuda_half_t.h +++ b/src/backend/cuda/codegen/literal/cuda_half_t.h @@ -435,7 +435,7 @@ struct __align__(8) half4_bfloat164 { TVec2 hi_half2 = *reinterpret_cast(&z); __nv_fp8x2_e4m3 lo_part(lo_half2), hi_part(hi_half2); result.__x = - (static_cast<__uint32_t>(lo_part.__x) | (static_cast<__uint32_t>(hi_part.__x) << 16)); + (static_cast(lo_part.__x) | (static_cast(hi_part.__x) << 16)); return result; } __host__ __device__ explicit half4_bfloat164(const __nv_fp8x4_e5m2& fp8x4) { @@ -455,7 +455,7 @@ struct __align__(8) half4_bfloat164 { TVec2 hi_half2 = *reinterpret_cast(&z); __nv_fp8x2_e5m2 lo_part(lo_half2), hi_part(hi_half2); result.__x = - (static_cast<__uint32_t>(lo_part.__x) | (static_cast<__uint32_t>(hi_part.__x) << 16)); + (static_cast(lo_part.__x) | (static_cast(hi_part.__x) << 16)); return result; } __host__ __device__ explicit half4_bfloat164(const __nv_fp8x4_e8m0& fp8x4) { @@ -475,7 +475,7 @@ struct __align__(8) half4_bfloat164 { TVec2 hi_half2 = *reinterpret_cast(&z); __nv_fp8x2_e8m0 lo_part(lo_half2), hi_part(hi_half2); result.__x = - (static_cast<__uint32_t>(lo_part.__x) | (static_cast<__uint32_t>(hi_part.__x) << 16)); + (static_cast(lo_part.__x) | (static_cast(hi_part.__x) << 16)); return result; } )";