DanielLeens commented on PR #11840:
URL: https://github.com/apache/seatunnel/pull/11840#issuecomment-5315289525

   # What Problem Does This PR Solve?
   
   On 2026-08-17, Apache PR #11834 showed the required `Build` check stuck 
reporting `queued` for a long time, even though the linked fork run already had 
62 jobs `completed`, 18 `in_progress`, and 7 `queued`. Root cause: 
`.github/workflows/update_build_status.yml` (a `*/15 * * * *` cron job) copies 
the **top-level** `status`/`conclusion` from `GET 
/repos/{owner}/{repo}/actions/runs/{run_id}` verbatim into the Apache-side 
`Build` check run via `PATCH /check-runs/{id}`. For reusable-workflow runs 
(this repo's `Build` → `backend.yml` fan-out is ~87 jobs), GitHub can leave the 
run-level `status` at `queued` long after child jobs are actively running or 
finished, so the check-run patch degenerates into `queued -> queued` and the PR 
appears permanently stuck even though CI is actually progressing (or already 
done).
   
   This PR adds a job-level fallback: when the top-level run is `queued`, it 
lists the real jobs via `GET /actions/runs/{run_id}/jobs` and derives a more 
accurate `status`/`conclusion` from them before patching the check run, and 
switches the check run's `details_url` field from a non-existent API property 
to the run's real `html_url`.
   
   # 1. Code Change Review
   
   ## 1.1 Core Logic Analysis
   
   Scope note: this PR does not touch which check names are required for branch 
protection, and it does not add/remove/rename any workflow job 
(`build_main.yml`'s `Build` → `backend.yml` job graph is untouched). The single 
file changed is `.github/workflows/update_build_status.yml` (+118/-8), a 
maintenance cron job that reconciles the *live status* of the already-existing 
`Build` check run against the real fork-side workflow run.
   
   **Before** (`update_build_status.yml`, old lines 158-159):
   ```js
   status: workflowRun.status,
   details_url: workflowRun.details_url
   ```
   
   **After** (fed by `normalizeWorkflowRun`, lines 151-183):
   ```js
   status: normalizedWorkflowRun.status,
   details_url: normalizedWorkflowRun.details_url   // = workflowRun.html_url
   ```
   
   Key findings, all backed by cross-checking the actual GitHub REST response 
shapes against the other workflow that originates this data 
(`notify_test_workflow.yml`, which creates the check run with `output.text = 
JSON.stringify({owner, repo, run_id})` targeting the **fork** repo, and 
`details_url: actions_url` where `actions_url` is manually built as 
`https://github.com/<fork>/actions/runs/<run_id>`):
   
   1. **A real, previously-silent bug is fixed as a side effect.** The GitHub 
"Get a workflow run" response object has no `details_url` field — only 
`html_url`. So `workflowRun.details_url` at the old code was always 
`undefined`. Since `buildCheckRun.details_url` (from the Checks API) is never 
`undefined`, the `sameDetailsUrl` check was always false, meaning the cron job 
unconditionally re-PATCHed the `Build` check run for *every* eligible open PR 
on *every* 15-minute tick, regardless of whether anything had actually changed. 
Switching to `workflowRun.html_url`, which matches the format of `actions_url` 
set at check-run creation time, makes `sameDetailsUrl` a meaningful, 
usually-true comparison, so the no-op-skip logic will finally work as intended. 
This is a genuine efficiency/correctness fix beyond what the PR description 
explicitly claims credit for — worth calling out in the PR description for 
reviewers/history, but not a blocker.
   
   2. **`normalizeWorkflowRun` correctness**: only branches when 
`workflowRun.status === 'queued'`; all other statuses pass through unchanged 
except for the `details_url` fix above — correct, minimal-blast-radius design.
      - `jobs.length === 0 || jobs.every(job => job.status === 'queued')` → 
leaves status as `queued`, i.e., no regression versus current behavior when the 
job list itself confirms nothing has started.
      - `jobs.every(job => job.status === 'completed')` → `status = 
'completed'`, `conclusion = deriveCompletedConclusion(...)`.
      - Otherwise (mixed) → `status = 'in_progress'`, `conclusion = null`. This 
is the case that fixes the reported #11834 symptom (62 completed / 18 
in_progress / 7 queued → correctly reported as `in_progress`, not stuck 
`queued`).
   
   3. **`deriveCompletedConclusion` — conclusion-vocabulary mapping is 
necessary and correct for its primary purpose.** Job-level `conclusion` values 
from the Jobs API can include `startup_failure`, which is not a legal value for 
the Checks API's `conclusion` enum. Without this mapping, a `startup_failure` 
job conclusion passed straight to `PATCH /check-runs` would be rejected by 
GitHub with a 422. The function correctly folds `startup_failure` into 
`failure`. Priority order implemented: `failure/startup_failure` > `timed_out` 
> `action_required` > `cancelled` > all-`skipped` > `neutral`(+skipped only) > 
default `success`.
   
   4. **Minor gap — `stale` job conclusion is unhandled.** If a completed job's 
conclusion is `stale` and no other job conclusion matches an earlier branch, 
`deriveCompletedConclusion` falls through to the default `return 'success'`, 
silently reporting a stale job as a successful build. `stale` is a legal Checks 
API conclusion value that could have been mapped explicitly. This is a 
low-probability edge case, and any resulting mis-patch is non-fatal (see 
finding 6 below), but it is a real, unaddressed edge in an otherwise careful 
mapping function.
   
   5. **Theoretical `conclusion: null` PATCH failure.** If all jobs are 
`completed` but somehow none have a non-empty string `conclusion`, 
`deriveCompletedConclusion` returns `fallbackConclusion`, which is 
`workflowRun.conclusion` — typically `null` for a top-level run still reporting 
`queued`. That `null` would then be sent as `patchParams.conclusion` while 
`status: 'completed'`, which the Checks API PATCH endpoint requires to be a 
valid conclusion string, not `null`. This is a very low-probability scenario 
but is a latent gap in the fallback path.
   
   6. **No cascading failure risk.** Findings 4-5 above are contained: the 
whole `processPullRequest` body is wrapped by an outer `try { ... } catch 
(error) { console.error(...) }`, so a bad PATCH for one PR just logs and moves 
on — it does not fail the workflow run or block other PRs in the batch. Given 
this safety net, findings 4-5 are correctness nits, not availability risks.
   
   ## 1.2 Compatibility Impact
   
   - **No branch-protection required-check-name changes.** The check run 
created by `notify_test_workflow.yml` is still literally named `'Build'` 
(unchanged), and `build_main.yml`'s trigger/job structure is untouched. Branch 
protection rules referencing the `Build` status check are unaffected.
   - **Net compatibility effect is positive, not neutral.** Because of the 
`details_url` bug fix above, currently in-flight PRs whose `Build` check is 
genuinely stuck `queued` at the top level should start reflecting real progress 
after this merges, rather than requiring maintainers to manually re-run/monitor 
them. I traced the `details_url` values from both origin points and confirmed 
they use the same URL format, so the comparison will behave sanely for existing 
check runs immediately after merge — no first-run flood of unexpected diffs is 
expected.
   - No change to `on:` triggers (still `schedule: */15 * * * *` only) or 
`permissions:` — the new jobs-listing call is covered by the existing `actions: 
read` grant.
   
   ## 1.3 Performance / Side-Effect Analysis
   
   Extra cost is one additional paginated `GET .../jobs` call, but only when 
the top-level run is still `queued` — the common `in_progress`/`completed` path 
is untouched. Given the reported job count (~87 jobs, single page under the 
100-per-page limit), this is a single extra request per affected PR per 
15-minute tick, well within GitHub Actions' rate limits for a scheduled 
maintenance job. The `details_url` fix is actually a net perf win: it should 
stop the previously-guaranteed always-patch on every tick for every open PR, 
replacing it with real no-ops once state converges.
   
   ## 1.4 Error Handling and Logging
   
   No blocking issues found. Two low-severity items already covered in 1.1:
   
   | # | Location | Problem | Risk | Improvement | Severity |
   |---|----------|---------|------|-------------|----------|
   | 1 | `update_build_status.yml` (`deriveCompletedConclusion`) | Job 
`conclusion: 'stale'` is not explicitly mapped and falls through to `'success'` 
| A stale job could be reported as a successful build for the derived 
`queued`->`completed` fallback path only | Add an explicit `stale` branch (e.g. 
map to `'stale'` or `'cancelled'`) | Low |
   | 2 | `update_build_status.yml` | If all jobs are `completed` with zero 
non-empty `conclusion` values, `fallbackConclusion` (often `null`) is patched 
as `conclusion` while `status: 'completed'` | PATCH request would likely be 
rejected by the Checks API (422); caught and logged by the outer try/catch, no 
cascading failure | Guard with a final default (e.g. `'neutral'`) instead of 
passing through a possibly-null fallback | Low |
   
   Both are edge cases in a fallback path that only activates when GitHub 
itself is already misreporting state, and both fail safely rather than crashing 
the workflow.
   
   # 2. Code Quality Assessment
   
   ## 2.1 Standards
   Code style is consistent with the rest of the file. The comment explaining 
the fallback exists (reusable-workflow queued-at-top-level quirk) matches the 
"explain why, not what" bar. No stray debug output, no hardcoded PR/run IDs 
from the incident report leaked into the code, no leftover scaffolding.
   
   ## 2.2 Test Coverage / Stability
   This is the weakest part of the submission. There is no automated test 
harness for this inline `actions/github-script` logic in the repository (a 
pre-existing repo-wide gap, not one this PR introduces). Validation was done 
via local Node.js simulation and a live reproduction against a fork workflow 
run — neither the simulation script nor its output is included in the PR, so it 
cannot be independently verified from the repository. Critically, this PR's own 
CI run does not exercise this new logic at all, since `update_build_status.yml` 
only triggers on `schedule`, never on `pull_request`/`push` — real-world 
confirmation will only come from observing the next `*/15` cron run against a 
genuinely stuck PR. This is inherent to the "CI fixing CI" nature of the change.
   
   ## 2.3 Documentation
   No user-facing docs required (internal CI infrastructure, not a 
connector/config/API change) — correctly out of scope. The PR description 
explains the incident, root cause, and fix scope clearly and matches the actual 
diff.
   
   # 3. Architectural Soundness
   
   ## 3.1 Design cohesion
   The fix is appropriately scoped to the layer that owns the bug: it patches 
the status-derivation logic in the reconciliation cron job, not the underlying 
`build_main.yml`/`backend.yml` job graph (which is the actual source-of-truth 
CI). It does not attempt to "fix" GitHub's top-level `queued` staleness 
upstream — correctly recognizes that's outside SeaTunnel's control and works 
around it locally.
   
   ## 3.2 Coupling
   `normalizeWorkflowRun` takes the fork's `{owner, repo, run_id}` as an 
explicit parameter rather than reaching into outer closures more than 
necessary, keeping the new functions independently testable in principle.
   
   ## 3.3 Failure isolation
   As noted in 1.4, all new failure modes are caught either by a dedicated 
try/catch around the new `normalizeWorkflowRun` call or the pre-existing outer 
per-PR catch — a single bad job listing or malformed conclusion cannot take 
down the batch or the whole scheduled run.
   
   ## 3.4 Extensibility
   If GitHub later exposes similarly unreliable top-level state for other check 
runs, the helper functions are generically written (not `Build`-check-specific) 
and could be reused.
   
   # 4. Issue Summary
   
   | # | Severity | Location | Issue | Blocking? |
   |---|----------|----------|-------|-----------|
   | 1 | Low | `update_build_status.yml` (`deriveCompletedConclusion`) | 
`stale` job conclusion unmapped, falls through to `success` | No |
   | 2 | Low | `update_build_status.yml` | Possible `null` conclusion patched 
when status is `completed` in a degenerate all-jobs-no-conclusion case | No 
(fails safely) |
   | 3 | Informational | PR description | Does not mention that switching 
`details_url` to `html_url` also fixes a pre-existing always-patch bug — a 
positive side effect worth documenting for posterity | No |
   | 4 | Informational | Test coverage | No automated test harness for this 
script (repo-wide pre-existing gap); this PR's own CI run does not exercise the 
new code path | No |
   
   **No blockers found.**
   
   # 5. Merge Recommendation
   
   ### Conclusion: Ready to merge
   
   This is a narrowly-scoped, well-isolated fix to an internal CI 
reconciliation script. I traced the root cause independently (confirmed the 
pre-existing `workflowRun.details_url` was always `undefined` against the real 
GitHub Actions API response shape), verified the new script is syntactically 
valid, and found no path by which this change could rename/remove a required 
check, widen/narrow CI trigger coverage, or strand in-flight PRs. The two 
low-severity edge cases (unmapped `stale` conclusion, theoretical 
`null`-conclusion PATCH) are both in a fallback branch that only activates when 
GitHub itself is already misreporting state, and both fail safely into per-PR 
logged errors rather than crashing the scheduled job or affecting other PRs.
   
   The one real gap is verification depth: this PR's own required checks never 
exercise the new logic, so a green `Build`/`Notify test workflow`/`labeler` on 
this PR provides no evidence about correctness of 
`normalizeWorkflowRun`/`deriveCompletedConclusion`. Recommend the two 
low-severity findings be addressed in a fast follow-up (both are trivial 
one-line additions), and that after merge the next few `*/15` cron runs be 
watched to confirm the derived-status patches land as expected in production.
   
   Overall assessment: sound, minimal, low-risk fix for a real production CI 
annoyance, with two non-blocking polish items and a test-coverage caveat that 
is honestly disclosed rather than concealed.
   
   (Note: since I authored this PR myself, this review is posted as a plain 
comment rather than a formal approve/request-changes review — GitHub doesn't 
allow self-approval, and I want the same level of independent scrutiny applied 
here as to anyone else's PR.)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to