This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/trafficserver-ci.git
The following commit(s) were added to refs/heads/main by this push:
new b308b5f Wait for mirrored PR refs before fanout (#443)
b308b5f is described below
commit b308b5fdfc757efab2e7d1f9f61cf7bf648826c9
Author: Brian Neradt <[email protected]>
AuthorDate: Mon Jun 15 10:13:30 2026 -0500
Wait for mirrored PR refs before fanout (#443)
GitHub delivers the Jenkins PR trigger and the mirror webhook independently.
Without an explicit readiness check, the top-level PR job can start child
jobs
before the controller mirror has fetched the new PR head and merge refs.
This adds a controller-side readiness stage to the GitHub PR fanout
pipelines.
The stage polls the configured Git URL until the mirrored PR head matches
GITHUB_PR_HEAD_SHA and the PR merge ref exists, allowing the Jenkins quiet
period to be reduced to zero once the webhook is active.
---
github-mirror/README.md | 18 ++++++++----
jenkins/github/github_polling.pipeline | 52 +++++++++++++++++++++++++++++++++-
jenkins/github/toplevel.pipeline | 52 +++++++++++++++++++++++++++++++++-
3 files changed, 114 insertions(+), 8 deletions(-)
diff --git a/github-mirror/README.md b/github-mirror/README.md
index 8d2dd4e..46e3951 100644
--- a/github-mirror/README.md
+++ b/github-mirror/README.md
@@ -381,15 +381,19 @@ minute.
```
6. Update Jenkins job configuration so the PR and branch top-level jobs pass
- the ATS mirror URL as `GITHUB_URL`. While the temporary one-minute cron is
- active, set the GitHub PR top-level job quiet period to at least 90 seconds
- so the mirror has time to fetch new PR refs before child jobs start.
+ the ATS mirror URL as `GITHUB_URL`. The repo-managed GitHub PR top-level
+ jobs wait for the mirrored PR head and merge refs before starting child
+ jobs, so the GitHub PR top-level job quiet period can be 0.
```text
GITHUB_URL=https://ci.trafficserver.apache.org/mirror/trafficserver.git
- quietPeriod=90
+ quietPeriod=0
```
+ If the mirror readiness job is not working, set this `quietPeriod` to
+ something like 10 seconds to allow the webhook mechanism enough time to
+ trigger a mirror update.
+
Then run a small PR job such as docs or RAT before starting the full build
fanout.
@@ -558,8 +562,10 @@ should fail during the local merge with shallow-history or
missing-ancestor
errors. Raise the depth before disabling shallow clone globally.
During the temporary cron rollout, set the top-level PR job quiet period to at
-least 90 seconds. Once the webhook is live and verified, the quiet period can
-be removed or reduced.
+least 90 seconds. Once the webhook is live and verified, the repo-managed
+top-level PR jobs wait until the mirrored PR head matches `GITHUB_PR_HEAD_SHA`
+and the PR merge ref exists before starting child jobs. After that gate is in
+place, the Jenkins quiet period can be set to 0.
For branch jobs, configure the top-level branch jobs' `GITHUB_URL` parameter to
the same ATS mirror URL. Child jobs will receive that value from the fanout
job.
diff --git a/jenkins/github/github_polling.pipeline
b/jenkins/github/github_polling.pipeline
index 9035ae7..b47709c 100644
--- a/jenkins/github/github_polling.pipeline
+++ b/jenkins/github/github_polling.pipeline
@@ -1,5 +1,46 @@
TOP_JOB_DESC = "Builds:\\n"
+String githubUrl() {
+ return env.GITHUB_URL ?: GITHUB_REPO_GIT_URL.replace("git://",
"https://")
+}
+
+void waitForMirrorUpdate() {
+ def mirrorUrl = githubUrl()
+
+ echo "Waiting up to 2 minutes for ${mirrorUrl} to mirror PR
#${GITHUB_PR_NUMBER}"
+ withEnv(["MIRROR_URL=${mirrorUrl}"]) {
+ timeout(time: 2, unit: 'MINUTES') {
+ waitUntil {
+ def status = sh(
+ label: 'Check mirrored PR refs',
+ returnStatus: true,
+ script: '''#!/bin/bash
+set +x
+set -o pipefail
+export GIT_TERMINAL_PROMPT=0
+
+head_sha=$(git ls-remote "${MIRROR_URL}" "refs/pull/${GITHUB_PR_NUMBER}/head"
| awk '{print $1}') || exit 1
+merge_sha=$(git ls-remote "${MIRROR_URL}"
"refs/pull/${GITHUB_PR_NUMBER}/merge" | awk '{print $1}') || exit 1
+
+if [ "${head_sha}" = "${GITHUB_PR_HEAD_SHA}" ] && [ -n "${merge_sha}" ]; then
+ echo "Mirror has PR ${GITHUB_PR_NUMBER}: head ${head_sha}, merge
${merge_sha}"
+ exit 0
+fi
+
+echo "Waiting for mirror PR ${GITHUB_PR_NUMBER}: head ${head_sha:-missing},
expected ${GITHUB_PR_HEAD_SHA}; merge ${merge_sha:-missing}"
+exit 1
+'''
+ )
+ if (status != 0) {
+ sleep 2
+ return false
+ }
+ return true
+ }
+ }
+ }
+}
+
String buildJob(String ghcontext, String jobName, String shard='') {
//setGitHubPullRequestStatus(context: ghcontext, message:
'Building', state: 'PENDING')
@@ -7,7 +48,7 @@ String buildJob(String ghcontext, String jobName, String
shard='') {
currentBuild.description = "Builds:<br>"
}
currentBuild.displayName = "PR: #${GITHUB_PR_NUMBER} - Build:
#${BUILD_NUMBER}"
- https_github_url = env.GITHUB_URL ?:
GITHUB_REPO_GIT_URL.replace("git://", "https://")
+ def https_github_url = githubUrl()
def parms = [
string(name: 'SHA1', value: GITHUB_PR_HEAD_SHA),
@@ -42,6 +83,15 @@ pipeline {
agent none
stages {
+ stage('Wait for Mirror') {
+ agent { label 'controller' }
+ steps {
+ script {
+ waitForMirrorUpdate()
+ }
+ }
+ }
+
stage('Quick Checks') {
parallel {
stage('Format') {
diff --git a/jenkins/github/toplevel.pipeline b/jenkins/github/toplevel.pipeline
index e1e7012..7375c44 100644
--- a/jenkins/github/toplevel.pipeline
+++ b/jenkins/github/toplevel.pipeline
@@ -1,5 +1,46 @@
TOP_JOB_DESC = "Builds:\\n"
+String githubUrl() {
+ return env.GITHUB_URL ?: GITHUB_REPO_GIT_URL.replace("git://",
"https://")
+}
+
+void waitForMirrorUpdate() {
+ def mirrorUrl = githubUrl()
+
+ echo "Waiting up to 2 minutes for ${mirrorUrl} to mirror PR
#${GITHUB_PR_NUMBER}"
+ withEnv(["MIRROR_URL=${mirrorUrl}"]) {
+ timeout(time: 2, unit: 'MINUTES') {
+ waitUntil {
+ def status = sh(
+ label: 'Check mirrored PR refs',
+ returnStatus: true,
+ script: '''#!/bin/bash
+set +x
+set -o pipefail
+export GIT_TERMINAL_PROMPT=0
+
+head_sha=$(git ls-remote "${MIRROR_URL}" "refs/pull/${GITHUB_PR_NUMBER}/head"
| awk '{print $1}') || exit 1
+merge_sha=$(git ls-remote "${MIRROR_URL}"
"refs/pull/${GITHUB_PR_NUMBER}/merge" | awk '{print $1}') || exit 1
+
+if [ "${head_sha}" = "${GITHUB_PR_HEAD_SHA}" ] && [ -n "${merge_sha}" ]; then
+ echo "Mirror has PR ${GITHUB_PR_NUMBER}: head ${head_sha}, merge
${merge_sha}"
+ exit 0
+fi
+
+echo "Waiting for mirror PR ${GITHUB_PR_NUMBER}: head ${head_sha:-missing},
expected ${GITHUB_PR_HEAD_SHA}; merge ${merge_sha:-missing}"
+exit 1
+'''
+ )
+ if (status != 0) {
+ sleep 2
+ return false
+ }
+ return true
+ }
+ }
+ }
+}
+
String buildJob(String ghcontext, String jobName, String shard='') {
setGitHubPullRequestStatus(context: ghcontext, message:
'Building', state: 'PENDING')
@@ -7,7 +48,7 @@ String buildJob(String ghcontext, String jobName, String
shard='') {
currentBuild.description = "Builds:<br>"
}
currentBuild.displayName = "PR: #${GITHUB_PR_NUMBER} - Build:
#${BUILD_NUMBER}"
- https_github_url = env.GITHUB_URL ?:
GITHUB_REPO_GIT_URL.replace("git://", "https://")
+ def https_github_url = githubUrl()
def parms = [
string(name: 'SHA1', value: GITHUB_PR_HEAD_SHA),
@@ -42,6 +83,15 @@ pipeline {
agent none
stages {
+ stage('Wait for Mirror') {
+ agent { label 'controller' }
+ steps {
+ script {
+ waitForMirrorUpdate()
+ }
+ }
+ }
+
stage('Quick Checks') {
parallel {
stage('Format') {