From 35597e2e0a8154ee75b019ce804d8c19bd308778 Mon Sep 17 00:00:00 2001 From: Anton Vinogradov Date: Sat, 11 Jul 2026 21:36:09 +0300 Subject: [PATCH] Living comment: catch TeamCity's late estimates at trigger time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A freshly queued build's finishEstimate is computed asynchronously and is often empty at the moment the sweep triggers the re-runs — one short retry (7s) makes the very first version of the ⏳ line carry the settle ETA instead of waiting for the next sweep. Co-Authored-By: Claude Fable 5 --- .../igniteprchecker/jira/StandingVisas.java | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/github/igniteprchecker/jira/StandingVisas.java b/src/main/java/com/github/igniteprchecker/jira/StandingVisas.java index 532bbdb..8deb611 100644 --- a/src/main/java/com/github/igniteprchecker/jira/StandingVisas.java +++ b/src/main/java/com/github/igniteprchecker/jira/StandingVisas.java @@ -475,18 +475,34 @@ private static String suitesLabel(int blockers, int broken) { /** Max estimated finish across the just-queued builds (epoch seconds), or null when TC has none yet. */ private Long queuedEtaEpoch(String tcToken, List buildIds) { - long max = -1; - for (Long id : buildIds) { + // TeamCity computes a fresh queued build's estimates asynchronously — right after the trigger + // they are often still empty. One short retry catches most of them, so the very first version + // of the ⏳ line already tells when the re-runs should settle. + for (int attempt = 0; ; attempt++) { + long max = -1; + for (Long id : buildIds) { + try { + TcModel.Build b = tc.getBuildState(tcToken, id); + max = Math.max(max, b == null ? -1 : TcDates.epochSeconds(b.finishEstimate())); + } + catch (RuntimeException ignored) { + // no estimate for this one — the others still bound the ETA + } + } + if (max > 0) + return max; + if (attempt >= 1) + return null; + try { - TcModel.Build b = tc.getBuildState(tcToken, id); - max = Math.max(max, b == null ? -1 : TcDates.epochSeconds(b.finishEstimate())); + Thread.sleep(7_000); } - catch (RuntimeException ignored) { - // no estimate for this one — the others still bound the ETA + catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + + return null; } } - - return max > 0 ? max : null; } /** Queue-aware settle estimate for the PR's live re-runs, from the tracker; null when unknown. */