Notify when a newer CLI version is available#5470
Open
simonfaltum wants to merge 23 commits into
Open
Conversation
The version command only printed the current version, with no way to tell whether a newer one was available. This adds an update check that fetches the latest release from GitHub, compares it to the running build, and prints the upgrade command for the detected install method (Homebrew, WinGet, Chocolatey, or the install script). Exposed as both `databricks version --check` and a `databricks version check` subcommand. Development builds skip the check, and `--output json` is supported for scripting. Co-authored-by: Isaac
Co-authored-by: Isaac
Builds on the `version --check` command (#5469): after a command finishes, the CLI shows a one-line notice when a newer release is available, including the upgrade command for the detected install method. The latest version is fetched in the background at most once per day (cached via libs/cache, namespaced by CLI version) and the notice itself is shown at most once per day. It is suppressed for development builds, non-interactive/CI runs, JSON output, the version/completion/help commands, on command error, and via DATABRICKS_CLI_DISABLE_UPDATE_CHECK. The background refresh never blocks the command. Co-authored-by: Isaac
Co-authored-by: Isaac
Contributor
Approval status: pending
|
Collaborator
|
Commit: 31f5610
22 interesting tests: 15 SKIP, 7 KNOWN
Top 28 slowest tests (at least 2 minutes):
|
Cap the release lookup at 2s and, when GitHub is unreachable, times out, or rate-limits, report it gently instead of failing the command: `version --check` now prints that it couldn't reach GitHub and links to the releases page to check manually, and still exits 0. Co-authored-by: Isaac
…ion-update-notice
If many CLIs start at once on a cold cache, each would otherwise fire its own GitHub request. Add a best-effort, machine-wide single-flight lock (an O_EXCL sentinel file) so only one process fetches at a time; the rest skip silently. Also skip the check entirely when the file cache is disabled. Combined with the 24h cache TTL, a fleet sends at most one request to GitHub per machine per day, and any failure stays silent. Co-authored-by: Isaac
5 tasks
When a command finishes before the background fetch, the process exits and the goroutine is killed without running its deferred release, leaving the lock file behind. Reclaiming it after 30s (still well above the 3s fetch timeout, so a live fetch is never stolen) shortens the window where the next command skips the refresh. Adds a test for the stale-reclaim path. Co-authored-by: Isaac
Adopts the cleaner structure from the alternative PR #5482: the suppression policy is now a pure shouldNotify(notifyConditions) function (table-tested with no mocks), condition-gathering is split from the policy, and rendering (noticeMessage) is split from gating so both are unit-testable. Also suppresses on the Databricks Runtime and includes the release-notes URL in the message. Keeps the single-flight lock, opt-out env, libs/cache, and once-per-day display throttle from this PR. Co-authored-by: Isaac
…e' into simonfaltum/version-update-notice
…pdate-check # Conflicts: # NEXT_CHANGELOG.md
The comment already cited GitHub's recommendation to pin the API version but no X-GitHub-Api-Version header was actually sent. Co-authored-by: Isaac
…ion-update-notice
… behavior gatherConditions called dbr.RunsOnRuntime and cmdio.GetInteractiveMode unconditionally. Both panic on a context that skipped the usual command setup, which happens for help invocations (cobra resolves --help and bare invocations before PersistentPreRunE installs cmdio) and for tests that execute commands without root.Execute's dbr detection (this broke cmd/auth tests in CI). Add dbr.HasDetection and cmdio.HasIO probes and treat unknown as 'suppress' / 'not DBR'. Also: - Cache fetch failures (as an empty entry) so offline or airgapped machines do not retry GitHub on every command. - Move the single-flight lock from os.TempDir to the cache root (per-user) via the new cache.BaseDir, so another user's stale lock on a shared machine can never block this one. Co-authored-by: Isaac
Review feedback: version check / version --check / --version --check was too many permutations. Instead, `databricks version` now performs the check directly. The --version flag (cobra built-in) stays lightweight, and `version --output json` keeps the build-info shape and skips the network lookup so scripts are unaffected. Also drop the always-nil error return from versioncheck.Check; lookup failures are reported via Result.CheckFailed. Co-authored-by: Isaac
…' into simonfaltum/version-update-notice
The base PR folded the check into `version` itself, so the parent-walk case now uses a real subcommand (completion zsh). Co-authored-by: Isaac
Settled middle ground: `databricks version` matches `databricks --version` exactly (no network call, no output change, original help text restored, which also fixes the help acceptance test). The update check runs only via `version --check`; the `check` subcommand stays removed so there is a single way to invoke it. Co-authored-by: Isaac
…' into simonfaltum/version-update-notice
simonfaltum
added a commit
that referenced
this pull request
Jun 10, 2026
…5469) ## Why `databricks version` (and `databricks --version`) only prints the version you're on. There's no way to tell whether you're behind, and even once you know, the command to upgrade depends on how you installed the CLI (Homebrew, WinGet, Chocolatey, or the install script). A version check also shouldn't be fragile: if GitHub is slow or unreachable it must not hang or fail the command. ## Changes Before: `databricks version` prints the current version and nothing else. Now: `databricks version --check` fetches the latest release from GitHub, compares it with the running build, and prints the upgrade command for your install method: ``` Databricks CLI v0.240.0 A new version is available: 0.245.0 To upgrade, run: brew upgrade databricks ``` Per review feedback this is the only way to run the check (the `version check` subcommand from an earlier revision is gone). Bare `databricks version` and `databricks --version` stay lightweight and identical to today: no network call, no output change. Details: - New `libs/versioncheck` package fetches `api.github.com/repos/databricks/cli/releases/latest`, semver-compares, and infers the install method from the executable path (resolving symlinks so a Homebrew shim classifies as Homebrew, not the install script). - The lookup is capped at 2s. If GitHub is unreachable, times out, or rate-limits, the check fails gently: it prints that it couldn't reach GitHub and links to the releases page, and still exits 0 instead of erroring. - Development and snapshot builds short-circuit without a network call. - `databricks version --check --output json` returns the structured result (including `check_failed`) for scripting. First PR in a two-PR stack; the follow-up (#5470) adds a passive once-per-day notice. ## Test plan - [x] Unit tests: install-method detection across OSes, semver comparison, and the full check against a mock server (update available, up to date, dev build, server error fails gently, unreachable fails gently). - [x] White-box render tests asserting exact output for all states, including the "couldn't reach GitHub" message. - [x] Acceptance test (`acceptance/cmd/version`) covering lightweight `version`, `version --check`, and `version --check --output json`. - [x] Full local acceptance suite (`go test ./acceptance`), including the `help` output test. - [x] `./task fmt`, lint on changed packages, and `./task checks` clean. This pull request and its description were written by Isaac.
PR #5469 (version --check) merged to main, so the shared versioncheck plumbing now comes from main; this branch keeps only the passive-notice delta (latestRelease struct with the release URL). NEXT_CHANGELOG keeps only this PR's entry since the v1.3.0 release moved the earlier entries into CHANGELOG.md. Co-authored-by: Isaac
The notice is already suppressed in tests by independent gates (dev-build binaries, pipe-backed stderr, CI env), but runs that download released binaries (-useversion) should not depend on those staying true. Setting the opt-out explicitly guarantees no test run can fetch from GitHub or print the notice into compared output. Co-authored-by: Isaac
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
PR #5469 added
databricks version --check, but most people won't run it. This adds a passive nudge so users on an old CLI find out without having to ask, while making sure a fleet of CLIs can't stampede GitHub and that any failure is silent.Changes
After a command finishes, the CLI prints a one-line advisory when a newer release is available:
Behavior:
libs/cache(the bundle file cache), namespaced by CLI version so it resets after an upgrade. The notice is shown at most once per day.O_EXCLsentinel lock (placed in the per-user cache root via the newcache.BaseDir, so another user's lock on a shared machine can't interfere), so when many CLIs start at once only one hits GitHub and the rest skip. A lock older than 30 seconds is treated as abandoned (e.g. the process exited mid-fetch, killing the goroutine before its deferred release ran) and reclaimed. Combined with the 24h cache, a fleet sends at most one request per machine per day. No new dependency: it's stdlibosand works on Linux, macOS, and Windows.version/completion/helpcommands, when the command errored, when the file cache is disabled (DATABRICKS_CACHE_ENABLED=false), and viaDATABRICKS_CLI_DISABLE_UPDATE_CHECK.PersistentPreRunEinstalls cmdio, and tests execute commands withoutroot.Execute's DBR detection. Condition gathering uses the newcmdio.HasIO/dbr.HasDetectionprobes and treats unknown as "suppress" / "not DBR" instead of panicking.Structure: the suppression policy is a pure
shouldNotify(notifyConditions)function with condition-gathering split out, and rendering (noticeMessage) is split from gating, so both are unit-testable without mocks. (This structure and the Databricks-Runtime suppression were adopted from the alternative exploration in #5482.)Second PR in the stack, on top of #5469 (merged down into this branch, not rebased).
Test plan
shouldNotifypolicy table (every suppression reason),gatherConditionsenv/flag wiring (including a bare context without cmdio/dbr detection), command exemption, the single-flight lock (acquire / locked-out / re-acquire / stale-reclaim), and failure caching (no refetch after a failed lookup).version --check(text and JSON) reports the update, the notice shows under a PTY and is throttled on the next run, never appears without a TTY, respects the opt-out, and leaves no lock files behind../task fmt,lint-q, andchecksclean.This pull request and its description were written by Isaac.