Skip to content

Bare local server start bootstraps latest when nothing is set up#274

Open
sdairs wants to merge 2 commits into
mainfrom
issue-264-bare-start-installs-latest
Open

Bare local server start bootstraps latest when nothing is set up#274
sdairs wants to merge 2 commits into
mainfrom
issue-264-bare-start-installs-latest

Conversation

@sdairs

@sdairs sdairs commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Closes #264.

Summary

Running clickhousectl local server start with no installed version, no default set, and no --version flag now bootstraps automatically: it installs latest and starts with it, instead of failing with NoDefaultVersion and exit code 1.

  • If a default exists → use it (unchanged).
  • If no default is set (NoDefaultVersion) → resolve latest, install if missing, and start. We deliberately do not set it as the default, so unpinned users keep tracking latest on later starts. A note is printed to stderr so the auto-download isn't silent.
  • A VersionNotFound default (file points at a removed binary) stays an error — only the no default at all case bootstraps.

Changes

  • start_server (src/local/mod.rs) — the bare-start branch matches on get_default_version() and handles NoDefaultVersion by bootstrapping latest. Message: No version specified and no default set; installing latest....
  • ensure_installed (src/version_manager/install.rs) — bug fix required for the feature. For latest/master builds the exact version is only known after download, so install_resolved returned VersionAlreadyInstalled, breaking ensure_installed's "return existing, don't error" contract. Because bare start never sets a default, every repeat start hits this path and would fail. Now maps VersionAlreadyInstalledOk(version). This also fixes the pre-existing server start --version latest-on-repeat failure.
  • Docs / help text — README server section and server start help document the bootstrap; the "typical workflow" help text drops the three-step install stable && use stable && start dance in favour of a bare server start; stable-as-tag examples switched to latest (the install keyword catalog keeps stable documented).

Skip re-downloading master when unchanged (etag change detection)

Because bare start never sets a default, every repeat re-entered the latest path and re-downloaded the full ~153MB master binary (then re-ran clickhouse --version), even when master hadn't moved. This PR now short-circuits that.

Master exposes no readable version (the --version string is shared across many commits, sibling metadata files 403), but builds.clickhouse.com returns a stable, content-keyed etag on a HEAD request (~50ms, no body). We use it as a change-detection key:

  • New src/version_manager/master.rs — a per-platform sidecar (~/.clickhouse/versions/.master-builds.json) recording each installed master build's etag + version; a best-effort head_info() HEAD helper; a pure should_reuse/reuse_if_unchanged decision; and a record() writer.
  • install_resolved — for the master source, HEAD-and-compare before downloading. Etag match + binary present → reuse the installed build, skipping the download and version detection. Otherwise download and re-record. Handles "etag recorded but binary missing" by falling through to download. Applies to both install latest and server start --version latest / bare start (shared path).

This also fixes a latent bug: because a master build's detected version string is shared across commits, the old code would re-download a moved master, detect the same version, hit VersionAlreadyInstalled, and discard the fresh binary — paying the full download cost yet keeping the stale build. Reuse/overwrite now key on the etag, so a changed master correctly overwrites in place.

Verification

Manual end-to-end in an isolated HOME (no installs, no default):

  • Bare server start → message prints, latest downloads, server starts, exit 0, no default file created. ✅
  • Second bare server start → reuses the installed build, starts, exit 0 (previously errored). ✅
  • Default pointing at a removed binary → Error: Version <x> not found, exit 1. ✅

Etag change detection, against the live CDN with the built binary:

  • First install latest → downloads master, writes the sidecar with the build's etag. ~12.4s. ✅

  • Second install latestlatest is up to date (master build unchanged); using <v>, single HEAD, no download. ~0.14s. ✅

  • 7 new unit tests cover the reuse decision (etag match/mismatch, missing binary, no record, failed HEAD) and sidecar round-trip.

  • cargo build, cargo clippy (clean), cargo test (all pass). ✅

🤖 Generated with Claude Code

Closes #264.

Running `clickhousectl local server start` with no installed version, no
default set, and no `--version` flag now installs `latest` and starts with
it, instead of erroring with `NoDefaultVersion`.

- `start_server` (`local/mod.rs`): the bare-start branch matches on
  `get_default_version()`. `NoDefaultVersion` -> resolve `latest`,
  `ensure_installed_local_first`, and start, without setting a default (so
  unpinned users keep tracking latest on later starts). A `VersionNotFound`
  default (file points at a removed binary) stays an error.

- `ensure_installed` (`version_manager/install.rs`): for `latest`/master
  builds the exact version is only known after download, so `install_resolved`
  was returning `VersionAlreadyInstalled` and breaking the "ensure" contract.
  Since bare start never sets a default, every repeat start hit this path and
  failed. Map `VersionAlreadyInstalled` -> `Ok(version)`. Also fixes the
  pre-existing `server start --version latest`-on-repeat failure.

- Docs/help: README server section + `server start` help document the
  bootstrap; the "typical workflow" help drops the three-step
  install/use/start dance for a bare `server start`; `stable`-as-tag examples
  switched to `latest`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bare `server start` and `install/server start --version latest` track the
rolling master build, so every repeat re-downloaded the full ~153MB master
binary and re-ran `clickhouse --version`, even when master hadn't moved.

Master exposes no readable version (the `--version` string is shared across
many commits, sibling metadata files 403), but `builds.clickhouse.com` returns
a stable, content-keyed `etag` on a HEAD request (~50ms, no body). Use it as a
change-detection key:

- New `version_manager/master.rs`: a per-platform sidecar
  (`~/.clickhouse/versions/.master-builds.json`) recording each installed
  master build's `etag` + version; a best-effort `head_info()` HEAD helper; a
  pure `should_reuse`/`reuse_if_unchanged` decision; and a `record()` writer.
- `install_resolved`: for the master source, HEAD-and-compare before
  downloading. Etag match + binary present -> reuse the installed build, skip
  the download and version detection. Otherwise download and re-record. Handles
  "etag recorded but binary missing" by falling through to download.

This also fixes a latent bug: because a master build's detected version string
is shared across commits, the old code would re-download a moved master, detect
the same version, hit `VersionAlreadyInstalled`, and discard the fresh binary —
paying the full download cost yet keeping the stale build. Reuse/overwrite now
key on the etag, so a changed master correctly overwrites in place.

Measured: repeat `install latest` drops from ~12.4s (153MB download) to ~0.14s
(single HEAD).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sdairs sdairs marked this pull request as ready for review June 26, 2026 12:06
@sdairs sdairs requested a review from iskakaushik as a code owner June 26, 2026 12:06

@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 high effort and found 2 potential issues.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit f9d1999. Configure here.

// otherwise it pulls the newer master build.
let spec = version_manager::parse_version_spec("latest")?;
let platform = version_manager::platform::Platform::detect()?;
eprintln!("No version specified and no default set; installing latest...");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bootstrap message on every start

Low Severity

When server start runs without a specified version and no default is configured, it always prints a message indicating it's "installing latest." This message is misleading because it appears even when latest is already installed and up-to-date, and no actual download or new installation occurs.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f9d1999. Configure here.

// (or there was no record), so overwrite the stale binary in place.
let version_dir = paths::version_dir(&exact_version)?;
if version_dir.exists() && !force {
if version_dir.exists() && !force && !is_master {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Master update deletes in-use binary

High Severity

When the remote master etag changes, install_resolved removes the existing versions/&lt;version&gt;/ tree before installing the new build, without checking whether a local server is still running that version. local remove refuses the same operation, but unpinned bare server start can trigger this path on every master move.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f9d1999. Configure here.

// (or there was no record), so overwrite the stale binary in place.
let version_dir = paths::version_dir(&exact_version)?;
if version_dir.exists() && !force {
if version_dir.exists() && !force && !is_master {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium version_manager/install.rs:145

For master/latest installs, the post-download VersionAlreadyInstalled check is skipped (!is_master at line 145), so when the downloaded master binary reports the same --version string as an already-installed stable/release build, lines 151–155 remove_dir_all that existing versions/<exact_version>/ directory and replace it with the master binary. This silently overwrites a user's explicitly installed stable version with a master build instead of returning the existing install. The collision is real because master builds report the same numeric version string as released builds that share the commit. Consider distinguishing master installs from stable installs (e.g., a marker/sidecar in the version dir) so a master download only overwrites a prior master install, not a stable one — or fall back to VersionAlreadyInstalled when the existing dir has no master marker.

Also found in 1 other location(s)

crates/clickhousectl/src/version_manager/master.rs:139

reuse_if_unchanged only checks that versions/&lt;record.version&gt;/clickhouse still exists before reusing it. If a user later overwrites that same version directory with a different build via clickhousectl local install --force (for example, replacing a recorded master build with a stable/package build that has the same numeric version), the stale sidecar entry still matches the old master etag and this function will silently reuse the wrong binary on the next latest install/start instead of downloading the current master build.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @crates/clickhousectl/src/version_manager/install.rs around line 145:

For master/`latest` installs, the post-download `VersionAlreadyInstalled` check is skipped (`!is_master` at line 145), so when the downloaded master binary reports the same `--version` string as an already-installed stable/release build, lines 151–155 `remove_dir_all` that existing `versions/<exact_version>/` directory and replace it with the master binary. This silently overwrites a user's explicitly installed stable version with a master build instead of returning the existing install. The collision is real because master builds report the same numeric version string as released builds that share the commit. Consider distinguishing master installs from stable installs (e.g., a marker/sidecar in the version dir) so a master download only overwrites a prior master install, not a stable one — or fall back to `VersionAlreadyInstalled` when the existing dir has no master marker.

Also found in 1 other location(s):
- crates/clickhousectl/src/version_manager/master.rs:139 -- `reuse_if_unchanged` only checks that `versions/<record.version>/clickhouse` still exists before reusing it. If a user later overwrites that same version directory with a different build via `clickhousectl local install --force` (for example, replacing a recorded master build with a stable/package build that has the same numeric version), the stale sidecar entry still matches the old master `etag` and this function will silently reuse the wrong binary on the next `latest` install/start instead of downloading the current master build.

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.

[enhancement]: bare local server start should auto-install latest when nothing is installed

1 participant