[SCM] PostGIS branch master updated. 3.7.0beta1-11-g7538038dc

git at osgeo.org git at osgeo.org
Thu Jul 23 07:12:19 PDT 2026


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".

The branch, master has been updated
       via  7538038dc5e255a033f3664dfad038311e771e17 (commit)
      from  705f2d999f4379b6f37ea03d7d590f6e00d1d191 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 7538038dc5e255a033f3664dfad038311e771e17
Author: Darafei Praliaskouski <komzpa at gmail.com>
Date:   Thu Jul 23 07:12:17 2026 -0700

    ci: show Jenkins matrix child axes in CI status (!490)
    
    Summary:
    - summarize non-success Jenkins matrix children in CI status rows
    - infer concise labels from active axes such as `PG_VER` and `label`
    - link directly to the single non-success matrix child when there is exactly one
    
    The Winnie master failure that prompted this work had one active failing child, PG19. The same code reports active matrix progress and failures generically across Jenkins matrix jobs; the live smoke on 2026-07-23 reports examples such as `Jenkins / Winnie` as `build 5285; running: PG19` and `Jenkins / Debbie main` as `build 5090; running: PG14`, while single-configuration Make Dist rows keep their parent build message.
    
    Validation:
    - `python3 -m unittest utils.test_ci_status`
    - `python3 -m py_compile utils/ci-status.py`
    - `python3 -m json.tool utils/ci-status.json >/dev/null`
    - `git diff --check HEAD~1..HEAD`
    - `./utils/check_news.sh`
    - live smoke: `python3 utils/ci-status.py --branch master --format json --timeout 30`
    
    ---------
    
    Co-authored-by: Darafei Praliaskouski <me at komzpa.net>
    Reviewed-on: https://gitea.osgeo.org/postgis/postgis/pulls/490

diff --git a/utils/ci-status.py b/utils/ci-status.py
index 477762ff4..6410cafc5 100755
--- a/utils/ci-status.py
+++ b/utils/ci-status.py
@@ -740,6 +740,117 @@ def jenkins_builds(job_url, check, timeout):
     return builds
 
 
+def jenkins_matrix_configurations(job_url, timeout):
+    tree = (
+        "activeConfigurations[name,url,color,"
+        "lastBuild[number,url,result,building,timestamp,duration],"
+        "lastCompletedBuild[number,url,result,timestamp],"
+        "lastFailedBuild[number,url,result,timestamp]]"
+    )
+    url = job_url + "api/json?" + urllib.parse.urlencode({"tree": tree})
+    return http_json(url, timeout=timeout).get("activeConfigurations") or []
+
+
+def jenkins_matrix_axes(configuration):
+    axes = {}
+    for item in str(configuration.get("name") or "").split(","):
+        if "=" not in item:
+            continue
+        name, value = item.split("=", 1)
+        axes[name.strip()] = value.strip()
+    return axes
+
+
+def jenkins_matrix_axis_text(name, value):
+    if name == "PG_VER":
+        return f"PG{value}"
+    if name == "label":
+        return value
+    return f"{name}={value}"
+
+
+def jenkins_matrix_summary_axes(configurations):
+    parsed = [jenkins_matrix_axes(configuration) for configuration in configurations]
+    priority = (
+        "label",
+        "PG_VER",
+        "POSTGIS_TAG",
+        "OS_BUILD",
+        "GEOS_VER",
+        "GDAL_VER",
+        "GCC_TYPE",
+        "SFCGAL_VER",
+        "CGAL_VER",
+    )
+    varying = []
+    for name in priority:
+        values = {axes.get(name) for axes in parsed if axes.get(name)}
+        if len(values) > 1:
+            varying.append(name)
+    return varying[:3]
+
+
+def jenkins_matrix_configuration_label(configuration, selected):
+    axes = jenkins_matrix_axes(configuration)
+    parts = [
+        jenkins_matrix_axis_text(name, axes[name])
+        for name in selected
+        if name in axes
+    ]
+    if parts:
+        return ", ".join(parts)
+    return configuration.get("name") or configuration.get("url") or "configuration"
+
+
+def jenkins_matrix_details(job_url, timeout):
+    try:
+        configurations = jenkins_matrix_configurations(job_url, timeout)
+    except RECOVERABLE_PROVIDER_ERRORS:
+        return None
+    if len(configurations) < 2:
+        return None
+
+    selected = jenkins_matrix_summary_axes(configurations)
+
+    by_status = {
+        FAILURE: [],
+        IN_PROGRESS: [],
+        UNKNOWN: [],
+    }
+    for configuration in configurations:
+        build = configuration.get("lastBuild") or {}
+        status = normalize_jenkins_status(build)
+        if status == SUCCESS:
+            continue
+        label = jenkins_matrix_configuration_label(configuration, selected)
+        by_status.setdefault(status, []).append((label, build))
+
+    parts = []
+    for status, prefix in (
+        (FAILURE, "failed"),
+        (IN_PROGRESS, "running"),
+        (UNKNOWN, "unknown"),
+    ):
+        items = by_status.get(status) or []
+        if not items:
+            continue
+        item_labels = [label for label, build in items]
+        parts.append(f"{prefix}: {', '.join(item_labels)}")
+    if not parts:
+        return None
+
+    non_success = [
+        item
+        for status in (FAILURE, IN_PROGRESS, UNKNOWN)
+        for item in by_status.get(status) or []
+    ]
+    url = non_success[0][1].get("url") if len(non_success) == 1 else None
+    return {
+        "message": "; ".join(parts),
+        "url": url,
+    }
+
+
 def jenkins_badge_url(job_url, check, branch):
     parsed = urllib.parse.urlparse(job_url)
     if parsed.scheme not in ("http", "https") or not parsed.netloc or "/job/" not in parsed.path:
@@ -827,6 +938,12 @@ def jenkins_check(check, branch, timeout):
         completed_at=current.get("timestamp"),
         message=f"build {current.get('number')}",
     )
+    if result["status"] != SUCCESS:
+        details = jenkins_matrix_details(job_url, timeout)
+        if details:
+            result["message"] = f"{result['message']}; {details['message']}"
+            if details.get("url"):
+                result["url"] = details["url"]
     if previous:
         result.update(previous_fields(normalize_jenkins_status(previous), previous))
     return result
diff --git a/utils/test_ci_status.py b/utils/test_ci_status.py
index 667b55b4d..5401aaeab 100644
--- a/utils/test_ci_status.py
+++ b/utils/test_ci_status.py
@@ -79,6 +79,99 @@ class RequiredFailureHtmlTest(unittest.TestCase):
         self.assertEqual(CI_STATUS.SUCCESS, passed["stale_base_status"])
         self.assertEqual("Stale passed", passed["status_label"])
 
+    def test_jenkins_matrix_failure_names_failing_axis(self):
+        check_config = {
+            "name": "Jenkins / Winnie",
+            "provider": "jenkins",
+            "required": True,
+            "job_url": "https://ci.example.test/job/PostGIS_trunk/",
+        }
+        branch = {
+            "name": "master",
+            "label": "master",
+            "version_or_trunk": "trunk",
+        }
+        current = {
+            "number": 5284,
+            "result": "FAILURE",
+            "url": "https://ci.example.test/job/PostGIS_trunk/5284/",
+            "timestamp": 1784791530000,
+            "actions": [
+                {"lastBuiltRevision": {"SHA1": "a" * 40}},
+            ],
+        }
+        matrix = [
+            {
+                "name": "PG_VER=15,OS_BUILD=64",
+                "lastBuild": {
+                    "number": 5279,
+                    "result": "SUCCESS",
+                    "url": "https://ci.example.test/job/PostGIS_trunk/PG_VER=15/5279/",
+                },
+            },
+            {
+                "name": "PG_VER=19,OS_BUILD=64",
+                "lastBuild": {
+                    "number": 5284,
+                    "result": "FAILURE",
+                    "url": "https://ci.example.test/job/PostGIS_trunk/PG_VER=19/5284/",
+                },
+            },
+        ]
+
+        with (
+            mock.patch.object(CI_STATUS, "jenkins_queued_check", return_value=None),
+            mock.patch.object(CI_STATUS, "jenkins_builds", return_value=[current]),
+            mock.patch.object(CI_STATUS, "jenkins_matrix_configurations", return_value=matrix),
+        ):
+            result = CI_STATUS.jenkins_check(check_config, branch, timeout=5)
+
+        self.assertEqual(CI_STATUS.FAILURE, result["status"])
+        self.assertEqual("build 5284; failed: PG19", result["message"])
+        self.assertEqual("https://ci.example.test/job/PostGIS_trunk/PG_VER=19/5284/", result["url"])
+        self.assertEqual("a" * 40, result["revision"])
+
+    def test_jenkins_single_configuration_matrix_keeps_parent_message(self):
+        check_config = {
+            "name": "Jenkins / Make Dist",
+            "provider": "jenkins",
+            "required": True,
+            "job_url": "https://ci.example.test/job/PostGIS_Make_Dist/",
+        }
+        branch = {
+            "name": "master",
+            "label": "master",
+            "version_or_trunk": "trunk",
+        }
+        current = {
+            "number": 7808,
+            "building": True,
+            "result": None,
+            "url": "https://ci.example.test/job/PostGIS_Make_Dist/7808/",
+        }
+        matrix = [
+            {
+                "name": "label=debbie",
+                "lastBuild": {
+                    "number": 7808,
+                    "building": True,
+                    "result": None,
+                    "url": "https://ci.example.test/job/PostGIS_Make_Dist/label=debbie/7808/",
+                },
+            },
+        ]
+
+        with (
+            mock.patch.object(CI_STATUS, "jenkins_queued_check", return_value=None),
+            mock.patch.object(CI_STATUS, "jenkins_builds", return_value=[current]),
+            mock.patch.object(CI_STATUS, "jenkins_matrix_configurations", return_value=matrix),
+        ):
+            result = CI_STATUS.jenkins_check(check_config, branch, timeout=5)
+
+        self.assertEqual(CI_STATUS.IN_PROGRESS, result["status"])
+        self.assertEqual("build 7808", result["message"])
+        self.assertEqual("https://ci.example.test/job/PostGIS_Make_Dist/7808/", result["url"])
+
     def test_stale_summary_distinguishes_passed_and_failed(self):
         branch = {
             "name": "stable-synthetic",

-----------------------------------------------------------------------

Summary of changes:
 utils/ci-status.py      | 117 ++++++++++++++++++++++++++++++++++++++++++++++++
 utils/test_ci_status.py |  93 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 210 insertions(+)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list