Skip to content

feat: Stream Connect's startup logs to stderr while waiting for it to start - #72

Merged
nealrichardson merged 1 commit into
mainfrom
stream-startup-logs
Oct 23, 2026
Merged

nealrichardson merged 1 commit into
mainfrom
stream-startup-logs

Conversation

@nealrichardson

Copy link
Copy Markdown
Collaborator

Fixes #13.

What it does

While with-connect waits for Connect's HTTP server to start, it prints each new line of the container log to stderr as it appears. In CI you can see Connect working instead of a silent step that looks hung. Streaming stops as soon as Connect is up, so a -- COMMAND's output isn't mixed with Connect's logs. Nothing new goes to stdout, which in start-only mode carries only the CONNECT_API_KEY= / CONNECT_SERVER= / CONTAINER_ID= lines that callers eval or append to $GITHUB_OUTPUT.

Design choices

  • Polling, not logs(stream=True, follow=True). wait_for_http_server() already polls container.logs() every 2 seconds. A new ContainerLogs helper remembers how many bytes it has printed and prints only the new tail on each poll. A follow stream blocks, so it would need a thread or non-blocking reads, plus cleanup when startup finishes or fails. Polling reuses the existing loop and adds no concurrency. The cost is that lines show up in batches of up to 2 seconds, which is fine for "is it doing anything?".
  • Only complete lines are printed while streaming. A partial trailing line waits for the next poll, so lines never get split around with-connect's own messages.
  • No duplicate output on failure. The failure paths that used to dump the full container.logs() (port never opened, HTTP server never started, invalid license, bootstrap failure) call ContainerLogs.dump(), which prints only the lines that haven't been shown yet. If nothing was streamed (for example with --quiet), it prints the full log under Container logs: as before. If some lines were already streamed, it prints the rest under Container logs (continued):, or nothing if there is nothing left.
  • --quiet also suppresses the stream. People who pass --quiet want less noise, and failures still print the full log. The help text, the action's quiet input, and the README describe it as "Suppress image pull progress and the Connect logs shown while Connect starts".
  • The Running Posit Connect v... line still comes from the full log on each poll, so it works the same way.
  • wait_for_http_server() and get_api_key() take an optional startup_logs argument. Without one, they don't stream and they dump the full log on failure, which is the existing behavior.

Verification

Ran against ghcr.io/posit-dev/connect:latest using README.md as a stand-in license, so Connect starts and with-connect then fails at "Unable to obtain a valid license":

  • Without --quiet: Connect's log lines appeared on stderr in batches as each poll came in, followed by Running Posit Connect v2026.10.0 and the license error. No Container logs: dump, and no line was printed twice (Starting HTTP server appeared once; sort | uniq -d found nothing).
  • With --quiet: no stream. On failure, the full log printed once under Container logs:, as before.
  • With 2>/dev/null: nothing was printed (stdout was empty).

The success path (a valid license) couldn't be run here; unit tests cover it.

Tests

New tests next to test_valid_license_http_server_starts, all registered in the __main__ block:

  • lines are streamed to stderr (never stdout) once each, partial lines are held back, and the version line still prints
  • --quiet-style ContainerLogs streams nothing
  • invalid license after streaming doesn't re-dump the log; with quiet it dumps the full log
  • dump() prints only unseen lines with a (continued) header, and prints nothing when everything was shown

uv run test_main.py and uv run --with pytest pytest -q (45 passed) both pass.

Conflicts with open PRs

🤖 Generated with Claude Code

@nealrichardson nealrichardson left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to make sense, just need to rebase and resolve conflicts

… start

While waiting for the HTTP server to start, print each new line of the
container log to stderr as it appears, so CI runs don't look hung.
Streaming stops once Connect is up. --quiet suppresses the stream, and
failure paths print only the log lines that haven't been shown yet.

Fixes #13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nealrichardson

Copy link
Copy Markdown
Collaborator Author

Rebased onto main. The two conflicts were with #75 and #71: the port-wait failure path keeps #75's "container exited" error but prints logs with startup_logs.dump(), and the license failure no longer stops the container itself since main() does that.

@nealrichardson
nealrichardson merged commit baccac4 into main Oct 23, 2026
13 checks passed
@nealrichardson
nealrichardson deleted the stream-startup-logs branch October 23, 2026 19:06
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.

feat: stream out server logs while Connect is starting up

1 participant