feat(cli): add appkit doctor command - #489
Open
IamGalymzhan wants to merge 18 commits into
Open
Conversation
Contributor
|
Contributor
📦 Bundle size reportCompared against
|
| dist | raw | gzip |
|---|---|---|
| JS (runtime) | 820 KB (+29 KB) | 286 KB (+9.9 KB) |
| Type declarations | 302 KB (+17 KB) | 103 KB (+5.9 KB) |
| Source maps | 1.6 MB (+65 KB) | 536 KB (+21 KB) |
| Other | 11 KB | 3.7 KB |
| Total | 2.7 MB (+110 KB) | 929 KB (+37 KB) |
Per-entry composition (own code — deps external (as shipped))
| Entry | Initial (gz) | Lazy (gz) | Total (gz) | node_modules (min) | Own code (min) |
|---|---|---|---|---|---|
. |
86 KB (+10 B) | 2.5 KB | 89 KB (+10 B) | external | 281 KB (+34 B) |
./beta |
44 KB (+4.6 KB) | 429 B (+198 B) | 45 KB (+4.8 KB) | external | 129 KB (+9.5 KB) |
./type-generator |
19 KB | 0 B | 19 KB | external | 54 KB |
Chunks:
| Entry | Chunk | Load | Size (gz) |
|---|---|---|---|
. |
index.js |
initial | 82 KB |
. |
utils.js |
initial | 4.0 KB |
. |
remote-tunnel-manager.js |
lazy | 2.5 KB |
./beta |
beta.js |
initial | 29 KB |
./beta |
stream-manager.js |
initial | 5.8 KB |
./beta |
wide-event-emitter.js |
initial | 3.2 KB |
./beta |
databricks.js |
initial | 3.0 KB |
./beta |
configuration.js |
initial | 2.1 KB |
./beta |
service-context.js |
initial | 1.3 KB |
./beta |
client-options.js |
initial | 220 B |
./beta |
supervisor-api.js |
lazy | 184 B |
./beta |
databricks.js |
lazy | 132 B |
./beta |
index.js |
lazy | 113 B |
./type-generator |
index.js |
initial | 19 KB |
@databricks/appkit-ui
npm tarball (packed): 305 KB (+101 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).
| dist | raw | gzip |
|---|---|---|
| JS (runtime) | 360 KB (+390 B) | 119 KB (+60 B) |
| Type declarations | 205 KB | 74 KB |
| Source maps | 686 KB (+390 B) | 224 KB (+66 B) |
| CSS | 16 KB | 3.3 KB |
| Total | 1.2 MB (+780 B) | 422 KB (+126 B) |
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
| Entry | Initial (gz) | Lazy (gz) | Total (gz) | node_modules (min) | Own code (min) |
|---|---|---|---|---|---|
./js |
4.3 KB | 49 KB | 54 KB | 208 KB | 12 KB |
./js/beta |
20 B | 0 B | 20 B | 0 B | 0 B |
./react |
429 KB (+55 B) | 49 KB | 478 KB (+55 B) | 1.3 MB | 168 KB (+390 B) |
./react/beta |
20 B | 0 B | 20 B | 0 B | 0 B |
Chunks:
| Entry | Chunk | Load | Size (gz) |
|---|---|---|---|
./js |
index.js |
initial | 4.2 KB |
./js |
chunk |
initial | 120 B |
./js |
apache-arrow |
lazy | 49 KB |
./js/beta |
beta.js |
initial | 20 B |
./react |
index.js |
initial | 427 KB |
./react |
tslib |
initial | 2.1 KB |
./react |
apache-arrow |
lazy | 49 KB |
./react/beta |
beta.js |
initial | 20 B |
Diagnose whether an AppKit app's declared Databricks resources are actually usable, beyond the startup env-var check. Registered as `appkit doctor`. Three layers per resource: - auth: validate DATABRICKS_HOST, then currentUser.me() (once, app-wide) - config: offline env-var presence check - existence: live per-type probe — control-plane .get() for warehouse, serving, genie, job, volume, vector index, uc_function; a real SELECT 1 connection for Lakebase/postgres. Errors are classified (NOT_FOUND, INVALID_VALUE, ACCESS_DENIED) with clean one-line messages. Actionable hints translate opaque failures into the fix: expired/missing credentials to the right `databricks auth login`, a serving endpoint keyed by id to its name, and a Lakebase auth failure to the PGUSER/identity mismatch. Reaches the Databricks SDK / @databricks/appkit only through a runtime import in databricks-client.ts, keeping the SDK-free shared package free of the dependency and degrading gracefully when it is absent. Output is a friendly list (errors first; plugin/type + reason shown only on rows needing attention) or --json; exit code is non-zero on any error so it can gate CI. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
IamGalymzhan
force-pushed
the
feat/doctor-command
branch
from
July 24, 2026 11:24
8907117 to
0744f9a
Compare
Contributor
🤖 AppKit PR bot🔬 Run evalsStart an eval for this PR from the evals-monitor app: Go to Evals Monitor → 📦 Try this PR's app templateScaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh run download 30629284434 -R databricks/appkit -n appkit-template-0.48.0-pr.8dea943-feat-doctor-command-489 -D appkit-pr-489 \
&& unzip -o "appkit-pr-489/appkit-template-0.48.0-pr.8dea943-feat-doctor-command-489.zip" -d "appkit-pr-489" \
&& databricks apps init --template "appkit-pr-489"The template pins |
IamGalymzhan
force-pushed
the
feat/doctor-command
branch
from
July 27, 2026 09:40
fc846c6 to
1dde6f9
Compare
Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
IamGalymzhan
force-pushed
the
feat/doctor-command
branch
from
July 27, 2026 09:55
1dde6f9 to
7569c08
Compare
Run existence probes concurrently via Promise.all (independent network reads; input order preserved), cache DATABRICKS_HOST in a local, and drop a redundant `unknown | undefined` parameter type. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
…bility - Add --env-file flag to load an explicit env file (e.g. .env.local), overriding the auto-loaded .env so doctor checks the app's real env. - Add --detail flag to show the full raw SDK error; by default the report shows a short "authentication failed" headline plus an actionable hint. - Make auth hints action-first and reference the profile/host in use: classify unreachable-host (DNS/TLS) failures, recover the SDK-resolved profile so the login hint targets the right one, and surface it in the header. - Collapse auth-skipped resources into a single line, keep rows with real findings, and nudge toward --detail at the end. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Match the capitalized "Details:" label used in the rendered report. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
…port Extend `appkit doctor` to understand Databricks Asset Bundles and to render a cleaner, single-list report. - Classify each resource's provenance from databricks.yml + app.yaml (bundle.ts): external (probed live) vs bundle-managed (created on deploy, reported as such rather than a false NOT_FOUND). - Add an offline three-file wiring check (checks-wiring.ts) that spans app.yaml <-> databricks.yml <-> plugin env vars, catching gaps the official validators miss: VALUEFROM_UNBOUND, BUNDLE_REF_MISSING, and ENV_UNWIRED. A wiring error gates the exit code for pre-deploy CI. - Filter to plugins marked requiredByTemplate: true. - Redesign the report: one flat, severity-sorted checklist (no titled sub-sections), every row sharing one shape (glyph + label, indented detail, hint set off by blank lines). Reserve colour for actionable tokens (cyan ids/code spans, bold env vars) via picocolors, which auto-disables for non-TTY / NO_COLOR. - Shorten and standardise error messages: drop redundant snake_case types and SDK noise, quote the value you'd act on, and give missing env vars an actionable hint. - Add --env-file and --detail flags. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
CI forces colour on, so picocolors wraps the summary line in escape codes; the exact-match and startsWith assertions failed against `\x1b[32m3 ok\x1b[39m`. Strip ANSI in the capture helper so assertions are colour-agnostic in any environment. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
…L, drop --manifest Act on code-review findings for the doctor command: - Declare js-yaml + @types/js-yaml in shared's package.json. bundle.ts imported js-yaml but only resolved via pnpm hoisting / being bundled into appkit — a fragile packaging gap. Declare it like dotenv/picocolors. - Surface malformed bundle YAML instead of swallowing it. readYaml now throws on a present-but-unparseable databricks.yml/app.yaml rather than returning null (which set present:false, skipping all wiring checks and reporting a false all-clear on deploy-breaking config). Absent files still degrade gracefully. - Remove the --manifest flag. Multiple plugin manifests is a non-scenario (appkit.plugins.json is generated at a fixed path), and the flag opened two footguns: a mistyped path silently checked nothing, and it could desync from the fixed bundle-file paths. Dropping it closes both. - Document the known limitation that used non-GA plugins aren't checked (plugin sync strips requiredByTemplate for non-GA), with a fast-follow note, in both the filter comment and the README. - Comment the deliberate, unrestored process.env mutation in getServiceClient (safe in a one-shot CLI). Adds tests for the malformed-YAML throw. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
…tion
The prior code set process.env.DATABRICKS_CONFIG_PROFILE to apply an
explicit --profile — a hidden, unrestored global side effect. The SDK's
own Config exposes a first-class `profile` field, so pass it straight
into new WorkspaceClient({ profile }) instead. No env mutation, no leak
past the call, and it correctly overrides an ambient
DATABRICKS_CONFIG_PROFILE. Verified against the real SDK.
Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
Address a security finding: sensitive data could reach captured output. - Strip URL userinfo (user:pass@) from DATABRICKS_HOST at capture, so credentials embedded in the host never reach the report or --json. Validation still runs on the raw value. - Gate auth.raw in --json behind --detail, matching the human report. A bare --json omits the raw SDK error (CI commonly captures --json); --json --detail opts back in. Also folds in pending doctor cleanups the tree already carried and that these changes build on: an errorMessage() helper in a new utils.ts, and STATUS_SEVERITY / AUTH_UNAVAILABLE_CODE constants shared via types.ts to stop run.ts and report.ts drifting. Adds tests for host sanitization and the --json raw gate (92 total). Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
ENV_UNWIRED was always a warning, but exitCodeFor gates only on errors — so a required plugin's env var with no app.yaml entry (set locally via .env, unset in the deployed container) exited 0, the exact works-locally-breaks-on-deploy case the wiring check exists to catch. Gate on target.required: error for required (fails the exit code, blocks pre-deploy CI), warn for optional. Tests cover both. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
No probe, currentUser.me(), or Lakebase SELECT 1 had a deadline, so a reachable-but-unresponsive endpoint would hang doctor forever — and as a CI gate, the job would never return. Add withTimeout (Promise.race against a 10s wall-clock deadline) around each existence probe and the auth me() call. It can't cancel the underlying request, but it stops doctor *waiting* so the report returns: a timed-out probe becomes a PROBE_TIMEOUT error row, a timed-out auth becomes an auth failure. The timer is always cleared, so a fast result leaves nothing pending and the CLI exits promptly. Lakebase connection setup was already bounded (connectionTimeoutMillis: 10s); this covers a hung query on an established connection too. Tests: withTimeout unit tests (fake timers), a hung-probe run test, and a hung-auth test. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
summary counted only resources; printReport folded auth + wiring in at render time but never wrote it back, so printReportJson emitted the unfolded counts. A --json consumer reading summary.error === 0 missed auth failures and wiring errors (e.g. a required ENV_UNWIRED) — the exact CI-gate case. Build summary over everything with a status (resources + auth + wiring) once in runDoctor, and add a top-level exitCode as the unambiguous pass/fail signal. printReport now renders the summary verbatim (no re-folding, so human and --json agree) and exitCodeFor just reads the field. Moves the aggregation coverage to run.test.ts. Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
The probe's own .catch covered a probe rejection, but checkResource had no top-level guard: an unexpected throw (a config-layer error, a synchronous throw in probe dispatch, anything else) rejected Promise.all and lost the entire report to a stack trace. Wrap each resource in checkResourceSafe, mapping any throw to a PROBE_EXCEPTION error row so the run always resolves and the rest of the report survives. Test verifies a synchronous probe throw becomes a row rather than crashing (confirmed it fails without the guard). Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
The Lakebase pool's default error-only logger dumps the raw SDK ApiError
(stack + full response blob) to stderr on a failed token fetch, burying
doctor's clean one-line classification. Pass logger: { error: false } so
doctor owns the failure output. Reorder createLakebasePool so a
caller-supplied logger overrides the appkit default.
Also trims the Lakebase auth hint and prunes redundant tests.
Signed-off-by: Galymzhan <zhangazy2004@gmail.com>
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.
appkit doctor - cli command that checks connectivity to the Databricks resources
The command does three level of checks:
DATABRICKS_HOSTand authenticates to the workspaceappkit.plugings.json↔apps.yaml↔databricks.ymlOutput is a list of resources and problems if any with hints. Also there is options --json for machine readable output. and --detail for more detailed overview with error itself.
Examples:
Failed auth
Genie Space is not provided, Job is not accessible or doesn't exists
Files are not added to the app.yaml
Everything is setup correctly
How it works:
appkit.plugins.json