Skip to content

fix(ci): fall back to Observer when live WorkflowRun logs are empty - #733

Open
artemiyokulov wants to merge 2 commits into
openchoreo:mainfrom
artemiyokulov:fix/ci-workflow-logs-observer-fallback
Open

fix(ci): fall back to Observer when live WorkflowRun logs are empty#733
artemiyokulov wants to merge 2 commits into
openchoreo:mainfrom
artemiyokulov:fix/ci-workflow-logs-observer-fallback

Conversation

@artemiyokulov

@artemiyokulov artemiyokulov commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix empty WorkflowRun step logs in the CI / Build UI after successful runs that use Argo podGC.strategy: OnWorkflowSuccess.
  • When hasLiveObservability=true but live openchoreo-api logs are empty and the run is terminal, fall back to Observer (POST /api/v1/logs/query), matching GenericWorkflowService.getWorkflowRunLogs.
  • Skip Observer fallback for incremental sinceSeconds polls while a step is still running.

Fixes openchoreo/openchoreo#4404
Related: openchoreo/openchoreo#4403

Context

hasLiveObservability is true while the Argo Workflow CR exists. Live /logs reads pods. After success + podGC, pods are gone → live returns [] while the CR (and flag) remain. The CI backend previously returned that empty array; the workflows backend already had Observer fallback.

Test plan

  • Succeeded WorkflowRun with podGC: OnWorkflowSuccess, pods deleted, Workflow CR still present
  • Build UI step logs non-empty via Observer fallback (hasLiveObservability=true still)
  • Running WorkflowRun still uses live path; empty live during startup does not force Observer
  • Incremental poll with sinceSeconds does not fall back when live is empty
  • Older runs with hasLiveObservability=false still use Observer-only path

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes
    • Improved workflow log retrieval to show live logs as soon as they become available.
    • Incremental log updates now correctly return no results when there are no new lines.
    • Added more reliable fallback handling for completed or failed workflows.
    • Preserved log filtering by workflow step and time range.

hasLiveObservability stays true while the Argo Workflow CR exists, but
live /logs reads pods. After OnWorkflowSuccess podGC, pods are gone and
the CI plugin returned [] forever. Align fetchWorkflowRunLogs with
GenericWorkflowService: for terminal runs, fall back to Observer when
live logs are empty (skip for incremental sinceSeconds polls).

Fixes #732
Related: openchoreo/openchoreo#4403

Signed-off-by: Artemy Okulov <artemy.okulov@x5.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Changeset detected — the following file(s) will be released with this PR:

.changeset/ci-workflow-logs-observer-fallback.md

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

fetchWorkflowRunLogs now returns live logs immediately, handles empty incremental polls, and falls back to filtered Observer logs for terminal workflow runs when live logs are unavailable.

Changes

Workflow log fallback

Layer / File(s) Summary
Live log routing
plugins/openchoreo-ci-backend/src/services/WorkflowService.ts
Live logs are mapped and returned when available. Empty incremental polls return immediately.
Terminal Observer fallback
plugins/openchoreo-ci-backend/src/services/WorkflowService.ts
Terminal status detection and Observer retrieval preserve time limits, step filters, incremental windows, response validation, and log mapping.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Suggested reviewers: kaviththiranga

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the problem and intended behavior, but it omits most required template sections, including release, documentation, security, and test details. Complete the required template sections and provide the requested documentation, testing, security, environment, and impact details.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the main change: falling back to Observer when live WorkflowRun logs are empty.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
plugins/openchoreo-ci-backend/src/services/WorkflowService.ts (1)

614-624: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the shared 29-day window constant.

The same cap, comment, and computation exist in fetchWorkflowRunEvents at Lines 748-753. Move 29 * 24 * 60 * 60 * 1000 to a module-level constant and reference it from both methods. This keeps the two Observer queries aligned when the limit changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/openchoreo-ci-backend/src/services/WorkflowService.ts` around lines
614 - 624, Extract the shared 29-day millisecond value into a module-level
constant, preserving the existing explanatory comment, and update both
fetchWorkflowRunEvents and the current time-window calculation to reference it
instead of duplicating the literal. Keep the existing requested-duration and
Math.min behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/openchoreo-ci-backend/src/services/WorkflowService.ts`:
- Around line 552-577: Update isWorkflowRunTerminal to treat a workflow run as
terminal whenever the API data has completedAt set, regardless of the derived
Ready reason. Preserve the existing terminal-status handling for runs without
completedAt, so statuses such as Succeeded, Failed, and Cancelled remain
terminal.

---

Nitpick comments:
In `@plugins/openchoreo-ci-backend/src/services/WorkflowService.ts`:
- Around line 614-624: Extract the shared 29-day millisecond value into a
module-level constant, preserving the existing explanatory comment, and update
both fetchWorkflowRunEvents and the current time-window calculation to reference
it instead of duplicating the literal. Keep the existing requested-duration and
Math.min behavior unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d9d37a3-389f-493f-8e2c-2f53b86fb05f

📥 Commits

Reviewing files that changed from the base of the PR and between 9130de9 and 7527941.

📒 Files selected for processing (1)
  • plugins/openchoreo-ci-backend/src/services/WorkflowService.ts

Comment on lines +552 to +577
private async isWorkflowRunTerminal(
namespaceName: string,
runName: string,
token?: string,
): Promise<boolean> {
const terminal = new Set([
'Succeeded',
'Failed',
'Completed',
'Error',
'Cancelled',
]);
try {
const client = createOpenChoreoApiClient({
baseUrl: this.baseUrl,
token,
logger: this.logger,
});
const { data, error, response } = await client.GET(
'/api/v1/namespaces/{namespaceName}/workflowruns/{runName}',
{ params: { path: { namespaceName, runName } } },
);
if (error || !response.ok || !data) {
return false;
}
return terminal.has(deriveWorkflowRunStatus(data));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find all WorkflowRun Ready-condition reasons and task phases used in the repo.
set -uo pipefail

# Locate other terminal-status checks to compare the accepted value sets.
rg -nP -C4 "'Succeeded'|\"Succeeded\"" --type=ts -g '!**/node_modules/**' | head -100

# Locate reason/phase constants and unions.
rg -nP -C3 "(reason|phase)\s*[:=]\s*['\"]" --type=ts -g '!**/node_modules/**' | head -80

# Show every caller/definition of deriveWorkflowRunStatus.
rg -nP -C6 '\bderiveWorkflowRunStatus\b' --type=ts -g '!**/node_modules/**'

Repository: openchoreo/backstage-plugins

Length of output: 166


🏁 Script executed:

#!/bin/bash
set -u

echo "Repo files around workflow service:"
fd -a 'WorkflowService.ts' . || true

echo
echo "Git status/stat:"
git status --short || true
git diff --stat || true

echo
echo "Search deriveWorkflowRunStatus in TS files:"
rg -n -C 8 '\bderiveWorkflowRunStatus\b' . --glob '*.ts' || true

echo
echo "Search terminal status strings in TS files:"
rg -n -C 4 "Succeeded|Failed|Completed|Error|Cancelled|DeadlineExceeded|Terminated|Timeout|Running|Pending" . --glob '*.ts' || true

Repository: openchoreo/backstage-plugins

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -u

echo "Focus WorkspaceService.ts file:"
wc -l plugins/openchoreo-ci-backend/src/services/WorkflowService.ts
sed -n '1,90p' plugins/openchoreo-ci-runtime/src/services/WorkflowService.ts 2>/dev/null || true
sed -n '1,90p' plugins/openchoreo-ci-backend/src/services/WorkflowService.ts
sed -n '520,610p' plugins/openchoreo-ci-backend/src/services/WorkflowService.ts

echo
echo "targeted deriveWorkflowRunStatus occurrences:"
rg -n -C 8 '\bderiveWorkflowRunStatus\b' plugins packages --glob '*.ts' | sed -n '1,220p'

echo
echo "WorkflowRun status type definitions:"
rg -n -C 8 "WorkflowRun|workflowruns|completedAt|readyCondition" packages --glob '*.ts' plugins --glob '*.ts' | sed -n '1,260p'

echo
echo "terminal status occurrences outside generated:"
rg -n -C 4 "Succeeded|Failed|Completed|Error|Cancelled|DeadlineExceeded|Terminated|Timeout|Running|Pending" plugins packages --glob '*.ts' --glob '!**/packages/openchoreo-client-node/src/generated/**/*.ts' | sed -n '1,240p'

Repository: openchoreo/backstage-plugins

Length of output: 50384


🏁 Script executed:

#!/bin/bash
set -u

echo "WorkflowService surrounding fetchWorkflowRunLogs:"
sed -n '410,590p' plugins/openchoreo-ci-backend/src/services/WorkflowService.ts

echo
echo "Programmatic behavior probe for deriveWorkflowRunStatus / isWorkflowRunTerminal terminal allowlist"
node - <<'JS'
function deriveWorkflowRunStatus(run) {
  const readyCondition = (run.status?.conditions ?? []).find(
    (c) => c.type === 'Ready',
  );
  const tasks = run.status?.tasks ?? [];

  if (run.status?.completedAt) {
    if (tasks.some((t) => t.phase === 'Failed' || t.phase === 'Error')) {
      return 'Failed';
    }
    const reason = readyCondition?.reason;
    if (reason && reason !== 'Running' && reason !== 'Pending') {
      return reason;
    }
    return 'Succeeded';
  }

  if (readyCondition) {
    return readyCondition.reason ||
      (readyCondition.status === 'True' ? 'Succeeded' : 'Running');
  }

  if (tasks.some((t) => t.phase === 'Failed' || t.phase === 'Error')) {
    return 'Failed';
  }
  if (tasks.every((t) => t.phase === 'Succeeded') && tasks.length > 0) {
    return 'Succeeded';
  }
  if (tasks.some((t) => t.phase === 'Running')) {
    return 'Running';
  }
  if (run.status?.startedAt) return 'Running';
  return 'Pending';
}

const terminal = new Set([
  'Succeeded',
  'Failed',
  'Completed',
  'Error',
  'Cancelled',
]);

for (const name of [
  'DeadlineExceeded',
  'Terminated',
  'Timeout',
  'Cancelled',
  'Succeeded',
  'Running',
  'Pending',
]) {
  const run = {
    status: { completedAt: '2026-08-01T00:00:00Z', conditions: [{ type: 'Ready', reason: name }] },
  };
  const derived = deriveWorkflowRunStatus(run);
  console.log(JSON.stringify({ reason: name, derived, terminal, isTerminal: terminal.has(derived) }));
}
JS

Repository: openchoreo/backstage-plugins

Length of output: 6277


Treat completed workflow runs as terminal.

isWorkflowRunTerminal only checks a fixed set of status names, while deriveWorkflowRunStatus returns the Ready reason any time status.completedAt is set unless the reason is Running or Pending. Completed runs with reasons such as DeadlineExceeded, Terminated, or Timeout are seen as non-terminal, so fetchWorkflowRunLogs returns the empty live [] result instead of falling back to Observer. Use completedAt as the terminal signal here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/openchoreo-ci-backend/src/services/WorkflowService.ts` around lines
552 - 577, Update isWorkflowRunTerminal to treat a workflow run as terminal
whenever the API data has completedAt set, regardless of the derived Ready
reason. Preserve the existing terminal-status handling for runs without
completedAt, so statuses such as Succeeded, Failed, and Cancelled remain
terminal.

Signed-off-by: Artemy Okulov <artemy.okulov@x5.ru>
Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@kaviththiranga

Copy link
Copy Markdown
Contributor

@artemiyokulov Thank you for your contribution. Can you please resolve the review comment from @coderabbitai? Thanks

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="openchoreo/backstage-plugins"
pr_number="733"

gh api "repos/${repo}/pulls/${pr_number}/comments?per_page=100" \
  --paginate \
  --jq '.[] | select(.user.login == "coderabbitai") | {
    id,
    path,
    line,
    original_line,
    side,
    body,
    html_url,
    created_at
  }'

gh api graphql \
  -F owner="openchoreo" \
  -F name="backstage-plugins" \
  -F number="$pr_number" \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          comments(first: 20) {
            nodes {
              author { login }
              body
              url
            }
          }
        }
      }
    }
  }
}' \
  --jq '.data.repository.pullRequest.reviewThreads.nodes[]
  | select(.isResolved == false)
  | select(any(.comments.nodes[]; .author.login == "coderabbitai"))
  | {path, line, isResolved, comments: [.comments.nodes[] | select(.author.login == "coderabbitai")]}'

Length of output: 7587


@kaviththiranga @artemiyokulov The open comment is in plugins/openchoreo-ci-backend/src/services/WorkflowService.ts at Line 577.

Please update isWorkflowRunTerminal to return true when workflowRun.status.completedAt is set. Keep the existing terminal-status checks for workflow runs without completedAt.

Without this change, completed runs with Ready reasons such as DeadlineExceeded, Terminated, or Timeout do not use the Observer log fallback.

You are interacting with an AI system.

@LakshanSS LakshanSS left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@artemiyokulov Kind reminder on this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI plugin: empty WorkflowRun step logs when hasLiveObservability=true after podGC

4 participants