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') {

Reply via email to