Skip to content

Recommend upgrading the CLI when a newer release is available#5482

Closed
shreyas-goenka wants to merge 4 commits into
mainfrom
outdated-cli-notice
Closed

Recommend upgrading the CLI when a newer release is available#5482
shreyas-goenka wants to merge 4 commits into
mainfrom
outdated-cli-notice

Conversation

@shreyas-goenka

@shreyas-goenka shreyas-goenka commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Why

Users on outdated CLI versions hit bugs that are already fixed and miss new features, with no signal that an upgrade exists. This adds a non-intrusive "a newer release is available" advisory, modeled on the GitHub CLI (gh) — same language, same distribution channel (GitHub releases).

The latency problem, and how this avoids it

The hard requirement is that the check must never add latency to a command. So:

  • The hot path does zero network I/O. Every run reads the latest known version from a local cache at ~/.databricks/cli-version-check.json.
  • Refresh is throttled to once per day and decoupled from the command. Only when the cache is older than 24h does a background goroutine fetch api.github.com/repos/databricks/cli/releases/latest. The command never blocks on it.
  • The notice is rendered from cache, not a live call — even the run that shows it pays no network cost.
  • The cache write is atomic (temp file + rename), so a short-lived or interrupted process can never leave a corrupt cache.
  • A failed refresh (e.g. no network) is silent — logged at debug only, never surfaced as an error.

What the user sees

Printed to stderr, after a successful command, in yellow, with an install-method-aware upgrade command:

A new release of the Databricks CLI is available: 0.200.0 → 1.2.1
https://github.com/databricks/cli/releases/tag/v1.2.1
To upgrade, run: brew upgrade databricks

The upgrade command adapts to how the CLI was installed (all per the install docs):

  • Homebrew (detected from the binary location under Cellar/): brew upgrade databricks
  • Windows: winget upgrade Databricks.DatabricksCLI
  • Otherwise: the install script curl -fsSL https://raw.githubusercontent.com/databricks/setup-cli/main/install.sh | sh

It is suppressed unless it's actionable and wanted: released builds only (dev/snapshot builds skip entirely — no cache, no network), interactive text output (TTY), not on Databricks Runtime, not in CI, not --output json.

Structure

  • libs/upgradecheck/ — cache read/write, GitHub fetch, version comparison (golang.org/x/mod/semver, already a dependency). Network base URL is context-overridable for tests.
  • cmd/root/upgrade_notice.go — the cobra glue: a pure shouldNotify policy + message/upgrade-command builders, wired into PersistentPreRunE (background refresh) and Execute (print on success).

Testing

Unit tests cover the cache lifecycle (fetch → cache → outdated) against an httptest server, the 24h staleness throttle, corrupt-cache self-healing, server-failure handling, the release-version gate, the message/upgrade-command builders, and every suppression condition.

Manually verified end-to-end with a release-tagged local build (-X ...build.buildVersion=0.200.0) run under a pseudo-terminal: the recommendation renders on stderr; the Homebrew path shows brew upgrade databricks; --output json, CI=1, and non-TTY all suppress it; and a live fetch against api.github.com returns the real latest release.

This pull request and its description were written by Isaac.

Print a short advisory on stderr when the installed CLI is older than the
latest GitHub release, modeled on `gh`'s update notifier.

The check never adds latency to a command: every run reads the latest known
version from a local cache (~/.databricks/cli-version-check.json). The GitHub
API is only contacted by a background goroutine, and only when the cache is
older than 24h; the notice is always rendered from the cache. The cache write
is atomic, so a short-lived process can't corrupt it.

The notice is suppressed unless it is actionable and wanted: only for released
builds, interactive text output (TTY), and not on Databricks Runtime or in CI.

Co-authored-by: Isaac
Co-authored-by: Isaac
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Commit: 0af75c1

Run: 27212402052

Env 🟨​KNOWN 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🟨​ aws linux 7 15 261 926 8:02
🟨​ aws windows 7 2 15 261 924 17:11
💚​ aws-ucws linux 7 15 357 840 8:20
💚​ aws-ucws windows 7 15 359 838 14:49
💚​ azure linux 1 17 264 924 6:26
💚​ azure windows 1 17 266 922 11:41
💚​ azure-ucws linux 1 17 362 836 8:21
💚​ azure-ucws windows 1 17 364 834 15:00
💚​ gcp linux 1 17 260 927 7:41
💚​ gcp windows 1 17 262 925 11:04
24 interesting tests: 15 SKIP, 7 KNOWN, 2 flaky
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
🟨​ TestAccept 🟨​K 🟨​K 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/permissions 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestAccept/bundle/resources/permissions/dashboards/create ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p 🙈​s 🙈​s
🔄​ TestAccept/bundle/resources/permissions/dashboards/create/DATABRICKS_BUNDLE_ENGINE=direct ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions 🟨​K 🟨​K 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions 🟨​K 🟨​K 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 🟨​K 🟨​K 💚​R 💚​R
🙈​ TestAccept/bundle/resources/postgres_branches/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/replace_existing 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/update_protected 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/without_branch_id 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_projects/update_display_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/synced_database_tables/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/grants/select 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
Top 29 slowest tests (at least 2 minutes):
duration env testname
6:05 azure-ucws windows TestAccept
6:04 azure windows TestAccept
6:03 gcp windows TestAccept
6:00 aws-ucws windows TestAccept
4:34 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:24 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:15 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:54 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:49 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:27 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:23 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:15 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:11 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:10 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:02 gcp linux TestAccept
3:02 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:58 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:52 azure linux TestAccept
2:52 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:50 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:50 azure-ucws linux TestAccept
2:49 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:49 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:45 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:45 aws-ucws linux TestAccept
2:40 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:32 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:28 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:16 azure-ucws windows TestAccept/bundle/resources/volumes/recreate/DATABRICKS_BUNDLE_ENGINE=terraform

- Append an OS/install-method aware upgrade command to the notice:
  `brew upgrade databricks` for Homebrew installs (detected from the
  binary location), `winget upgrade Databricks.DatabricksCLI` on Windows,
  otherwise the install script.
- Make the once-per-day cache TTL on the background refresh explicit.
- A failed refresh (e.g. no network) stays silent; it is not surfaced.
- Fix CI lint: use errors.Is(fs.ErrNotExist) / assert.ErrorIs in tests.

Co-authored-by: Isaac
@shreyas-goenka

Copy link
Copy Markdown
Contributor Author

Closing because Simon has the same PR up: #5470

simonfaltum added a commit that referenced this pull request Jun 9, 2026
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
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