Skip to content

feat(workflow): support NDJSON output for view --watch --json (#64) - #65

Merged
pradyb merged 5 commits into
developfrom
feat/64-view-watch-json
Sep 30, 2026
Merged

pradyb merged 5 commits into
developfrom
feat/64-view-watch-json

Conversation

@pradyb

@pradyb pradyb commented Sep 30, 2026

Copy link
Copy Markdown
Owner

What / why

workflow approve --watch (#26) streams its events as NDJSON under --json. workflow view --watch had no equivalent — --watch and --json were mutually exclusive (--watch is an
interactive bubbletea view, incompatible with machine-readable output). Raised as #64 while
resolving #26.

With --json, --watch now 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) polls GetWorkflowRunDetail and diffs each job's
    and step's status against what was already reported (viewWatchState/viewWatchProgress), so
    a poll that finds nothing new produces no events — a job/step is reported "started" the first
    time it's seen in_progress and "completed" the first time it's seen completed. A job/step
    that jumps straight from unseen to completed (skipping in_progress between polls) reports
    only job_completed/step_completed, no synthetic "started" event.
  • Reuses the existing --interval flag (no new flags) and the same Sleep-injection pattern
    approve --watch (workflow approve: add --watch to decide each approval gate as it appears #26) established, so tests don't really wait between polls.
  • Stops cleanly on cmd.Context() cancellation (Ctrl-C), reporting nothing further — same
    semantics as approve --watch.
  • The plain interactive --watch (no --json) is unchanged.
  • The now-obsolete TestViewCommand_WatchAndJSONRejected test was replaced with two end-to-end
    tests 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 WatchViewRun
    end-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.
  • Mutation-tested the diff logic (removed the !p.completed guard) and confirmed the test suite
    catches 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.sh reports 93.7% (floor 85%).
  • Not verified live against a real GitHub Actions run in this PR, for the same token-permission
    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 GetWorkflowRunDetail path
    unchanged, just adding a new consumer of it.

Note on base branch

Stacked on feat/26-approve-watch (itself stacked on feat/1-keyring-token-storage, #62), per
the 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

)

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
@pradyb
pradyb deleted the branch develop September 30, 2026 03:08
@pradyb pradyb closed this Sep 30, 2026
@pradyb pradyb reopened this Sep 30, 2026
@pradyb
pradyb changed the base branch from feat/26-approve-watch to develop September 30, 2026 03:08
…atch-json

Retargeting #65 from feat/26-approve-watch to develop after #63 (#26) merged;
both branches added an Unreleased/Added entry, kept both (#64's and #26's
entries first, matching this branch's existing order).

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
@pradyb
pradyb merged commit 93c75fd into develop Sep 30, 2026
7 checks passed
@pradyb
pradyb deleted the feat/64-view-watch-json branch September 30, 2026 03:13
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.

workflow view --watch: support NDJSON output for scripting

1 participant