Skip to content

feat: "newer version available" notice on pcr runs + start CHANGELOG.md#87

Merged
KaluJo merged 2 commits into
mainfrom
feat/update-notifier
May 18, 2026
Merged

feat: "newer version available" notice on pcr runs + start CHANGELOG.md#87
KaluJo merged 2 commits into
mainfrom
feat/update-notifier

Conversation

@KaluJo
Copy link
Copy Markdown
Collaborator

@KaluJo KaluJo commented May 18, 2026

Summary

Adds an update-notifier on every interactive pcr command. Modeled
on the npm update-notifier package and cargo's own behaviour:

  • Background refresh at command start. Fires a 3 s-timeout
    GET https://registry.npmjs.org/pcr-dev/latest on a detached
    thread, writes the result to ~/.pcr-dev/update-check.json. The
    thread is never joined — slow DNS / captive portals / network
    failures never delay the user's primary signal. Registry hit is
    rate-limited to once per 24 h via the cache TTL.

  • One-line notice at command end. Reads the cache; if the cached
    pcr-dev@latest is greater than CARGO_PKG_VERSION under naive
    major.minor.patch semver AND we haven't shown the notice in the
    last hour (so back-to-back pcr log; pcr show stays quiet),
    prints:

      ┌─ pcr 0.3.0 is available (you have 0.2.9)
      └─ run: brew upgrade pcr
    
  • Install-method-aware suggestion. Inspects current_exe() for
    /Cellar/, /opt/homebrew/, or /node_modules/ and prints the
    right upgrade command (brew upgrade pcr, npm i -g pcr-dev@latest,
    or a generic install link).

  • Quiet by default for machines and scripts. Hard skips:

    • --json output (would corrupt JSON-RPC / piped consumers)
    • pcr hook and pcr mcp (internal stdio channels)
    • CI=* env (CI runners don't need the nag)
    • PCR_NO_UPDATE_CHECK=1 (explicit opt-out)
  • Forward-compatible cache schema. Older cache payloads without
    the last_notice_unix field decode cleanly (covered by a regression
    test) so a CLI upgrade never silently disables the notice forever.

Also adds CHANGELOG.md — none previously existed in this repo —
seeded with an [Unreleased] section that catalogues this feature
plus the in-flight fixes from PRs #85 (vscode dual-watch dedup) and
#86 (watcher correctness + perf) so they have a single grep-able
home when those PRs merge into the next release.

README gets a short "Update notifier" subsection under "CI / agent
use" documenting PCR_NO_UPDATE_CHECK and the quiet-subcommand list.

Why now

Triggered by user feedback: "if you do pcr anything and there's a
new update available it should tell me". pcr ships through three
distribution channels (npm, Homebrew, raw releases), so users on
older versions never get notified when a fix lands — unless they
manually npm outdated -g or brew upgrade. This closes that loop.

The check goes through the npm registry (not GitHub Releases) because
the npm dist-tag is the authoritative "latest" pointer — Homebrew's
formula auto-updates from the npm release after the GitHub Release
workflow finishes, and direct-download users are presumed to be the
ones manually managing versions.

Branching

Off main rather than stacked on fix/vscode-dual-watch-dedup /
fix/watcher-shutdown-perf-correctness. No file overlap with either,
and the feature is useful immediately at v0.2.9 without waiting for
those PRs to merge.

Test plan

  • cargo fmt --all --check clean
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo test --workspace134 passed across all binaries, 0 failures (was 128 on this main baseline; +6 new tests in update_check::tests):
    • semver_compare_basic — straight numeric comparisons
    • semver_compare_handles_prerelease_suffix — beta tags don't trigger demotion prompts
    • semver_compare_rejects_malformed — garbage versions never falsely "newer"
    • cache_roundtrip_through_serde — JSON round-trip integrity
    • cache_decodes_partial_legacy_payload — forward-compat with older caches
    • quiet_subcommands_skiphook + mcp correctly suppressed
  • cargo build -p pcr-cli --release succeeds, pcr --version and PCR_NO_UPDATE_CHECK=1 pcr status smoke-tested locally.

Future polish

  • The 60 s startup latency for pcr start means the notice (printed at end) is only seen when the user Ctrl-Cs. Could move the print earlier for start specifically, but kept end-of-command for v1 simplicity.
  • Could light up the notice format with ANSI dim/bold if stderr_is_terminal() and !NO_COLOR; deliberately kept plain for v1 to avoid fighting colour-discipline elsewhere.
  • Migration path: when PR watcher correctness + perf: graceful shutdown, mtime-cached scans, single-row UPDATEs, pcr_dir fail-loud #86 (pcr_dir() -> Result<PathBuf>) merges, swap cache_path() to use ? and drop the marker comment — noted inline.

Made with Cursor

KaluJo and others added 2 commits May 18, 2026 16:49
Adds a best-effort background update check that prints a soft
"X is available — run: brew upgrade pcr" notice at the end of every
interactive `pcr` command when a newer `pcr-dev` ships on npm.

Modeled on the `update-notifier` npm package and `cargo`'s own
behaviour:

- Background thread fires off a 3-second-timeout GET against
  https://registry.npmjs.org/pcr-dev/latest at the *start* of the
  command, runs concurrently with the command itself, and writes the
  result to `~/.pcr-dev/update-check.json` regardless of whether the
  foreground command has exited. The thread is intentionally not
  joined — network failures, captive portals, slow DNS never delay
  the user's primary signal.
- At the *end* of the command, the cached file is read and a one-line
  notice is printed to stderr if (a) the cached version is greater
  than `CARGO_PKG_VERSION` under naive `major.minor.patch` semver,
  and (b) we haven't shown the notice in the last hour (so back-to-
  back `pcr log; pcr show` doesn't double-print).
- Suggested upgrade command is install-method-aware: inspects
  `current_exe()` for `/Cellar/`, `/opt/homebrew/`, or `/node_modules/`
  and prints `brew upgrade pcr`, `npm i -g pcr-dev@latest`, or a
  generic `https://pcr.dev/install` link respectively.
- Hard skips: `--json` output, the hidden `hook` + `mcp` subcommands
  (they're stdio JSON-RPC / Stop-hook channels), `CI=*` env, and
  `PCR_NO_UPDATE_CHECK=1` for users who want to opt out.

6 new unit tests cover semver comparison (including prerelease
suffixes), forward-compatible cache deserialisation (older payloads
without `last_notice_unix` decode cleanly), and the quiet-subcommand
skip list. Workspace tests: 134 passed (was 128 on this main baseline).

Also adds CHANGELOG.md (none previously existed in this repo) seeded
with an Unreleased section that catalogues this feature plus the
in-flight fixes from PRs #85 (vscode dual-watch dedup) and #86
(watcher correctness + perf) so they have a single grep-able home
when those PRs merge.

Independent of PR #85 / #86 — touches `lib.rs`, `entry.rs`, and a
new file. Safe to merge in any order.

Made with [Cursor](https://cursor.com)

Co-authored-by: Cursor <cursoragent@cursor.com>
PR #86 changed `config::pcr_dir()` to return `Result<PathBuf>` so
that auth + SQLite + watcher state never silently fall back to
`/tmp` when neither `$HOME` nor `%USERPROFILE%` resolves. The
update-notifier is best-effort, so it absorbs the Err the same way
it absorbs every other failure: collapse to `None` and silently
skip the cache operation. The foreground command never sees the
error.

Cache helper signatures:
  cache_path() -> PathBuf            // before
  cache_path() -> Option<PathBuf>    // after

load_cache + save_cache add a `let-else` guard at the top. Tests
unchanged — all 6 update_check::tests still pass, workspace 153
passing / 0 failing.

Co-authored-by: Cursor <cursoragent@cursor.com>
@KaluJo KaluJo force-pushed the feat/update-notifier branch from 0efdd0c to ce75c18 Compare May 18, 2026 14:51
@KaluJo KaluJo merged commit 308a4d7 into main May 18, 2026
2 checks passed
@KaluJo KaluJo deleted the feat/update-notifier branch May 18, 2026 14:53
@KaluJo KaluJo mentioned this pull request May 18, 2026
KaluJo added a commit that referenced this pull request May 18, 2026
Bumps the workspace to 0.3.0 — the 0.x minor (rather than 0.2.10
patch) is motivated by the breaking signature change to
`pcr_core::config::pcr_dir()` from #86 (returns `Result<PathBuf>`
instead of `PathBuf`). The CLI surface (`pcr <cmd>` flags / exit
codes / output format) is unchanged.

Version touchpoints:

  * `Cargo.toml` workspace.package.version → 0.3.0
  * `crates/pcr-napi/package.json` version + all 4 optionalDependencies
  * `crates/pcr-napi/npm/{darwin-arm64,darwin-x64,linux-x64-gnu,
    win32-x64-msvc}/package.json` versions
  * `README.md` TUI mock version stamp
  * `CHANGELOG.md` `[Unreleased]` promoted to `[0.3.0] — 2026-05-18`
    with full release notes catalogued by PR (#85, #86, #87, #88)
    and grouped Added / Changed / Fixed / Tests.

Workspace verification:

  * `cargo fmt --all --check` clean
  * `cargo clippy --workspace --all-targets -- -D warnings` clean
  * `cargo test --workspace` — 153 passing, 0 failing (was 128 on
    the v0.2.9 baseline; +25 across the 4 merged PRs)
  * `cargo build -p pcr-cli --release` → `pcr 0.3.0 (rust)`

After this lands on `main`, the release commit is tagged `v0.3.0`
locally and pushed; that triggers the release workflow which
publishes npm + builds binaries + dispatches the homebrew formula
update.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant