You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by tq...@apache.org on 2026/10/15 11:40:02 UTC
(tvm) 01/02: [CI] Use explicit node fallback and artifact upload helpers
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 96c0200bac79485578aa5df18d54b50a5d69a9a3
Author: Tianqi Chen <tq...@users.noreply.github.com>
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 1682ca1147..0f9a14801e 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',
+ )
+ }
}