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

hello-stephen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 1f241cf9b45 [chore](ci) Connect PR checks and comment triggers to 
current TeamCity (#70459)
1f241cf9b45 is described below

commit 1f241cf9b451730d06fc2edbba89bef27ba4efda
Author: Dongyang Li <[email protected]>
AuthorDate: Wed Nov 11 16:39:10 2026 +0800

    [chore](ci) Connect PR checks and comment triggers to current TeamCity 
(#70459)
    
    ### Background
    
    Doris PR comments use the existing workflow and shell helper to select
    and trigger TeamCity pipelines. This migration replaces the retired
    environment and embedded credentials with the current TeamCity and a
    dedicated token.
    
    ### What problem does this PR solve?
    
    Issue Number: None
    Related PR: None
    PR Type: Build/CI/Chore
    
    Problem Summary:
    
    Connect master, branch-4.2 and branch-4.1 PR checks and comment commands
    to the current TeamCity. Preserve file-based trigger/skip selection and
    authorized `skip buildall`. Keep the comment entry point simple: submit
    the requested pipeline with PR number, target branch and an exact VCS
    revision; artifact resolution remains the target pipeline's
    responsibility.
    
    ### Details
    
    - **Change map:** The existing shell helper is 210 lines; tests cover
    direct submission, command parsing, file filters, skip status coverage,
    authorization and credential tracing. The workflow retains the original
    file-selection functions; `.asf.yaml` updates required contexts for
    three branches. No separate production Python client or new
    dependencies.
    - **Affected:** Compile, FEUT, BEUT, CloudUT, P0, CloudP0, VaultP0 and
    NonConcurrent remain required. External and Performance are optional.
    Current command keywords match the `TeamCity/<keyword>` status suffix.
    - **Developer and release impact:** `run p0` submits P0 directly, with
    only `env.PR_NUMBER` and `env.CI_BRANCH` as business parameters and the
    commit in TeamCity's native revision field. The client does not search
    or convert artifact manifests, choose producers, automatically compile
    missing artifacts, override artifact dependencies or deduplicate
    requests. TeamCity owns artifact retrieval and queue replacement. `run
    buildall` and the existing authorized `skip buildall` policy remain;
    Performance is enabled only for master using a positive branch
    condition. Temporary validation VCS roots are rejected before
    submission.
    - **File selection:** Existing FEUT, BEUT, CloudUT, regression and
    Performance file-pattern functions remain unchanged.
    `trigger_or_skip_build` submits related changes and publishes skip
    statuses for unrelated changes. Skipping Compile also resolves its five
    downstream checks. Failed file lookups do not falsely select a skip.
    - **Behavior change:** The new environment publishes current check
    names; External and Performance no longer block merges. Commands run
    trusted default-branch code. Token authentication replaces the retired
    endpoint and public account credentials; authenticated requests are not
    traced.
    - **License and third-party:** Existing Apache licensing; no dependency
    additions.
    - **Rollback:** Revert workflow routing and required-context changes
    together without restoring retired credentials or interrupting running
    builds.
    
    ### Testing
    
    - Local: 26 tests pass, including direct submission of all five
    regression lanes with only two business parameters; exact revision
    binding; repeated submissions; file filtering; skip buildall permissions
    and status coverage on master and both release branches; stale heads;
    HTTP failures; and token redaction.
    - Local: actionlint, shell syntax validation and git diff --check pass.
    - Live: Previous CloudUT submission through the existing shell helper
    completed successfully on branch-4.2 (764 passed, 27 ignored). Earlier
    Compile canaries completed on all three branches. These results predate
    this final simplification.
    - Live: Final direct P0 submission
    [1065](https://8.218.164.131/buildConfiguration/Doris_P0/1065) retained
    the exact requested commit and did not create a Compile dependency. It
    failed before execution because the target pipeline still selects build
    number `UNBOUND`; commit-based artifact lookup is not implemented there
    yet. Compile 852 has an unexpired artifact manifest for this exact
    PR/head, so this failure is a pipeline lookup gap rather than missing
    compilation. All five regression configurations currently use the same
    UNBOUND default. CloudUT canaries
    [1066](https://8.218.164.131/buildConfiguration/Doris_CloudUT/1066)
    (master),
    [1067](https://8.218.164.131/buildConfiguration/Doris_CloudUT/1067)
    (branch-4.2), and
    [1068](https://8.218.164.131/buildConfiguration/Doris_CloudUT/1068)
    (branch-4.1) are running. The live harness uses the committed shell
    helper and substitutes server-local authentication/PR-read transport; it
    does not post GitHub comments.
    
    ### Notes for Reviewers
    
    The issue_comment workflow becomes active only after merge into the
    default branch. Live branch protection must be checked after ASF
    reconciliation. Regression pipelines must resolve artifacts for the
    requested commit and fail when unavailable; the comment client
    deliberately has no producer fallback. The experimental commit-tag
    approach is not part of this PR and has not been deployed to production
    regression configurations. Performance's temporary validation VCS
    configuration still prevents general PR comment runs.
---
 .asf.yaml                                          |  54 ++-
 .github/workflows/comment-to-trigger-teamcity.yml  | 267 ++++----------
 regression-test/pipeline/common/github-utils.sh    |   4 +-
 regression-test/pipeline/common/teamcity-utils.sh  | 399 ++++++---------------
 .../pipeline/common/tests/test_teamcity_utils.py   | 352 ++++++++++++++++++
 5 files changed, 557 insertions(+), 519 deletions(-)

diff --git a/.asf.yaml b/.asf.yaml
index ca9e5e0c381..bd0f6df1571 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -42,6 +42,8 @@ github:
     merge:   false
     rebase:  false
   protected_branches:
+    # master, branch-4.2 and branch-4.1 use the migrated TeamCity contexts.
+    # External and Performance report results without gating these branches.
     master:
       required_status_checks:
         strict: false
@@ -50,20 +52,18 @@ github:
           - Check for secrets
           - Clang Formatter
           - CheckStyle
-          - P0 Regression (Doris Regression)
-          - NonConcurrent Regression (nonConcurrent)
-          - External Regression (Doris External Regression)
-          - cloud_p0 (Doris Cloud Regression)
-          - vault_p0 (Doris Cloud Regression)
-          - FE UT (Doris FE UT)
-          - BE UT (Doris BE UT)
+          - TeamCity/p0
+          - TeamCity/nonconcurrent
+          - TeamCity/cloud_p0
+          - TeamCity/vault_p0
+          - TeamCity/feut
+          - TeamCity/beut
           - Build Third Party Libraries (Linux)
           - Build Third Party Libraries (macOS)
           - Build Third Party Libraries (macOS-arm64)
-          - COMPILE (DORIS_COMPILE)
+          - TeamCity/compile
           - code-review
-          - Cloud UT (Doris Cloud UT)
-          - performance (Doris Performance)
+          - TeamCity/cloudut
       required_pull_request_reviews:
         dismiss_stale_reviews: true
         require_code_owner_reviews: true
@@ -76,15 +76,14 @@ github:
           - Clang Formatter
           - CheckStyle
           - Build Broker
-          - FE UT (Doris FE UT)
-          - BE UT (Doris BE UT)
-          - Cloud UT (Doris Cloud UT)
-          - COMPILE (DORIS_COMPILE)
-          - P0 Regression (Doris Regression)
-          - NonConcurrent Regression (nonConcurrent)
-          - External Regression (Doris External Regression)
-          - cloud_p0 (Doris Cloud Regression)
-          - vault_p0 (Doris Cloud Regression)
+          - TeamCity/feut
+          - TeamCity/beut
+          - TeamCity/cloudut
+          - TeamCity/compile
+          - TeamCity/p0
+          - TeamCity/nonconcurrent
+          - TeamCity/cloud_p0
+          - TeamCity/vault_p0
       required_pull_request_reviews:
         require_code_owner_reviews: true
         required_approving_review_count: 1
@@ -97,15 +96,14 @@ github:
           - Clang Formatter
           - CheckStyle
           - Build Broker
-          - FE UT (Doris FE UT)
-          - BE UT (Doris BE UT)
-          - Cloud UT (Doris Cloud UT)
-          - COMPILE (DORIS_COMPILE)
-          - P0 Regression (Doris Regression)
-          - NonConcurrent Regression (nonConcurrent)
-          - External Regression (Doris External Regression) 
-          - cloud_p0 (Doris Cloud Regression)
-          - vault_p0 (Doris Cloud Regression)
+          - TeamCity/feut
+          - TeamCity/beut
+          - TeamCity/cloudut
+          - TeamCity/compile
+          - TeamCity/p0
+          - TeamCity/nonconcurrent
+          - TeamCity/cloud_p0
+          - TeamCity/vault_p0
       required_pull_request_reviews:
         require_code_owner_reviews: true
         required_approving_review_count: 1
diff --git a/.github/workflows/comment-to-trigger-teamcity.yml 
b/.github/workflows/comment-to-trigger-teamcity.yml
index 853aa6bf17f..00fa548b9b5 100644
--- a/.github/workflows/comment-to-trigger-teamcity.yml
+++ b/.github/workflows/comment-to-trigger-teamcity.yml
@@ -42,47 +42,21 @@ jobs:
           contains(github.event.comment.body, 'run feut') ||
           contains(github.event.comment.body, 'run cloudut') ||
           contains(github.event.comment.body, 'run p0') ||
-          contains(github.event.comment.body, 'run p1') ||
           contains(github.event.comment.body, 'run external') ||
           contains(github.event.comment.body, 'run cloud_p0') ||
-          contains(github.event.comment.body, 'run cloud_p1') ||
           contains(github.event.comment.body, 'run vault_p0') ||
-          contains(github.event.comment.body, 'run nonConcurrent') ||
-          contains(github.event.comment.body, 'run check_coverage') ||
+          contains(github.event.comment.body, 'run nonconcurrent') ||
           contains(github.event.comment.body, 'run performance') ||
-          contains(github.event.comment.body, 'skip buildall') ||
-          contains(github.event.comment.body, 'skip check_coverage')
+          contains(github.event.comment.body, 'skip buildall')
         )
       }}
 
-    # Serialize duplicate commands for one PR. The next run then sees the build
-    # queued by the previous run and can safely skip an identical PR revision.
-    concurrency:
-      group: >-
-        comment-to-trigger-teamcity-${{ github.event.issue.number }}-${{
-          contains(github.event.comment.body, 'run buildall') && 'buildall' ||
-          contains(github.event.comment.body, 'run compile') && 'compile' ||
-          contains(github.event.comment.body, 'run beut') && 'beut' ||
-          contains(github.event.comment.body, 'run feut') && 'feut' ||
-          contains(github.event.comment.body, 'run cloudut') && 'cloudut' ||
-          contains(github.event.comment.body, 'run p0') && 'p0' ||
-          contains(github.event.comment.body, 'run p1') && 'p1' ||
-          contains(github.event.comment.body, 'run external') && 'external' ||
-          contains(github.event.comment.body, 'run cloud_p0') && 'cloud-p0' ||
-          contains(github.event.comment.body, 'run cloud_p1') && 'cloud-p1' ||
-          contains(github.event.comment.body, 'run vault_p0') && 'vault-p0' ||
-          contains(github.event.comment.body, 'run nonConcurrent') && 
'non-concurrent' ||
-          contains(github.event.comment.body, 'run check_coverage') && 
'check-coverage' ||
-          contains(github.event.comment.body, 'run performance') && 
'performance' ||
-          contains(github.event.comment.body, 'skip buildall') && 
'skip-buildall' ||
-          'skip-check-coverage'
-        }}
-      cancel-in-progress: false
-
     runs-on: ubuntu-latest
     env:
       COMMENT_BODY: ${{ github.event.comment.body }}
       COMMENT_USER_ID: ${{ github.event.comment.user.id }}
+      TEAMCITY_CURRENT_URL: ${{ vars.TEAMCITY_CURRENT_URL }}
+      TEAMCITY_CURRENT_TOKEN: ${{ secrets.TEAMCITY_CURRENT_TOKEN }}
       GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
     steps:
@@ -91,49 +65,28 @@ jobs:
       run: |
         COMMENT_BODY=$(echo "${COMMENT_BODY}" | xargs)
         PULL_REQUEST_NUM="$(echo "${{ github.event.issue.pull_request.url }}" 
| awk -F/ '{print $NF}')"
-        COMMIT_ID_FROM_TRIGGER="$(curl -s -H "Authorization:Bearer ${{ 
secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/${{ github.repository 
}}/pulls/${PULL_REQUEST_NUM}" | jq -r '.head.sha')"
-        TARGET_BRANCH="$(curl -s -H "Authorization:Bearer ${{ 
secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/${{ github.repository 
}}/pulls/${PULL_REQUEST_NUM}" | jq -r '.base.ref')"
+        COMMIT_ID_FROM_TRIGGER="$(curl -s -H "Authorization:Bearer 
${GITHUB_TOKEN}" "https://api.github.com/repos/${{ github.repository 
}}/pulls/${PULL_REQUEST_NUM}" | jq -r '.head.sha')"
+        TARGET_BRANCH="$(curl -s -H "Authorization:Bearer ${GITHUB_TOKEN}" 
"https://api.github.com/repos/${{ github.repository 
}}/pulls/${PULL_REQUEST_NUM}" | jq -r '.base.ref')"
         if [[ "${COMMENT_BODY}" == *'run buildall'* ||
             "${COMMENT_BODY}" == *'run compile'* ||
             "${COMMENT_BODY}" == *'run beut'* ||
             "${COMMENT_BODY}" == *'run feut'* ||
             "${COMMENT_BODY}" == *'run cloudut'* ||
             "${COMMENT_BODY}" == *'run p0'* ||
-            "${COMMENT_BODY}" == *'run p1'* ||
             "${COMMENT_BODY}" == *'run external'* ||
             "${COMMENT_BODY}" == *'run cloud_p0'* ||
-            "${COMMENT_BODY}" == *'run cloud_p1'* ||
             "${COMMENT_BODY}" == *'run vault_p0'* ||
-            "${COMMENT_BODY}" == *'run nonConcurrent'* ||
-            "${COMMENT_BODY}" == *'run check_coverage'* ||
+            "${COMMENT_BODY}" == *'run nonconcurrent'* ||
             "${COMMENT_BODY}" == *'run performance'* ]]; then
             echo "comment_trigger=true" | tee -a "$GITHUB_OUTPUT"
             echo "comment_skip=false" | tee -a "$GITHUB_OUTPUT"
-            echo "comment_skip_coverage=false" | tee -a "$GITHUB_OUTPUT"
         elif [[ "${COMMENT_BODY}" == *'skip buildall'* ]]; then
-            echo "comment_skip_coverage=false" | tee -a "$GITHUB_OUTPUT"
             if [[ "${COMMENT_USER_ID}" == '27881198' ||
                   "${COMMENT_USER_ID}" == '2899462' ||
                   "${COMMENT_USER_ID}" == '37901441' ]]; then
                 echo "comment_trigger=false" | tee -a "$GITHUB_OUTPUT"
                 echo "comment_skip=true" | tee -a "$GITHUB_OUTPUT"
                 echo "COMMENT_USER_ID ${COMMENT_USER_ID} is allowed to skip 
buildall."
-            elif [[ "${COMMENT_USER_ID}" == '9208457' && "${TARGET_BRANCH}" == 
*'branch-2.1'* ]]; then
-                echo "COMMENT_USER_ID ${COMMENT_USER_ID} is allowed to skip 
buildall for branch-2.1"
-                echo "comment_trigger=false" | tee -a "$GITHUB_OUTPUT"
-                echo "comment_skip=true" | tee -a "$GITHUB_OUTPUT"
-            elif [[ "${COMMENT_USER_ID}" == '98214048' && "${TARGET_BRANCH}" 
== *'branch-3.0'* ]]; then
-                echo "COMMENT_USER_ID ${COMMENT_USER_ID} is allowed to skip 
buildall for branch-3.0"
-                echo "comment_trigger=false" | tee -a "$GITHUB_OUTPUT"
-                echo "comment_skip=true" | tee -a "$GITHUB_OUTPUT"
-            elif [[ "${COMMENT_USER_ID}" == '101034200' && "${TARGET_BRANCH}" 
== *'branch-3.1'* ]]; then
-                echo "COMMENT_USER_ID ${COMMENT_USER_ID} is allowed to skip 
buildall for branch-3.1"
-                echo "comment_trigger=false" | tee -a "$GITHUB_OUTPUT"
-                echo "comment_skip=true" | tee -a "$GITHUB_OUTPUT"
-            elif [[ ("${COMMENT_USER_ID}" == '9208457' || "${COMMENT_USER_ID}" 
== '2899462') && "${TARGET_BRANCH}" == *'branch-4.0'* ]]; then
-                echo "COMMENT_USER_ID ${COMMENT_USER_ID} is allowed to skip 
buildall for branch-4.0"
-                echo "comment_trigger=false" | tee -a "$GITHUB_OUTPUT"
-                echo "comment_skip=true" | tee -a "$GITHUB_OUTPUT"
             elif [[ "${COMMENT_USER_ID}" == '9208457' && "${TARGET_BRANCH}" == 
*'branch-4.1'* ]]; then
                 echo "COMMENT_USER_ID ${COMMENT_USER_ID} is allowed to skip 
buildall for branch-4.1"
                 echo "comment_trigger=false" | tee -a "$GITHUB_OUTPUT"
@@ -146,27 +99,9 @@ jobs:
                 echo "COMMENT_USER_ID ${COMMENT_USER_ID} is not allowed to 
skip buildall."
                 exit
             fi
-        elif [[ "${COMMENT_BODY}" == *'skip check_coverage'* ]] && 
-             [[ "${COMMENT_USER_ID}" == '37901441' || 
-              "${COMMENT_USER_ID}" == '27881198' ||
-              "${COMMENT_USER_ID}" == '9208457' ||
-              "${COMMENT_USER_ID}" == '10553413' ||
-              "${COMMENT_USER_ID}" == '1736049' ||
-              "${COMMENT_USER_ID}" == '64513324' ||
-              "${COMMENT_USER_ID}" == '2274896' ||
-              "${COMMENT_USER_ID}" == '98214048' ||
-              "${COMMENT_USER_ID}" == '19986811' ||
-              "${COMMENT_USER_ID}" == '48427519' ||
-              "${COMMENT_USER_ID}" == '6919662' ||
-              "${COMMENT_USER_ID}" == '101034200' ||
-              "${COMMENT_USER_ID}" == '2899462' ]]; then
-            echo "comment_trigger=false" | tee -a "$GITHUB_OUTPUT"
-            echo "comment_skip=false" | tee -a "$GITHUB_OUTPUT"
-            echo "comment_skip_coverage=true" | tee -a "$GITHUB_OUTPUT"
         else
             echo "comment_trigger=false" | tee -a "$GITHUB_OUTPUT"
             echo "comment_skip=false" | tee -a "$GITHUB_OUTPUT"
-            echo "comment_skip_coverage=false" | tee -a "$GITHUB_OUTPUT"
             echo "find no keyword in comment body, skip this action."
             exit
         fi
@@ -174,6 +109,20 @@ jobs:
         echo "PULL_REQUEST_NUM=${PULL_REQUEST_NUM}" | tee -a "$GITHUB_OUTPUT"
         echo "COMMIT_ID_FROM_TRIGGER=${COMMIT_ID_FROM_TRIGGER}" | tee -a 
"$GITHUB_OUTPUT"
         echo "TARGET_BRANCH=${TARGET_BRANCH}" | tee -a "$GITHUB_OUTPUT"
+        echo "TARGET_BRANCH=${TARGET_BRANCH}" >> "$GITHUB_ENV"
+        case "$TARGET_BRANCH" in
+          master|branch-4.2|branch-4.1)
+            if [[ "$COMMENT_BODY" == *'run '* ]]; then
+              permission=$(curl --fail --silent --show-error \
+                -H "Authorization: Bearer ${GITHUB_TOKEN}" \
+                "https://api.github.com/repos/${{ github.repository 
}}/collaborators/${{ github.event.comment.user.login }}/permission" | jq -r 
.permission)
+              [[ "$permission" == write || "$permission" == maintain || 
"$permission" == admin ]] || {
+                echo 'Repository write access is required to trigger current 
TeamCity'; exit 1;
+              }
+            fi
+            ;;
+          *) echo "No TeamCity PR pipeline configured for $TARGET_BRANCH"; 
exit 1;;
+        esac
         COMMENT_BODY_DELIMITER=$(dd if=/dev/urandom bs=15 count=1 status=none 
| base64)
         {
           echo "COMMENT_BODY<<${COMMENT_BODY_DELIMITER}"
@@ -181,26 +130,34 @@ jobs:
           echo "${COMMENT_BODY_DELIMITER}"
         } | tee -a "$GITHUB_OUTPUT"
 
-        reg="run 
(buildall|compile|p0|p1|feut|beut|cloudut|external|clickbench|cloud_p0|cloud_p1|vault_p0|nonConcurrent|performance|check_coverage)(
 [1-9]*[0-9]+)*"
-        COMMENT_TRIGGER_TYPE="$(echo -e "${COMMENT_BODY}" | xargs | grep -E 
"${reg}" | awk -F' ' '{print $2}' | sed -n 1p | sed 's/\r//g')"
-        COMMENT_REPEAT_TIMES="$(echo -e "${COMMENT_BODY}" | xargs | grep -E 
"${reg}" | awk -F' ' '{print $3}' | sed -n 1p | sed 's/\r//g')"
-        if [[ -n "${COMMENT_REPEAT_TIMES}" && ! "${COMMENT_REPEAT_TIMES}" =~ 
^[0-9]+$ ]]; then
-          echo "COMMENT_REPEAT_TIMES '${COMMENT_REPEAT_TIMES}' is not a valid 
number, ignoring."
-          COMMENT_REPEAT_TIMES=""
+        COMMENT_TRIGGER_TYPE=""
+        COMMENT_REPEAT_TIMES="1"
+        reg="^run 
(buildall|compile|p0|feut|beut|cloudut|external|cloud_p0|vault_p0|nonconcurrent|performance)(
 ([1-9][0-9]*))?$"
+        if [[ "${COMMENT_BODY}" =~ $reg ]]; then
+          COMMENT_TRIGGER_TYPE=${BASH_REMATCH[1]}
+          COMMENT_REPEAT_TIMES=${BASH_REMATCH[3]:-1}
+        elif [[ "${COMMENT_BODY}" != 'skip buildall' ]]; then
+          echo 'Unsupported command; use run <TeamCity check suffix> [count] 
or run buildall'; exit 1
+        fi
+        if [[ "$COMMENT_TRIGGER_TYPE" == performance ]]; then
+          [[ "$TARGET_BRANCH" == master ]] || { echo 'Performance is available 
only for master'; exit 1; }
         fi
         echo "COMMENT_TRIGGER_TYPE=${COMMENT_TRIGGER_TYPE}" | tee -a 
"$GITHUB_OUTPUT"
         echo "COMMENT_REPEAT_TIMES=${COMMENT_REPEAT_TIMES}" | tee -a 
"$GITHUB_OUTPUT"
 
     - name: "Checkout master"
-      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) || 
fromJSON(steps.parse.outputs.comment_skip) || 
fromJSON(steps.parse.outputs.comment_skip_coverage) }}
+      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) || 
fromJSON(steps.parse.outputs.comment_skip) }}
       uses: actions/checkout@v4
+      with:
+        ref: ${{ github.event.repository.default_branch }}
+        persist-credentials: false
 
     - name: "Check if pr need run build"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) }}
       id: changes
       run: |
         source regression-test/pipeline/common/github-utils.sh
-        set -x
+        set +x
         if _get_pr_changed_files "${{ steps.parse.outputs.PULL_REQUEST_NUM 
}}"; then
           if file_changed_fe_ut; then
             echo "changed_fe_ut=true" | tee -a "$GITHUB_OUTPUT"
@@ -220,16 +177,11 @@ jobs:
           if file_changed_regression_p0; then
             echo "changed_p0=true" | tee -a "$GITHUB_OUTPUT"
             echo "changed_external=true" | tee -a "$GITHUB_OUTPUT"
-            echo "changed_nonConcurrent=true" | tee -a "$GITHUB_OUTPUT"
+            echo "changed_nonconcurrent=true" | tee -a "$GITHUB_OUTPUT"
           else
             echo "changed_p0=false" | tee -a "$GITHUB_OUTPUT"
             echo "changed_external=false" | tee -a "$GITHUB_OUTPUT"
-            echo "changed_nonConcurrent=false" | tee -a "$GITHUB_OUTPUT"
-          fi
-          if file_changed_regression_p1; then
-            echo "changed_p1=true" | tee -a "$GITHUB_OUTPUT"
-          else
-            echo "changed_p1=false" | tee -a "$GITHUB_OUTPUT"
+            echo "changed_nonconcurrent=false" | tee -a "$GITHUB_OUTPUT"
           fi
           if file_changed_performance; then
             echo "changed_performance=true" | tee -a "$GITHUB_OUTPUT"
@@ -243,16 +195,6 @@ jobs:
             echo "changed_cloud_p0=false" | tee -a "$GITHUB_OUTPUT"
             echo "changed_vault_p0=false" | tee -a "$GITHUB_OUTPUT"
           fi
-          if file_changed_cloud_p1; then
-            echo "changed_cloud_p1=true" | tee -a "$GITHUB_OUTPUT"
-          else
-            echo "changed_cloud_p1=false" | tee -a "$GITHUB_OUTPUT"
-          fi
-          if file_changed_regression_p0 && file_changed_be_ut && 
file_changed_cloud_p0; then
-            echo "need_check_coverage=true" | tee -a "$GITHUB_OUTPUT"
-          else
-            echo "need_check_coverage=false" | tee -a "$GITHUB_OUTPUT"
-          fi
         else
           echo "INFO: failed to _get_pr_changed_files, default trigger all"
           echo "changed_fe_ut=true" | tee -a "$GITHUB_OUTPUT"
@@ -260,13 +202,10 @@ jobs:
           echo "changed_cloud_ut=true" | tee -a "$GITHUB_OUTPUT"
           echo "changed_p0=true" | tee -a "$GITHUB_OUTPUT"
           echo "changed_external=true" | tee -a "$GITHUB_OUTPUT"
-          echo "changed_p1=true" | tee -a "$GITHUB_OUTPUT"
           echo "changed_performance=true" | tee -a "$GITHUB_OUTPUT"
           echo "changed_cloud_p0=true" | tee -a "$GITHUB_OUTPUT"
-          echo "changed_cloud_p1=true" | tee -a "$GITHUB_OUTPUT"
           echo "changed_vault_p0=true" | tee -a "$GITHUB_OUTPUT"
-          echo "changed_nonConcurrent=true" | tee -a "$GITHUB_OUTPUT"
-          echo "need_check_coverage=true" | tee -a "$GITHUB_OUTPUT"
+          echo "changed_nonconcurrent=true" | tee -a "$GITHUB_OUTPUT"
         fi
 
     # - name: "Setup tmate session"
@@ -286,9 +225,8 @@ jobs:
         source regression-test/pipeline/common/github-utils.sh
 
         if [[ ! -f all_files ]]; then
-          echo "WARNING: all_files not found (GitHub API call likely failed in 
previous step)."
-          echo "Skipping sensitive check — previous step already defaulted to 
trigger-all."
-          exit 0
+          echo "Cannot check pipeline script changes: all_files is missing."
+          exit 1
         fi
 
         changed_sensitive=false
@@ -310,18 +248,11 @@ jobs:
           exit 1
         fi
 
-    - name: "skip check coverage"
-      if: ${{ fromJSON(steps.parse.outputs.comment_skip_coverage) }}
-      run: |
-        source ./regression-test/pipeline/common/teamcity-utils.sh
-        set -x
-        skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" 
check_coverage
-
-    - name: "Trigger or Skip feut"
+    - name: "Trigger feut"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["feut", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
-        set -x
+        set +x
         trigger_or_skip_build \
           "${{ steps.changes.outputs.changed_fe_ut }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
@@ -331,11 +262,11 @@ jobs:
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
 
 
-    - name: "Trigger or Skip beut"
+    - name: "Trigger beut"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["beut", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
-        set -x
+        set +x
         trigger_or_skip_build \
           "${{ steps.changes.outputs.changed_be_ut }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
@@ -344,11 +275,11 @@ jobs:
           "beut" \
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
 
-    - name: "Trigger or Skip cloudut"
+    - name: "Trigger cloudut"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["cloudut", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
-        set -x
+        set +x
         trigger_or_skip_build \
           "${{ steps.changes.outputs.changed_cloud_ut }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
@@ -357,15 +288,15 @@ jobs:
           "cloudut" \
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
 
-    - name: "Trigger or Skip compile"
+    - name: "Trigger compile"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["compile", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
-        set -x
-        if [[ "${{ steps.changes.outputs.changed_p0 }}" == "true" ]] || [[ 
"${{ steps.changes.outputs.changed_cloud_p0 }}" == "true" ]]; then
-          changed="true"
+        set +x
+        if [[ "${{ steps.changes.outputs.changed_p0 }}" == true || "${{ 
steps.changes.outputs.changed_cloud_p0 }}" == true ]]; then
+          changed=true
         else
-          changed="false"
+          changed=false
         fi
         trigger_or_skip_build \
           "${changed}" \
@@ -375,14 +306,14 @@ jobs:
           "compile" \
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
 
-    - name: "Trigger or Skip p0"
+    - name: "Trigger p0"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["p0", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
         if [[ ${{ steps.parse.outputs.COMMENT_TRIGGER_TYPE }} == "buildall" 
]]; then
           echo "COMMENT_TRIGGER_TYPE is buildall, trigger compile is enough, 
compile will trigger p0" && exit
         fi
-        set -x
+        set +x
         trigger_or_skip_build \
           "${{ steps.changes.outputs.changed_p0 }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
@@ -391,31 +322,14 @@ jobs:
           "p0" \
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
 
-    - name: "Trigger or Skip p1"
-      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["p1", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
-      run: |
-        source ./regression-test/pipeline/common/teamcity-utils.sh
-        COMMIT_ID_FROM_TRIGGER=${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER 
}}
-        if [[ ${{ steps.parse.outputs.COMMENT_TRIGGER_TYPE }} == "buildall" 
]]; then
-          echo "COMMENT_TRIGGER_TYPE is buildall, trigger compile is enough, 
compile will trigger p1" && exit
-        fi
-        set -x
-        trigger_or_skip_build \
-          "${{ steps.changes.outputs.changed_p1 }}" \
-          "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
-          "${{ steps.parse.outputs.TARGET_BRANCH }}" \
-          "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" \
-          "p1" \
-          "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
-
-    - name: "Trigger or Skip external"
+    - name: "Trigger external"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["external", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
         if [[ ${{ steps.parse.outputs.COMMENT_TRIGGER_TYPE }} == "buildall" 
]]; then
           echo "COMMENT_TRIGGER_TYPE is buildall, trigger compile is enough, 
compile will trigger external" && exit
         fi
-        set -x
+        set +x
         trigger_or_skip_build \
           "${{ steps.changes.outputs.changed_external }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
@@ -424,14 +338,14 @@ jobs:
           "external" \
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
 
-    - name: "Trigger or Skip cloud_p0"
+    - name: "Trigger cloud_p0"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["cloud_p0", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
         if [[ ${{ steps.parse.outputs.COMMENT_TRIGGER_TYPE }} == "buildall" 
]]; then
           echo "COMMENT_TRIGGER_TYPE is buildall, trigger compile is enough, 
compile will trigger cloud_p0" && exit
         fi
-        set -x
+        set +x
         trigger_or_skip_build \
           "${{ steps.changes.outputs.changed_cloud_p0 }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
@@ -441,14 +355,14 @@ jobs:
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
 
 
-    - name: "Trigger or Skip vault_p0"
+    - name: "Trigger vault_p0"
       if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["vault_p0", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
         if [[ ${{ steps.parse.outputs.COMMENT_TRIGGER_TYPE }} == "buildall" 
]]; then
           echo "COMMENT_TRIGGER_TYPE is buildall, trigger compile is enough, 
compile will trigger vault_p0" && exit
         fi
-        set -x
+        set +x
         trigger_or_skip_build \
           "${{ steps.changes.outputs.changed_vault_p0 }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
@@ -457,59 +371,27 @@ jobs:
           "vault_p0" \
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
 
-    - name: "Trigger or Skip nonConcurrent"
-      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["nonConcurrent", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
+    - name: "Trigger nonconcurrent"
+      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["nonconcurrent", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
         if [[ ${{ steps.parse.outputs.COMMENT_TRIGGER_TYPE }} == "buildall" 
]]; then
-          echo "COMMENT_TRIGGER_TYPE is buildall, trigger compile is enough, 
compile will trigger nonConcurrent" && exit
+          echo "COMMENT_TRIGGER_TYPE is buildall, trigger compile is enough, 
compile will trigger nonconcurrent" && exit
         fi
-        set -x
+        set +x
         trigger_or_skip_build \
-          "${{ steps.changes.outputs.changed_nonConcurrent }}" \
+          "${{ steps.changes.outputs.changed_nonconcurrent }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
           "${{ steps.parse.outputs.TARGET_BRANCH }}" \
           "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" \
-          "nonConcurrent" \
+          "nonconcurrent" \
           "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
   
-    - name: "Trigger or Skip check_coverage"
-      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["check_coverage", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
+    - name: "Trigger performance"
+      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["performance", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) && steps.parse.outputs.TARGET_BRANCH 
== 'master' }}
       run: |
         source ./regression-test/pipeline/common/teamcity-utils.sh
-        if [[ ${{ steps.parse.outputs.COMMENT_TRIGGER_TYPE }} == "buildall" 
]]; then
-          echo "COMMENT_TRIGGER_TYPE is buildall, check_coverage will be 
triggered after p0,cloud_p0,external,nonConcurrent,beut" && exit
-        fi
-        set -x
-        trigger_or_skip_build \
-          "${{ steps.changes.outputs.need_check_coverage }}" \
-          "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
-          "${{ steps.parse.outputs.TARGET_BRANCH }}" \
-          "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" \
-          "check_coverage" \
-          "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
-
-    - name: "Trigger or Skip cloud_p1"
-      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["cloud_p1", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
-      run: |
-        source ./regression-test/pipeline/common/teamcity-utils.sh
-        if [[ ${{ steps.parse.outputs.COMMENT_TRIGGER_TYPE }} == "buildall" 
]]; then
-          echo "COMMENT_TRIGGER_TYPE is buildall, trigger compile is enough, 
compile will trigger cloud_p1" && exit
-        fi
-        set -x
-        trigger_or_skip_build \
-          "${{ steps.changes.outputs.changed_cloud_p1 }}" \
-          "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
-          "${{ steps.parse.outputs.TARGET_BRANCH }}" \
-          "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" \
-          "cloud_p1" \
-          "${{ steps.parse.outputs.COMMENT_REPEAT_TIMES }}"
-
-    - name: "Trigger or Skip performance"
-      if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && 
contains(fromJSON('["performance", "buildall"]'), 
steps.parse.outputs.COMMENT_TRIGGER_TYPE) }}
-      run: |
-        source ./regression-test/pipeline/common/teamcity-utils.sh
-        set -x
+        set +x
         trigger_or_skip_build \
           "${{ steps.changes.outputs.changed_performance }}" \
           "${{ steps.parse.outputs.PULL_REQUEST_NUM }}" \
@@ -526,12 +408,11 @@ jobs:
         skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" beut
         skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" compile
         skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" p0
-        skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" p1
         skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" external
-        skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" 
performance
+        if [[ "$TARGET_BRANCH" == master ]]; then
+          skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" 
performance
+        fi
         skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" cloud_p0
-        skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" cloud_p1
         skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" cloudut
         skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" vault_p0
-        skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" 
nonConcurrent
-        skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" 
check_coverage
+        skip_build "${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}" 
nonconcurrent
diff --git a/regression-test/pipeline/common/github-utils.sh 
b/regression-test/pipeline/common/github-utils.sh
index c5d00a47acd..1dbc8040cc9 100644
--- a/regression-test/pipeline/common/github-utils.sh
+++ b/regression-test/pipeline/common/github-utils.sh
@@ -129,7 +129,7 @@ _get_pr_changed_files_count() {
     REPO="${REPO:=doris}"
     try_times=10
     while [[ ${try_times} -gt 0 ]]; do
-        set -x
+        set +x
         if ret=$(
             curl -s \
                 -H "Accept: application/vnd.github+json" \
@@ -168,7 +168,7 @@ _get_pr_changed_files() {
     while [[ ${changed_files_count} -gt 0 ]]; do
         try_times=10
         while [[ ${try_times} -gt 0 ]]; do
-            set -x
+            set +x
             if curl -s \
                 -H "Accept: application/vnd.github+json" \
                 ${GITHUB_TOKEN:+-H "Authorization: Bearer ${GITHUB_TOKEN}"} \
diff --git a/regression-test/pipeline/common/teamcity-utils.sh 
b/regression-test/pipeline/common/teamcity-utils.sh
index 90c8749527a..df8d84ffb83 100644
--- a/regression-test/pipeline/common/teamcity-utils.sh
+++ b/regression-test/pipeline/common/teamcity-utils.sh
@@ -16,252 +16,94 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
-# PR的目标分支
-# 到
-# 可以跑的流水线
-# 的映射
-# 控制哪些分支可以跑哪些流水线
-declare -A targetBranch_to_pipelines
-targetBranch_to_pipelines=(
-    ['master']='feut beut cloudut compile p0 p1 external performance cloud_p0 
cloud_p1 vault_p0 nonConcurrent check_coverage check_coverage_fe'
-    ['branch-4.2']='feut beut cloudut compile p0 p1 external cloud_p0 cloud_p1 
vault_p0 nonConcurrent check_coverage check_coverage_fe'
-    ['branch-4.1']='feut beut cloudut compile p0 p1 external cloud_p0 cloud_p1 
vault_p0 nonConcurrent check_coverage check_coverage_fe'
-    ['branch-4.0']='feut beut cloudut compile p0 p1 external cloud_p0 cloud_p1 
vault_p0 nonConcurrent check_coverage check_coverage_fe'
-    ['branch-3.1']='feut beut cloudut compile p0 p1 external cloud_p0 cloud_p1 
vault_p0 nonConcurrent check_coverage check_coverage_fe'
-    ['branch-3.0']='feut beut cloudut compile p0 p1 external cloud_p0 cloud_p1 
vault_p0 nonConcurrent check_coverage'
-    ['branch-2.1']='feut beut compile p0 p1 external'
-    ['branch-2.0']='feut beut compile p0 p1 external'
+# Comment keywords match the suffix of the published TeamCity/<keyword> check.
+# External and Performance are optional and therefore absent from required 
checks.
+declare -A targetBranch_to_pipelines=(
+    [master]='compile feut beut cloudut p0 cloud_p0 vault_p0 external 
nonconcurrent performance'
+    [branch-4.2]='compile feut beut cloudut p0 cloud_p0 vault_p0 external 
nonconcurrent'
+    [branch-4.1]='compile feut beut cloudut p0 cloud_p0 vault_p0 external 
nonconcurrent'
 )
-
-# github中评论的要触发的流水线名字
-# 到
-# teamcity流水线实际的名称
-# 的映射
-# 新加流水线需要修改这里
-declare -A comment_to_pipeline
-comment_to_pipeline=(
-    ['feut']='Doris_Doris_FeUt'
-    ['beut']='Doris_DorisBeUt_BeUt'
-    ['cloudut']='Doris_DorisCloudUt_CloudUt'
-    ['compile']='Doris_DorisCompile_Compile'
-    ['p0']='Doris_DorisRegression_P0Regression'
-    ['p1']='Doris_DorisRegression_P1Regression'
-    ['external']='Doris_External_Regression'
-    ['performance']='Doris_DorisPerformance_Performance'
-    ['cloud_p0']='Doris_DorisRegression_CloudP0'
-    ['cloud_p1']='Doris_DorisCloudRegression_CloudP1'
-    ['vault_p0']='Doris_DorisCloudRegression_VaultP0'
-    ['nonConcurrent']='Doris_DorisRegression_NonConcurrentRegression'
-    ['check_coverage']='Doris_Coverage_Merge_P0_UT'
+declare -A comment_to_pipeline=(
+    [compile]=Doris_Compile [feut]=Doris_FEUT [beut]=Doris_BEUT
+    [cloudut]=Doris_CloudUT [p0]=Doris_P0 [cloud_p0]=Doris_CloudP0
+    [vault_p0]=Doris_VaultP0 [external]=Doris_External
+    [nonconcurrent]=Doris_NonConcurrent [performance]=Doris_Performance
 )
-
-# github中评论的要触发的流水线名字
-# 到
-# teamcity流水线返回结果给github的名称
-# 的映射
-# 新加流水线需要修改这里
 declare -A conment_to_context
-conment_to_context=(
-    ['compile']='COMPILE (DORIS_COMPILE)'
-    ['feut']='FE UT (Doris FE UT)'
-    ['beut']='BE UT (Doris BE UT)'
-    ['cloudut']='Cloud UT (Doris Cloud UT)'
-    ['p0']='P0 Regression (Doris Regression)'
-    ['p1']='P1 Regression (Doris Regression)'
-    ['external']='External Regression (Doris External Regression)'
-    ['performance']='performance (Doris Performance)'
-    ['cloud_p0']='cloud_p0 (Doris Cloud Regression)'
-    ['cloud_p1']='cloud_p1 (Doris Cloud Regression)'
-    ['vault_p0']='vault_p0 (Doris Cloud Regression)'
-    ['nonConcurrent']='NonConcurrent Regression (nonConcurrent)'
-    ['check_coverage']='check_coverage (Coverage)'
-)
-
-get_commit_id_of_build() {
-    # 获取某个build的commit id
-    if [[ -z "$1" ]]; then return 1; fi
-    local build_id="$1"
-    local commit_id
-    local ret
-    set -x
-    if ret=$(
-        curl -s -X GET \
-            -u OneMoreChance:OneMoreChance \
-            -H "Content-Type:text/plain" \
-            -H "Accept: application/json" \
-            "http://43.132.222.7:8111/app/rest/builds/${build_id}";
-    ); then
-        set +x
-        commit_id=$(echo "${ret}" | jq -r '.revisions.revision[0].version')
-        echo "${commit_id}"
-    else
-        set +x
-        return 1
-    fi
+for lane in "${!comment_to_pipeline[@]}"; do 
conment_to_context[$lane]="TeamCity/$lane"; done
+unset lane
+TEAMCITY_URL=${TEAMCITY_CURRENT_URL:-}
+
+# Never trace authenticated requests or follow redirects carrying credentials.
+teamcity_request() {
+    set +x
+    [[ ${TEAMCITY_URL:-} =~ ^https://[A-Za-z0-9.-]+(:[0-9]+)?$ && -n 
${TEAMCITY_CURRENT_TOKEN:-} ]] || {
+        echo 'TeamCity HTTPS URL/token is not configured' >&2; return 1;
+    }
+    curl --header "Authorization: Bearer ${TEAMCITY_CURRENT_TOKEN}" "$@"
 }
 
-get_running_build_of_pr() {
-    # "获取pr在某条流水线上正在跑的build"
-    local PULL_REQUEST_NUM="${PULL_REQUEST_NUM:-$1}"
-    local COMMENT_TRIGGER_TYPE="${COMMENT_TRIGGER_TYPE:-$2}"
-    if [[ -z "${PULL_REQUEST_NUM}" || -z "${COMMENT_TRIGGER_TYPE}" ]]; then
-        echo "Usage: get_queue_build_of_pr PULL_REQUEST_NUM 
COMMENT_TRIGGER_TYPE" && return 1
-    fi
-
-    local PIPELINE="${comment_to_pipeline[${COMMENT_TRIGGER_TYPE}]}"
-    local running_builds_list
-    local ret
-    set -x
-    if ret=$(
-        curl -s -X GET \
-            -u OneMoreChance:OneMoreChance \
-            -H "Content-Type:text/plain" \
-            -H "Accept: application/json" \
-            
"http://43.132.222.7:8111/app/rest/builds?locator=buildType:${PIPELINE},branch:pull/${PULL_REQUEST_NUM},running:true";
-    ); then
-        set +x
-        running_builds_list=$(echo "${ret}" | jq -r '.build[].id')
-        echo "${running_builds_list}"
-    else
-        set +x
-        return 1
-    fi
+teamcity_rest() {
+    local path=$1; shift
+    teamcity_request --globoff --fail --silent --show-error --max-time 45 
--max-filesize 8388608 \
+        -H 'Accept: application/json' -H 'Content-Type: application/json' \
+        "${TEAMCITY_URL}/app/rest/${path}" "$@"
 }
-# get_running_build_of_pr "$1" "$2"
-
-get_queue_build_of_pr() {
-    # "获取pr在某条流水线上排队的build"
-    local PULL_REQUEST_NUM="${PULL_REQUEST_NUM:-$1}"
-    local COMMENT_TRIGGER_TYPE="${COMMENT_TRIGGER_TYPE:-$2}"
-    if [[ -z "${PULL_REQUEST_NUM}" || -z "${COMMENT_TRIGGER_TYPE}" ]]; then
-        echo "Usage: get_queue_build_of_pr PULL_REQUEST_NUM 
COMMENT_TRIGGER_TYPE" && return 1
-    fi
 
-    local PIPELINE="${comment_to_pipeline[${COMMENT_TRIGGER_TYPE}]}"
-    local queue_builds_list
-    local ret
-    set -x
-    if ret=$(
-        curl -s -X GET \
-            -u OneMoreChance:OneMoreChance \
-            -H "Content-Type:text/plain" \
-            -H "Accept: application/json" \
-            
"http://43.132.222.7:8111/app/rest/buildQueue?locator=buildType:${PIPELINE}";
-    ); then
-        set +x
-        queue_builds_list=$(echo "${ret}" | jq ".build[] | select(.branchName 
== \"pull/${PULL_REQUEST_NUM}\") | .id")
-        echo "${queue_builds_list}"
-    else
-        set +x
-        echo "WARNING: failed to get queue build for PR ${PULL_REQUEST_NUM} of 
pipeline ${PIPELINE}" && return 1
-    fi
+teamcity_current_pr() {
+    set +x
+    curl --fail --silent --show-error --max-time 45 \
+        -H "Authorization: Bearer ${GITHUB_TOKEN:?}" \
+        "https://api.github.com/repos/apache/doris/pulls/${PULL_REQUEST_NUM}"; |
+        jq -e --arg head "$COMMIT_ID_FROM_TRIGGER" --arg branch 
"$PULL_REQUEST_TARGET_BRANCH" \
+            '.state=="open" and .draft==false and .head.sha==$head and 
.base.ref==$branch' >/dev/null
 }
-# get_queue_build_of_pr "$1" "$2"
-
-get_active_builds_of_revision() {
-    # Return active build IDs for the same PR, pipeline, and revision.
-    # Return 0 when a duplicate exists, 1 when none exists, and 2 when lookup 
fails.
-    local PULL_REQUEST_NUM="${PULL_REQUEST_NUM:-$1}"
-    local COMMENT_TRIGGER_TYPE="${COMMENT_TRIGGER_TYPE:-$2}"
-    local COMMIT_ID_FROM_TRIGGER="${COMMIT_ID_FROM_TRIGGER:-$3}"
-    if [[ -z "${PULL_REQUEST_NUM}" ||
-        -z "${COMMENT_TRIGGER_TYPE}" ||
-        -z "${COMMIT_ID_FROM_TRIGGER}" ]]; then
-        echo "Usage: get_active_builds_of_revision PULL_REQUEST_NUM 
COMMENT_TRIGGER_TYPE COMMIT_ID_FROM_TRIGGER" >&2
-        return 2
-    fi
-
-    local queue_build_ids
-    local running_build_ids
-    if ! queue_build_ids=$(get_queue_build_of_pr "${PULL_REQUEST_NUM}" 
"${COMMENT_TRIGGER_TYPE}"); then
-        echo "WARNING: failed to get queued builds for duplicate check" >&2
-        return 2
-    fi
-    if ! running_build_ids=$(get_running_build_of_pr "${PULL_REQUEST_NUM}" 
"${COMMENT_TRIGGER_TYPE}"); then
-        echo "WARNING: failed to get running builds for duplicate check" >&2
-        return 2
-    fi
-
-    local build_id
-    local build_revision
-    local duplicate_build_ids=()
-    for build_id in ${queue_build_ids} ${running_build_ids}; do
-        if ! build_revision=$(get_commit_id_of_build "${build_id}"); then
-            echo "WARNING: failed to get revision of active build ${build_id}" 
>&2
-            return 2
-        fi
-        if [[ "${build_revision}" == "${COMMIT_ID_FROM_TRIGGER}" ]]; then
-            duplicate_build_ids+=("${build_id}")
-        fi
-    done
 
-    if [[ ${#duplicate_build_ids[@]} -gt 0 ]]; then
-        printf '%s\n' "${duplicate_build_ids[@]}"
-        return 0
-    fi
-    return 1
+teamcity_current_root() {
+    teamcity_rest "buildTypes/id:$1/vcs-root-entries" |
+        jq -e '."vcs-root-entry" | length==1 and 
.[0]."vcs-root".id=="DorisMigrationValidation_ApacheDoris"' >/dev/null || {
+            echo "$1 is not configured for general PR builds; no build 
submitted" >&2; return 1;
+        }
 }
-# get_active_builds_of_revision "$1" "$2" "$3"
-
-cancel_running_build() {
-    local PULL_REQUEST_NUM="${PULL_REQUEST_NUM:-$1}"
-    local COMMENT_TRIGGER_TYPE="${COMMENT_TRIGGER_TYPE:-$2}"
-    if [[ -z "${PULL_REQUEST_NUM}" || -z "${COMMENT_TRIGGER_TYPE}" ]]; then
-        echo "Usage: get_queue_build_of_pr PULL_REQUEST_NUM 
COMMENT_TRIGGER_TYPE" && return 1
-    fi
 
-    local PIPELINE="${comment_to_pipeline[${COMMENT_TRIGGER_TYPE}]}"
-    local build_ids
-    if ! build_ids=$(get_running_build_of_pr "${PULL_REQUEST_NUM}" 
"${COMMENT_TRIGGER_TYPE}"); then return 1; fi
-    for id in ${build_ids}; do
-        set -x
-        if curl -s -X POST \
-            -u OneMoreChance:OneMoreChance \
-            -H "Content-Type:application/json" \
-            -H "Accept: application/json" \
-            "http://43.132.222.7:8111/app/rest/builds/id:${id}"; \
-            -d '{ "comment": "Canceling this running build before triggering a 
new one", "readdIntoQueue": false }'; then
-            set +x
-            echo -e "\nINFO: canceled running build(id ${id}) for PR 
${PULL_REQUEST_NUM} of pipeline ${PIPELINE}"
-        else
-            set +x
-            echo "WARNING: failed to cancel running build(id ${id}) for PR 
${PULL_REQUEST_NUM} of pipeline ${PIPELINE}"
-        fi
+teamcity_current_enqueue() {
+    local type=$1 params=$2 count=$3 ordinal id instance payload actual
+    teamcity_current_root "$type" || return 1
+    instance=$(teamcity_rest 
"vcs-root-instances?locator=buildType:(id:${type})&fields=vcs-root-instance(id,vcs-root-id)"
 |
+        jq -er '[."vcs-root-instance"[] | 
select(."vcs-root-id"=="DorisMigrationValidation_ApacheDoris")] | 
select(length==1) | .[0].id') || return 1
+    for ((ordinal=1; ordinal<=count; ordinal++)); do
+        teamcity_current_pr || { echo 'PR revision or target changed; no new 
build submitted' >&2; return 1; }
+        payload=$(jq -cn --arg type "$type" --arg pr "$PULL_REQUEST_NUM" --arg 
head "$COMMIT_ID_FROM_TRIGGER" --arg instance "$instance" --argjson params 
"$params" '
+            
{buildType:{id:$type},branchName:("pull/"+$pr),revisions:{failOnMissingRevisions:true,revision:[{version:$head,vcsBranchName:("refs/pull/"+$pr+"/head"),"vcs-root-instance":{id:$instance}}]},
+             
properties:{property:($params|to_entries|map({name:.key,value:.value}))},
+             
triggeringOptions:{queueAtTop:false,rebuildAllDependencies:false}}') || return 1
+        # Queue replacement and duplicate handling belong to TeamCity.
+        id=$(teamcity_rest buildQueue -X POST --data-binary "$payload" | jq 
-er .id) || return 1
+        actual=$(teamcity_rest "builds/id:${id}?fields=${TC_FIELDS}") || 
return 1
+        jq -e --arg head "$COMMIT_ID_FROM_TRIGGER" --argjson params "$params" '
+            ((.properties.property // []) | map({key:.name,value:.value}) | 
from_entries) as $p |
+            [.revisions.revision[].version]==[$head] and 
all($params|to_entries[]; $p[.key]==.value)' <<< "$actual" >/dev/null || return 
1
+        echo "INFO: ${type} build ${id}: 
${TEAMCITY_URL}/buildConfiguration/${type}/${id}" >&2
+        printf '%s\n' "$id"
     done
 }
-# cancel_running_build "$1" "$2"
 
-cancel_queue_build() {
-    local PULL_REQUEST_NUM="${PULL_REQUEST_NUM:-$1}"
-    local COMMENT_TRIGGER_TYPE="${COMMENT_TRIGGER_TYPE:-$2}"
-    if [[ -z "${PULL_REQUEST_NUM}" || -z "${COMMENT_TRIGGER_TYPE}" ]]; then
-        echo "Usage: get_queue_build_of_pr PULL_REQUEST_NUM 
COMMENT_TRIGGER_TYPE" && return 1
-    fi
-
-    local PIPELINE="${comment_to_pipeline[${COMMENT_TRIGGER_TYPE}]}"
-    local build_ids
-    if ! build_ids=$(get_queue_build_of_pr "${PULL_REQUEST_NUM}" 
"${COMMENT_TRIGGER_TYPE}"); then return 1; fi
-    for id in ${build_ids}; do
-        set -x
-        if curl -s -X POST \
-            -u OneMoreChance:OneMoreChance \
-            -H "Content-Type:application/json" \
-            -H "Accept: application/json" \
-            "http://43.132.222.7:8111/app/rest/buildQueue/id:${id}"; \
-            -d '{ "comment": "Canceling this queued build before triggering a 
new one", "readdIntoQueue": false }'; then
-            set +x
-            echo -e "\nINFO: canceled queue build(id ${id}) for PR 
${PULL_REQUEST_NUM} of pipeline ${PIPELINE}"
-        else
-            set +x
-            echo "WARNING: failed to cancel queue build(id ${id}) for PR 
${PULL_REQUEST_NUM} of pipeline ${PIPELINE}"
-        fi
-    done
-}
-# cancel_queue_build "$1" "$2"
+teamcity_current_trigger() (
+    set -euo pipefail
+    set +x
+    local lane=$1 count=${2:-1} params
+    [[ $PULL_REQUEST_NUM =~ ^[1-9][0-9]*$ && $COMMIT_ID_FROM_TRIGGER =~ 
^[0-9a-f]{40}$ && $count =~ ^[1-9][0-9]*$ && $count -le 20 ]]
+    [[ " ${targetBranch_to_pipelines[$PULL_REQUEST_TARGET_BRANCH]:-} " == *" 
$lane "* ]] || {
+        echo "No pipeline for $lane on $PULL_REQUEST_TARGET_BRANCH" >&2; exit 
1;
+    }
+    # Artifact lookup belongs to the pipeline; only submit the requested lane 
here.
+    local 
TC_FIELDS='id,revisions(revision(version)),properties(property(name,value))'
+    params=$(jq -cn --arg pr "$PULL_REQUEST_NUM" --arg branch 
"$PULL_REQUEST_TARGET_BRANCH" '{"env.PR_NUMBER":$pr,"env.CI_BRANCH":$branch}')
+    teamcity_current_enqueue "${comment_to_pipeline[$lane]}" "$params" "$count"
+)
 
 skip_build() {
+    set +x
     # 对于不需要跑teamcity pipeline的PR,直接调用github的接口返回成功
     if [[ -z "${GITHUB_TOKEN}" ]]; then
         echo "ERROR: env GITHUB_TOKEN not set"
@@ -274,10 +116,14 @@ skip_build() {
     local COMMIT_ID_FROM_TRIGGER="$1"
     local COMMENT_TRIGGER_TYPE="$2"
 
+    if [[ -z ${conment_to_context[$COMMENT_TRIGGER_TYPE]:-} ]]; then
+        echo "ERROR: Unknown TeamCity check ${COMMENT_TRIGGER_TYPE}" >&2
+        return 1
+    fi
     local state="${TC_BUILD_STATE:-success}" # 可选值 success failure pending
     local 
payload="{\"state\":\"${state}\",\"target_url\":\"\",\"description\":\"Skip 
teamCity 
build\",\"context\":\"${conment_to_context[${COMMENT_TRIGGER_TYPE}]}\"}"
-    set -x
-    if curl -L \
+    set +x
+    if curl --fail --silent --show-error --max-time 45 \
         -X POST \
         -H "Accept: application/vnd.github+json" \
         -H "Authorization: Bearer ${GITHUB_TOKEN:-}" \
@@ -299,29 +145,31 @@ trigger_build() {
     local COMMIT_ID_FROM_TRIGGER="${COMMIT_ID_FROM_TRIGGER:-$2}"
     local COMMENT_TRIGGER_TYPE="${COMMENT_TRIGGER_TYPE:-$3}"
     local COMMENT_REPEAT_TIMES="${COMMENT_REPEAT_TIMES:-$4}"
+    local 
PULL_REQUEST_TARGET_BRANCH="${PULL_REQUEST_TARGET_BRANCH:-${TARGET_BRANCH:-}}"
     if [[ -z "${PULL_REQUEST_NUM}" || -z "${COMMIT_ID_FROM_TRIGGER}" || -z 
"${COMMENT_TRIGGER_TYPE}" ]]; then
         echo "Usage: add_build PULL_REQUEST_NUM COMMIT_ID_FROM_TRIGGER 
COMMENT_TRIGGER_TYPE [COMMENT_REPEAT_TIMES]"
         return 1
     fi
-    local PIPELINE="${comment_to_pipeline[${COMMENT_TRIGGER_TYPE}]}"
-    set -x
-    if curl -s -X POST \
-        -u OneMoreChance:OneMoreChance \
-        -H "Content-Type:text/plain" \
-        -H "Accept: application/json" \
-        
"http://43.132.222.7:8111/httpAuth/action.html?add2Queue=${PIPELINE}&branchName=pull/${PULL_REQUEST_NUM}&name=env.pr_num_from_trigger&value=${PULL_REQUEST_NUM:-}&name=env.commit_id_from_trigger&value=${COMMIT_ID_FROM_TRIGGER:-}&name=env.repeat_times_from_trigger&value=${COMMENT_REPEAT_TIMES:-1}";;
 then
-        set +x
-        echo "INFO: Add new build to PIPELINE ${PIPELINE} of PR 
${PULL_REQUEST_NUM} with COMMENT_REPEAT_TIMES ${COMMENT_REPEAT_TIMES:-1}"
-    else
-        set +x
-        return 1
-    fi
+    teamcity_current_trigger "$COMMENT_TRIGGER_TYPE" 
"${COMMENT_REPEAT_TIMES:-1}"
 }
 # trigger_build "$1" "$2" "$3" "$4"
 
+# A skipped Compile also resolves the checks that its fanout would publish.
+teamcity_skip_unaffected() {
+    teamcity_current_pr || { echo 'PR revision or target changed; no skip 
status written' >&2; return 1; }
+    local lane lanes="$COMMENT_TRIGGER_TYPE"
+    if [[ $COMMENT_TRIGGER_TYPE == compile ]]; then
+        lanes='compile p0 cloud_p0 vault_p0 external nonconcurrent'
+    fi
+    for lane in $lanes; do
+        skip_build "$COMMIT_ID_FROM_TRIGGER" "$lane" || return 1
+    done
+}
+
 trigger_or_skip_build() {
-    # 根据相关文件是否修改,来触发or跳过跑流水线
-    local FILE_CHANGED="$1" # 默认为"true"
+    set +x
+    # Preserve the existing per-pipeline file filter.
+    local FILE_CHANGED="${1:-true}"
     local PULL_REQUEST_NUM="${PULL_REQUEST_NUM:-$2}"
     local PULL_REQUEST_TARGET_BRANCH="${PULL_REQUEST_TARGET_BRANCH:-$3}"
     local COMMIT_ID_FROM_TRIGGER="${COMMIT_ID_FROM_TRIGGER:-$4}"
@@ -334,55 +182,14 @@ trigger_or_skip_build() {
         return 1
     fi
 
-    # 有些分支不需要某些跑流水线,在targetBranch_to_pipelines中的分支,需要判断
-    if [[ "${!targetBranch_to_pipelines[*]}" =~ ${PULL_REQUEST_TARGET_BRANCH} 
]]; then
-        if [[ "${targetBranch_to_pipelines[${PULL_REQUEST_TARGET_BRANCH}]}" =~ 
${COMMENT_TRIGGER_TYPE} ]]; then
-            echo "INFO: pr ${PULL_REQUEST_NUM} to branch 
${PULL_REQUEST_TARGET_BRANCH} need to run ${COMMENT_TRIGGER_TYPE}"
-        else
-            echo "INFO: pr ${PULL_REQUEST_NUM} to branch 
${PULL_REQUEST_TARGET_BRANCH} not need to run ${COMMENT_TRIGGER_TYPE}"
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "${COMMENT_TRIGGER_TYPE}"
-            return 0
-        fi
-    fi
-
-    if [[ "${FILE_CHANGED:-"true"}" == "true" ]]; then
-        local duplicate_build_ids
-        local duplicate_lookup_status=0
-        duplicate_build_ids=$(
-            get_active_builds_of_revision \
-                "${PULL_REQUEST_NUM}" \
-                "${COMMENT_TRIGGER_TYPE}" \
-                "${COMMIT_ID_FROM_TRIGGER}"
-        ) || duplicate_lookup_status=$?
-        if [[ ${duplicate_lookup_status} -eq 0 ]]; then
-            echo "INFO: active build(s) ${duplicate_build_ids//$'\n'/,} 
already exist for PR ${PULL_REQUEST_NUM}, pipeline ${COMMENT_TRIGGER_TYPE}, 
revision ${COMMIT_ID_FROM_TRIGGER}; skip duplicate trigger"
-            return 0
-        elif [[ ${duplicate_lookup_status} -ne 1 ]]; then
-            echo "WARNING: duplicate lookup failed for PR ${PULL_REQUEST_NUM}, 
pipeline ${COMMENT_TRIGGER_TYPE}; continue with the existing trigger flow"
-        fi
-
-        cancel_running_build "${PULL_REQUEST_NUM}" "${COMMENT_TRIGGER_TYPE}"
-        cancel_queue_build "${PULL_REQUEST_NUM}" "${COMMENT_TRIGGER_TYPE}"
-        trigger_build "${PULL_REQUEST_NUM}" "${COMMIT_ID_FROM_TRIGGER}" 
"${COMMENT_TRIGGER_TYPE}" "${COMMENT_REPEAT_TIMES}"
-    else
-        cancel_running_build "${PULL_REQUEST_NUM}" "${COMMENT_TRIGGER_TYPE}"
-        cancel_queue_build "${PULL_REQUEST_NUM}" "${COMMENT_TRIGGER_TYPE}"
-        skip_build "${COMMIT_ID_FROM_TRIGGER}" "${COMMENT_TRIGGER_TYPE}"
-        if [[ ${COMMENT_TRIGGER_TYPE} == "compile" ]]; then
-            # skip compile 的时候,也把 p0 p1 external cloud_p0 cloud_p1 vault_p0 
nonConcurrent check_coverage 都 skip 了
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "p0"
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "p1"
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "external"
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "cloud_p0"
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "cloud_p1"
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "vault_p0"
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "nonConcurrent"
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "check_coverage"
-        elif [[ ${COMMENT_TRIGGER_TYPE} == "beut" ]]; then
-            # skip beut 的时候,也把 check_coverage skip 了
-            skip_build "${COMMIT_ID_FROM_TRIGGER}" "check_coverage"
-        fi
-    fi
+    [[ " ${targetBranch_to_pipelines[$PULL_REQUEST_TARGET_BRANCH]:-} " == *" 
$COMMENT_TRIGGER_TYPE "* ]] || {
+        echo "No pipeline for $COMMENT_TRIGGER_TYPE on 
$PULL_REQUEST_TARGET_BRANCH" >&2; return 1;
+    }
+    case "$FILE_CHANGED" in
+        true) trigger_build "$PULL_REQUEST_NUM" "$COMMIT_ID_FROM_TRIGGER" 
"$COMMENT_TRIGGER_TYPE" "${COMMENT_REPEAT_TIMES:-1}" ;;
+        false) teamcity_skip_unaffected ;;
+        *) echo "Invalid FILE_CHANGED: $FILE_CHANGED" >&2; return 1 ;;
+    esac
 }
 # trigger_or_skip_build "$1" "$2" "$3" "$4" "$5"
 
diff --git a/regression-test/pipeline/common/tests/test_teamcity_utils.py 
b/regression-test/pipeline/common/tests/test_teamcity_utils.py
new file mode 100644
index 00000000000..df740ef82cb
--- /dev/null
+++ b/regression-test/pipeline/common/tests/test_teamcity_utils.py
@@ -0,0 +1,352 @@
+#!/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.
+
+"""Exercise the existing shell entry points without a network or real 
credentials."""
+import json
+import os
+import re
+from pathlib import Path
+import subprocess
+import tempfile
+import textwrap
+import unittest
+
+HELPER = Path(__file__).parents[1] / 'teamcity-utils.sh'
+HEAD = 'a' * 40
+MOCK = r'''
+import json, os, re, sys
+from pathlib import Path
+root=Path(os.environ['FIXTURE_ROOT']); path=root/'state.json'; 
state=json.loads(path.read_text())
+args=sys.argv[1:]; url=next(x for x in args if 
x.startswith('https://ci.example/app/rest/'))
+route=url.split('/app/rest/',1)[1]; method=args[args.index('-X')+1] if '-X' in 
args else 'GET'
+rows=state.get('builds',[])
+def emit(value): print(json.dumps(value))
+if method=='POST':
+ assert route=='buildQueue'
+ body=json.loads(args[args.index('--data-binary')+1]); 
state.setdefault('posts',[]).append(body)
+ bid=100+len(state['posts']); 
body.update(id=bid,number=str(bid),buildTypeId=body['buildType']['id'],state='queued',status='UNKNOWN')
+ 
body['revisions']['revision']=[{'version':body['revisions']['revision'][0]['version']}]
+ state.setdefault('builds',[]).append(body); path.write_text(json.dumps(state))
+ if state.get('lose_response'):
+  state['lose_response']=False; path.write_text(json.dumps(state));sys.exit(1)
+ emit({'id':bid});sys.exit()
+if 'vcs-root-entries' in route:
+ 
emit({'vcs-root-entry':[{'vcs-root':{'id':state.get('root','DorisMigrationValidation_ApacheDoris')}}]})
+elif route.startswith('vcs-root-instances'):
+ 
emit({'vcs-root-instance':[{'id':3,'vcs-root-id':'DorisMigrationValidation_ApacheDoris'}]})
+elif route.startswith('builds/id:'):
+ bid=int(route.split(':')[1].split('?')[0]);emit(next(b for b in rows if 
b['id']==bid))
+else: raise AssertionError(route)
+'''
+
+
+def build(bid=1, lane='Doris_CloudUT', state='running', head=HEAD, 
params=None):
+    return {'id': bid, 'number': str(bid), 'buildTypeId': lane, 'state': state,
+            'status': 'SUCCESS', 'branchName': 'pull/123',
+            'revisions': {'revision': [{'version': head}]},
+            'properties': {'property': [{'name': k, 'value': v} for k, v in 
(params or {}).items()]}}
+
+
+class TeamCityUtilsTests(unittest.TestCase):
+    def setUp(self):
+        self.temp = tempfile.TemporaryDirectory()
+        self.addCleanup(self.temp.cleanup)
+        self.root = Path(self.temp.name)
+        (self.root / 'mock.py').write_text(MOCK)
+        (self.root / 'status_mock.py').write_text('''import json, os, sys
+from pathlib import Path
+p=Path(os.environ['FIXTURE_ROOT'])/'statuses.json'
+rows=json.loads(p.read_text()) if p.exists() else []
+assert '-d' in sys.argv and '-X' in sys.argv
+if os.environ.get('STATUS_FAIL')=='true': sys.exit(22)
+rows.append(json.loads(sys.argv[sys.argv.index('-d')+1]));p.write_text(json.dumps(rows))
+''')
+        self.save({})
+        (self.root / 'event.json').write_text(json.dumps({'comment': {
+            'id': 45, 'body': 'run cloudut', 'updated_at': 
'2026-11-11T00:00:00Z'}}))
+        self.env = {**os.environ, 'TARGET_BRANCH': 'master', 
'TEAMCITY_CURRENT_URL': 'https://ci.example',
+                    'TEAMCITY_CURRENT_TOKEN': 'test-current-token', 
'GITHUB_TOKEN': 'test-github-token', 'FIXTURE_ROOT': str(self.root),
+                    'GITHUB_EVENT_PATH': str(self.root / 'event.json')}
+
+    def save(self, value):
+        (self.root / 'state.json').write_text(json.dumps(value))
+
+    def state(self):
+        return json.loads((self.root / 'state.json').read_text())
+
+    def run_shell(self, command, mock=True, check=True, env=None):
+        prefix = 'source "$1"\n'
+        if mock:
+            prefix += '''teamcity_request(){ python3 "$FIXTURE_ROOT/mock.py" 
"$@"; }
+teamcity_current_pr(){ [[ ${PR_FRESH:-true} == true ]]; }
+curl(){ python3 "$FIXTURE_ROOT/status_mock.py" "$@"; }
+'''
+        result = subprocess.run(['bash', '-c', prefix + command, 'test', 
str(HELPER)],
+                                env={**self.env, **(env or {})}, text=True, 
capture_output=True)
+        if check:
+            self.assertEqual(result.returncode, 0, result.stdout + 
result.stderr)
+        return result
+
+    def trigger(self, lane='cloudut', count=1, **kwargs):
+        return self.run_shell('trigger_or_skip_build true 123 master ' + HEAD 
+ ' ' + lane + ' ' + str(count), **kwargs)
+
+    def skipped_contexts(self):
+        p = self.root / 'statuses.json'
+        return [row['context'] for row in json.loads(p.read_text())] if 
p.exists() else []
+
+    def test_unrelated_files_skip_without_submitting(self):
+        self.run_shell('trigger_or_skip_build false 123 master ' + HEAD + ' 
feut 1')
+        self.assertEqual(self.skipped_contexts(), ['TeamCity/feut'])
+        self.assertFalse(self.state().get('posts'))
+
+    def test_compile_skip_covers_downstream_checks(self):
+        self.run_shell('trigger_or_skip_build false 123 master ' + HEAD + ' 
compile 1')
+        self.assertEqual(self.skipped_contexts(), ['TeamCity/compile', 
'TeamCity/p0', 'TeamCity/cloud_p0',
+                                                  'TeamCity/vault_p0', 
'TeamCity/external', 'TeamCity/nonconcurrent'])
+        self.assertFalse(self.state().get('posts'))
+
+    def test_failed_skip_and_stale_head_do_not_succeed(self):
+        for env in [{'STATUS_FAIL': 'true'}, {'PR_FRESH': 'false'}]:
+            result = self.run_shell('trigger_or_skip_build false 123 master ' 
+ HEAD + ' feut 1', check=False, env=env)
+            self.assertNotEqual(result.returncode, 0)
+            self.assertFalse(self.skipped_contexts())
+
+    def file_filter_outputs(self, files, failed=False):
+        workflow = (HELPER.parents[3] / 
'.github/workflows/comment-to-trigger-teamcity.yml').read_text()
+        block = workflow.split('    - name: "Check if pr need run build"\n', 
1)[1].split('    # - name:', 1)[0]
+        script = textwrap.dedent(block.split('      run: |\n', 1)[1])
+        github_helper = HELPER.with_name('github-utils.sh')
+        override = 'source "' + str(github_helper) + '"\n'
+        override += '_get_pr_changed_files(){ return 1; }' if failed else 
'_get_pr_changed_files(){ return 0; }'
+        script = script.replace('source 
regression-test/pipeline/common/github-utils.sh', override)
+        script = script.replace('${{ steps.parse.outputs.PULL_REQUEST_NUM }}', 
'123')
+        for name in ['all_files', 'modified_files']:
+            (self.root / name).write_text('\n'.join(files) + '\n')
+        for name in ['added_files', 'removed_files']:
+            (self.root / name).write_text('')
+        output = self.root / 'filter-output'; output.write_text('')
+        result = subprocess.run(['bash', '-eo', 'pipefail', '-c', script], 
cwd=self.root,
+                                env={**self.env, 'GITHUB_OUTPUT': 
str(output)}, text=True, capture_output=True)
+        self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
+        return dict(line.split('=', 1) for line in 
output.read_text().splitlines())
+
+    def test_original_file_filters_and_workflow_wiring(self):
+        for files, expected in [(['fe/fe-core/src/main/java/Test.java'], 
{'feut', 'p0', 'cloud_p0', 'vault_p0', 'external', 'nonconcurrent', 
'performance', 'compile'}),
+                                (['cloud/src/meta-service/meta_service.cpp'], 
{'cloudut', 'cloud_p0', 'vault_p0', 'compile'}),
+                                (['README.md'], set()),
+                                
(['regression-test/pipeline/p0/conf/regression-conf.groovy'], set())]:
+            flags = self.file_filter_outputs(files)
+            workflow = (HELPER.parents[3] / 
'.github/workflows/comment-to-trigger-teamcity.yml').read_text()
+            selected = set()
+            for lane in ['compile', 'feut', 'beut', 'cloudut', 'p0', 
'cloud_p0', 'vault_p0', 'external', 'nonconcurrent', 'performance']:
+                block = workflow.split('    - name: "Trigger ' + lane + '"\n', 
1)[1].split('    - name:', 1)[0]
+                script = textwrap.dedent(block.split('      run: |\n', 1)[1])
+                script = script.replace('source 
./regression-test/pipeline/common/teamcity-utils.sh', 'trigger_or_skip_build(){ 
echo "SELECTED=$1"; }')
+                values = {'COMMENT_TRIGGER_TYPE': lane, 'PULL_REQUEST_NUM': 
'123', 'TARGET_BRANCH': 'master',
+                          'COMMIT_ID_FROM_TRIGGER': HEAD, 
'COMMENT_REPEAT_TIMES': '1'}
+                for key, value in flags.items(): script = script.replace('${{ 
steps.changes.outputs.' + key + ' }}', value)
+                for key, value in values.items(): script = script.replace('${{ 
steps.parse.outputs.' + key + ' }}', value)
+                result = subprocess.run(['bash', '-eo', 'pipefail', '-c', 
script], text=True, capture_output=True)
+                self.assertEqual(result.returncode, 0, result.stdout + 
result.stderr)
+                if 'SELECTED=true' in result.stdout: selected.add(lane)
+            self.assertEqual(selected, expected)
+
+    def test_file_lookup_does_not_trace_github_token(self):
+        helper = HELPER.with_name('github-utils.sh')
+        script = 'source "$1"\n' + '''curl(){
+            if [[ "$*" == *--version* ]]; then return 0;
+            elif [[ "$*" == *'/files?'* ]]; then echo 
'[{"filename":"README.md","status":"modified"}]';
+            else echo '{"changed_files":1}'; fi
+        }
+        _get_pr_changed_files 123
+'''
+        result = subprocess.run(['bash', '-c', script, 'test', str(helper)], 
cwd=self.root,
+                                env=self.env, text=True, capture_output=True)
+        self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
+        self.assertNotIn('test-github-token', result.stdout + result.stderr)
+
+    def test_failed_file_lookup_does_not_select_skip(self):
+        flags = self.file_filter_outputs([], failed=True)
+        self.assertTrue(flags and all(value == 'true' for value in 
flags.values()))
+
+    def test_keywords_match_required_checks(self):
+        repo = HELPER.parents[3]
+        required = set(re.findall(r'- (TeamCity/\S+)', (repo / 
'.asf.yaml').read_text()))
+        result = self.run_shell('for lane in "${!comment_to_pipeline[@]}"; do 
echo "$lane ${conment_to_context[$lane]}"; done')
+        mappings = dict(line.split() for line in result.stdout.splitlines())
+        self.assertEqual(set(mappings.values()), required | 
{'TeamCity/external', 'TeamCity/performance'})
+        self.assertTrue(all(context == 'TeamCity/' + lane for lane, context in 
mappings.items()))
+        workflow = (repo / 
'.github/workflows/comment-to-trigger-teamcity.yml').read_text()
+        commands = re.search(r'reg="\^run \(([^)]+)\)', 
workflow).group(1).split('|')
+        self.assertEqual(set(commands), set(mappings) | {'buildall'})
+
+    def parse_comment(self, command, branch='master', permission='write', 
check=True, user_id='27881198'):
+        workflow = (HELPER.parents[3] / 
'.github/workflows/comment-to-trigger-teamcity.yml').read_text()
+        block = workflow.split('    - name: "Parse PR comment"\n', 
1)[1].split('    - name:', 1)[0]
+        script = textwrap.dedent(block.split('      run: |\n', 1)[1])
+        script = script.replace('${{ github.event.issue.pull_request.url }}', 
'https://api.github.com/repos/apache/doris/pulls/123')
+        script = script.replace('${{ github.repository }}', 
'apache/doris').replace('${{ github.event.comment.user.login }}', 'tester')
+        output = self.root / 'workflow-output'; output.write_text('')
+        prefix = 'curl(){ printf "%s\\n" "$MOCK_GITHUB_RESPONSE"; };\n'
+        result = subprocess.run(['bash', '-eo', 'pipefail', '-c', prefix + 
script], text=True, capture_output=True,
+                                env={**self.env, 'COMMENT_BODY': command, 
'COMMENT_USER_ID': user_id,
+                                     'GITHUB_TOKEN': 'test-github-token', 
'GITHUB_OUTPUT': str(output),
+                                     'GITHUB_ENV': str(self.root / 
'workflow-env'),
+                                     'MOCK_GITHUB_RESPONSE': 
json.dumps({'head': {'sha': HEAD}, 'base': {'ref': branch}, 'permission': 
permission})})
+        if check:
+            self.assertEqual(result.returncode, 0, result.stdout + 
result.stderr)
+        return result, output.read_text()
+
+    def test_workflow_parses_exact_check_keywords(self):
+        for lane in ['compile', 'feut', 'beut', 'cloudut', 'p0', 'cloud_p0', 
'vault_p0',
+                     'external', 'nonconcurrent', 'performance', 'buildall']:
+            result, output = self.parse_comment('run ' + lane + ' 2')
+            self.assertIn('COMMENT_TRIGGER_TYPE=' + lane + '\n', output)
+            self.assertIn('COMMENT_REPEAT_TIMES=2\n', output)
+        for command in ['run feut-extra', 'please run feut', 'run 
nonConcurrent', 'run p1', 'run cloud_p1', 'run check_coverage']:
+            result, output = self.parse_comment(command, check=False)
+            self.assertNotIn('COMMENT_TRIGGER_TYPE=', output)
+
+    def test_skip_buildall_authorization_is_preserved(self):
+        _, output = self.parse_comment('skip buildall')
+        self.assertIn('comment_skip=true', output)
+        _, output = self.parse_comment('skip buildall', user_id='123456', 
check=False)
+        self.assertNotIn('comment_skip=true', output)
+        _, output = self.parse_comment('skip buildall', branch='branch-4.2', 
user_id='9208457')
+        self.assertIn('comment_skip=true', output)
+
+    def test_skip_buildall_statuses(self):
+        workflow = (HELPER.parents[3] / 
'.github/workflows/comment-to-trigger-teamcity.yml').read_text()
+        block = workflow.split('    - name: "Skip buildall"\n', 1)[1]
+        script = textwrap.dedent(block.split('      run: |\n', 1)[1])
+        script = script.replace('source 
./regression-test/pipeline/common/teamcity-utils.sh', ':')
+        script = script.replace('${{ 
steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}', HEAD)
+        for branch in ['master', 'branch-4.2', 'branch-4.1']:
+            (self.root / 'statuses.json').write_text('[]')
+            self.run_shell(script, env={'TARGET_BRANCH': branch})
+            expected = {'TeamCity/' + lane for lane in ['compile', 'feut', 
'beut', 'cloudut', 'p0', 'cloud_p0', 'vault_p0', 'external', 'nonconcurrent']}
+            if branch == 'master': expected.add('TeamCity/performance')
+            self.assertEqual(set(self.skipped_contexts()), expected)
+            self.assertFalse(self.state().get('posts'))
+
+    def test_workflow_branch_and_permission_checks(self):
+        for branch in ['branch-4.2', 'branch-4.1']:
+            result, _ = self.parse_comment('run performance', branch=branch, 
check=False)
+            self.assertNotEqual(result.returncode, 0)
+            self.parse_comment('run buildall', branch=branch)
+            self.parse_comment('run feut', branch=branch)
+        for branch, permission in [('branch-4.0', 'write'), ('master', 
'read')]:
+            result, _ = self.parse_comment('run feut', branch=branch, 
permission=permission, check=False)
+            self.assertNotEqual(result.returncode, 0)
+
+    def test_credentials_are_not_traced(self):
+        result = self.run_shell('''curl(){ printf '%s\\n' "$@" > 
"$FIXTURE_ROOT/curl-args"; }
+set -x
+teamcity_request --fail https://ci.example
+''', mock=False)
+        self.assertNotIn('test-current-token', result.stdout + result.stderr)
+        args = (self.root / 'curl-args').read_text()
+        self.assertIn('Authorization: Bearer test-current-token', args)
+        self.assertNotIn('--user', args)
+
+    def test_retired_branch_has_no_submission(self):
+        result = self.run_shell('trigger_or_skip_build true 123 branch-4.0 ' + 
HEAD + ' feut 1', check=False)
+        self.assertNotEqual(result.returncode, 0)
+        self.assertFalse(self.state().get('posts'))
+
+    def test_release_branches_reject_performance(self):
+        for branch in ['branch-4.2', 'branch-4.1']:
+            result = self.run_shell('trigger_or_skip_build true 123 ' + branch 
+ ' ' + HEAD + ' performance 1', check=False)
+            self.assertNotEqual(result.returncode, 0)
+            self.assertFalse(self.state().get('posts'))
+
+    def test_status_token_is_not_traced(self):
+        result = self.run_shell('curl(){ return 0; }; set -x; skip_build 
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" feut',
+                                mock=False, env={'GITHUB_TOKEN': 
'test-github-token'})
+        self.assertNotIn('test-github-token', result.stdout + result.stderr)
+
+    def test_missing_private_token_has_no_public_fallback(self):
+        result = self.run_shell('curl(){ exit 99; }; teamcity_request 
https://ci.example', mock=False,
+                                check=False, env={'TEAMCITY_CURRENT_TOKEN': 
''})
+        self.assertEqual(result.returncode, 1)
+
+    def test_regressions_submit_directly_with_only_pr_parameters(self):
+        for lane, pipeline in [('p0', 'Doris_P0'), ('cloud_p0', 
'Doris_CloudP0'),
+                               ('vault_p0', 'Doris_VaultP0'), ('external', 
'Doris_External'),
+                               ('nonconcurrent', 'Doris_NonConcurrent')]:
+            self.save({})
+            self.trigger(lane)
+            body = self.state()['posts'][0]
+            self.assertEqual(body['buildType']['id'], pipeline)
+            self.assertEqual({p['name']: p['value'] for p in 
body['properties']['property']},
+                             {'env.PR_NUMBER': '123', 'env.CI_BRANCH': 
'master'})
+            self.assertNotIn('custom-artifact-dependencies', body)
+            self.assertEqual(len(self.state()['posts']), 1)
+
+    def test_exact_revision_submission(self):
+        self.trigger()
+        body = self.state()['posts'][0]
+        self.assertEqual(body['buildType']['id'], 'Doris_CloudUT')
+        self.assertEqual(body['branchName'], 'pull/123')
+        self.assertEqual(body['properties']['property'][0]['name'], 
'env.PR_NUMBER')
+
+    def test_active_build_does_not_prevent_submission(self):
+        self.save({'builds': [build()]})
+        self.trigger()
+        self.assertEqual(len(self.state()['posts']), 1)
+        self.assertEqual(self.state()['builds'][0]['state'], 'running')
+
+    def test_other_revision_is_preserved_and_not_reused(self):
+        self.save({'builds': [build(head='f' * 40)]})
+        self.trigger()
+        self.assertEqual(len(self.state()['posts']), 1)
+        self.assertEqual(self.state()['builds'][0]['state'], 'running')
+
+    def test_each_request_is_submitted_to_teamcity(self):
+        self.trigger(count=2)
+        self.trigger(count=2)
+        self.assertEqual(len(self.state()['posts']), 4)
+        for body in self.state()['posts']:
+            self.assertNotIn('env.CI_COMMENT_REQUEST_KEY', {p['name'] for p in 
body['properties']['property']})
+
+    def test_uncertain_post_does_not_automatically_retry(self):
+        self.save({'lose_response': True})
+        self.assertNotEqual(self.trigger(check=False).returncode, 0)
+        self.assertEqual(len(self.state()['posts']), 1)
+
+    def test_changed_pr_does_not_submit(self):
+        self.assertNotEqual(self.trigger(check=False, env={'PR_FRESH': 
'false'}).returncode, 0)
+        self.assertFalse(self.state().get('posts'))
+
+    def test_temporary_configuration_rejected(self):
+        self.save({'root': 'Doris_Performance_Schema70411_Debug'})
+        self.assertNotEqual(self.trigger('performance', 
check=False).returncode, 0)
+        self.assertFalse(self.state().get('posts'))
+
+    def test_invalid_count_rejected(self):
+        for count in [0, 21, 'abc']:
+            self.assertNotEqual(self.trigger(count=count, 
check=False).returncode, 0)
+        self.assertFalse(self.state().get('posts'))
+
+    def test_removed_lane_has_no_legacy_fallback(self):
+        self.assertNotEqual(self.trigger('p1', check=False).returncode, 0)
+        self.assertFalse(self.state().get('posts'))
+
+
+if __name__ == '__main__':
+    unittest.main()


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to