Skip to content

feat: tell the user when a newer release exists - #14

Open
jeroenrinzema wants to merge 1 commit into
fix/report-release-versionfrom
feat/update-notice
Open

jeroenrinzema wants to merge 1 commit into
fix/report-release-versionfrom
feat/update-notice

Conversation

@jeroenrinzema

@jeroenrinzema jeroenrinzema commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #13 — review that first. This branch is based on fix/report-release-version and its diff here is only the update check.

Interactive runs now report a newer release underneath the command's own output:

Update Available
New version 0.0.6 is available, you are on 0.0.5. Download: https://github.com/brainpodnl/cli/releases/latest

What it costs

  • One request a day, at most. The result is cached in version-check.json beside the configuration file; every other run that day reads it and touches nothing.
  • No body to download. It reads where https://github.com/brainpodnl/cli/releases/latest redirects, so the answer is a 302 with content-length: 0. The releases API would return the whole release, notes and assets included, and carries an unauthenticated rate limit.
  • No waiting. The request starts before the command runs and is read after it, so a command that talks to the API pays nothing. Anything still in flight is given 250 ms and then left to finish into the cache for the next run to read.
  • Stamped before the request, not after. A run that ends early, or a machine with no route to GitHub, still checks once a day rather than once a command.

What stays silent

--json, anything whose stderr is not a terminal, anything with CI set, and BRAINPOD_NO_UPDATE_CHECK=1. Those runs make no request at all.

A version neither side can order is never compared: only plain major.minor.patch is ranked, so the 0.0.0-dev+<sha> that #13 gives unstamped builds says nothing rather than nagging a developer about their own checkout.

Why this is stacked

The notice is only as good as the version the binary reports, and on main that is the hardcoded 0.1.0 no release ever carried — the comparison would have been dead on arrival. #13 makes --version the release tag and gates it in CI, so this branch simply reads env!("BRAINPOD_VERSION") and adds no version plumbing of its own.

Verification

  • cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo test (97 passed).
  • Cold run against the real redirect: 239 ms total, cache written as {"checkedAt":…,"latest":"0.0.5"}.
  • Warm run: 15 ms, no network.
  • Unreachable GitHub (dead proxy): 12 ms, cache still stamped, no notice.
  • Unstamped build (brainpod 0.0.0-dev+a3844d058a01) with a 9.9.9 release cached: silent.
  • Same cache against a BRAINPOD_VERSION=0.0.5 build: notice rendered. --json and BRAINPOD_NO_UPDATE_CHECK=1 printed nothing.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Background-task cancellation, cache-write failures, clock rollback, and concurrent runs can prevent reliable checks or violate the daily request limit.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 4 Medium severity

Open (4)
What changed in this PR

Adds background release checks for interactive CLI runs while minimizing network and latency costs.

Changes:

  • Adds cached, daily release checks and update notices.
  • Aligns crate metadata with release tags and validates future releases.
  • Documents update-check behavior and opt-out controls.
File Description
src/​update.rs Implements release checking, caching, notices, and tests.
src/​main.rs Integrates checks into command execution.
src/​output.rs Exposes shared terminal styling.
README.md Documents update notifications.
Cargo.toml Aligns the crate version with releases.
Cargo.lock Updates locked package metadata.
.github/​workflows/​release.yml Validates release tags against the crate version.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/update.rs
Comment on lines +53 to +55
let cache = read(&path);
let known = cache.as_ref().and_then(|cache| cache.latest.clone());
if !due(cache.as_ref(), now()) {

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.

Not taking this one. The race is real but its cost is one extra 302 with an empty body, and the runs most likely to overlap — scripts and CI — are already silent, since the check needs a terminal on stderr and honours CI. A lock file buys nothing against that and brings its own failure modes: staleness after a SIGKILL, a shared BRAINPOD_CONFIG on a network filesystem, and cleanup on a path that is deliberately best-effort everywhere else. "At most once a day" is about not turning every command into a request, which the stamp does hold. Happy to revisit if the endpoint ever costs more than a redirect.

Comment thread src/update.rs Outdated
Comment thread src/update.rs
Comment thread src/update.rs Outdated
@jeroenrinzema
jeroenrinzema changed the base branch from main to fix/report-release-version September 21, 2026 20:40
@jeroenrinzema
jeroenrinzema force-pushed the fix/report-release-version branch from f78bd8c to f31a032 Compare September 21, 2026 20:44
The check reads where the releases 'latest' alias redirects, which answers
with no body, at most once a day, cached beside the configuration file. It
starts before the command and is read after it, so it waits on nothing the
command was not already waiting on, and runs a program reads stay silent.

The crate version is what the notice compares against, so it is brought back
in line with the published tags and the release workflow now refuses a tag
that disagrees with it.
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