Skip to content

feat: add version update feature#114

Merged
dewabisma merged 5 commits into
mainfrom
beast/update-cli-feature
Jun 23, 2026
Merged

feat: add version update feature#114
dewabisma merged 5 commits into
mainfrom
beast/update-cli-feature

Conversation

@dewabisma

@dewabisma dewabisma commented Jun 22, 2026

Copy link
Copy Markdown
Contributor
  • Auto detect new version and show on every command
  • Add update command to do self update
image

Note

Medium Risk
Self-update downloads and overwrites the running binary from GitHub, which is a supply-chain and permissions-sensitive path even though it does not touch wallet or chain auth logic.

Overview
Adds in-place CLI updates from GitHub releases and a non-blocking “new version available” notice on normal commands.

quantus update downloads the platform archive from Quantus-Network/quantus-cli, extracts quantus-cli-v{version}-{target}/quantus, and replaces the running binary via self_update (blocking work on spawn_blocking). Flags: --check, --yes, and --version for a specific tag. Permission errors get a hint to use sudo.

Background check (version_check): queries GitHub’s latest release (or reads ~/.quantus/update_check.json with a 4-hour TTL), compares semver-ish versions, and prints a notice after the command finishes (up to a 3s wait). Skipped for quantus update and when QUANTUS_NO_UPDATE_CHECK is set.

Deps: self_update 0.43 (aligned with existing reqwest 0.12 / indicatif 0.18); README documents the update flow.

Reviewed by Cursor Bugbot for commit 10166c5. Configure here.

- Auto detect new version and show on every command
- Add update command to do self update
@dewabisma dewabisma requested review from illuzen and n13 June 22, 2026 04:09

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 10166c5. Configure here.

Comment thread src/version_check.rs
latest.bright_green().bold()
);
log_print!(" Download it from {}", RELEASES_PAGE_URL.bright_cyan());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Update notice mid-command output

Medium Severity

The background update task prints its banner as soon as notify_if_update_available finishes, including when a warm cache makes that almost instant. main only waits for the task afterward, so on slow commands the “new version available” lines can appear in the middle of normal command output instead of after it.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 10166c5. Configure here.

Comment thread src/cli/update.rs
if self_update::version::bump_is_greater(current, latest_version).unwrap_or(false) {
return Ok(UpdateOutcome::UpdateAvailable(latest_version.to_string()));
}
return Ok(UpdateOutcome::AlreadyLatest(current.to_string()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Check-only uses wrong latest

Medium Severity

quantus update --check treats the first entry from ReleaseList::fetch() as “latest”, which can be a prerelease or any newest tag on the full releases list. Background checks and self_update’s install path follow GitHub’s /releases/latest (stable) semantics, so --check can report an upgrade that quantus update will not perform.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 10166c5. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dewabisma this one is true positive

@n13

n13 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

I've reviewed PR #114 thoroughly—pulled the full diff, checked the actual GitHub release asset names and archive layout, and cross-referenced the codebase conventions. Here's my review.

Summary

The PR adds two things: a quantus update self-update command (src/cli/update.rs) and a best-effort background "new version available" notice (src/version_check.rs) wired into main.rs. The overall design is reasonable, the archive-path assumptions are correct (I verified quantus-cli-v1.5.0-aarch64-apple-darwin.tar.gz extracts to quantus-cli-v1.5.0-aarch64-apple-darwin/quantus, matching bin_path_in_archive), and dirs/indicatif/reqwest reuse is sensible. But there are correctness issues worth fixing before merge.

Blocking / High value

1. Background notice can print in the middle of command output (Bugbot #1 — valid)

notify_if_update_available() calls log_print! itself, and it runs concurrently with execute_command. main only awaits the handle afterward—it doesn't control when the banner prints. With a warm 4h cache the check resolves almost instantly, so the "new version available" lines land in the middle of the actual command's output, not after it.

The fix is to make the task return the data and let main print after the command completes:

// version_check.rs
pub async fn check_for_update() -> Option<(String, String)> {
    if std::env::var_os(DISABLE_ENV).is_some() { return None; }
    let current = env!("CARGO_PKG_VERSION");
    let latest = latest_version().await?;
    is_newer(current, &latest).then(|| (current.to_string(), latest))
}

Then finish_update_check awaits the handle and does the log_print! itself.

2. update --check resolves a different "latest" than update installs (Bugbot #2 — valid, illuzen confirmed)

In run_update, the check-only path uses ReleaseList::fetch().first(), which is the newest tag in the full list (can include prereleases/drafts). But the install path (self_update's Update) and the background check (/releases/latest) both follow GitHub's stable latest semantics. So quantus update --check can advertise an upgrade that quantus update then refuses to install. Make --check use the same /releases/latest resolution as the install path.

3. DRY violation — two implementations of "fetch latest + compare versions"

This is the root cause that makes #2 possible, and it's worth calling out given the repo's strict DRY rule. self_update is now a dependency and already provides both release fetching and self_update::version::bump_is_greater. Yet version_check.rs hand-rolls a second GitHub fetch (raw reqwest against /releases/latest) plus its own normalize / parse_version / is_newer. There are now three behaviors across two code paths for the same job. Consolidating onto one helper (e.g. a single latest_stable_version() used by both the command and the background check) removes the duplication and fixes #2 simultaneously.

Medium

4. Silent failures conflict with the project's "fail early / always log" rule

version_check.rs deliberately swallows every error (.ok()?, let _ =, and the module doc says "swallowed silently"). For a background check you correctly don't want to spam errors, but per the repo rule failures should still be discoverable. Downgrade them to log_verbose! so -v surfaces what happened, rather than total silence.

5. Latency regression on fast commands with a cold cache

finish_update_check waits up to 3s, and fetch_latest_version has a 3s request timeout. On the first run (or every 4h when the cache expires), a fast command like quantus version now blocks up to ~3s on the update check before exiting. Worth either shortening the join timeout or making the wait conditional.

Minor / nits

  • Stale comment in is_newer: the comment says it falls back to "a conservative string inequality," but the code returns false. Fix the comment (or the behavior).
  • Unnecessary clone in run_update: if version.starts_with('v') { version.clone() } ... clones an owned String that isn't used afterward.
  • Possible duplicate HTTP stack: the Cargo.toml comment claims self_update avoids duplicate deps by sharing reqwest, but the lockfile newly pulls in ureq, ureq-proto, socks, cookie_store, utf8-zero. Worth confirming with cargo tree -i ureq that a second HTTP client isn't actually being compiled despite the reqwest feature—otherwise the "no duplicate dependencies" rationale doesn't fully hold.

Things that are correct (verified)

  • Archive path / identifier logic is right for all four published targets; .tar.gz vs .zip correctly disambiguates from the sha256sums-*.txt assets.
  • Running blocking self_update work under spawn_blocking is the right call.
  • --version tag normalization (v prefix handling) matches the actual release tag scheme (v1.5.0).
  • Permission-denied sudo hint and QUANTUS_NO_UPDATE_CHECK opt-out are nice touches, and the unit tests on the version parsing are good.

Net: the feature is well-built and close, but I'd want #1, #2, and the #3 consolidation addressed before merge (the first two are confirmed bugs, and fixing #3 resolves #2 cleanly). #4 and #5 are quick follow-ups in line with the repo's conventions.

Want me to implement these fixes on the branch?

@n13 n13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-review (commits 963aa916891a90)

Verdict: ✅ Approve

Every issue from the previous review has been addressed, and CI is fully green (Build & Test on macOS + Ubuntu, Clippy, Format, Examples, CodeQL).

Resolved

  1. Notice mid-command output — fixed. notify_if_update_available is now a synchronous, cache-only read; the background refresh_cache_in_background only warms the cache; and the notice is printed from finish_update_check after command output, so it can no longer race into the middle.
  2. update --check wrong "latest" — fixed. Check-only now calls latest_stable_version(), which uses the same /releases/latest resolution as the install path, so a reported upgrade is always one quantus update can actually install.
  3. DRY — fixed. configure_updater() / latest_stable_version() in cli::update are now the single source of truth; version_check delegates fetch + comparison to self_update (bump_is_greater). The hand-rolled reqwest fetch and custom parse_version/is_newer/normalize are gone.
  4. Silent failures — fixed. Cache read/parse/write and refresh errors are surfaced via log_verbose! (with NotFound correctly treated as expected), in line with the fail-early / always-log convention.
  5. Latency — improved. The network fetch no longer blocks command execution; it runs concurrently, the end-of-command wait is bounded by REFRESH_GRACE (3s), and it's a no-op when the cache is fresh.

The earlier nits (unnecessary clone, stale is_newer comment) are also gone.

Minor / non-blocking (optional follow-ups)

  • Doc vs behavior mismatch: the module docs say "The first run with a cold cache shows nothing; a later run (once the cache is warm) shows the notice," but finish_update_check awaits the refresh before notifying, so on a cold/stale cache it can show on the same run — and a fast command like quantus version can wait up to ~3s at the end while the refresh completes. Either drop the await (show strictly on the next run, matching the docs and giving zero added latency) or tweak the comment to match the current "show on same run when possible" behavior.
  • Possible duplicate HTTP stack: self_update still appears to pull ureq/ureq-proto/socks/cookie_store into the lockfile despite the reqwest feature. A quick cargo tree -i ureq would confirm whether a second HTTP client is actually compiled; otherwise the "no duplicate deps" rationale in Cargo.toml doesn't fully hold.

Nice work — good to merge once you're happy with the optional notes.

@dewabisma dewabisma merged commit 5107e88 into main Jun 23, 2026
9 checks passed
@dewabisma dewabisma deleted the beast/update-cli-feature branch June 23, 2026 05:47
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.

3 participants