Skip to content

feat: Read .env file to load CLICKHOUSE_ environment variables#223

Merged
sdairs merged 9 commits into
mainfrom
raymond-read-envfile
Jun 3, 2026
Merged

feat: Read .env file to load CLICKHOUSE_ environment variables#223
sdairs merged 9 commits into
mainfrom
raymond-read-envfile

Conversation

@ohlookadollar
Copy link
Copy Markdown
Contributor

@ohlookadollar ohlookadollar commented May 27, 2026

Summary

Motivation: With agentic signups involving a third party, a seamless handoff improves the experiences of managing the clickhouse cluster. One pattern we have seen is the the third party cli signs up for clickhouse and storages the access creds in local .env.

  • Add .env support: clickhousectl reads CLICKHOUSE_CLOUD_API_KEY and CLICKHOUSE_CLOUD_API_SECRET from a .env file in the current directory as a fallback when they aren't exported in the shell. Treated identically to real env vars at the resolver, so basic-auth requests are byte-for-byte the same.
  • Precedence preserved: shell exports always win per-key; CLI flags > credentials.json > env (incl. .env) > OAuth tokens. Only CLICKHOUSE_-prefixed entries are read.
  • CWD-only — no walk-up — to avoid permission errors crossing mount points and accidental cross-project credential pickup from an ancestor directory.
  • cloud auth status and the --debug describe() line label the .env path only when both credentials come exclusively from the file. Mixed shell+.env provenance falls back to a plain "Active" so the row never implies the file was the sole source.
  • Resolver and provenance helpers take an injected env-lookup closure; production wraps std::env::var, tests pass a HashMap lookup so precedence is exercised without mutating the real process environment.

Test plan

  • cargo build -p clickhousectl — clean
  • cargo clippy -p clickhousectl --tests — clean
  • cargo test -p clickhousectl — 286 unit + 39 integration tests pass, including:
    • dotenv::tests::* — walk-up, closest-wins, filtering non-CLICKHOUSE_ keys, malformed-file silence, comments/quotes
    • cloud::client::tests::dotenv_only_resolves_to_env_source / real_env_overrides_dotenv / mixed_real_and_dotenv
    • cli_request_shape_test::dotenv_creds_produce_basic_auth_request — end-to-end proof that .env produces the same Authorization: Basic … header as shell env
    • cli_request_shape_test::shell_env_overrides_dotenv_creds_in_request — shell env wins on the wire
  • Manual smoke: .env in tempdir + cloud --debug auth status with shell vars unset — debug line and status table both show (loaded from /…/.env), "Env vars" row marked Active
  • Manual: confirm a real cloud org list against staging using a .env file (no shell export) returns results
  • Manual: confirm shell export CLICKHOUSE_CLOUD_API_KEY=… overrides a different value in .env for a real API call

Note

Medium Risk
Changes authentication credential resolution and precedence; mistakes could pick up wrong keys from a local .env, though scope is limited to cwd-only CLICKHOUSE_ keys and shell env still overrides.

Overview
Adds project-local .env support for ClickHouse Cloud API credentials: CLICKHOUSE_CLOUD_API_KEY and CLICKHOUSE_CLOUD_API_SECRET can live in cwd/.env (only CLICKHOUSE_-prefixed keys) and are merged into the existing env tier without mutating the process environment.

Resolver behavior: Shell exports win per key over .env; overall order stays CLI flags → .clickhouse/credentials.json → env (shell + .env fallback) → OAuth. Empty values are treated as absent. A new dotenv module loads via dotenvy, caches in OnceLock, and main calls dotenv::init() at startup.

UX / debugging: cloud auth status and --debug env descriptions use shared env_cred_presence / dotenv_env_provenance so they match what actually authenticates; the .env path is shown only when both creds come solely from the file.

Tests: Injected env/credentials/token lookups for unit tests; integration tests assert .env produces the same Basic auth header as shell env and that shell overrides file.

Reviewed by Cursor Bugbot for commit 9632709. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds project-local .env discovery to clickhousectl so CLICKHOUSE_-prefixed credentials can be sourced from the nearest ancestor .env file, with per-key precedence preserved (real exported env vars win).

Changes:

  • Introduces a new dotenv module that walks up from cwd, parses the closest .env, and snapshots only CLICKHOUSE_ keys without mutating std::env.
  • Extends cloud credential resolution to fall back to the .env snapshot when shell env vars are unset, and surfaces provenance in debug/status output.
  • Adds unit + integration tests to validate walk-up behavior and request auth header equivalence.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Documents .env support, discovery rules, and precedence behavior.
crates/clickhousectl/tests/cli_request_shape_test.rs Adds end-to-end tests proving .env creds produce identical Basic auth requests and that shell env overrides .env.
crates/clickhousectl/src/main.rs Initializes dotenv snapshot early and updates cloud auth status env-row reporting to include .env provenance.
crates/clickhousectl/src/dotenv.rs New module that discovers/parses .env into an in-memory OnceLock snapshot, filtering to CLICKHOUSE_ keys.
crates/clickhousectl/src/cloud/client.rs Updates auth resolver to consult .env snapshot as env-tier fallback and adds provenance helper used in debug descriptions.
crates/clickhousectl/src/cloud/cli.rs Updates CLI help text to mention .env-backed env var behavior.
crates/clickhousectl/Cargo.toml Adds dotenvy dependency.
Cargo.lock Locks dotenvy and updates transitive dependencies (including windows-sys).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/clickhousectl/src/main.rs Outdated
Comment thread crates/clickhousectl/src/cloud/client.rs Outdated
@ohlookadollar ohlookadollar temporarily deployed to cloud-integration May 27, 2026 14:19 — with GitHub Actions Inactive
when BOTH key and secret come exclusively from the file. Mixed shell+
.env provenance now renders plain "Active".
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Comment thread crates/clickhousectl/tests/cli_request_shape_test.rs Outdated
@ohlookadollar ohlookadollar temporarily deployed to cloud-integration May 27, 2026 14:47 — with GitHub Actions Inactive
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@ohlookadollar ohlookadollar temporarily deployed to cloud-integration May 27, 2026 18:20 — with GitHub Actions Inactive
@ohlookadollar ohlookadollar marked this pull request as ready for review May 27, 2026 19:06
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 571537b. Configure here.

sdairs and others added 5 commits June 2, 2026 19:32
An exported-but-empty CLICKHOUSE_CLOUD_API_KEY= (or a bare KEY= line in
.env) previously counted as a real credential: it shadowed a populated
.env value and resolved to empty Basic-auth creds (→ 401). Collapse empty
to absent at a single chokepoint (non_empty), routed through the shared
env_or_dotenv merge so the resolver, the .env provenance helper, and the
cloud auth status table can never disagree about what's present.

Also fix a stale comment claiming .env is read from ancestor directories;
the loader is cwd-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thread the credentials-file and OAuth-token loaders through
resolve_auth_with_sources as injection points (mirroring env_lookup),
so the env/dotenv precedence tests no longer short-circuit on a saved
.clickhouse/credentials.json under the test cwd. Production behaviour is
unchanged — the thin resolve_auth wrapper passes the real loaders.

Add credentials_file_overrides_env, which populates both the file and
env tiers and asserts the file tier wins — now testable thanks to the
injectable loader.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sdairs sdairs temporarily deployed to cloud-integration June 2, 2026 19:18 — with GitHub Actions Inactive
@sdairs sdairs merged commit d97f223 into main Jun 3, 2026
14 checks passed
@sdairs sdairs deleted the raymond-read-envfile branch June 3, 2026 07:30
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.

4 participants