SRVOCF-1038: Show build status and pipeline failures in the functions list - #177
SRVOCF-1038: Show build status and pipeline failures in the functions list#177matejvasek wants to merge 18 commits into
Conversation
Design for surfacing GitHub Actions build status and pipeline failures in the functions list, via an SSE stream from the backend (polling GH Actions) read with consoleFetch. Covers the status merge with the existing cluster watch, new Building/BuildFailed statuses, parameterless user-scoped endpoints, fakegithub Actions API with /_admin control, and the test strategy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add admin endpoints so tests and dev can script a repo's GitHub Actions workflow run status, conclusion, and jobs, making build status deterministic to exercise end to end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fetch the latest workflow run for a repo's default branch through the GitHub Actions REST API and, on failure, derive a "<job> / <step>" reason from the failed job. Includes fakegithub coverage and failureReason fallbacks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a snapshot endpoint and an SSE watch endpoint that streams per-repo build status, polling GitHub on an interval with heartbeats and periodic repo rediscovery. Per-repo errors are surfaced in the snapshot, and each snapshot is marshalled once with the bytes reused as the change key so unchanged polls are skipped. Wire the routes into main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stream build status over SSE via consoleFetch, sending the PAT in the X-SCM-Token header, with reconnect/backoff and stop-on-auth-error. Includes a consoleFetch stream test stub. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge streamed build status into the functions list: render Building and BuildFailed (with a link to the run and a failure-reason tooltip), while letting a Running cluster status win over a stale Failed build. Also repairs the setup-guide test orphaned by a master helper rename. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pass the auth connectionId into useBuildStatus so the stream tears down and reconnects with the current PAT on in-place login and account switch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…limit usage The build-status poll loop hit GitHub every 3s per repo, exhausting the 5,000/hr core rate limit. Wire a per-client in-memory httpcache transport so unchanged responses come back as 304 Not Modified, which do not count against the primary rate limit. GitHub sends Cache-Control: max-age=60 on these responses, which would let the cache serve a stale build status for up to ~60s. A forceRevalidate transport sets Cache-Control: max-age=0 on every request so the cache always revalidates with a conditional request: unchanged status stays a free 304, but a real change is seen on the next poll. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A 2xx response with no body previously fell through the `if (!res.body) return;` guard and permanently stopped the SSE stream, so the build-status badges would silently freeze until the next connectionId change. Treat a body-less response like any other stream end: fall through to the backoff-and-reconnect path instead of giving up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LatestWorkflowRun took the newest run across *all* workflows in a repo
(ListRepositoryWorkflowRuns), so an unrelated workflow (lint, CodeQL, a cron)
could mask or misrepresent the func build: a passing lint run could hide a
failed build, or a failing unrelated workflow could paint a function red.
Filter to the func build workflow by file name via ListWorkflowRunsByFileName.
The identifier is func's own DefaultGitHubWorkflowFilename ("func-deploy.yaml"),
re-exported from the scaffold package as scaffold.WorkflowFilename so it stays in
sync with what we actually scaffold. The scm layer stays func-agnostic: the
workflow file name is passed in as a parameter, supplied by the func-aware
handler. A repo without that workflow file returns 404, which we map to a nil
run (no build signal) so non-func repos and not-yet-pushed workflows fall back
to the cluster-derived status instead of erroring.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tream consoleFetch applies a default ~60s request timeout that aborts the request when it fires. On the long-lived build-status SSE stream that tore the connection down every minute regardless of the backend's 15s heartbeats, forcing a reconnect and a full initial snapshot re-fetch from GitHub each time. Pass timeout 0 to disable it so the stream is ended only by the hook's own AbortController (on unmount or connectionId change). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e functions A function that is deployed and available (serving `Running` or idle `ScaledToZero`) now keeps its cluster status while a rebuild runs or fails, instead of being overwritten by `Building`/`BuildFailed`. The build activity is surfaced only as a small secondary indicator next to the status: a spinner (tooltip "Build in progress") while building, or a red danger-colored warning icon (tooltip "Latest build failed: <reason>", link to the run) when the latest build failed. This stops an available function from flip-flopping to a build-centric status on every redeploy and keeps availability accurate. Deferred: giving a cluster `Error` (broken deployed revision) the same non-destructive treatment. `Error` is overloaded (it also covers a repo/list error with no cluster resource), so doing it right means gating on cluster presence rather than the status string. Noted in the design doc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… workflow
The fake's by-file-name runs endpoint
(/repos/{owner}/{repo}/actions/workflows/{workflow}/runs) shared a handler with
the repo-wide endpoint and ignored the {workflow} path segment, so both returned
every scripted run. That gave the fakegithub and e2e suites no fidelity for
workflow-file scoping: a regression where build status stopped querying only
func-deploy.yaml would go uncaught.
Give each scripted run a workflow-file identity (defaulting to
functions.WorkflowFilename so it stays in sync with what the client requests, and
overridable via the admin /_admin/actions/runs "workflow" field) and filter by
the {workflow} path segment on the by-file-name route. The repo-wide route still
returns all runs. Add a test asserting a run under a different workflow is not
returned when querying the func workflow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…id-stream Once the SSE stream is established, a per-repo LatestWorkflowRun error (including ErrUnauthorized) is logged and the last-known status is carried forward, and the 30s rediscover ListRepos error was only logged. So if the caller's PAT was revoked after connecting, every poll failed, the change-detection key never moved, no new frame was sent, and the client showed stale build status indefinitely without ever seeing an auth error to trigger re-auth. ListRepos is a single global call, so its ErrUnauthorized unambiguously means the token is no longer valid. End the stream in that case; the client's reconnect then hits the initial ListRepos, gets a 401 before the SSE upgrade, and its existing isAuthError path stops the loop / prompts re-auth. Detection latency is bounded by the rediscover interval. Non-auth rediscover errors still just log. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The per-repo build-status item carried an "Err" field populated with the raw error string from a failed workflow-run fetch. That string was never consumed by the frontend but was serialized onto the wire, exposing internal error detail to the browser. Drop the field: a failed fetch with no prior state now reports a plain "None" item and the cause is logged server-side instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Pipeline controller notification For optional jobs, comment This repository is configured in: LGTM mode |
|
Skipping CI for Draft Pull Request. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
/test all |
|
/test e2e-aws |
|
@matejvasek: This pull request references SRVOCF-1038 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.1.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
@matejvasek: all tests passed! Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
Summary
/api/v1/func/build/watch) plus a snapshot endpoint (/api/v1/func/build/status), user-scoped like/listscm.Client.LatestWorkflowRunvia GitHub Actions, scoped tofunc-deploy.yaml, ETag-cached to keep 304s free against the rate limituseBuildStatus, list merge); Playwright e2e against the real backend and fakegithubdocs/design/2026-08-26-SRVOCF-1038-build-status-design.mdFixes SRVOCF-1038
Checklist
docs/ARCHITECTURE.md(if there are relevant changes to our layered architecture)Additional Info
Error(needs gating on cluster presence, not the status string, sinceErroris overloaded with a repo/list-level error). Noted in the design doc.🤖 Generated with Claude Code