This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch jenkins-cleanup-trusted-main
in repository https://gitbox.apache.org/repos/asf/tvm.git

commit 025bec00f3d34b79ae0bed866db4cb4a7dc55006
Author: Tianqi Chen <[email protected]>
AuthorDate: Thu Oct 15 11:39:52 2026 +0000

    [CI] Use explicit node fallback and artifact upload helpers
---
 ci/jenkins/README.md                         | 16 +++++++++++--
 ci/jenkins/generated/arm_jenkinsfile.groovy  | 20 +++++++++-------
 ci/jenkins/generated/common.groovy           | 36 ++++++++++++++++++++++++++++
 ci/jenkins/generated/cpu_jenkinsfile.groovy  | 25 ++++++++++++-------
 ci/jenkins/generated/gpu_jenkinsfile.groovy  | 32 ++++++++++++++++---------
 ci/jenkins/generated/wasm_jenkinsfile.groovy | 28 ++++++++++++----------
 6 files changed, 115 insertions(+), 42 deletions(-)

diff --git a/ci/jenkins/README.md b/ci/jenkins/README.md
index e33f38da4c..0f8062b0d9 100644
--- a/ci/jenkins/README.md
+++ b/ci/jenkins/README.md
@@ -55,13 +55,25 @@ expanding local or remote globs.
 Use `s3.py --artifacts NAME...` to select registry entries or `--paths PATH...`
 to select raw paths; the selections are mutually exclusive. Existing 
Jenkinsfiles
 can keep `--bundle` and `--items`, which are aliases with the same semantics.
-The shared helper's legacy `upload(tag, options)` passes those options 
unchanged.
+`upload_artifacts(tag, options)` passes those options unchanged, as does the
+retained legacy `upload(tag, options)`.
 `artifacts.py` exposes the declarative `ARTIFACTS` registry; there is no 
`data.py`
 compatibility module.
 
 The shared helper groups internal dependencies first, public entry points next,
 and retained legacy functions last. Existing function signatures, shared 
variables,
-startup and retry behavior remain available to the platform Jenkinsfiles.
+startup and retry behavior remain available to existing Jenkinsfiles.
+
+Native build and test stages use `with_node(options, body)`. Callers provide 
exact
+`instance` and optional `retry_instance` labels, a stage `name`, a workspace
+`folder`, and optional environment `variables`. Skipped CI returns before node
+allocation or setup. Each attempt checks out sources in its workspace and runs
+the body under a timeout; callers explicitly run `docker_init(image)` in the 
body.
+When a retry label is supplied, build and test failures use the same fallback
+policy, retaining the newest-build guard. User cancellation and milestone 
interruptions propagate without retry, as does
+failure of the fallback attempt. Agent loss and attempt timeouts remain 
eligible
+for fallback. Omit `retry_instance` for a single attempt.
+Stage and parallel layout remain in the platform files.
 
 Host-side commands live in `scripts/`. GitHub bot and release API helpers live
 in `../github/`. The retained `determine_docker_images.py` supports both
diff --git a/ci/jenkins/generated/arm_jenkinsfile.groovy 
b/ci/jenkins/generated/arm_jenkinsfile.groovy
index 2ac6165a7e..c826e2d104 100644
--- a/ci/jenkins/generated/arm_jenkinsfile.groovy
+++ b/ci/jenkins/generated/arm_jenkinsfile.groovy
@@ -19,12 +19,16 @@
 ci = evaluate(readTrusted('ci/jenkins/generated/common.groovy'))
 ci.start()
 
-ci.build('ARM-GRAVITON3', 'BUILD: arm', 'tvm/build-arm', images.ci_arm, 'arm') 
{
-  sh(
-    script: "${docker_run} ${images.ci_arm} 
./tests/scripts/task_config_build_arm.sh build",
-    label: 'Create ARM CMake config',
-  )
-  ci.cmake_build(images.ci_arm, 'build')
-  ci.make_cpp_tests(images.ci_arm, 'build')
-  ci.upload('arm', '--artifacts tvm_lib --artifacts cpptest')
+stage('Build') {
+  ci.with_node(instance: 'ARM-GRAVITON3-SPOT', retry_instance: 'ARM-GRAVITON3',
+    name: 'BUILD: arm', folder: 'tvm/build-arm', variables: ['PLATFORM=arm']) {
+    ci.docker_init(images.ci_arm)
+    sh(
+      script: "${docker_run} ${images.ci_arm} 
./tests/scripts/task_config_build_arm.sh build",
+      label: 'Create ARM CMake config',
+    )
+    ci.cmake_build(images.ci_arm, 'build')
+    ci.make_cpp_tests(images.ci_arm, 'build')
+    ci.upload_artifacts('arm', '--artifacts tvm_lib --artifacts cpptest')
+  }
 }
diff --git a/ci/jenkins/generated/common.groovy 
b/ci/jenkins/generated/common.groovy
index 734deba37f..f7a87f3889 100644
--- a/ci/jenkins/generated/common.groovy
+++ b/ci/jenkins/generated/common.groovy
@@ -286,6 +286,42 @@ def run_on_node(label, name, folder, image, variables, 
body) {
 
 // Public entry points.
 
+// The caller supplies exact node labels; omitting retry_instance disables 
fallback.
+// Docker setup stays in the body so each attempt uses the caller's chosen 
image.
+def with_node(options, body) {
+  if (skip_ci) {
+    Utils.markStageSkippedForConditional(options.name)
+    return
+  }
+  def run = { instance ->
+    node(instance) {
+      ws(per_exec_ws(options.folder)) {
+        init_git()
+        timeout(time: max_time, unit: 'MINUTES') {
+          withEnv(options.variables ?: []) { body() }
+        }
+      }
+    }
+  }
+  try {
+    run(options.instance)
+  } catch (Throwable ex) {
+    // Jenkins also uses FlowInterruptedException for agent loss and timeouts.
+    // Actual interruptions (user cancellation or a milestone) must not retry.
+    if (ex instanceof 
org.jenkinsci.plugins.workflow.steps.FlowInterruptedException) {
+      if (ex.isActualInterruption()) throw ex
+    } else if (ex instanceof InterruptedException) {
+      throw ex
+    }
+    if (!options.retry_instance) throw ex
+    retry_latest(ex) { run(options.retry_instance) }
+  }
+}
+
+def upload_artifacts(tag, options) {
+  upload(tag, options)
+}
+
 def docker_init(image) {
   // Clear out all Docker images that aren't going to be used
   sh(
diff --git a/ci/jenkins/generated/cpu_jenkinsfile.groovy 
b/ci/jenkins/generated/cpu_jenkinsfile.groovy
index 881cd2cedc..edadf43273 100644
--- a/ci/jenkins/generated/cpu_jenkinsfile.groovy
+++ b/ci/jenkins/generated/cpu_jenkinsfile.groovy
@@ -19,19 +19,26 @@
 ci = evaluate(readTrusted('ci/jenkins/generated/common.groovy'))
 ci.start()
 
-ci.build('CPU', 'BUILD: CPU', 'tvm/build-cpu', images.ci_cpu, 'cpu') {
-  sh(
-    script: "${docker_run} ${images.ci_cpu} 
./tests/scripts/task_config_build_cpu.sh build",
-    label: 'Create CPU CMake config',
-  )
-  ci.cmake_build(images.ci_cpu, 'build')
-  ci.make_cpp_tests(images.ci_cpu, 'build')
-  ci.upload('cpu', '--artifacts tvm_lib --artifacts cpptest')
+stage('Build') {
+  ci.with_node(instance: 'CPU-SPOT', retry_instance: 'CPU',
+    name: 'BUILD: CPU', folder: 'tvm/build-cpu', variables: ['PLATFORM=cpu']) {
+    ci.docker_init(images.ci_cpu)
+    sh(
+      script: "${docker_run} ${images.ci_cpu} 
./tests/scripts/task_config_build_cpu.sh build",
+      label: 'Create CPU CMake config',
+    )
+    ci.cmake_build(images.ci_cpu, 'build')
+    ci.make_cpp_tests(images.ci_cpu, 'build')
+    ci.upload_artifacts('cpu', '--artifacts tvm_lib --artifacts cpptest')
+  }
 }
 
 stage('Test') {
   parallel('unittest: CPU': {
-    ci.test('CPU', 'unittest: CPU', 'tvm/ut-python-cpu', images.ci_cpu, 'cpu') 
{
+    ci.with_node(instance: 'CPU-SPOT', retry_instance: 'CPU',
+      name: 'unittest: CPU', folder: 'tvm/ut-python-cpu',
+      variables: ['PLATFORM=cpu', 'TEST_STEP_NAME=unittest: CPU']) {
+      ci.docker_init(images.ci_cpu)
       ci.download('cpu')
       ci.cpp_unittest(images.ci_cpu)
       ci.python_unittest(images.ci_cpu)
diff --git a/ci/jenkins/generated/gpu_jenkinsfile.groovy 
b/ci/jenkins/generated/gpu_jenkinsfile.groovy
index 98c04b332e..668c0dadd2 100644
--- a/ci/jenkins/generated/gpu_jenkinsfile.groovy
+++ b/ci/jenkins/generated/gpu_jenkinsfile.groovy
@@ -19,21 +19,28 @@
 ci = evaluate(readTrusted('ci/jenkins/generated/common.groovy'))
 ci.start()
 
-ci.build('CPU', 'BUILD: GPU', 'tvm/build-gpu', images.ci_gpu, 'gpu') {
-  sh "${docker_run} --no-gpu ${images.ci_gpu} 
./tests/scripts/task_config_build_gpu.sh build"
-  ci.cmake_build("${images.ci_gpu} --no-gpu", 'build')
-  ci.upload('gpu', '--artifacts tvm_lib --artifacts tvm_lib_gpu_extra')
+stage('Build') {
+  ci.with_node(instance: 'CPU-SPOT', retry_instance: 'CPU',
+    name: 'BUILD: GPU', folder: 'tvm/build-gpu', variables: ['PLATFORM=gpu']) {
+    ci.docker_init(images.ci_gpu)
+    sh "${docker_run} --no-gpu ${images.ci_gpu} 
./tests/scripts/task_config_build_gpu.sh build"
+    ci.cmake_build("${images.ci_gpu} --no-gpu", 'build')
+    ci.upload_artifacts('gpu', '--artifacts tvm_lib --artifacts 
tvm_lib_gpu_extra')
 
-  // Check the compiler-only GPU configuration after publishing the test build.
-  sh 'rm -rf build'
-  sh "${docker_run} --no-gpu ${images.ci_gpu} 
./tests/scripts/task_config_build_gpu_other.sh build"
-  sh "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_gpu} --no-gpu 
./tests/scripts/task_build.py --sccache-bucket tvm-sccache-prod 
--sccache-region us-west-2 --cmake-target tvm_runtime --build-dir build"
+    // Check the compiler-only GPU configuration after publishing the test 
build.
+    sh 'rm -rf build'
+    sh "${docker_run} --no-gpu ${images.ci_gpu} 
./tests/scripts/task_config_build_gpu_other.sh build"
+    sh "${docker_run} --env CI_NUM_EXECUTORS ${images.ci_gpu} --no-gpu 
./tests/scripts/task_build.py --sccache-bucket tvm-sccache-prod 
--sccache-region us-west-2 --cmake-target tvm_runtime --build-dir build"
+  }
 }
 
 stage('Test') {
   parallel(
     'unittest: GPU': {
-      ci.test('GPU', 'unittest: GPU', 'tvm/ut-python-gpu', images.ci_gpu, 
'gpu') {
+      ci.with_node(instance: 'GPU-SPOT', retry_instance: 'GPU',
+        name: 'unittest: GPU', folder: 'tvm/ut-python-gpu',
+        variables: ['PLATFORM=gpu', 'TEST_STEP_NAME=unittest: GPU']) {
+        ci.docker_init(images.ci_gpu)
         ci.download('gpu')
         sh(
           script: "${docker_run} ${images.ci_gpu} 
./tests/scripts/task_java_unittest.sh",
@@ -46,13 +53,16 @@ stage('Test') {
       }
     },
     'docs: GPU': {
-      ci.test('GPU', 'docs: GPU', 'tvm/docs-python-gpu', images.ci_gpu, 'gpu') 
{
+      ci.with_node(instance: 'GPU-SPOT', retry_instance: 'GPU',
+        name: 'docs: GPU', folder: 'tvm/docs-python-gpu',
+        variables: ['PLATFORM=gpu', 'TEST_STEP_NAME=docs: GPU']) {
+        ci.docker_init(images.ci_gpu)
         ci.download('gpu')
         sh(
           script: "${docker_run} ${images.ci_gpu} 
./tests/scripts/task_python_docs.sh",
           label: 'Build docs',
         )
-        ci.upload('docs', '--paths docs.tgz')
+        ci.upload_artifacts('docs', '--paths docs.tgz')
         sh(
           script: "aws s3 cp --no-progress _docs 
s3://${s3_bucket}/${s3_prefix}/docs --recursive",
           label: 'Upload docs to S3',
diff --git a/ci/jenkins/generated/wasm_jenkinsfile.groovy 
b/ci/jenkins/generated/wasm_jenkinsfile.groovy
index ddf932529d..da38b1c237 100644
--- a/ci/jenkins/generated/wasm_jenkinsfile.groovy
+++ b/ci/jenkins/generated/wasm_jenkinsfile.groovy
@@ -19,16 +19,20 @@
 ci = evaluate(readTrusted('ci/jenkins/generated/common.groovy'))
 ci.start()
 
-ci.build('CPU', 'BUILD: WASM', 'tvm/build-wasm', images.ci_wasm, 'wasm') {
-  sh(
-    script: "${docker_run} ${images.ci_wasm} 
./tests/scripts/task_config_build_wasm.sh build",
-    label: 'Create WASM CMake config',
-  )
-  ci.cmake_build(images.ci_wasm, 'build')
-  ci.make_cpp_tests(images.ci_wasm, 'build')
-  ci.cpp_unittest(images.ci_wasm)
-  sh(
-    script: "${docker_run} ${images.ci_wasm} ./tests/scripts/task_web_wasm.sh",
-    label: 'Run WASM lint and tests',
-  )
+stage('Build') {
+  ci.with_node(instance: 'CPU-SPOT', retry_instance: 'CPU',
+    name: 'BUILD: WASM', folder: 'tvm/build-wasm', variables: 
['PLATFORM=wasm']) {
+    ci.docker_init(images.ci_wasm)
+    sh(
+      script: "${docker_run} ${images.ci_wasm} 
./tests/scripts/task_config_build_wasm.sh build",
+      label: 'Create WASM CMake config',
+    )
+    ci.cmake_build(images.ci_wasm, 'build')
+    ci.make_cpp_tests(images.ci_wasm, 'build')
+    ci.cpp_unittest(images.ci_wasm)
+    sh(
+      script: "${docker_run} ${images.ci_wasm} 
./tests/scripts/task_web_wasm.sh",
+      label: 'Run WASM lint and tests',
+    )
+  }
 }

Reply via email to