Skip to content

feat(appkit): add appkit db check - #678

Draft
ditadi wants to merge 6 commits into
stack/database-dev-flow/07-apps-initfrom
stack/database-dev-flow/08-db-check
Draft

ditadi wants to merge 6 commits into
stack/database-dev-flow/07-apps-initfrom
stack/database-dev-flow/08-db-check

Conversation

@ditadi

@ditadi ditadi commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

🥞 Stacked PR (generated by Stack)

Use this link to review incremental changes.


Stacked on #675. The design is #677.

What did you change, and why?

A pull request can break the committed migration history in ways the deployed app finds only on start. appkit db check is an offline check of the committed migrations for CI. It reads only the checkout, so it runs without Databricks or Lakebase credentials, and it takes no history lock. It exits 1, with the fix for each finding, when:

  • config/database/schema.ts changed and no committed migration has the change
  • the journal lists a migration generated before the one ahead of it, which a branch that ran the newer one never runs
  • a snapshot does not follow the one before it, which happens when two migrations were generated from one snapshot on parallel git branches
  • an entry's idx is not its position in the journal, since drizzle-kit names snapshots by idx
  • a migration's .sql or snapshot is not committed, or a .sql is not in the journal
  • a snapshot file is not a drizzle-kit snapshot, because its id or prevId is not a string

The schema comparison is hasUnmigratedChanges in migrations.ts, which shares diffSchema with appkit db migrate. So check fails exactly when migrate would write a migration, and an ambiguous rename counts as a change without a prompt. The comparison runs only when the latest snapshot is a valid drizzle-kit snapshot. Before this revision, a null or {} snapshot skipped both the chain and the schema diff, so check could pass a schema.ts change without a migration.

Two pull requests that both add 0001_migration conflict in git first, and check catches a resolution that keeps both. The fix it names merges the base branch, resets config/database/migrations to the base branch's, then runs appkit db clean and appkit db migrate. The clean is there because the developer's branch already ran the old migration.

database.md lists db check among the commands and among those that take no history lock. Its CI section runs the check on every pull request and explains the conflict. The template README names it.

How do you know it works?

  • check.test.ts generates real migration history with drizzle-kit and a real schema.ts, with every Databricks and Lakebase call throwing. It covers a sound history, a schema.ts change without its migration, an ambiguous column rename, a merge that keeps two migrations from one snapshot, a when out of order, an idx that is not its position, uncommitted and unlisted files, and a null and a {} snapshot. The CLI's check.test.ts covers the output and the exit code.
  • The full vitest suite passed against a local PostgreSQL 14. A clean build, the size gate, the docs build and typecheck passed. The packed tarballs export the same root and ./beta names as main, and ./beta/database-tooling adds only runCheck.
  • The template smoke passed with pnpm on a fresh store and with npm.
  • This revision was not run against a real Lakebase workspace. appkit db check makes no Databricks or Lakebase call.

`appkit db check` checks the committed migrations against
config/database/schema.ts without Databricks or Lakebase access, so CI can
run it on every pull request. It fails when:
- schema.ts changed and no committed migration has the change;
- the journal lists a migration generated before the one ahead of it,
  which a branch that ran the newer one never runs, or an entry whose idx
  is not its position;
- a snapshot does not follow the one before it, which happens when two
  migrations were generated from one snapshot on parallel git branches;
- a migration's .sql or snapshot is not committed, or a .sql is not in
  the journal.

Each finding names its fix. Parallel migrations are fixed by taking the
base branch's migrations, then `appkit db clean` and `appkit db migrate`.

The schema comparison reuses the diff `appkit db migrate` runs, so check
fails exactly when migrate would write a migration, and an ambiguous
rename counts as a change instead of prompting. The plugin docs' CI
section runs `appkit db check` on every pull request, and the template
README names it.

Signed-off-by: ditadi <victordperd@gmail.com>
@ditadi
ditadi force-pushed the stack/database-dev-flow/08-db-check branch from 8ef366e to 0d6a48a Compare October 20, 2026 12:22
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 41109406024 -R databricks/appkit -n appkit-template-0.89.0-pr.3aaaedd-stack-database-dev-flow-08-db-check-678 -D appkit-pr-678 \
  && unzip -o "appkit-pr-678/appkit-template-0.89.0-pr.3aaaedd-stack-database-dev-flow-08-db-check-678.zip" -d "appkit-pr-678" \
  && databricks apps init --template "appkit-pr-678"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 1.4 MB (+24 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 1.4 MB (+26 KB) 524 KB (+8.5 KB)
Type declarations 483 KB (+4.8 KB) 177 KB (+1.8 KB)
Source maps 2.9 MB (+51 KB) 973 KB (+16 KB)
Other 11 KB 3.7 KB
Total 4.8 MB (+82 KB) 1.6 MB (+26 KB)
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 106 KB 2.5 KB 108 KB external 343 KB
./beta 108 KB (+174 B) 485 B 108 KB (+174 B) external 324 KB (+474 B)
./beta/database-tooling 50 KB (+4.9 KB) 0 B 50 KB (+4.9 KB) external 148 KB (+14 KB)
./testing 45 KB 32 KB 77 KB external 223 KB
./tsdown 520 B 0 B 520 B external 813 B
./type-generator 26 KB 0 B 26 KB external 74 KB

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 101 KB
. utils.js initial 4.6 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 88 KB
./beta modular.js initial 6.0 KB
./beta stream-manager.js initial 5.9 KB
./beta service-context.js initial 4.2 KB
./beta databricks.js initial 3.3 KB
./beta client.js initial 647 B
./beta index.js initial 20 B
./beta supervisor-api.js lazy 192 B
./beta databricks.js lazy 178 B
./beta index.js lazy 115 B
./beta/database-tooling index.js initial 50 KB
./testing manifest.js initial 32 KB
./testing index.js initial 10 KB
./testing wide-event-emitter.js initial 2.9 KB
./testing index.js lazy 28 KB
./testing remote-tunnel-manager.js lazy 2.5 KB
./testing utils.js lazy 1.8 KB
./tsdown index.js initial 520 B
./type-generator index.js initial 26 KB

@databricks/appkit-ui

npm tarball (packed): 397 KB (+4 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 429 KB 147 KB
Type declarations 267 KB 98 KB (+1 B)
Source maps 853 KB 286 KB
CSS 16 KB 3.2 KB
Total 1.5 MB 534 KB (+1 B)
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
./js 5.3 KB 49 KB 55 KB 208 KB 14 KB
./js/beta 2.1 KB 0 B 2.1 KB 0 B 5.0 KB
./react 433 KB 49 KB 481 KB 1.3 MB 177 KB
./react/beta 4.9 KB 0 B 4.9 KB 0 B 12 KB

Chunks:

Entry Chunk Load Size (gz)
./js index.js initial 5.2 KB
./js chunk initial 120 B
./js apache-arrow lazy 49 KB
./js/beta beta.js initial 2.1 KB
./react index.js initial 431 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 4.9 KB

Conflict in migrations.ts: generateMigrationFiles keeps PR678's diffSchema,
which hasUnmigratedChanges shares, and takes its folder from the history
lock as on the merged side. appkit db check stays read-only: it reads only
the checkout's files, takes no history lock and reports no deployed state.
Its tests generate fixtures under the history lock, give the parallel
checkout its own config/database/migrations, and mock the
@databricks/appkit/beta/database-tooling path.

Signed-off-by: ditadi <victordperd@gmail.com>
Signed-off-by: ditadi <victordperd@gmail.com>
Signed-off-by: ditadi <victordperd@gmail.com>
A snapshot file holding null, or an object without id and prevId, was taken
as no link, so check skipped the chain and the schema diff and could pass a
schema.ts change without a migration. Check now accepts a snapshot only with
string id and prevId, and otherwise reports it. --help and the docs also list
the idx check.

Signed-off-by: ditadi <victordperd@gmail.com>
ditadi added a commit that referenced this pull request Oct 30, 2026
The design doc promised behavior the corrected stack does not ship. It
now describes what the code does:

- The tooling export is @databricks/appkit/beta/database-tooling, beta
  and internal to the AppKit CLI.
- migrate, introspect, clean and init hold config/database/.appkit-db.lock.
  init holds one lock for its whole run, and init --schema none, seed,
  verify and clean --delete-branch take none. The doc says how to
  recover a stale lock file.
- init --schema none hands off nothing, and schema public is never
  handed off.
- Values in variable-overrides.json count for the deploy boundary, and
  an unresolved branch or database reference refuses every write.
- Introspect keeps .private() on declared columns, writes new columns
  private, and refuses a schema.ts that does not load or a foreign key
  it does not declare.
- verify --deployed compares the committed migrations with the source
  snapshot of the active deployment, and fails when it cannot read it.
- Migrations log one line per run. There is no span and no metric.
- #675 keeps the plugin and appkit db hidden. A reveal is a later,
  separate change.

The appkit db check section stays as it is until #678 settles.

Signed-off-by: ditadi <victordperd@gmail.com>

This branch has not been deployed

No deployments
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.

1 participant