diff --git a/backend/app/services/builds.py b/backend/app/services/builds.py
index 3b5ec8d..0685c47 100644
--- a/backend/app/services/builds.py
+++ b/backend/app/services/builds.py
@@ -16,7 +16,8 @@
import base64
import html
-from typing import Dict, List
+import re
+from typing import Dict, List, Tuple
import requests
@@ -50,9 +51,36 @@ def _extract_trigger(build: Dict) -> str:
return ""
+def _summarize_trigger(trigger: str) -> Tuple[str, str, str]:
+ if not trigger:
+ return "", "", ""
+
+ pr_url = ""
+ for line in trigger.splitlines():
+ if line.strip().lower().startswith("reviewed-on:"):
+ pr_url = line.split(":", 1)[-1].strip()
+ break
+
+ author_match = re.search(r"\(([^)]+)\)\.", trigger)
+ author = author_match.group(1).strip() if author_match else ""
+
+ pr_match = re.search(r"#(\d+)", trigger)
+ title_match = re.search(r"Merge pull request '([^']+)'", trigger)
+
+ if pr_match:
+ pr_number = pr_match.group(1)
+ title = title_match.group(1).strip() if title_match else ""
+ if title:
+ return f"Merge PR #{pr_number} · {title}", pr_url, author
+ return f"Merge PR #{pr_number}", pr_url, author
+
+ return trigger.splitlines()[0].strip(), pr_url, author
+
+
def normalize_build(build: Dict) -> Dict:
commits = _extract_commits(build)
trigger = _extract_trigger(build)
+ trigger_label, trigger_url, trigger_author = _summarize_trigger(trigger)
status = (build.get("result") or "RUNNING").lower()
if build.get("building"):
status = "running"
@@ -65,6 +93,9 @@ def normalize_build(build: Dict) -> Dict:
"url": build.get("url"),
"commits": commits,
"trigger": trigger,
+ "trigger_label": trigger_label,
+ "trigger_url": trigger_url,
+ "trigger_author": trigger_author,
}
diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py
index ceb448f..0c1a885 100644
--- a/backend/tests/test_api.py
+++ b/backend/tests/test_api.py
@@ -112,6 +112,9 @@ def test_build_history(monkeypatch):
{"commit": "9ac3f91", "message": "Anade la API de Jenkins", "author": "Dev One"}
]
assert first["trigger"] == "Triggered by Merge pull request #37"
+ assert first["trigger_label"] == "Merge PR #37"
+ assert first["trigger_url"] == ""
+ assert first["trigger_author"] == ""
second = builds[1]
assert second["number"] == 204
@@ -119,6 +122,9 @@ def test_build_history(monkeypatch):
assert second["duration_seconds"] == 1
assert second["commits"] == []
assert second["trigger"] == ""
+ assert second["trigger_label"] == ""
+ assert second["trigger_url"] == ""
+ assert second["trigger_author"] == ""
def test_build_history_error_returns_empty(monkeypatch):
diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte
index 0fac69b..a943460 100644
--- a/frontend/src/App.svelte
+++ b/frontend/src/App.svelte
@@ -377,14 +377,25 @@ along with this program. If not, see .
#{build.number}
- {#if build.commits.length === 0}
-
- {build.trigger || 'Ejecución manual'}
-
- {:else}
+ {#if build.trigger_label || build.trigger}
+
+ {:else if build.commits.length > 0}
{build.commits[0].commit} · {build.commits[0].author}
+ {:else}
+ Ejecución manual
{/if}
@@ -396,17 +407,39 @@ along with this program. If not, see .
- {#if build.commits.length > 0}
+ {#if build.trigger_label || build.trigger}
- Mensaje
- {build.commits[0].message}
+ Disparo
+
+ {#if build.trigger_url}
+
+ {build.trigger_label || build.trigger}
+
+ {:else}
+ {build.trigger_label || build.trigger}
+ {/if}
+ {#if build.trigger_author}
+ {build.trigger_author}
+ {/if}
+
{/if}
- {#if build.trigger}
+ {#if build.commits.length > 0}
- Disparo
- {build.trigger}
+ Commit
+
+ {build.commits[0].commit} · {build.commits[0].author}
+
+
+
+ Mensaje
+ {build.commits[0].message}
{/if}
diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css
index 2eb6441..867117c 100644
--- a/frontend/src/styles/app.css
+++ b/frontend/src/styles/app.css
@@ -697,6 +697,25 @@ li {
font-size: 0.95rem;
}
+.summary-trigger {
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+ flex-wrap: wrap;
+ color: #fbbf24;
+ font-weight: 700;
+ font-size: 0.95rem;
+}
+
+.summary-trigger a {
+ color: inherit;
+ text-decoration: none;
+}
+
+.summary-trigger a:hover {
+ text-decoration: underline;
+}
+
.summary-meta {
display: flex;
gap: 0.6rem;
@@ -748,6 +767,21 @@ li {
color: #cbd5f5;
}
+.history-link {
+ color: #fbbf24;
+ text-decoration: none;
+ font-weight: 700;
+}
+
+.history-link:hover {
+ text-decoration: underline;
+}
+
+.muted {
+ color: #94a3b8;
+ font-size: 0.95rem;
+}
+
.history-message {
margin: 0.2rem 0 0;
background: rgba(248, 113, 113, 0.12);
@@ -835,3 +869,15 @@ li {
font-size: 0.8rem;
opacity: 0.85;
}
+.trigger-author {
+ padding: 0.1rem 0.4rem;
+ border-radius: 999px;
+ background: rgba(148, 163, 184, 0.16);
+ color: #cbd5f5;
+ font-size: 0.78rem;
+ font-weight: 700;
+}
+
+.history-row .trigger-author {
+ margin-left: 0.35rem;
+}