[SCM] PostGIS branch master updated. 3.7.0beta2-5-g10b5cd953
git at osgeo.org
git at osgeo.org
Sun Aug 9 23:45:09 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 10b5cd9534f6a436a73af191a367ed6e8be7155f (commit)
via 3cdb2994be6a4d06faeeb847f8ff181e5f12656f (commit)
from 2affb0e413349726510e5aa901d996aeed3f612e (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 10b5cd9534f6a436a73af191a367ed6e8be7155f
Merge: 2affb0e41 3cdb2994b
Author: Darafei Praliaskouski <komzpa at gmail.com>
Date: Sun Aug 9 23:45:07 2026 -0700
Merge pull request 'ci: preserve failures in killed Woodpecker pipelines' (!714) from Komzpa/postgis:fix/master-woodpecker-killed-status-correction-20260810 into master
## Summary
A killed Woodpecker pipeline may already contain a decisive failed step. Keep such a pipeline failed instead of masking it as unjudged.
The report now recognizes Docker cancellation and deadline-expiry errors at exit 0 as agent loss, while retaining ordinary nonzero failed steps as failures.
## Validation
`python3 utils/docs/tests/test_ci_status.py`
Reviewed-on: https://gitea.osgeo.org/postgis/postgis/pulls/714
commit 3cdb2994be6a4d06faeeb847f8ff181e5f12656f
Author: Darafei Praliaskouski <me at komzpa.net>
Date: Mon Aug 10 10:43:15 2026 +0400
ci: preserve failures in killed Woodpecker pipelines
diff --git a/utils/docs/ci_status/report.py b/utils/docs/ci_status/report.py
index d74c2a589..b0644ed7a 100644
--- a/utils/docs/ci_status/report.py
+++ b/utils/docs/ci_status/report.py
@@ -537,32 +537,61 @@ def woodpecker_leaf_steps(workflow):
return [workflow]
+def woodpecker_step_is_agent_loss(step):
+ state = str(step.get("state") or step.get("status")).lower()
+ if step.get("exit_code") != 0:
+ return False
+ if state == "killed":
+ return True
+ if state != "failure":
+ return False
+
+ error = " ".join(
+ str(step.get(key) or "")
+ for key in ("error", "message")
+ ).lower()
+ return (
+ "context deadline exceeded" in error
+ or "canceled" in error
+ or "cancelled" in error
+ )
+
+
+def woodpecker_has_conclusive_failure(pipeline):
+ for workflow in pipeline.get("workflows") or []:
+ for step in woodpecker_leaf_steps(workflow):
+ status = normalize_woodpecker_status(step.get("state") or step.get("status"))
+ if status == FAILURE and not woodpecker_step_is_agent_loss(step):
+ return True
+ return False
+
+
def woodpecker_killed_details(pipeline):
workflows = pipeline.get("workflows") or []
if not workflows:
return None
non_success = []
- killed_zero = []
+ agent_loss = []
for workflow in workflows:
for step in woodpecker_leaf_steps(workflow):
status = normalize_woodpecker_status(step.get("state") or step.get("status"))
if status == SUCCESS:
continue
non_success.append(step)
- if str(step.get("state") or step.get("status")).lower() == "killed" and step.get("exit_code") == 0:
- killed_zero.append(step)
+ if woodpecker_step_is_agent_loss(step):
+ agent_loss.append(step)
- if not non_success or len(non_success) != len(killed_zero):
+ if not non_success or len(non_success) != len(agent_loss):
return None
labels = [
str(step.get("name") or f"step {step.get('pid') or step.get('id')}")
- for step in killed_zero[:3]
+ for step in agent_loss[:3]
]
- suffix = f" ({', '.join(labels)}" + (", ..." if len(killed_zero) > len(labels) else "") + ")"
+ suffix = f" ({', '.join(labels)}" + (", ..." if len(agent_loss) > len(labels) else "") + ")"
return {
- "message": f"agent lost: {plural(len(killed_zero), 'step')} killed at exit 0{suffix}",
+ "message": f"agent lost: {plural(len(agent_loss), 'step')} stopped at exit 0{suffix}",
"status_label": "Agent lost",
}
@@ -605,6 +634,11 @@ def woodpecker_check(check, branch, timeout):
except RECOVERABLE_PROVIDER_ERRORS:
pass
current_status = normalize_woodpecker_status(current.get("status"))
+ if (
+ str(current.get("status")).lower() == "killed"
+ and woodpecker_has_conclusive_failure(current)
+ ):
+ current_status = FAILURE
message = current.get("message")
extra = {}
if current_status != SUCCESS:
diff --git a/utils/docs/tests/test_ci_status.py b/utils/docs/tests/test_ci_status.py
index 0cf9bf95e..d29a5b51a 100644
--- a/utils/docs/tests/test_ci_status.py
+++ b/utils/docs/tests/test_ci_status.py
@@ -688,7 +688,77 @@ class CIStatusTest(unittest.TestCase):
self.assertEqual(CI_STATUS.UNKNOWN, result["status"])
self.assertEqual("Agent lost", result["status_label"])
- self.assertEqual("agent lost: 3 steps killed at exit 0 (clone, prepare, check-xml)", result["message"])
+ self.assertEqual("agent lost: 3 steps stopped at exit 0 (clone, prepare, check-xml)", result["message"])
+
+ def test_woodpecker_killed_pipeline_with_deadline_exceeded_is_agent_loss(self):
+ check_config = {
+ "name": "Woodpecker",
+ "provider": "woodpecker",
+ "required": True,
+ "api_url": "https://woodie.example.test/api/repos/30/pipelines",
+ "web_url": "https://woodie.example.test/repos/30",
+ }
+ branch = {"name": "stable-3.6", "label": "3.6"}
+ pipeline = {
+ "number": 6852,
+ "event": "push",
+ "branch": "stable-3.6",
+ "ref": "refs/heads/stable-3.6",
+ "status": "killed",
+ "commit": "e" * 40,
+ "workflows": [
+ {
+ "pid": 1,
+ "name": "regress",
+ "state": "killed",
+ "children": [{
+ "pid": 4,
+ "name": "test-upgrades",
+ "state": "failure",
+ "exit_code": 0,
+ "error": "Post docker.sock/wait: context deadline exceeded",
+ }],
+ },
+ ],
+ }
+
+ with mock.patch.object(CI_STATUS, "http_json", return_value=[pipeline]):
+ result = CI_STATUS.woodpecker_check(check_config, branch, timeout=5)
+
+ self.assertEqual(CI_STATUS.UNKNOWN, result["status"])
+ self.assertEqual("Agent lost", result["status_label"])
+
+ def test_woodpecker_killed_pipeline_with_nonzero_failure_is_failure(self):
+ check_config = {
+ "name": "Woodpecker",
+ "provider": "woodpecker",
+ "required": True,
+ "api_url": "https://woodie.example.test/api/repos/30/pipelines",
+ "web_url": "https://woodie.example.test/repos/30",
+ }
+ branch = {"name": "master", "label": "master"}
+ pipeline = {
+ "number": 6874,
+ "event": "push",
+ "branch": "master",
+ "ref": "refs/heads/master",
+ "status": "killed",
+ "commit": "f" * 40,
+ "workflows": [
+ {
+ "pid": 1,
+ "name": "tools",
+ "state": "failure",
+ "children": [{"pid": 4, "name": "build", "state": "failure", "exit_code": 2}],
+ },
+ ],
+ }
+
+ with mock.patch.object(CI_STATUS, "http_json", return_value=[pipeline]):
+ result = CI_STATUS.woodpecker_check(check_config, branch, timeout=5)
+
+ self.assertEqual(CI_STATUS.FAILURE, result["status"])
+ self.assertEqual("failed: tools (build)", result["message"])
def test_woodpecker_running_workflows_are_summarized(self):
check_config = {
-----------------------------------------------------------------------
Summary of changes:
utils/docs/ci_status/report.py | 48 +++++++++++++++++++++----
utils/docs/tests/test_ci_status.py | 72 +++++++++++++++++++++++++++++++++++++-
2 files changed, 112 insertions(+), 8 deletions(-)
hooks/post-receive
--
PostGIS
More information about the postgis-tickets
mailing list