Skip to content

SRVOCF-1038: Show build status and pipeline failures in the functions list - #177

Draft
matejvasek wants to merge 18 commits into
masterfrom
SRVOCF-1038-build-status
Draft

SRVOCF-1038: Show build status and pipeline failures in the functions list#177
matejvasek wants to merge 18 commits into
masterfrom
SRVOCF-1038-build-status

Conversation

@matejvasek

@matejvasek matejvasek commented Sep 2, 2026

Copy link
Copy Markdown

Summary

  • 🎁 Surface GitHub Actions build status (Building / BuildFailed, with failure reason and a link to the run) in the functions list
  • 🎁 Live updates over SSE (/api/v1/func/build/watch) plus a snapshot endpoint (/api/v1/func/build/status), user-scoped like /list
  • 🔧 scm.Client.LatestWorkflowRun via GitHub Actions, scoped to func-deploy.yaml, ETag-cached to keep 304s free against the rate limit
  • 🧪 Ginkgo tests (handler, scm/github, fakegithub); Vitest tests (useBuildStatus, list merge); Playwright e2e against the real backend and fakegithub
  • 📚 Design spec in docs/design/2026-08-26-SRVOCF-1038-build-status-design.md

Fixes SRVOCF-1038

Checklist

  • Updated docs/ARCHITECTURE.md (if there are relevant changes to our layered architecture)

Additional Info

  • Build status is non-destructive over an available function: Running/ScaledToZero keep their cluster badge with a secondary build indicator, so availability is never misrepresented.
  • Deferred: non-destructive treatment for a cluster Error (needs gating on cluster presence, not the status string, since Error is overloaded with a repo/list-level error). Noted in the design doc.
  • Draft while the xhigh self-review findings are triaged (no high-severity; remainder are low / prototype-scope).

🤖 Generated with Claude Code

matejvasek and others added 18 commits September 1, 2026 22:32
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>
@openshift-merge-bot

Copy link
Copy Markdown

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Sep 2, 2026
@openshift-ci

openshift-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci

openshift-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign spadgett for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@matejvasek

Copy link
Copy Markdown
Author

/test all

@matejvasek

Copy link
Copy Markdown
Author

/test e2e-aws

@matejvasek matejvasek changed the title feat(SRVOCF-1038): show build status and pipeline failures in the functions list SRVOCF-1038: Show build status and pipeline failures in the functions list Sep 2, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Sep 2, 2026
@openshift-ci-robot

openshift-ci-robot commented Sep 2, 2026

Copy link
Copy Markdown

@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.

Details

In response to this:

Summary

  • 🎁 Surface GitHub Actions build status (Building / BuildFailed, with failure reason and a link to the run) in the functions list
  • 🎁 Live updates over SSE (/api/v1/func/build/watch) plus a snapshot endpoint (/api/v1/func/build/status), user-scoped like /list
  • 🔧 scm.Client.LatestWorkflowRun via GitHub Actions, scoped to func-deploy.yaml, ETag-cached to keep 304s free against the rate limit
  • 🧪 Ginkgo tests (handler, scm/github, fakegithub); Vitest tests (useBuildStatus, list merge); Playwright e2e against the real backend and fakegithub
  • 📚 Design spec in docs/design/2026-08-26-SRVOCF-1038-build-status-design.md

Fixes SRVOCF-1038

Checklist

  • Updated docs/ARCHITECTURE.md (if there are relevant changes to our layered architecture)

Additional Info

  • Build status is non-destructive over an available function: Running/ScaledToZero keep their cluster badge with a secondary build indicator, so availability is never misrepresented.
  • Deferred: non-destructive treatment for a cluster Error (needs gating on cluster presence, not the status string, since Error is overloaded with a repo/list-level error). Noted in the design doc.
  • Draft while the xhigh self-review findings are triaged (no high-severity; remainder are low / prototype-scope).

🤖 Generated with Claude Code

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
matejvasek requested a review from Cragsmann September 2, 2026 00:42
@openshift-ci

openshift-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

@matejvasek: all tests passed!

Full PR test history. Your PR dashboard.

Details

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 kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants