Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/comment-to-trigger-teamcity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,17 @@ jobs:
exit 1
fi

- name: "Cancel superseded buildall builds"
if: ${{ fromJSON(steps.parse.outputs.comment_trigger) && steps.parse.outputs.COMMENT_TRIGGER_TYPE == 'buildall' }}
env:
PULL_REQUEST_NUM: ${{ steps.parse.outputs.PULL_REQUEST_NUM }}
COMMIT_ID_FROM_TRIGGER: ${{ steps.parse.outputs.COMMIT_ID_FROM_TRIGGER }}
run: |
source ./regression-test/pipeline/common/teamcity-utils.sh
cancel_superseded_buildall_builds \
"${PULL_REQUEST_NUM}" \
"${COMMIT_ID_FROM_TRIGGER}"

- name: "skip check coverage"
if: ${{ fromJSON(steps.parse.outputs.comment_skip_coverage) }}
run: |
Expand Down
140 changes: 140 additions & 0 deletions regression-test/pipeline/common/teamcity-utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,146 @@ get_commit_id_of_build() {
fi
}

is_buildall_pipeline() {
local build_type_id="$1"
local pipeline_id

# check_coverage_fe is triggered by the buildall downstream chain but does
# not have a standalone PR comment command, so it is not in the map above.
if [[ "${build_type_id}" == "Doris_Coverage_CheckCoverageFe" ]]; then
return 0
fi

for pipeline_id in "${comment_to_pipeline[@]}"; do
if [[ "${build_type_id}" == "${pipeline_id}" ]]; then
return 0
fi
done
return 1
}

cancel_superseded_buildall_builds() {
# Before a new run buildall fan-out starts, cancel every queued/running
# buildall pipeline for the same PR that is still bound to an older head.
local pull_request_num="${PULL_REQUEST_NUM:-$1}"
local current_commit_id="${COMMIT_ID_FROM_TRIGGER:-$2}"
if [[ ! "${pull_request_num}" =~ ^[0-9]+$ ||
! "${current_commit_id}" =~ ^[0-9a-f]{40}$ ]]; then
echo "Usage: cancel_superseded_buildall_builds PULL_REQUEST_NUM COMMIT_ID_FROM_TRIGGER"
return 1
fi

local teamcity_url="http://43.132.222.7:8111"
local branch_name="pull/${pull_request_num}"
local fields='build(id,state,buildTypeId,branchName,revisions(revision(version)))'
local queued_json
local running_json
local active_json

if ! queued_json=$(
curl --fail --silent --show-error --retry 4 \
-u OneMoreChance:OneMoreChance \
-H "Accept: application/json" \
--get \
--data-urlencode "locator=count:1000" \
--data-urlencode "fields=${fields}" \
"${teamcity_url}/app/rest/buildQueue"
); then
echo "ERROR: failed to list queued TeamCity builds before run buildall"
return 1
fi

if ! running_json=$(
curl --fail --silent --show-error --retry 4 \
-u OneMoreChance:OneMoreChance \
-H "Accept: application/json" \
--get \
--data-urlencode "locator=branch:(name:${branch_name}),state:running,defaultFilter:false,count:1000" \
--data-urlencode "fields=${fields}" \
"${teamcity_url}/app/rest/builds"
); then
echo "ERROR: failed to list running TeamCity builds for PR ${pull_request_num}"
return 1
fi

if ! active_json=$(jq -s \
'[.[0].build[]?, .[1].build[]?] | unique_by(.id)' \
<<<"${queued_json}"$'\n'"${running_json}"); then
echo "ERROR: failed to parse active TeamCity builds for PR ${pull_request_num}"
return 1
fi

local build_json
local build_id
local build_state
local build_type_id
local build_branch
local build_commit_id
local cancel_endpoint
local cancel_payload
local http_code
local canceled_count=0

while IFS= read -r build_json; do
build_id=$(jq -er '.id' <<<"${build_json}") || return 1
build_state=$(jq -r '.state // empty' <<<"${build_json}")
build_type_id=$(jq -r '.buildTypeId // empty' <<<"${build_json}")
build_branch=$(jq -r '.branchName // empty' <<<"${build_json}")
build_commit_id=$(jq -r '[.revisions.revision[]?.version][0] // empty' <<<"${build_json}")

[[ "${build_branch}" == "${branch_name}" ]] || continue
is_buildall_pipeline "${build_type_id}" || continue
if [[ ! "${build_commit_id}" =~ ^[0-9a-f]{40}$ ]]; then
echo "WARNING: keep build ${build_id}; no immutable revision binding was returned"
continue
fi
[[ "${build_commit_id}" != "${current_commit_id}" ]] || continue

if [[ "${TEAMCITY_CANCEL_DRY_RUN:-false}" == "true" ]]; then
echo "INFO: would cancel superseded ${build_state} build ${build_id} (${build_type_id}) revision ${build_commit_id}"
continue
fi

if [[ "${build_state}" == "queued" ]]; then
cancel_endpoint="buildQueue/id:${build_id}"
elif [[ "${build_state}" == "running" ]]; then
cancel_endpoint="builds/id:${build_id}"
else
echo "WARNING: keep build ${build_id}; unsupported active state ${build_state}"
continue
fi

cancel_payload=$(jq -cn \
--arg comment "Superseded by PR #${pull_request_num} revision ${current_commit_id}; old revision ${build_commit_id}" \
'{comment: $comment, readdIntoQueue: false}')
http_code=$(
curl --silent --show-error --retry 4 \
-o /dev/null -w '%{http_code}' \
-u OneMoreChance:OneMoreChance \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
--data-binary "${cancel_payload}" \
"${teamcity_url}/app/rest/${cancel_endpoint}"
) || return 1

case "${http_code}" in
2??)
echo "INFO: canceled superseded ${build_state} build ${build_id} (${build_type_id}) revision ${build_commit_id}"
((canceled_count += 1))
;;
404|409)
echo "INFO: build ${build_id} finished while cancellation was being requested"
;;
*)
echo "ERROR: failed to cancel build ${build_id}; TeamCity returned HTTP ${http_code}"
return 1
;;
esac
done < <(jq -c '.[]' <<<"${active_json}")

echo "INFO: superseded buildall cancellation completed for PR ${pull_request_num}; canceled=${canceled_count}"
}

get_running_build_of_pr() {
# "获取pr在某条流水线上正在跑的build"
local PULL_REQUEST_NUM="${PULL_REQUEST_NUM:-$1}"
Expand Down
Loading