Repository navigation
feat(workflow): support NDJSON output for view --watch --json (#64) - #65
Merged
Merged
Conversation
) Stays attached to a run and decides each newly-appearing environment approval gate (after confirmation, unless --yes) until the run completes, instead of requiring a re-run of `workflow approve` after every gate. - New --watch/-W, --interval (default 10s, min 5), --timeout (0 = none) flags - --watch --yes requires --environment, so an unattended watch never approves a gate that didn't exist when it started - Ctrl-C stops cleanly without deciding anything further - --watch --dry-run falls through to the existing non-looping preview - NDJSON event output under --json (one compact object per event) - internal/testutils: MockGitHubServer.SetResponseSequence lets tests simulate a run's state changing across successive polls Closes #26 Claude-Session: https://claude.ai/code/session_01Wy6Fqe5usEojMuNcAjKvRD
--watch and --json on `workflow view` were previously mutually exclusive (--watch is an interactive bubbletea view). With --json, --watch now instead streams each job/step status transition as compact single-line JSON (NDJSON) until the run completes, for scripting or logging a watched run: job_started, job_completed, step_started, step_completed, run_done - pkg/workflow/watch_view.go: WatchViewRun polls GetWorkflowRunDetail, diffing job/step status against what was already reported so a poll with no change produces no events; a job/step that jumps straight to completed (skipping in_progress) reports only completed, not a synthetic started - Reuses the existing --interval flag and the DI Sleep pattern from #26's approve --watch; stops cleanly on ctx cancellation (Ctrl-C) - The plain interactive --watch (no --json) is unchanged Closes #64 Claude-Session: https://claude.ai/code/session_01Wy6Fqe5usEojMuNcAjKvRD
The merge of develop (containing #63's squash-merged commit) into this branch (which already had #63's pre-squash commits as ancestors) silently duplicated the entire "workflow approve --watch" test section instead of recognizing it as already present, since the two copies reached the file via different diff paths (squash vs. original commits) and didn't line up for git's merge to dedupe automatically. go vet caught it immediately ("approveRunPath redeclared in this block"). Removed the duplicate, confirmed the file is otherwise identical, gofmt clean, full suite green. Claude-Session: https://claude.ai/code/session_01Wy6Fqe5usEojMuNcAjKvRD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What / why
workflow approve --watch(#26) streams its events as NDJSON under--json.workflow view --watchhad no equivalent —--watchand--jsonwere mutually exclusive (--watchis aninteractive bubbletea view, incompatible with machine-readable output). Raised as #64 while
resolving #26.
With
--json,--watchnow streams each job/step status transition as compact single-line JSON(NDJSON) until the run completes, instead of erroring:
{"kind":"job_started","job":"build"} {"kind":"step_started","job":"build","step":"checkout"} {"kind":"step_completed","job":"build","step":"checkout","conclusion":"success"} {"kind":"job_completed","job":"build","conclusion":"success"} {"kind":"run_done","conclusion":"success"}Behaviour notes
pkg/workflow/watch_view.go(WatchViewRun) pollsGetWorkflowRunDetailand diffs each job'sand step's status against what was already reported (
viewWatchState/viewWatchProgress), soa poll that finds nothing new produces no events — a job/step is reported "started" the first
time it's seen
in_progressand "completed" the first time it's seencompleted. A job/stepthat jumps straight from unseen to completed (skipping
in_progressbetween polls) reportsonly
job_completed/step_completed, no synthetic "started" event.--intervalflag (no new flags) and the sameSleep-injection patternapprove --watch(workflow approve: add --watch to decide each approval gate as it appears #26) established, so tests don't really wait between polls.cmd.Context()cancellation (Ctrl-C), reporting nothing further — samesemantics as
approve --watch.--watch(no--json) is unchanged.TestViewCommand_WatchAndJSONRejectedtest was replaced with two end-to-endtests asserting the NDJSON shape and event ordering, plus a run-lookup-error case.
Testing
pkg/workflow/watch_view_test.go: unit tests for the diff logic (started-then-completed,completed-without-observed-start, independently-keyed steps within a job) plus
WatchViewRunend-to-end tests (polls until completion, already-completed run, context cancellation).
cmd/workflow/workflow_test.go:TestViewCommand_WatchJSON_StreamsNDJSONUntilRunCompletes(asserts exact event kinds/order) and
TestViewCommand_WatchJSON_RunLookupErrorSetsHasError.!p.completedguard) and confirmed the test suitecatches the regression (a re-poll of the same completed job re-reports
job_completed).gofmt -l .,go vet ./...,go build ./...,go test -race ./...all clean;./scripts/check-coverage.shreports 93.7% (floor 85%).reason noted on feat(workflow): add --watch to workflow approve for multi-gate runs (#26) #63/workflow approve: add --watch to decide each approval gate as it appears #26 — this reuses the already-live-tested
GetWorkflowRunDetailpathunchanged, just adding a new consumer of it.
Note on base branch
Stacked on
feat/26-approve-watch(itself stacked onfeat/1-keyring-token-storage, #62), perthe same "implement it stacked on the last one" request. Will need rebasing/retargeting once the
branches below it merge to
develop.Closes #64
https://claude.ai/code/session_01Wy6Fqe5usEojMuNcAjKvRD