Fix release-branch Jenkins builds (#14121)

* bring back Jenkins makefile and requirements.txt

* Try using CHANGE_ID to identify pull requests everywhere.

* Remove slashes from dockerfile tag

Authored-by: David Riazati <driazati@users.noreply.github.com>
diff --git a/ci/jenkins/Makefile b/ci/jenkins/Makefile
new file mode 100644
index 0000000..5c9e0ac
--- /dev/null
+++ b/ci/jenkins/Makefile
@@ -0,0 +1,27 @@
+# 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.
+
+_venv: requirements.txt
+	rm -rf _venv
+	python3 -mvenv _venv
+	_venv/bin/pip3 install -r requirements.txt
+
+all: _venv
+	_venv/bin/python3 generate.py
+
+.PHONY: all venv
+.DEFAULT_GOAL=all
diff --git a/ci/jenkins/generated/arm_jenkinsfile.groovy b/ci/jenkins/generated/arm_jenkinsfile.groovy
index 0fc71b4..6a61ee3 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 2022-12-09T15:39:24.387114
+// Generated at 2023-02-24T10:59:48.308906
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/cortexm_jenkinsfile.groovy b/ci/jenkins/generated/cortexm_jenkinsfile.groovy
index 25846f5..a634e6c 100644
--- a/ci/jenkins/generated/cortexm_jenkinsfile.groovy
+++ b/ci/jenkins/generated/cortexm_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 2022-12-09T15:39:24.437899
+// Generated at 2023-02-24T10:59:48.195517
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/cpu_jenkinsfile.groovy b/ci/jenkins/generated/cpu_jenkinsfile.groovy
index f9ede00..501ba15 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 2022-12-09T15:39:24.540570
+// Generated at 2023-02-24T10:59:48.278505
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/docker_jenkinsfile.groovy b/ci/jenkins/generated/docker_jenkinsfile.groovy
index 9e1946c..2196cda 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 2022-12-09T15:39:24.508775
+// Generated at 2023-02-24T12:50:23.899457
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
@@ -589,7 +589,7 @@
     returnStdout: true,
     script: 'git log -1 --format=\'%h\''
   ).trim()
-  def full_name = "${image_name}:${env.BRANCH_NAME}-${hash}-${env.BUILD_NUMBER}"
+  def full_name = "${image_name}:${env.BRANCH_NAME.replace('/', '-')}-${hash}-${env.BUILD_NUMBER}"
   sh(
     script: "${docker_build} ${image_name} --spec ${full_name}",
     label: 'Build docker image'
diff --git a/ci/jenkins/generated/gpu_jenkinsfile.groovy b/ci/jenkins/generated/gpu_jenkinsfile.groovy
index a560969..fbba71e 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 2022-12-09T15:39:24.455336
+// Generated at 2023-02-24T10:59:48.211755
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/hexagon_jenkinsfile.groovy b/ci/jenkins/generated/hexagon_jenkinsfile.groovy
index c2f39a0..d81e952 100644
--- a/ci/jenkins/generated/hexagon_jenkinsfile.groovy
+++ b/ci/jenkins/generated/hexagon_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 2022-12-09T15:39:24.369191
+// Generated at 2023-02-24T10:59:48.326310
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/i386_jenkinsfile.groovy b/ci/jenkins/generated/i386_jenkinsfile.groovy
index ae66fbe..875a0fb 100644
--- a/ci/jenkins/generated/i386_jenkinsfile.groovy
+++ b/ci/jenkins/generated/i386_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 2022-12-09T15:39:24.421467
+// Generated at 2023-02-24T10:59:48.262737
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/lint_jenkinsfile.groovy b/ci/jenkins/generated/lint_jenkinsfile.groovy
index f8dccc8..834c500 100644
--- a/ci/jenkins/generated/lint_jenkinsfile.groovy
+++ b/ci/jenkins/generated/lint_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 2022-12-09T15:39:24.476946
+// Generated at 2023-02-24T10:59:48.231928
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/minimal_jenkinsfile.groovy b/ci/jenkins/generated/minimal_jenkinsfile.groovy
index 6c4abb0..f7f3534 100644
--- a/ci/jenkins/generated/minimal_jenkinsfile.groovy
+++ b/ci/jenkins/generated/minimal_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 2022-12-09T15:39:24.492813
+// Generated at 2023-02-24T10:59:48.162133
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/riscv_jenkinsfile.groovy b/ci/jenkins/generated/riscv_jenkinsfile.groovy
index 7b9bbe7..d48e691 100644
--- a/ci/jenkins/generated/riscv_jenkinsfile.groovy
+++ b/ci/jenkins/generated/riscv_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 2022-12-09T15:39:24.405262
+// Generated at 2023-02-24T10:59:48.246731
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/generated/wasm_jenkinsfile.groovy b/ci/jenkins/generated/wasm_jenkinsfile.groovy
index 8c8ee03..6169e0c 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 2022-12-09T15:39:24.526394
+// Generated at 2023-02-24T10:59:48.295360
 
 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
 // These are set at runtime from data in ci/jenkins/docker-images.yml, update
@@ -145,12 +145,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -174,15 +174,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -275,8 +275,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -287,7 +287,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -310,7 +310,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }
diff --git a/ci/jenkins/requirements.txt b/ci/jenkins/requirements.txt
new file mode 100644
index 0000000..d8086ec
--- /dev/null
+++ b/ci/jenkins/requirements.txt
@@ -0,0 +1 @@
+Jinja2>=3.0.0
diff --git a/ci/jenkins/templates/docker_jenkinsfile.groovy.j2 b/ci/jenkins/templates/docker_jenkinsfile.groovy.j2
index 07ae498..9d8bf2f 100644
--- a/ci/jenkins/templates/docker_jenkinsfile.groovy.j2
+++ b/ci/jenkins/templates/docker_jenkinsfile.groovy.j2
@@ -66,7 +66,7 @@
     returnStdout: true,
     script: 'git log -1 --format=\'%h\''
   ).trim()
-  def full_name = "${image_name}:${env.BRANCH_NAME}-${hash}-${env.BUILD_NUMBER}"
+  def full_name = "${image_name}:${env.BRANCH_NAME.replace('/', '-')}-${hash}-${env.BUILD_NUMBER}"
   sh(
     script: "${docker_build} ${image_name} --spec ${full_name}",
     label: 'Build docker image'
diff --git a/ci/jenkins/templates/utils/Prepare.groovy.j2 b/ci/jenkins/templates/utils/Prepare.groovy.j2
index d5aebdc..2da110e 100644
--- a/ci/jenkins/templates/utils/Prepare.groovy.j2
+++ b/ci/jenkins/templates/utils/Prepare.groovy.j2
@@ -15,12 +15,12 @@
   )
 
   // Determine merge commit to use for all stages
-  if (env.BRANCH_NAME == 'main') {
-    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to main.
+  if (!env.CHANGE_ID) {
+    // Only set upstream_revision to HEAD and skip merging to avoid a race with another commit merged to a branch.
     update_upstream_revision("HEAD")
   } else {
-    // This is PR branch so merge with latest main.
-    merge_with_main()
+    // This is PR branch so merge with latest upstream.
+    merge_with_upstream()
   }
 
   sh(
@@ -44,15 +44,15 @@
   }
 }
 
-def merge_with_main() {
+def merge_with_upstream() {
   sh (
-    script: 'git fetch origin main',
-    label: 'Fetch upstream',
+    script: "git fetch origin ${env.CHANGE_TARGET}",
+    label: "Fetch upstream branch ${env.CHANGE_TARGET}",
   )
   update_upstream_revision("FETCH_HEAD")
   sh (
     script: "git -c user.name=TVM-Jenkins -c user.email=jenkins@tvm.apache.org merge ${upstream_revision}",
-    label: 'Merge to origin/main'
+    label: "Merge to ${env.CHANGE_TARGET}"
   )
 }
 
@@ -145,8 +145,8 @@
 }
 
 def cancel_previous_build() {
-  // cancel previous build if it is not on main.
-  if (env.BRANCH_NAME != 'main') {
+  // cancel previous build if it is for a PR.
+  if (env.CHANGE_ID) {
     def buildNumber = env.BUILD_NUMBER as int
     // Milestone API allows us to cancel previous build
     // with the same milestone number
@@ -157,7 +157,7 @@
 
 def checkout_trusted_files() {
   // trust everything from branch builds
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     return;
   }
 
@@ -180,7 +180,7 @@
 }
 
 def should_skip_ci(pr_number) {
-  if (env.BRANCH_NAME == null || !env.BRANCH_NAME.startsWith('PR-')) {
+  if (!env.CHANGE_ID) {
     // never skip CI on build sourced from a branch
     return false
   }