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-goenkashreyas-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🙈​skipTime
🟨​aws linux7152619268:02
🟨​aws windows721526192417:11
💚​aws-ucws linux7153578408:20
💚​aws-ucws windows71535983814:49
💚​azure linux1172649246:26
💚​azure windows11726692211:41
💚​azure-ucws linux1173628368:21
💚​azure-ucws windows11736483415:00
💚​gcp linux1172609277:41
💚​gcp windows11726292511:04
24 interesting tests: 15 SKIP, 7 KNOWN, 2 flaky
Test Nameaws linuxaws windowsaws-ucws linuxaws-ucws windowsazure linuxazure windowsazure-ucws linuxazure-ucws windowsgcp linuxgcp 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):
durationenvtestname
6:05azure-ucws windowsTestAccept
6:04azure windowsTestAccept
6:03gcp windowsTestAccept
6:00aws-ucws windowsTestAccept
4:34gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:24gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:15gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:54azure-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:49gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:27aws-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:23azure-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:15aws-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:11azure-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:10azure linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:02gcp linuxTestAccept
3:02aws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:58azure windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:52azure linuxTestAccept
2:52aws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:50aws-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:50azure-ucws linuxTestAccept
2:49aws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:49azure-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:45aws-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:45aws-ucws linuxTestAccept
2:40azure windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:32aws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:28azure linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:16azure-ucws windowsTestAccept/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
ContributorAuthor

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 freeto 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

@shreyas-goenka@eng-dev-ecosystem-bot