Skip to content

Show a remote banner fetched from banner.json on main - #973

Open
u9g wants to merge 12 commits into
mainfrom
jason/banner
Open

u9g wants to merge 12 commits into
mainfrom
jason/banner

Conversation

@u9g

@u9g u9g commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Why

We want to tell people running an installed lk things like "a new version is out, run brew upgrade livekit-cli" or "livekit.toml layout changed" without shipping an auto-updater (decided against) and without waiting for them to update to see the notice.

What

  • banner.json at the repo root, fetched from main on every run. It is a list of notices, each with its own version selector:
    [
      {"message": "lk agent simulate can now export runs. Upgrade: brew upgrade livekit-cli", "versions": "< 2.18.0"},
      {"message": "lk 3.0 is out and moves the agent id to [cloud] in livekit.toml.", "versions": "< 3.0.0"}
    ]
    versions is a semver constraint; empty matches every build. Every entry that matches prints, in file order, so a new-feature announcement for old builds can sit next to an upgrade notice. The committed file is []. Editing the file on main is the whole publishing process.
  • Shown at the top, before the command's output, inside a fence on stderr via out.Status, so it honors --quiet and never mixes with stdout. Only shown on an interactive terminal, so scripts and pipes are unaffected.
  • The fetch starts during startup and runs concurrently with the command. Output written through the printer is held until the fetch completes, then flushed behind the banner, so the banner stays at the top and the round trip hides behind the command's own RPCs. The hold is bounded by a 1s timeout, and the file is cached at ~/.livekit/banner.json for an hour, so only the first run each hour fetches at all (~70-170ms to raw.githubusercontent.com). A failed fetch falls back to the stale cache. The root After hook waits for the flush so a fast command cannot exit with output still held. Non-interactive runs skip the fetch entirely.

Verification

go build ./cmd/lk, go vet ./cmd/lk, go test ./cmd/lk pass. Demo video in a comment below.

@u9g

u9g commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Demo: banner.json as a list of two notices (served locally for the recording). A 2.17.0 build prints both notices fenced at the top of lk room list on stderr, on the very first run. 2.18.6 matches only the < 3.0.0 one, --quiet silences it, --json | head keeps stdout clean, and a 3.0.0 build runs clean.

banner-sync.mp4

@rektdeckard rektdeckard left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks great, small nits mainly. My one question is whether there will ever be other conditions besides just your lk version that might want to trigger a banner? A specific command for example might want to provide usage tips that aren't dependent on version, but aren't appropriate to show at all times.

Comment thread cmd/lk/banner.go
Comment thread cmd/lk/banner.go Outdated
Comment thread cmd/lk/banner.go
@u9g

u9g commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Demo of the separator: one notice alone, then three entries in banner.json of which two match this version and render with a rule between them, then a piped run that skips the banner.

banner.mp4

@u9g
u9g force-pushed the jason/banner branch 3 times, most recently from 2b331e2 to b49f524 Compare September 14, 2026 14:47
@u9g
u9g requested a review from rektdeckard September 14, 2026 16:14
u9g added 12 commits September 14, 2026 13:26
The CLI fetches banner.json from the repo's main branch on every run and, when
the message is non-empty and the semver constraint in "versions" matches the
running build, prints it to stderr after the command finishes. The fetch is
never awaited: whatever has not arrived by then is dropped, so no command gets
slower. Non-interactive runs never see it.

Editing banner.json on main is the whole publishing process, so notices such as
"upgrade via brew" reach installed CLIs without a release or an auto-updater.
One entry per notice, each with its own versions constraint, so a new-feature
announcement for old builds can sit next to an upgrade notice without either
having to be removed first.
Every --json flag binds to a shared jsonOutput destination so the banner,
which prints after the command returns, can tell a --json run apart from
an interactive one and keep stderr clean for downstream parsers.
Status puts it on stderr and honors --quiet; the border sets it apart from
the command's own output.
Printing before the command means the notice cannot wait on the network,
so each run shows the banner.json cached at ~/.livekit/banner.json by the
run before it and refreshes that cache in the background. A notice lands
one run after it is published; no command gets slower.
The fetch is awaited with a 1s timeout and the file is cached for an hour,
so only the first run each hour pays the round trip. Non-interactive runs
skip the fetch entirely.
The cache is {data, downloadedAt}; a refetch happens once downloadedAt is
over an hour old. Sync tools and backups that rewrite mtime no longer force
a refetch or hide a stale file.
@rektdeckard

Copy link
Copy Markdown
Member

LGTM! Just reiterating the question: do we want to add an optional "command-level" filter so that banners can be shown only for specific commands and their subcommands? Also happy to cross that bridge when we get there.

@u9g

u9g commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

LGTM! Just reiterating the question: do we want to add an optional "command-level" filter so that banners can be shown only for specific commands and their subcommands? Also happy to cross that bridge when we get there.

Let's cross that bridge when we get there

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.

2 participants