Compare commits

...

1266 Commits

Author SHA1 Message Date
github-automation-metabase
a7ed9c32e3 🤖 backported "Better human validation of notification schemas " (#77925)
Better human validation of notification schemas  (#77922)

* fix: tighten schema

* 400 rather than 500

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-07-16 10:05:37 +00:00
Timothy S. Dean
233fd2a93f Backport "Better destination-db errors" (#77893)
Different endpoints dealing with destination databases handled checks differently, causing confusing error messages depending on code path. This consolidates and standardizes both the messages and the HTTP code.
2026-07-15 17:21:08 -06:00
github-automation-metabase
326778a550 🤖 backported "Update log4j-* versions" (#77847)
Co-authored-by: Timofey Kachalov <timofei@metabase.com>
2026-07-15 22:21:12 +04:00
Michael Eby
69b7e1c28f Revert "🤖 backported "docs: pdfs in subscriptions"" (#77840)
Revert "🤖 backported "docs: pdfs in subscriptions" (#77647)"

This reverts commit 22162d797e.
2026-07-15 11:27:47 -04:00
github-automation-metabase
e775b63183 🤖 backported "Search MB documentation link incorrectly has underline on hover" (#77826)
Search MB documentation link incorrectly has underline on hover (#77701)

Fix for underline

Co-authored-by: Benoit Vinay <ben@benoitvinay.com>
2026-07-15 15:23:29 +00:00
github-automation-metabase
1acc2afc39 🤖 backported "Treat a blank MB_CONFIG_FILE_PATH as unset" (#77834)
Treat a blank MB_CONFIG_FILE_PATH as unset (#77821)

JDK 25 changed the empty file path to resolve to the current directory
(File("").exists() is now true), so a long-standing MB_CONFIG_FILE_PATH=''
made the config-from-file loader try to parse the current directory as YAML
and fail at boot. Ignore blank values like metabase.config.core/config-str
does everywhere else, and don't count them as config-text-file being enabled
in anonymous stats.

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 15:09:03 +00:00
github-automation-metabase
7f8c41c3b2 🤖 backported "Don't GZIP sync-flush per object when serializing QP cache entries" (#77790)
Don't GZIP sync-flush per object when serializing QP cache entries (#77786)

* Don't GZIP sync-flush per object when serializing QP cache entries

Sync-flushing after every cached row meant one native Deflater call in a
JNI critical region per row; on JDK <= 21 (no G1 region pinning, JEP 423)
that stalls GC via the GCLocker under concurrent cache writes. Let the
deflater buffer normally instead, and have result-fn finish the stream
(draining the deflater and writing the GZIP trailer) before returning the
bytes. Old cache entries (unterminated gzip) still deserialize since the
reader already treats EOFException as end-of-stream.



* Don't flush the JSON download writer per row

Each per-row flush cascaded into a sync-flush of the gzipped HTTP response:
one native Deflater call in a JNI critical region per exported row (GCLocker
stalls on JDK <= 21) plus one TCP packet per row (#34795, fixed for the :api
writer but not :json downloads) and a worse compression ratio. Let the
BufferedWriter push rows through as it fills, like the CSV writer does;
finish! still flushes the tail.



---------

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 23:38:05 -04:00
Alexander Polyankin
5bae3e81b2 Revert "🤖 backported "Bound concurrent query-cache writes"" (#77782)
Revert "🤖 backported "Bound concurrent query-cache writes" (#77752)"

This reverts commit 2205e89bd6.
2026-07-14 23:36:57 -04:00
github-automation-metabase
2205e89bd6 🤖 backported "Bound concurrent query-cache writes" (#77752)
Bound concurrent query-cache writes (#77738)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-14 21:24:40 +00:00
github-automation-metabase
d574de9263 🤖 backported "Skip static-viz bundle size budget in dev builds" (#77743)
Skip static-viz bundle size budget in dev builds (#77741)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:27:37 -04:00
github-automation-metabase
83ebf8336b 🤖 backported "Bump Docker runtime images to Temurin 25 JRE" (#77737)
Bump Docker runtime images to Temurin 25 JRE (#77730)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-14 14:19:44 -04:00
github-automation-metabase
14c397aaf9 🤖 backported "Static viz imports optimization" (#77680)
Add backport resolution script

Co-authored-by: Metabase bot <metabase-bot@metabase.com>
2026-07-14 11:47:37 +00:00
github-automation-metabase
453b9bd455 🤖 backported "Resolve the default search engine from capability precedence" (#77632)
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-07-14 09:45:19 +00:00
github-automation-metabase
3bd28952ee 🤖 backported "[GIT-74954] Fix search reindex hang on metric reference cycles" (#77639)
Co-authored-by: Tyler Plude <tsplude@gmail.com>
Co-authored-by: mbql-backend-expert <agent@metabase.com>
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-07-14 11:13:14 +02:00
github-automation-metabase
95600cb26e 🤖 backported "utm query params for docs search" (#76985)
* Add backport resolution script

* Linting

* Remove backport file

---------

Co-authored-by: Metabase bot <metabase-bot@metabase.com>
Co-authored-by: losrebellos <ben@benoitvinay.com>
2026-07-14 10:43:19 +02:00
github-automation-metabase
22162d797e 🤖 backported "docs: pdfs in subscriptions" (#77647)
docs: pdfs in subscriptions (#77610)

* docs: pdfs in subscriptions

* Edit from Jeff

Co-authored-by: Michael Eby <38187276+michaeleby1@users.noreply.github.com>
2026-07-13 21:12:35 +00:00
github-automation-metabase
5c0c5bcd43 🤖 backported "Remove unused code" (#77620)
Remove unused code (#77613)

* Remove unused code

* Remove unused code

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-13 15:32:13 -04:00
github-automation-metabase
0fdf925ee5 🤖 backported "Record model views in the view log" (#77598)
Record model views in the view log (#77091)

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-07-13 10:03:02 +00:00
github-automation-metabase
66cf36fcd9 🤖 backported "Auto-fix modules config.edn" (#77553)
Auto-fix modules config.edn (#77541)

* Auto-fix modules config.edn

* Extract generic nrepl integration wrapper

* Remind claude to use this

* Demo the module config fixer with some tests

Co-authored-by: Ben Grabow <ben.grabow@metabase.com>
2026-07-10 22:24:48 +00:00
github-automation-metabase
9932950c1c 🤖 backported "Name the referring entity in remote sync not-found errors" (#77530)
Name the referring entity in remote sync not-found errors (#77430)

* Name the referring entity in remote sync not-found errors

When an import references content that doesn't exist on this instance, the
error named only the missing entity's raw serdes id. It now also names the
entity holding the dangling reference, and backtick-quotes names and ids so
leading and trailing whitespace is visible.

`load-one!` takes the referrer as an explicit parameter rather than carrying
it on the load context: `load-deps!` reduces over sibling deps and each
recursive `load-one!` returns its own context, so a context key would leave
later siblings labelled with the deepest child's referrer.

Message construction lives in `missing-reference-message` so the format can
change without touching the throw sites.

* Attach the referrer by catch-and-rethrow instead of threading it

Replaces the extra `load-one!`/`load-deps!` parameter with a try/catch around
the dependency load. The nearest enclosing `load-one!` unwinds first, so it is
the frame that attaches itself as the referrer; outer frames see the key
already present and rethrow unchanged. The original exception is kept as the
cause, preserving the stack trace of the detection site.

Adds an end-to-end assertion in bare-import-test that a real import of a Card
referencing a missing Database carries the Card as `:referrer`, which the
hand-built ex-info unit tests could not cover.

* Key the database-not-found error off ex-data, and name the referring card

`source-error-message` detected this case by matching "database not found"
against the cause's message text, then interpolated that raw internal string
into the user-facing error:

  Import failed: A referenced database does not exist on this instance.
  table id present, but database not found: [clickhouse nil some_table]

`import-table-fk` now tags the throw with `:error ::database-not-found` and
`:db-name`, and the render walks the whole cause chain looking for that key
rather than checking only the immediate cause. `::load-failure` carries the
referring entity, so both missing-content paths now render through
`missing-reference-message`:

  Import failed: Card `Some card` (`abc123`) references Database (`clickhouse`),
  which does not exist on this instance. ...

Also fixes `:database-names` in that ex-data, which selected names from
:model/Table rather than :model/Database. It has no consumers.

The end-to-end test uses a result_metadata `:table_id`, since that is the path
that actually reaches `import-table-fk` with an undeclared database:
`result-metadata-deps` derives deps only from `:field_ref`, and a Card's own
top-level `table_id` is recomputed from `dataset_query` by
`populate-query-fields` on insert.

* Backtick-quote file paths in remote sync ingest-error messages

GHY-3992 asks for content names to be quoted so leading and trailing
whitespace is visible. The ::not-found path already did this; the
ingest-errors branch still joined raw paths, and the message already uses
": " between path and reason and "; " between entries, so a path containing
punctuation or trailing space was ambiguous to read.

No fallback is added for a nil :file. All three ingest-error producers
(ingest.clj file-hierarchy!, ingestable.clj read and parse failures) always
set it, and only YamlIngestion and IngestableSnapshot produce a non-empty
ingest-errors; the other Ingestables delegate.

Splits source-error-message-entity-not-found-test into three deftests, one
per error branch, which kondo requires now that each has grown assertions.

* Render load-failure errors with the entity name, reason, and stripped keys

::load-failure fell through to the generic "Failed to reload from git
repository: <ex-message>" fallback, showing a NanoID and burying the
stripped-keys note. It now reads:

  Import failed: could not save Card `Orders by Month` (`abc123`).
  NOT NULL constraint failed: report_card.display.

and, when the entity was part of a dependency loop, adds "It may have been
saved without: `dashcards`, `parameters`." — a partial row for it survived
the import, which the user previously had no way to learn.

Renames the ::load-failure ex-data key `:referrer` to `:entity`. It names the
entity that failed to load, which is a referrer only relative to the missing
database in the ::database-not-found case; that branch now reads `:entity` and
passes it as the referrer. ::not-found keeps `:referrer`, where it really is
the parent.

The new branch is ordered after the tenant-collection and database-not-found
branches, both of which match on causes that a ::load-failure wraps.

::circular is left on the fallback. Rendering its cycle needs an ordered
expansion chain — `expanding` is a set — and it only fires when the
key-stripping recovery has already failed.

* Address CodeRabbit review

- Remove a duplicated `testing` block in impl_test.clj. It was byte-identical
  to the one above it, introduced when the deftest was split. Replaced with a
  doseq covering trailing, leading, and no surrounding whitespace.
- Mark `missing-reference-message` and `load-failure-message` private; neither
  is used outside impl.clj.
- Capture the exception once in bare-import-test's not-found case instead of
  running the same failing import twice, and flatten the resulting nested let.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-07-10 20:30:58 +00:00
github-automation-metabase
c6c50f90cf 🤖 backported "Bump js-yaml to 4.3.0" (#77503)
Bump `js-yaml` to 4.3.0 (#77459)

Bump js-yaml to 4.3.0

Co-authored-by: Timothy S. Dean <timothy.dean@metabase.com>
2026-07-10 15:17:34 +00:00
github-automation-metabase
46cec17c77 🤖 backported "Update jackson.* dependencies to 2.21.5" (#77466)
Update jackson.* dependencies to 2.21.5 (#77427)

Co-authored-by: Timofey Kachalov <timofei@metabase.com>
2026-07-10 10:59:41 +00:00
github-automation-metabase
906efe0e98 🤖 backported "Add tests for the node static-viz renderer" (#77514)
Add tests for the node static-viz renderer (#77508)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-10 08:13:33 +00:00
github-automation-metabase
47a99ab1b0 🤖 backported "Pin linkify-it to 5.0.2" (#77482)
Pin `linkify-it` to 5.0.2 (#77457)

Pin linkify-it to 5.0.2

Co-authored-by: Timothy S. Dean <timothy.dean@metabase.com>
2026-07-10 03:27:40 -04:00
github-automation-metabase
52d26ef0a7 🤖 backported "Static viz node renderer" (#77490)
Add backport resolution script

Co-authored-by: Metabase bot <metabase-bot@metabase.com>
2026-07-10 01:17:01 +00:00
github-automation-metabase
acc3c860fc 🤖 backported "remove experimental linear-release-sync workflow" (#77465)
remove experimental linear-release-sync workflow (#77302)

* remove experimental linear-release-sync workflow

* remove linear-release-lifecycle job from release workflow

* also cleanup helper functions

Co-authored-by: Chris Vandoodewaard <chris.vandoodewaard@metabase.com>
2026-07-09 21:19:48 +00:00
Ben Grabow
c5e4bea9c5 Downgrade SQLite -> H2 sample DB (#77333) (#77447)
* Downgrade SQLite -> H2 sample DB
2026-07-09 20:49:32 +00:00
github-automation-metabase
d631ff52fa 🤖 backported "Preserve native model column types during serialization (GHY-4043)" (#77441)
Preserve native model column types during serialization (GHY-4043) (#77405)

* Preserve native model column types during serialization (GHY-4043)

export-result-metadata checked (model? card) before the native branch, so
native models were routed through lib/model-preserved-keys — a metadata-merge
soft-key set that omits :base_type/:effective_type. On a read-only Remote Sync
target the column types could not be re-derived without executing the SQL, so
computed columns (e.g. STRING_AGG(...) AS names) fell back to type/* and their
filter UI collapsed to Is empty / Not empty.

Reorder the cond so native cards — models included — go through the branch that
preserves structural type info; only MBQL models, whose types are re-derivable
from the query at import, use the soft-key set.

* Update native-model extract test for preserved column types

extract-result-metadata-native-model-test asserted the old behavior — that a
native model dropped :base_type on export. Update it to expect structural type
info to survive, matching the GHY-4043 fix. Drop the now-unused clojure.set
require.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-07-09 20:30:28 +00:00
github-automation-metabase
0eb98f5fce 🤖 backported "Preserve MySQL role name case when expanding role grants" (#77436)
Preserve MySQL role name case when expanding role grants (#77415)

`parse-grant` lowercased role names parsed out of `SHOW GRANTS FOR <user>`.
Those names are fed back into `SHOW GRANTS FOR <user> USING <roles>`, and
MySQL 8 treats backticked role identifiers as case-sensitive, so any role
with uppercase characters made the follow-up query fail with error 3530 and
took the whole metadata sync step down with it.

Lowercasing is still correct for the privilege types in the sibling
`:privileges` branch; it was never right for role identifiers.

Also add the missing space before `USING` in the follow-up query, which
only parsed because the user identifier ends in a backtick.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-07-09 19:59:56 +00:00
github-automation-metabase
ab6a210e8f 🤖 backported "Static-viz: dispose ECharts instances (OOM fix) + faster SVG dominant-baseline patch" (#77429)
Add backport resolution script

Co-authored-by: Metabase bot <metabase-bot@metabase.com>
2026-07-09 19:53:30 +00:00
github-automation-metabase
917498f30e 🤖 backported "Extract a StaticVizRenderer protocol for static-viz rendering" (#77411)
Extract a StaticVizRenderer protocol for static-viz rendering (#77315)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-09 19:01:23 +00:00
github-automation-metabase
e46f3a36d8 🤖 backported "Minify the static-viz bundle" (#77249)
Minify the static-viz bundle (#77234)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-09 12:29:10 -04:00
github-automation-metabase
ce96774a4e 🤖 backported "Forward query edits from viz settings in SDK QuestionSettings" (#77356)
Forward query edits from viz settings in SDK QuestionSettings (#77262)

* test: add repro for SDK viz-settings column edits being dropped

Reproduces EMB-2057 — in modular embedding (embed.js), removing a
column via Visualization settings > Add or remove columns silently
no-ops. Fails against the current QuestionSettings.tsx onChange,
which drops the second question argument carrying the query edit.



* fix: forward query edits from viz settings in SDK QuestionSettings

QuestionSettings.tsx's onChange dropped the second `question`
argument that ChartSettings forwards for query-level edits (e.g.
add/remove columns), applying only the settings to the stale closure
question and never re-running the query. Column-removal edits landed
in the query object but the table kept rendering the old result.

Forward `nextQuestion` when present and re-run only in that case, so
pure viz-settings-only edits (no query change) keep their existing
no-run behavior.



* Fix code format

* move test to where it belongs

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 14:44:06 +00:00
github-automation-metabase
d88c637c2a 🤖 backported "Split databricks CI jobs in 3 parts." (#77347)
Split databricks CI jobs in 2 parts. (#77312)

Currently they are timing out with some regularity in CI. We've done
this same thing to bigquery and redshift already.

Not only will this get us pass/fail results more quickly; it also
reduces the scope of failures so retries don't have to redo as much
wasted work.

Co-authored-by: technomancy <technomancy@gmail.com>
2026-07-09 02:04:21 +00:00
github-automation-metabase
e7e199e384 🤖 backported "Reject replacing a dashcard's card with a question internal to another dashboard" (#77331)
Reject replacing a dashcard's card with a question internal to another dashboard (#77236)

Co-authored-by: Andrei Chernykh <andrei.chernykh@metabase.com>
2026-07-08 22:53:42 +00:00
github-automation-metabase
cd6cd50513 🤖 backported "disable coderabbit PR summaries" (#77317)
disable coderabbit PR summaries (#77114)

* disable PR summaries

* disable coderabbit yapping when skipped

Co-authored-by: Aleksandr Lesnenko <alxnddr@users.noreply.github.com>
2026-07-08 19:14:47 +00:00
github-automation-metabase
1c7fb61505 🤖 backported "Stop running metabase-enterprise.transforms-inspector-test in cloud DBs" (#77304)
Stop running metabase-enterprise.transforms-inspector-test in cloud DBs (#77231)

Stop running metabase-enterprise.transforms-inspector-test in cloud DBs.

These tests create new tables in the default schema, which is fine for
most drivers, but not for cloud DBs like snowflake and bigquery, because
concurrent CI jobs share the same jobs, which means this will cause test
failures in other unrelated tests which list tables, such as
metabase-enterprise.impersonation.driver-test/resilient-connection-options-test

We need to get these tests to stop creating tables in the default schema,
then we can drop the requirement for the :test/dynamic-dataset-loading feature.

Co-authored-by: technomancy <technomancy@gmail.com>
2026-07-08 18:38:40 +00:00
github-automation-metabase
7f4f61bf3c 🤖 backported "coderabbit config" (#77313)
coderabbit config (#77111)

Co-authored-by: Aleksandr Lesnenko <alxnddr@users.noreply.github.com>
2026-07-08 18:02:16 +00:00
github-automation-metabase
b6e8642360 🤖 backported "Fix notification seed legacy template paths" (#77295)
Fix notification seed legacy template paths (#77268)

* Fix notification seed crashing on pre-existing template rows

* add regression test

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-07-08 17:10:46 +00:00
github-automation-metabase
8de91c42bb 🤖 backported "DEV-2270: add .bun-version file, configure setup-bun to use version file" (#77282)
DEV-2270: add .bun-version file, configure setup-bun to use version file (#77208)

* add .bun-version file, configure setup-bun to use version file

* use package.json as the source of truth for the version of bun installed

Co-authored-by: Chris Vandoodewaard <chris.vandoodewaard@metabase.com>
2026-07-08 12:54:14 -04:00
Timofey Kachalov
5ccc6db6ca 🤖 backported "Update httpcore5, httpcore5-h2 and httpclient5 versions" (#77204) 2026-07-08 14:53:32 +04:00
github-automation-metabase
58a385e2cf 🤖 backported "Log only the error message for failed queries" (#77240)
Log only the error message for failed queries (#77223)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-07 22:08:50 -04:00
Jeff Bruemmer
31258b726c docs - remove link (#77220)
remove link
2026-07-07 18:13:48 -04:00
github-automation-metabase
d7dff1dd83 🤖 backported "Do not compute related-tables or metrics in get-report-details" (#77190)
Do not compute related-tables or metrics in get-report-details (#76945)

Don't compute related-tables or metrics in get-report-details

`get-report-details` drops `:related_tables` and `:metrics` via a `select-keys`, so don't ask `card-details` for them.

Conflicts:
	test/metabase/metabot/tools/entity_details_test.clj

No actual conflicts, only some additional unbackported tests that were adjacent to the tests added in this commit.

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-07-07 21:19:10 +00:00
github-automation-metabase
51b86ff7db 🤖 backported "Pool error fix" (#77219)
Pool error fix (#77212)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-07 17:05:23 -04:00
github-automation-metabase
4da68ba267 🤖 backported "Remove enhanced-database-tables and provide columnless tables in metabot context :used_tables" (#77138)
Remove enhanced-database-tables and provide columnless tables in metabot context :used_tables (#76944)

`enhance-context-with-schema` resolved each `:user_is_viewing` item's used tables and then ran them through
`table-utils/enhanced-database-tables`, which fetched and formatted the full visible-columns set for every used table, including all implicitly-joinable columns from related tables. That work was discarded anyway since columns were recalculated via `entity-details/get-table-details` when rendering the context. On large, highly-connected schemas this could load thousands of columns into the request metadata cache and exhausted the heap (metabase#76493).

`:used_tables` entries now carry only `{:id :type :name :database_schema :description}`. name/schema/description are kept for the `simple-entity` error fallback. `enhanced-database-tables` had no other callers, so it is removed.

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-07-07 15:57:31 -05:00
github-automation-metabase
03dc5f45b4 🤖 backported "Graalvm pool with staticly parsed sources" (#77207)
* Graalvm pool with staticly parsed sources (#77180)

* Remove unused js.color require (no pivot rendering on this branch)

---------

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-07 16:26:31 -04:00
github-automation-metabase
ef03831154 🤖 backported "Bump postgresql 42.7.11 -> 42.7.12" (#77192)
Bump postgresql 42.7.11 -> 42.7.12 (#77181)

Co-authored-by: Timothy S. Dean <timothy.dean@metabase.com>
2026-07-07 19:36:20 +00:00
github-automation-metabase
9aa709ca69 🤖 backported "Bump handlebars 4.3.1 -> 4.5.3" (#77172)
Bump handlebars 4.3.1 -> 4.5.3 (#77134)

* Bump handlebars 4.3.1 -> 4.5.3
* Add license for jnack/handlebars

Co-authored-by: Timothy S. Dean <timothy.dean@metabase.com>
2026-07-07 11:08:20 -06:00
github-automation-metabase
c2229e9636 🤖 backported "Pin maildev to 2.2.1" (#77149)
Pin maildev to 2.2.1 (#77143)

pin maildev to 2.2.1

Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-07-07 02:32:44 +00:00
github-automation-metabase
62e4a345bc 🤖 backported "[GHY-3974] Fix Usage Analytics breaking after upgrade (two failure modes)" (#77123)
[GHY-3974] Fix Usage Analytics breaking after upgrade (two failure modes) (#76551)

* [GHY-3974] Add failing tests for Usage Analytics upgrade failure modes

Two invariant tests covering the audit-DB upgrade breakage, asserted
through ensure-audit-db-installed! so they're independent of the fix
mechanism and leave the generic sync path untouched:

- Mode A (audit-db-self-heals-duplicate-rows-from-stale-schema-test):
  an interrupted host-adjust + schema sync duplicates a v_* view row and
  retires the original, breaking customer content. ensure-audit-db-installed!
  must reconcile back to one active row per view and keep dependent content
  on an active table.

- Mode B (checksum-not-advanced-until-host-adjust-completes-test):
  last-analytics-checksum must not advance if adjust-audit-db-to-host! is
  interrupted, so the next boot re-runs the load and the host-adjust.

Both fail on master.

* [GHY-3974] Fix Usage Analytics upgrade failure modes

Mode B (checksum committed before host-adjust): in maybe-load-analytics-content!
advance last-analytics-checksum only after adjust-audit-db-to-host! completes,
so an interrupted host-adjust leaves the checksum behind and the next boot
re-runs the load + adjust. Also force a reload, regardless of the checksum, when
audit tables are stuck at the postgres "source" schema on a non-postgres host
(audit-db-in-source-state?) so already-corrupted instances self-heal.

Mode A (schema-renamed view duplicated + retired by sync): add
reconcile-audit-db-duplicates!, run on every ensure-audit-db-installed! so
already-corrupted instances (whose checksum already matches, skipping the load)
still recover. For each duplicate same-name group in the audit DB it keeps the
row dependent content points at, repoints stray report_card.table_id refs to it,
normalizes it to the host-canonical name/schema, and deletes the orphans. Schema
is derived from the host engine, not the surviving row, so a duplicate left at
the postgres source schema (Mode B masking Mode A) is corrected too. Scoped
entirely to the audit DB; the generic sync path is untouched.

Both audit-test regression tests pass on h2 and postgres.

* [GHY-3974] Serialize audit DB load/adjust/sync across nodes (1b)

with-duplicate-ops-prevented is per-process only, so on a rolling upgrade
two pods could run the audit load/adjust/sync concurrently and produce the
half-applied schema states behind both failure modes. Wrap the
load+adjust+sync+reconcile in an app-DB cluster lock (metabase_cluster_lock)
so only one node runs it at a time. If another node already holds the lock it
is doing the same work, so skip rather than fail the boot.

The sync must run inside the lock to be serialized, so maybe-sync-audit-db!
now runs synchronously instead of in a background future: on a transactional
appdb a sync on another thread would escape the lock's transaction and
deadlock against its uncommitted metabase_table writes.

Full audit-app namespace passes on h2 (in-process RW lock) and postgres
(transaction + SELECT FOR UPDATE), 52 assertions each.

* [GHY-3974] Filter audit view lookups in SQL, not in memory

audit-view-table and the Mode A heal assertion were selecting every active
audit table and filtering by lower(name) in Clojure. Push the predicate into
the query with a lower(name) where clause.

* Better comments

* [GHY-3974] Don't force a reload for an inactive public orphan (MySQL fix)

audit-db-in-source-state? treated any `public`-schema audit table on a
non-postgres host as the interrupted-host-adjust state and forced a reload.
But a Mode A duplicate leaves an *inactive* `public` orphan, and on MySQL
(canonical schema NULL) forcing a reload made adjust-audit-db-to-source!
move the active NULL row to `public`, colliding with the orphan on the
(db_id, schema, name) unique index (Duplicate entry '…-public-v_content').

Only active rows signal the genuine stuck source state; inactive public rows
are retired orphans handled by reconcile-audit-db-duplicates!. Restrict the
check to active rows. Surfaced by the MySQL/MariaDB app-db CI run of
audit-db-self-heals-duplicate-rows-from-stale-schema-test.

* [GHY-3974] Update modules config: audit-app no longer uses config

Removing the metabase.config.core require from the audit-app module (the
unused config dep dropped when the sync future was removed) leaves config
out of enterprise/audit-app :uses. Fixes modules-config-up-to-date-test.

* [GHY-3974] Only swallow lock-contention failures for our own audit lock

A different cluster lock's acquisition failure raised from inside the locked
body (e.g. the permissions lock taken when serdes inserts a Database) also
carries :lock-names; it must propagate rather than be swallowed as audit
contention and silently abandon the load.

* [GHY-3974] Bail faster on audit lock contention

Drop the lock-acquisition timeout to 5s and retries to 2, so a node that loses
the boot-time audit lock skips in ~17s instead of ~150s. The work is idempotent
and the lock holder is doing it, so a losing node need not win; keeping a couple
retries still lets a node pick up the work if the holder crashes mid-load.

* [GHY-3974] Serialize audit DB install/adjust under the cluster lock too

The install-time adjust-audit-db-to-host! + schema sync (the ::installed and
::updated branches of maybe-install-audit-db!) previously ran outside the
cluster lock, so two nodes detecting an engine mismatch could adjust and sync
concurrently against a half-adjusted schema. Move install inside the lock so the
whole install+adjust+load+sync+reconcile is serialized across nodes; a node that
acquires the lock after the installer commits sees the DB already exists and
no-ops instead of colliding on the audit DB primary key.

* [GHY-3974] Harden reconcile-audit-db-duplicates! and Mode B test

- Sort rows by id before grouping so survivor selection (including the
  referenced? tiebreak) is deterministic regardless of appdb row order.
- Clear is_defective_duplicate on the survivor so it re-enters idx_unique_table
  (a flagged row has a NULL unique_table_helper and is excluded from the index).
- Comment why orphans are deleted before the survivor is re-canonicalized.
- Mode B test: move cleanup into a finally and reinstall the audit DB so an
  unexpected throw can't leave sibling tests without an audit DB.

* [GHY-3974] Make reconcile lossless: move content off orphans before deleting

The previous reconcile did (t2/update! :model/Card {:table_id orphan} {:table_id survivor})
then deleted the orphan. That update is a no-op — Card's before-update populate-query-fields
re-derives table_id from the unchanged dataset_query — and report_card.table_id is ON DELETE
CASCADE, so deleting the orphan destroyed any customer card whose query pointed at it. This
happens in real Mode A: bundled audit cards reference the retired row (making it the survivor)
while a customer question built during the corrupted window references the freshly-synced active
row (the deleted orphan).

Now repoint-cards-to-survivor! rewrites each referencing card's dataset_query (source-table)
and result_metadata, remapping field ids onto the survivor's same-named fields, before the
orphan is deleted. Handles both pMBQL and legacy field refs.

Adds audit-db-reconcile-preserves-content-on-the-deleted-duplicate-test, which asserts the card
survives and its field refs resolve to active fields on the survivor (fails on the old code).

* [GHY-3974] Use lib field-ref helpers instead of hand-indexing refs

repoint-refs now reads/writes the field id via lib/field-ref-id and
lib/with-field-ref-id rather than indexing the ref vector by position. Expose
with-field-ref-id from metabase.lib.core (the setter alongside the already-public
field-ref-id getter) so it's reachable through the lib module's public API.

* [GHY-3974] Use lib clause predicate / field-ref-id for remaining MBQL access

Replace the hand-rolled field-ref? predicate with lib/clause-of-type?, and the
positional field-id extraction in the test with lib/field-ref-id.

* better docstring

* [GHY-3974] Order reconcile rows in the query instead of in memory

Address review: let the DB sort (:order-by [[:id :asc]]) rather than sort-by; group-by
preserves the order within each group, keeping the survivor tiebreak deterministic.

* [GHY-3974] Move query id-remap into lib; handle result-metadata thoroughly

Review (@metamben): the ref manipulation should live in lib and be general-purpose,
and queries vs result metadata shouldn't share one generic walker.

- Add metabase.lib.walk.util/replace-field-ids and replace-table-ids (general, built on
  lib.walk): field refs everywhere via walk-clauses (filters/aggregations/breakouts/joins/
  nested stages), :source-field/:fk-field-id opts, native :template-tags :dimension refs, and
  :source-table per stage/join. Exposed via metabase.lib.core.
- audit-app repoint-cards-to-survivor! now rewrites dataset_query via the lib helpers, and
  handles result_metadata separately (non-generic): :id, :fk_target_field_id, and :field_ref
  covering both legacy ([:field id opts]) and pMBQL ([:field opts id]) shapes.
- Test asserts legacy result_metadata :field_ref is remapped onto the survivor.

Covers review threads: field-ref manipulation in lib (#383), FK/source-field refs (#418),
legacy result-metadata refs (#392).

* [GHY-3974] Batch the reconcile referenced? check into one query

Address review: instead of a t2/exists? per row in a duplicate group, fetch the set of
card-referenced table_ids for the whole group in a single query and test membership.

* [GHY-3974] Review cleanups: cond-> in replace-field-ids; update-existing in result-metadata remap

- lib replace-field-ids: use cond-> for the template-tags/:dimension updates instead of if/else.
- audit remap-result-metadata: use m/update-existing with an identity-for-unmapped fn instead of
  a cond-> with duplicate lookups; drop the redundant pos-int? guard in remap-result-metadata-ref
  (field-id-remap is always a map, so it already returns nil for names/nil).

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-07-06 20:53:51 +00:00
github-automation-metabase
aecb1c7bc2 🤖 backported "Take existing tables into account when determining already-loaded bigquery" (#77020)
Take existing tables into account when determining already-loaded bigquery (#76936)

We no longer have bigquery tables that constantly think that they need
to be recreated, but we have fallen a little too far in the other
direction; datasets that get their DB created but fail midway thru
will still be treated as loaded.

In snowflake we have a way of checking this down to the individual row
counts and counting as not-already-loaded if there's a mismatch. I
don't see a way to do this in bigquery, but we can at least check that
all the tables exist. If we get a failed partial load that gets all
the tables created but fails the rows that will still require manual
intervention.

Also stop using (config/current-major-version) when qualifying dataset
names; turns out this is not at all reliable in CI. This unfortunately
means that we don't actually have cross-major-version isolation; the
best we can do is isolate master/release from all other branches,
which is still an improvement over what we have now.
# Conflicts:
#	modules/drivers/bigquery-cloud-sdk/test/metabase/test/data/bigquery_cloud_sdk.clj

Co-authored-by: technomancy <technomancy@gmail.com>
2026-07-06 17:52:04 +00:00
Alexander Polyankin
281cf4d735 Revert "🤖 backported "Enable streaming results for app db queries with postgres"" (#77050)
Revert "🤖 backported "Enable streaming results for app db queries with postgr…"

This reverts commit bbe520d6bc.
2026-07-05 04:56:22 -04:00
github-automation-metabase
442bad5962 🤖 backported "Add request info to 'Request canceled before finishing' log line" (#77026)
Add request info to 'Request canceled before finishing' log line (#77023)

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-07-03 19:52:53 +00:00
github-automation-metabase
356330a25a 🤖 backported "Update c3p0 version" (#77018)
Update c3p0 version (#77000)

Co-authored-by: Timofey Kachalov <timofei@metabase.com>
2026-07-03 17:31:35 +00:00
github-automation-metabase
1374ea6333 🤖 backported "Stop pretending the cache is up to date" (#77013)
Stop pretending the cache is up to date (#76898)

Closes GIT-10454

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-07-03 16:31:42 +00:00
github-automation-metabase
a37231b4dd 🤖 backported "jdbc-data-warehouse-connection-pool-max-pending-checkouts" (#77015)
jdbc-data-warehouse-connection-pool-max-pending-checkouts (#77004)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-07-03 12:30:10 -04:00
github-automation-metabase
08ed13db99 🤖 backported "Pin jackson-databind for databrocks/redshift/snowflake drivers" (#77006)
Pin jackson-databind for databrocks/redshift/snowflake drivers (#76980)

Co-authored-by: Timofey Kachalov <timofei@metabase.com>
2026-07-03 16:29:14 +00:00
github-automation-metabase
b0767cfea2 🤖 backported "[lint] Implement Kondo hooks for metabase.util.match macros" (#76957)
[lint] Implement Kondo hooks for metabase.util.match macros (#73986)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-07-03 14:43:56 +00:00
github-automation-metabase
9912cbe599 🤖 backported "MB_JDBC_DATA_WAREHOUSE_CONNECTION_POOL_CHECKOUT_TIMEOUT_MS" (#76988) 2026-07-03 10:24:46 -04:00
github-automation-metabase
d8b2714c19 🤖 backported "Pin jackson-databind for bigquery-cloud-sdk" (#76977)
Pin jackson-databind for bigquery-cloud-sdk (#76899)

Co-authored-by: Timofey Kachalov <timofei@metabase.com>
2026-07-03 13:02:43 +00:00
github-automation-metabase
9a652fed9d 🤖 backported "[schema] Validation performance improvements" (#76952)
[schema] Validation performance improvements (#64815)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-07-03 10:05:23 +00:00
github-automation-metabase
a215bfeb6e 🤖 backported "Convert all ASCII chars in var names not allowed by mongo to _" (#76927)
Convert all ASCII chars in var names not allowed by mongo to _ (#76761)

Closes QUE2-704

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-07-02 22:28:34 +00:00
github-automation-metabase
9bb6992edb 🤖 backported "Don't error when table has a foreign key reference to an inaccessible table" (#76781)
Don't error when table has a foreign key reference to an inaccessible table (#76740)

* use promise allSettled

* Add e2e test

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-07-02 17:06:47 -04:00
github-automation-metabase
33ab5ca51b 🤖 backported "Limit metabot related-tables to bound context size" (#76773)
Limit metabot related-tables to bound context size (#76588)

* Limit metabot FK related-tables to bound context size

When bulding metabot's context we expand one related-table entry per FK column and fetch and format the full column
set for each related table. On a large, highly-connected schema this fetches a large number of columns into the
metadata cache, exhausting the heap (metabase#76493).

We now limit the number of related-tables we attempt to fetch to metabase.metabot.tools.entity-details/max-related-tables

* Report truncated related tables to the LLM

When the related-table list in the metabot context gets truncated due to exceeding the limit, leave a note in the
context that truncation occurred. For tables that were dropped, include them in a lighter weight list with only
id/name/related-by (also capped), so the LLM knows more related tables exist and can look any of them up individually.

`entity-details/related-tables` now returns the detailed list plus a total count and a list of truncated items; the
result surfaces related_tables_truncated / related_tables_total / related_table_refs (+ related_table_refs_truncated),
and llm-shape renders a <related-tables-truncated> note with the truncated items.

* Rename max-related-table-{,truncated-}refs and add table descriptions

* Don't :include-implicitly-joinable? when looking up FKs for metabot related-tables

Otherwise we fetch and cache the full column set for every FK-target table when looking up realted-tables for
metabot's context. Instead, just do a bulk metadata lookup on the FK target columns only so we don't blow up the
metadata cache for tables with a huge number of columns.

* Cleanup docstrings and formatting in related-tables->xml

* Exclude tables in :related_tables from :related_table_refs

The refs list doesn't need to duplicate the tables from :related_tables. This way, the LLM potentially gets more
info about truncated tables, since we also cap the :related_table_refs list.

* Drop :related_tables_truncated and :related_table_refs_truncated

They can be inferred from :related_tables_total and the size of two lists.

* Use render-llm-template for related-tables->xml and related-table-ref->xml

* Fold related-tables-result into related-tables

* Reduce max-related-tables from 20 to 10

* Make metabase.metabot.tools.entity-details/related-tables private

* Fix docstring typo

* Ensure that :related_tables_total = :realted_tables + :related_table_refs

Don't exclude entries from :related_table_refs just because the table id appears in :realted_tables. We specifically
want to allow duplicate table-ids as long as the (table-id, fk) pairs are distinct to preserve the total = related +
refs invariant.

* Cleanup realted-tables

* Return field id from fk-related-table-groups

To avoid having to call lib.metadata/field in the caller for each fk just to extract the :name.

* Include the field id in related-tables :related_by to distinguish fk fields with the same :name

* Simplify handling for related-tables without fields

- rename max-related-tables to max-related-tables-with-fields
- rename max-related-table-truncated-refs to max-related-tables
- rename :related_table_refs to :related_tables_without_fields
- return up to max-related-tables-with-fields (10) in :related_tables
- return up to 40 tables in :related_tables_without_fields
  - 40 = 50 - 10 = max-related-tables - max-related-tables-with-fields
- always use table-details to format related-tables, just pass :with-fields? false once max-related-tables-with-fields
  is exceeded

* Don't say roster

* Only include :related_tables_without_fields if the caller requested fields

If the caller did not pass `with-fields? true`, then there is no distinction between :related_tables and
:related_tables_without_fields, so don't include it.

* Fix docstring

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-07-02 13:51:57 -07:00
github-automation-metabase
4ca729d91f 🤖 backported "Fix Linear Release Sync Again" (#76911)
Fix Linear Release Sync Again (#76909)

* add catch logic

* do it right this time

Co-authored-by: Ryan Laurie <30528226+iethree@users.noreply.github.com>
2026-07-02 18:24:54 +00:00
github-automation-metabase
61654329ef 🤖 backported "Don't fail builds on linear release issue tracking" (#76905)
Don't fail builds on linear release issue tracking (#76889)

don't ever fail a build for this

Co-authored-by: Ryan Laurie <30528226+iethree@users.noreply.github.com>
2026-07-02 17:07:49 +00:00
github-automation-metabase
4eada27759 🤖 backported "Use report timezone if breakout field has datetime effective type" (#76895)
Use report timezone if breakout field has datetime effective type (#76697)

use report timezone if bucketed field as temporal effective type

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-07-02 15:44:05 +00:00
technomancy
9d5c15261b Adjust dataset hashing for snowflake and bigquery: 61 backport (#76823)
Adjust dataset hashing for snowflake and bigquery.

We are frequently seeing some rogue process dropping the test-data
database which is shared across all tests, causing a good deal of
instability. We have not been able to track down what code is doing
this. Changing the way we calculate database names based on dataset
will help us find the culprit and also help isolate branches for
stability.

When we are on master or a release branch, splice the current major
version into the qualified database name. When we aren't, use "sha__"
as a prefix instead of "sha_", which will keep backport branches from
affecting dev branches going forward and help us identify whether it's
backports or not that are causing the database drops we're seeing.

Stop checking the tracking table to determine whether the dataset is
loaded. The dataset will be tracked regardless, so this should not
factor into the question of whether it needs to be created.

Unrelated change: update get-or-create/create-database! to print when
it encounters a failure and deletes the database it was trying to
create. This should never happen for snowflake/bigquery because they
are flagged as not supporting dynamic dataset loading, but it's still
better to have this printed visibly instead of the error logs which
are hidden.
2026-07-02 08:07:42 -04:00
github-automation-metabase
5227bc929f 🤖 backported "fix(sdk): open SQL editor when Metabot navigates to native hash URL" (#76867)
fix(sdk): open SQL editor when Metabot navigates to native hash URL (#76738)

* fix(embedding-sdk): open SQL editor when Metabot navigates to native hash URL

When Metabot sends navigate_to with /question#<native_hash>, SdkAdHocQuestion
was returning questionId=null (no numeric slug), so isNewQuestion=false and the
SQL editor never opened. Now detects dataset_query.type==='native' and returns
'new-native', which makes SdkQuestionProvider set isNewQuestion=true and
useNewQuestionEditorSync open the editor.


Claude-Session: https://claude.ai/code/session_01Dwi9BjxKNSJsztkkVrsU8d

* test(sdk): use real embedding Metabot stream for SQL editor test

Replaces hand-rolled mock payload with a navigate_to event captured
verbatim from an actual embedding Metabot response, so the test
fixture matches real wire format (db id adjusted to match env).


Claude-Session: https://claude.ai/code/session_012wNQmcGH7PCXMD1PqFLiBS

* refactor(sdk): call resolveQuestionId inline instead of in useMemo

It's a cheap derivation, not worth complicating the memo for.


Claude-Session: https://claude.ai/code/session_012wNQmcGH7PCXMD1PqFLiBS

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 11:43:44 +00:00
github-automation-metabase
678d9bb698 🤖 backported "Claude Sonnet 5 support" (#76838)
Claude Sonnet 5 support (#76756)

 Conflicts:
	src/metabase/metabot/self/claude.clj
	test/metabase/metabot/self/claude_test.clj

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-07-01 22:32:13 +00:00
github-automation-metabase
b5290436af 🤖 backported "Bump kixi.stats to 0.5.9" (#76831)
Bump kixi.stats to 0.5.9 (#76789)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-07-01 19:00:13 +00:00
github-automation-metabase
8c23f111f2 🤖 backported "Add Linear Release tracking" (#76835)
Add Linear Release tracking (#76711)

* Add Linear Release tracking

* use uniform helper

* fix typos 🤦‍♀️

* get linear release version

Co-authored-by: Ryan Laurie <30528226+iethree@users.noreply.github.com>
2026-07-01 18:28:21 +00:00
github-automation-metabase
19993007de 🤖 backported "fix: bump dompurify, js-yaml, markdown-it; pin form-data" (#76734)
* fix: bump dompurify, js-yaml, markdown-it; pin form-data (#76533)

* fix: bump dompurify, echarts, js-yaml, markdown-it; pin form-data

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBYTJTjQQY4qo5EBCt5YwH

* fix: revert echarts bump; keep dompurify, js-yaml, markdown-it, form-data fixes

* fix: restore echarts 6.0.0 patch, remove 6.1.0 patch

* fix: regenerate release/bun.lock

* fix: pin form-data to 4.0.6 in root resolutions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBYTJTjQQY4qo5EBCt5YwH

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: bump js-cookie to 3.0.7

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 17:03:13 +00:00
github-automation-metabase
2a95073340 🤖 backported "fix: pin opentelemetry-api to 1.62.0" (#76736)
fix: pin opentelemetry-api to 1.62.0 (#76531)

* fix: pin opentelemetry-api to 1.62.0

* fix: move io.opentelemetry pin to correct alphabetical position in deps.edn

* fix: correct OTel comment attribution, fix stale sdk-testing comment, bump bigquery netty to 4.2.15


Claude-Session: https://claude.ai/code/session_01EBYTJTjQQY4qo5EBCt5YwH

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 17:01:28 +00:00
github-automation-metabase
10dd8c1c75 🤖 backported "Fix date-dependent test in ConversationFilters date dropdown" (#76817)
Fix date-dependent test in ConversationFilters date dropdown (#76803)

fix date-dependent ConversationFilters date dropdown test

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-07-01 14:36:36 +00:00
github-automation-metabase
b4344240e9 🤖 backported "Make bigquery throw when there is a next page that we cannot fetch" (#76725)
Make bigquery throw when there is a next page that we cannot fetch (#76709)

bigquery page fix

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-30 16:04:21 +00:00
github-automation-metabase
11fbf67cec 🤖 backported "fix: bump jackson-databind to 2.21.4" (#76735)
fix: bump jackson-databind to 2.21.4 (#76529)

* fix: bump jackson-databind to 2.21.4 and handlebars to 4.5.2

* fix: bypass handlebars 4.4+ root-classpath loader check

* fix: revert handlebars bump; keep only jackson-databind 2.21.4

* fix: bump jackson-core to 2.21.4 to match jackson-databind


Claude-Session: https://claude.ai/code/session_01EBYTJTjQQY4qo5EBCt5YwH

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 14:47:51 +00:00
github-automation-metabase
29c8655f52 🤖 backported "fix: bump netty to 4.1.135.Final (athena) and 4.2.15.Final (hive-like)" (#76733)
fix: bump netty to 4.1.135.Final (athena) and 4.2.15.Final (hive-like) (#76530)

* fix: bump netty to 4.1.135.Final (athena) and 4.2.15.Final (hive-like)

* fix: correct stale version in athena netty comment


Claude-Session: https://claude.ai/code/session_01EBYTJTjQQY4qo5EBCt5YwH

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 14:31:56 +00:00
github-automation-metabase
5eae69192c 🤖 backported "Fix setting writable and admin db details" (#76700)
Fix setting writable and admin db details (#76696)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-29 20:42:22 +00:00
github-automation-metabase
18a1622f69 🤖 backported "Perms graph reduce" (#76693)
Perms graph reduce (#76678)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-29 19:24:41 +00:00
github-automation-metabase
9ed2845a56 🤖 backported "Simple shared image buffers for rendering" (#76672)
Simple shared image buffers for rendering (#76504)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-29 16:15:38 +00:00
github-automation-metabase
cbc0af6686 🤖 backported "Preserve dashboard table column settings for stale SQL question metadata" (#76679)
Preserve dashboard table column settings for stale SQL question metadata (#76434)

* Fix dashcard table column settings merge

* Preserve dashcard-only table columns

Co-authored-by: Anderson <ands.alves.nunes@gmail.com>
2026-06-29 14:42:47 +00:00
github-automation-metabase
bbe520d6bc 🤖 backported "Enable streaming results for app db queries with postgres" (#76676)
Enable streaming results for app db queries with postgres (#76645)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-29 14:27:28 +00:00
github-automation-metabase
ee89eb4ce9 🤖 backported "Add ExplicitSize test for measuring a child that renders content after mount" (#76621)
Add ExplicitSize unit test for measuring a child that renders content after mount

PivotTable returns null on its first render, so ExplicitSize has nothing
to measure at mount. The regression was that once it rendered content,
its size was never recalculated without a resize event, leaving the
child stuck at null dimensions. Cover that contract directly at the unit
layer rather than through a flaky e2e viz-switch repro.

Backport note: release 61 has no ExplicitSize.unit.spec.tsx, and its
ExplicitSize component predates the resize-observer-entry measurement
work. So this backports only the metabase#51926 test and its harness —
not the base tests from the source file that exercise that unrelated,
un-backported behavior. The metabase#51926 fix itself is already present
(componentDidUpdate re-measures when an element appears after mount).

Co-authored-by: Nemanja <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 21:35:23 +02:00
github-automation-metabase
329161ec55 🤖 backported "fix: add optimistic updates for comments api in documents with many cards" (#75807)
fix: add optimistic updates for comments api in documents with many cards (#75763)

* fix: invalidate in-flight queries

* cleanup test

* use different approach

* update comment

* update comments

Co-authored-by: Uladzimir Havenchyk <125459446+uladzimirdev@users.noreply.github.com>
2026-06-26 15:01:14 +00:00
github-automation-metabase
2c3e64409a 🤖 backported "Skip sandboxing during metric expansion" (#76579)
Skip sandboxing during metric expansion (#76125)

Closes QUE2-681

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-06-25 23:10:32 +00:00
github-automation-metabase
c9656e3d92 🤖 backported "Dont quarantine drivers on master or release branches" (#76495)
Dont quarantine drivers on master or release branches (#76468)

dont quarantine drivers on master or release branches

Co-authored-by: Ryan Laurie <30528226+iethree@users.noreply.github.com>
2026-06-25 16:56:30 -06:00
github-automation-metabase
1fc66bb418 🤖 backported "Throttle heavy log4j calls" (#76545)
Throttle heavy log4j calls (#76482)

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-06-25 19:34:37 +00:00
github-automation-metabase
646c58d306 🤖 backported "Fix model filter dropdown lost on serdes sync (GHY-3946)" (#76525)
Fix model filter dropdown lost on serdes sync (GHY-3946) (#76349)

* Fix model filter dropdown lost on serdes sync (GHY-3946)

A dashboard/model filter mapped to an MBQL model column rendered as a
text-search box instead of a value dropdown after the content was synced
via serdes (git sync).

Root cause: a model's result_metadata is serialized with overrides only
(display_name, semantic_type, ...); the structural :id that links each
column to its underlying Field is dropped on export and never restored on
import, because populate-result-metadata stores passed-in metadata
verbatim during deserialization. Without that link the column can't
resolve has_field_values, so the widget falls back to text search.

Fix: add a generic serdes load-finalize! lifecycle hook that runs after
every entity is loaded (when the referenced Tables/Fields are present),
and implement it for Card to re-derive an MBQL model's result_metadata
field links from its query while preserving the serialized overrides.
This keeps the serialized form unchanged (no :id in the YAML) and avoids
the load-ordering fragility of re-inferring at insert time.

* Drive model metadata restore through model-preserved-keys

Rework the Card load-finalize! to mirror the export: recompute the fresh
columns from the query, then overlay the serialized overrides using the
same lib/model-preserved-keys set (with this model's native flag) that the
export uses to decide what to keep. This replaces the opaque
infer-metadata-with-model-overrides call and removes the separate native
guard — native-ness now flows through model-preserved-keys, so native
models (whose infer-metadata yields nil) keep their already-complete
serialized metadata untouched.

* Remove comments on native models in card.clj

Removed comments about native models and infer-metadata.

* Switch to serializing model result_metadata :id as a portable ref

Carry each model column's :id through serdes as a portable field
reference (via *export-field-fk*), instead of recomputing it on load.
serdes already knows how to remap it on import (import-result-metadata),
and its FK resolution handles load ordering. This drops the load-finalize!
hook (and its serialization/load plumbing) in favor of a one-key change to
the model result_metadata keep-set.

* Re-derive model result_metadata :id on deserialization

Instead of serializing the model column :id (reverted), restore it on
import: when deserializing a model, populate-result-metadata re-derives
the result_metadata from the query (recovering each column's :id link to
its underlying Field) and blends the serialized overrides on top. This
assumes the referenced Fields are already present on the destination; if
they aren't resolvable, it falls back to the passed-in metadata.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-06-25 12:25:33 -05:00
github-automation-metabase
6405d45344 🤖 backported "Fix BigQuery OOM with information_schema queries" (#76480)
Fix BigQuery OOM with information_schema queries (#76459)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-24 17:00:08 -04:00
github-automation-metabase
64670d4ae3 🤖 backported "Fix perms OOM" (#76471)
Fix perms OOM (#76463)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-24 20:55:42 +00:00
github-automation-metabase
b3898f366f 🤖 backported "Consolidate unreferenced-entities e2e flake fixes into one guard" (#76486)
Consolidate unreferenced-entities e2e flake fixes into one guard (#76466)

The dependency graph is recomputed asynchronously on entity create/update,
so waitForBackfillComplete (global one-time flag) doesn't guarantee freshly
seeded entities have surfaced in the unreferenced list. Visiting too early
renders an incomplete list and findByText times out.

Replace the per-test ad-hoc waits with a single waitForUnreferencedAnalysis
helper that polls the unreferenced endpoint until every seeded entity (table
included, via the slower DB-sync path) is present. Apply it to all ten
presence-asserting tests, including five that shared the same race but were
never individually patched. The lone absence-asserting test keeps the global
backfill wait, since presence can't be polled for entities that should be
absent.

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 20:43:36 +00:00
github-automation-metabase
9c8bc2e933 🤖 backported "docs: add page for dashboard heading and text cards" (#76475)
docs: add page for dashboard heading and text cards (#76353)

* docs: add page for dashboard heading and text cards

* Optimized 6 images, saved 251.7 KB (49.2% smaller)

* Remove broken links

* Edits from Jeff

---------

Co-authored-by: Michael Eby <38187276+michaeleby1@users.noreply.github.com>
2026-06-24 20:01:46 +00:00
github-automation-metabase
33234b3216 🤖 backported "Bigquery sync-fields less memory" (#76451)
Bigquery sync-fields less memory (#76441)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-24 14:16:55 -04:00
technomancy
fcbb9f58ba Backport bigquery dataset preservation to 61 (#76310)
Improve dataset creation for Bigquery (#76031)

* Stop deleting datasets for now in bigquery.

* Don't assume the database existing means all tables exist.
2026-06-24 11:11:09 -07:00
github-automation-metabase
a9391a0920 🤖 backported "Respect explicit null collection_id in MCP creation" (#76239)
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2026-06-24 14:02:06 +00:00
github-automation-metabase
c77f3a78d3 🤖 backported "Fix BigQuery getQueryResults pagination" (#76398)
Fix BigQuery getQueryResults pagination (#76396)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-23 23:54:16 -04:00
Alexander Polyankin
e100165d0d v61 flaky test fix (#76393) 2026-06-23 22:06:10 -04:00
github-automation-metabase
564b048d11 🤖 backported "Fix bigquery nested fields sync" (#76391)
Fix bigquery nested fields sync (#76389)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-23 21:40:42 -04:00
github-automation-metabase
0cf9b3b589 🤖 backported "Improve memory usage of sync-tables-and-database!" (#76367) 2026-06-23 20:56:32 -04:00
github-automation-metabase
e305716044 🤖 backported "Notification temp storage across dashboard" (#76370)
Notification temp storage across dashboard (#76355)

Co-authored-by: dpsutton <dan@dpsutton.com>
2026-06-23 21:34:32 +00:00
github-automation-metabase
7f4e9ab325 🤖 backported "Further pdf export improvements" (#76360)
Further pdf export improvements (#76016)

* use cssText

* include font props on <text>, <tspan>, and <textPath> nodes

* re-apply SVG transform after cssText copy in html2canvas patch

Chrome's getComputedStyle().cssText is empty, so the cssText copy wiped the Leaflet grid SVG's transform and offset its serialized bounds, re-breaking the #68751 PDF-export grid fix. Re-apply transform/transform-origin/transform-box after the cssText copy; Firefox keeps the bulk-copy perf win. Re-enable the GridMapPdfExport loki test with its baseline reference.

* resolve vars in svg props

* fix hidden row chart axis labels

* gauge value positioning

* fix gauge chart labels

* re-skip grid map loki test

Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
2026-06-23 19:41:02 +00:00
github-automation-metabase
945f9c6cce 🤖 backported "Validate sqlserver additional options when connecting" (#76332)
Validate sqlserver additional options when connecting (#76293)

validate sqlserver optios when connecting

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-06-23 14:38:23 +00:00
github-automation-metabase
14897ff2d4 🤖 backported "Preserve nulls in day-of-week calculation" (#76283)
Preserve nulls in day-of-week calculation (#76180)

Closes QUE2-687

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-06-23 14:26:32 +00:00
github-automation-metabase
7d6da18f1c 🤖 backported "Add missing dimensions in underlying-records drill-thru" (#75940)
Add missing dimensions in underlying-records drill-thru (#75048)

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-06-23 13:51:25 +00:00
github-automation-metabase
e9222dee07 🤖 backported "Push sync limit to drivers" (#76313) 2026-06-22 19:51:43 -04:00
github-automation-metabase
62b0410b63 🤖 backported "Include dashId in downloaded diagnostics" (#76090)
* Include dashId in downloaded diagnostics (#75803)

* Include dashId in downloaded diagnostics

* e2e test

---------

Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>

* Fixing backport

---------

Co-authored-by: Nathan Voxland <nathan@metabase.com>
Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>
2026-06-22 20:17:36 +00:00
github-automation-metabase
84bae90b78 🤖 backported "Fix appdb reindex misdirecting writes to the live index" (#76271)
* Fix appdb reindex misdirecting writes to the live index (#76199)

* Fixed backport

---------

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-06-22 20:06:52 +00:00
github-automation-metabase
53795444df 🤖 backported "Fix OOM with BigQuery and Redshift sync" (#76291)
Fix OOM with BigQuery and Redshift sync (#76276)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-22 14:26:21 -04:00
github-automation-metabase
31cbef2344 🤖 backported "Dont override lib temporal types if driver type is text" (#76265)
Dont override lib temporal types if driver type is text (#75663)

* dont override lib temporal types if driver type is text

* make test driver agnostic

* fix tests

* add sqlite test

* rename unreliable-driver-type? to prefer-lib-type?



---------

Co-authored-by: Riley Thompson <riley@metabase.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 16:12:13 +00:00
github-automation-metabase
59fbe7ecb5 🤖 backported "Fix OOM with sync metadata for postgres/redshift" (#76258)
Fix OOM with sync metadata for postgres/redshift (#76218)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-22 10:56:46 -04:00
github-automation-metabase
2b12ad47b8 🤖 backported "Fix OOM with json/decode" (#76259)
Fix OOM with json/decode (#76221)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-22 10:47:36 -04:00
github-automation-metabase
7150d11879 🤖 backported "Do not fingerprint MongoDB binary blobs" (#76216)
Do not fingerprint MongoDB binary blobs (#76214)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-21 16:03:29 +00:00
github-automation-metabase
2bb51bf024 🤖 backported "Limit max sync / scan / fingerprint fields" (#76205)
Limit max sync / scan / fingerprint fields (#76189)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-20 01:04:05 +00:00
github-automation-metabase
b2661b179e 🤖 backported "Fix memory leak with notifications and GraalVM" (#76172)
Fix memory leak with notifications and GraalVM (#76145)

* Fix memory leak with table notifications

* fix

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-19 14:02:58 +00:00
github-automation-metabase
e379c402a6 🤖 backported "BigQuery - sample-table with adaptive page size" (#76128)
* BigQuery - `sample-table` with adaptive page size (#76068)

* ci: re-trigger CI

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 21:01:15 -04:00
github-automation-metabase
3beb18ba44 🤖 backported "Fix OOM in table-sync permission setup by SELECTing DISTINCT (#76077)" (#76116)
Fix OOM in table-sync permission setup by SELECTing DISTINCT (#76077) (#76100)

When the sync-and-analyze job creates new tables, `set-default-table-permissions!`
calls `load-perm-context`, which loaded *every* table-level `data_permissions`
row for the database to figure out, per (group, perm-type, schema), the set of
distinct permission values already in use. The result set grew with the table
count, so on databases with very many tables it materialized millions of rows
(2.5M+ `org.postgresql.core.Tuple` / `toucan2.instance.Instance` in the heap
dump that prompted this) and OOM'd the JVM on a scheduler thread.

`schema-vals-idx` only ever needs the *distinct* (group_id, perm_type,
schema_name, perm_value) tuples — it reduces the rows into a set keyed by
(group, perm-type, schema), so duplicate rows were already being collapsed in
memory. Pushing that DISTINCT into SQL makes the loaded set bounded by
groups × perm-types × schemas × values instead of growing with table count.

Rows loaded by the introspection query (measured; one perm-type, one schema):

    groups  tables   before (groups×tables)   after (DISTINCT)
      5       10              100                    20
      5      100            1,000                    20
      5    1,000           10,000                    20   <- flat across tables
     10      100            2,000                    40
     50      100           10,000                   200   <- linear in groups

Before, the result scaled with the table count (the thing that blew up); after,
it is flat across table count and grows only linearly with the number of groups.
The produced `schema-vals-idx` is byte-identical because it accumulates into a
set — verified in a regression test.

Not scoping the query to the synced tables' schemas (a further reduction):
a `schema_name IN (...)` filter would add NULL/casing correctness risk, and the
DISTINCT collapse alone removes the geometric blowup that caused the OOM.

Co-authored-by: dpsutton <dan@dpsutton.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 19:07:30 -05:00
github-automation-metabase
fb34df5d69 🤖 backported "BigQuery - ignore tables and fields with long names that we cannot insert into the app db" (#76108)
BigQuery - ignore tables and fields with long names that we cannot insert into the app db (#76074)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-18 22:23:27 +00:00
github-automation-metabase
fa3d329acd 🤖 backported "downgrade apache sshd-core" (#76112)
downgrade apache sshd-core (#76104)

Why? Because 3.0.0-M2 leaks connections.

 Revert mina-sshd 3.0.0-M2 → 2.16 to stop SSH channel leak OOMs

  3.0.0-M2 leaks one KexListener per SSH port-forward channel, never
  removed on channel close. Each Metabase DB query over an SSH tunnel
  opens and closes a `direct-tcpip` channel, so the listener list grows
  unbounded for the life of the (long-lived) SSH connection.

  Heap dump from a crashed prod instance (highway):
  - 1 SSH connection, but 67,221 closed TcpipClientChannel /
    Nio2Session / socket-channel objects retained.
  - KexFilter.listeners (a session-scoped CopyOnWriteArrayList) retained
    572 MB ≈ 38% of heap — full of AbstractChannel$$Lambda instances,
    each capturing one closed channel.

  Root cause in mina-sshd 3.x (dev_3.0 branch):
  - AbstractChannel.init() registers an anonymous KexListener on the
    session for every channel and never removes it on close.
  - AbstractSession.removeKexListener() is also broken: it calls
    addKexListener() instead of remove (copy-paste bug), so even a
    correct removal attempt adds a duplicate.

  The 2.x lineage has no KexFilter / addKexListener / per-channel KEX
  listener — the filter-chain mechanism is entirely new in 3.x — so the
  leak cannot occur on 2.16.

Co-authored-by: dpsutton <dan@dpsutton.com>
2026-06-18 17:09:21 -04:00
github-automation-metabase
ddc125a55a 🤖 backported "Pass scopes through the oidc check function" (#76078)
Pass scopes through the oidc check function (#75416)

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-06-18 17:19:35 +00:00
github-automation-metabase
8ce24cf4da 🤖 backported "Avoid duplicate app db requests in public and embedded cards and dashboards" (#76041)
Avoid duplicate app db requests in public and embedded cards and dashboards (#76026)

* In-memory entity cache for public/embed paths

* now cards

* fix

* fix

* fixes

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* ffix tests

* fix

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-17 16:57:02 -07:00
github-automation-metabase
11c20483af 🤖 backported "Expose Mcp-Session-Id header for CORS requests" (#76007)
Expose Mcp-Session-Id header for CORS requests (#75838)

Co-authored-by: Rodrigo López Dato <rodrigo@metabase.com>
2026-06-17 14:58:39 +00:00
github-automation-metabase
b75717f87e 🤖 backported "Fix memory leak with GraalVM" (#76000)
Fix memory leak with GraalVM (#75982)

* Fix memory leak with GraalVM

* fix

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-17 13:43:15 +00:00
github-automation-metabase
65a69a3ba4 🤖 backported "Fix dependencies cycles handling" (#75980)
Fix dependencies cycles handling (#75978)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-17 02:11:38 +00:00
github-automation-metabase
551dc9a759 🤖 backported "Introduce multi-process stale-while-revalidate behavior with query caching" (#75965)
Introduce multi-process stale-while-revalidate behavior with query caching (#75949)

* Cache empty query results

* Introduce stale-while-revalidate behavior with query caching

* fix author

* fix

* fix

* fix

* fix

* fix

* fix migrations

* fix

* fix

* review comments

* fix

* fix mysql

* fix

* fix

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-16 13:46:08 -07:00
github-automation-metabase
35f2fd467c 🤖 backported "Fix lib-related N+1 issues with DB calls in parameters" (#75960)
* Fix lib-related N+1 issues with DB calls in parameters (#75888)

* remove duplications

* dedup

* fix

* add tests

* fix

* bulk load

* fix

* fixes

* tests

* fixes

* fix

* fixes

* fix

* fix

* fixes

* fix

* fix /values

* fix

* fix

* fix index usage

* fix import

---------

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
Co-authored-by: Aleksandr Lesnenko <alxnddr@gmail.com>
2026-06-16 20:42:29 +00:00
github-automation-metabase
f1b43a3aa1 🤖 backported "Cache empty query results" (#75953)
Cache empty query results (#75946)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-16 11:55:03 -07:00
github-automation-metabase
62a77b2328 🤖 backported "deleted database handling for transforms" (#75852)
deleted database handling for transforms (#74829)

* wip

* wip: fe

* tests

* tests

* serialize as tombstone

* mark dead transforms as stale

* fix tests

* fix

* fix migration

* move check

* fix FE when no dbs are available

* fix e2e test

* handle nil database in validation

* fix for broken dependency surfacing

* fix comment

* lenient for serdes

* fix: don't display while loading

* refactor

* handle migration comments

* move migration to 60

* handle N+1 in batched-hydrate for source-readable? by providing a per-model cache

* refactor

* random ids

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-06-16 09:45:22 +01:00
github-automation-metabase
68c1b838ec 🤖 backported "Return nested fields from sandboxed queries" (#75741)
Return nested fields from sandboxed queries (#75358)

* Return nested fields from sandboxed queries

Closes QUE2-651

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-06-16 01:47:05 +03:00
github-automation-metabase
d9fb5a68e8 🤖 backported "docs: collections in the library" (#75880)
docs: collections in the library (#75448)

* collections in the library

* Optimized 3 images, saved 123.3 KB (51.4% smaller)

* Apply suggestions from code review



* edits from jeff

---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-06-15 15:18:21 +00:00
github-automation-metabase
65d104b382 🤖 backported "Fix history.back 404 when Metabase is under a sub-path in interactive embedding" (#75855)
Fix history.back 404 when Metabase is under a sub-path in interactive embedding (#75700)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-06-15 09:23:28 +00:00
github-automation-metabase
9b2032b6c0 🤖 backported "Fix flaky with-query-execution! test helper delivering stale batched executions" (#75834)
Fix flaky with-query-execution! test helper delivering stale batched executions (#75831)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-13 02:23:48 +00:00
github-automation-metabase
b46eec8160 🤖 backported "Improve app db connection pool usage with public dashboards" (#75819)
Improve app db connection pool usage with public dashboards (#75770)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-06-12 16:55:26 -07:00
github-automation-metabase
98ea989615 🤖 backported "GDGT-2617 Hide Security Center setup prompt from non-admins" (#75824)
GDGT-2617 Hide Security Center setup prompt from non-admins (#75690)

* Hide Security Center setup prompt from non-admins (GDGT-2617)

The SecurityCenterPromoCard rendered for all users in the navbar promo slot,
but links to the admin-only /admin/security-center route, so non-admins hit a
permission error on click. Gate the card on getUserIsAdmin and skip its
admin-only RTK Query requests for non-admins.

* GDGT-2617: Refactor tests

* GDGT-2617 small adjustment

---------

Co-authored-by: Stas Gavrylov <stas@metabase.com>
Co-authored-by: Egor Grushin <egor@metabase.com>
2026-06-12 23:36:46 +00:00
github-automation-metabase
4456a41ba0 🤖 backported "[BOT-1685] Fix UX of Metabot inline SQL search" (#75799)
[BOT-1685] Fix UX of Metabot inline SQL search (#75772)

* Scope inline SQL @-mention search to the selected database via table_db_id

* Recreate the Metabot prompt tiptap editor on database change so @-mention scoping doesn't use a stale onlyDatabaseId

Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-06-12 16:12:12 +00:00
github-automation-metabase
5309085745 🤖 backported "Fall back on effective-type for time fields" (#75758)
Fall back on effective-type for time fields (#75442)

Closes QUE2-650

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-06-11 22:01:21 +00:00
github-automation-metabase
b9fcba4a8c 🤖 backported "Fix filter not respected when downloading visualizer-affected card from dashboard" (#75746)
Fix filter not respected when downloading visualizer-affected card from dashboard (#75720)

Fix filtered dashboard card xlsx downloads

Co-authored-by: Anderson <ands.alves.nunes@gmail.com>
2026-06-11 19:51:31 +00:00
github-automation-metabase
a738f158dd 🤖 backported "Fix Athena sync failing on partitioned Iceberg tables" (#75732)
Fix Athena sync failing on partitioned Iceberg tables (#75666)

Syncing a partitioned Iceberg table left it with no fields at all.

When the driver can't get field metadata over JDBC it runs `DESCRIBE` instead (#43980, #58441). `DESCRIBE` output ends with a partition section that re-lists the partition columns. For Hive tables
they reappear with their real types and were already deduplicated; for Iceberg tables they reappear with the partition transform (e.g. `identity`) where the type should be. The parser treated
those rows as real columns, couldn't map `identity` to a base type, and the insert for *all* of the table's fields failed on the null base type.

Fix:

- Stop parsing `DESCRIBE` output at the partition section — it never contains new columns. (Section markers verified against Hive's `TextDescTableFormatter`, where this format comes from; link in
the code.)
- Also drop any row that repeats an earlier column name, in case the marker text ever changes.
- Unknown database types now fall back to `:type/*` instead of failing the sync, same as the JDBC path.

Co-authored-by: Timothy S. Dean <timothy.dean@metabase.com>
2026-06-11 18:37:14 +00:00
github-automation-metabase
51be9b374b 🤖 backported "Tidy query preprocessing, dataset streaming, and permission helpers" (#75712) 2026-06-11 13:03:44 -05:00
github-automation-metabase
281a469745 🤖 backported "Add JVM hardware info to diagnostic details" (#75725)
Add JVM hardware info to diagnostic details (#75561)

Include the number of CPUs, max JVM memory, and total physical host
memory in the bug-reporting diagnostic info, under the system-info
section. The max JVM figure matches the "Maximum memory available to
JVM" line printed at startup; the total host memory shows how much
the machine actually has, making it easier to diagnose performance
problems from a bug report.

Co-authored-by: Rodrigo López Dato <rodrigo@metabase.com>
2026-06-11 17:31:29 +00:00
github-automation-metabase
27c36a9a92 🤖 backported "Support query_handle consistently across MCP tools" (#75520)
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2026-06-11 14:38:38 +02:00
github-automation-metabase
80242537f7 🤖 backported "Report where MCP saves questions; default to personal collection" (#75513)
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-06-11 14:38:26 +02:00
github-automation-metabase
4a7a53f63d 🤖 backported "Further MCP improvements for HTTPS detection" (#75635)
Further MCP improvements for HTTPS detection (#75531)

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-06-10 17:06:57 +00:00
Mahatthana (Kelvin) Nomsawadi
d3c7e37a71 chore: after gold SDK release checklist on release-x.61.x (#75489)
chore: remove 'latest' tag step from release-x.61.x SDK workflow
2026-06-10 07:59:36 +01:00
Alexander Polyankin
a51cba9fc3 [61] Fix regexp QP perf issue (#75553)
* fix perf with qp regex

* test
2026-06-09 16:59:03 -04:00
Alexander Polyankin
94c9bb202d [61] Stop logging invalid template tag params (#75533)
* Stop logging invalid template tag params

* fix test
2026-06-09 13:07:28 -07:00
github-automation-metabase
7c4dd3ea79 🤖 backported "Show clearer permission state on inaccessible metric pages" (#75529)
Show clearer permission state on inaccessible metric pages (#75068)

* Show clearer permission state on inaccessible metric pages

* Update read-only metric e2e test to assert the new permission screen

* Detect metric view permission from the query result, not can_run_adhoc_query

* Lighten MetricPageCard doc comment

* Restore read-only metric test: read-only users can view, editing restricted

* Extract setupForbiddenCardEndpoints test helper for forbidden card mocks

Co-authored-by: Andrei Chernykh <andrei.chernykh@metabase.com>
2026-06-09 18:58:32 +00:00
github-automation-metabase
a3c2905430 🤖 backported "Missed spots with deprecating /mcp route" (#75516)
Missed spots with deprecating /mcp route (#75500)

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-06-09 18:32:39 +00:00
github-automation-metabase
f104a2bd2d 🤖 backported "add admin transforms redirect routes" (#75506)
add admin transforms redirect routes (#75497)

Co-authored-by: Aleksandr Lesnenko <alxnddr@users.noreply.github.com>
2026-06-09 17:04:23 +00:00
Chris Truter
4fdb803d99 🤖 Backport MCP canonical URL + OAuth proxy discovery to v61 (#75025, #75110) (#75494)
Fix MCP OAuth discovery behind a TLS-terminating proxy (#75110)
2026-06-09 16:13:43 +00:00
github-automation-metabase
066aeeaab2 🤖 backported "Fix password-disable when using OIDC" (#75492)
Fix password-disable when using OIDC (#75361)

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-06-09 15:35:16 +00:00
github-automation-metabase
48db31ce3c 🤖 backported "Fix flaky homepage x-ray save test" (#75437)
Fix flaky homepage x-ray save test (#75424)

The "Save this" button on automatic (x-ray) dashboards rendered enabled as
soon as the header was visible, regardless of whether the dashboard had
finished loading. Clicking it before `dashboard` was populated fired a
false `x-ray_saved` analytics event and no-opped `save()`, so no
`POST /api/dashboard/save` request was ever sent.

* Disable the button until `dashboard` is loaded.

* Assert x-ray save via the saved-state UI, not the request alias

cy.wait("@saveDashboard") flaked with 'No request ever occurred' because
it depends on catching the POST in a timing window. Assert the real
user-observable outcome instead: after clicking 'Save this', the header
switches to a 'Saved' button + 'See it' link. Cypress retries that until
the save lands, which (with the disabled-until-loaded guard) is robust.

* Wait for x-ray dashboard readiness (param URL sync) before saving

the dashboard isn't in the store yet. useDashboardUrlQuery syncs the dashboard
params into the URL query string once it's loaded, so a populated location.search
is a reliable readiness signal. Clicking Save before then loses the save.

---------

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 21:58:56 +00:00
github-automation-metabase
a72c57efd8 🤖 backported "Set oauth dynamic registration to false when MCP is disabled" (#75411)
* Set oauth dynamic registration to false when MCP is disabled (#74923)

* update module dep map

---------

Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>
2026-06-08 18:11:58 +00:00
github-automation-metabase
143faf7ea8 🤖 backported "Relax schema in security center" (#75390)
Relax schema in security center (#75382)

security center: relax semver schema

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-06-08 11:28:01 +00:00
github-automation-metabase
75616cb21a 🤖 backported "Check for VolumeOperationAllowedLocalPaths connecting to databricks." (#74927)
* Check for VolumeOperationAllowedLocalPaths connecting to databricks. (#74375)

* Check for VolumeOperationAllowedLocalPaths connecting to databricks.

* Move additional options check to validate-db-details! method.

* Review suggestions: check a few other cases.

* trigger rebuild

---------

Co-authored-by: technomancy <technomancy@gmail.com>
Co-authored-by: Phil Hagelberg <phil@hagelb.org>
2026-06-05 17:20:40 +00:00
github-automation-metabase
02ba9730f2 🤖 backported "Clear setup-token after use" (#75317)
Clear setup-token after use (#75198)
# Conflicts:
#	test/metabase/setup_rest/api_test.clj

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-06-05 11:51:46 -05:00
github-automation-metabase
a1cab4e0df 🤖 backported "Use unicode regex for bigquery alias" (#75297)
* Use unicode regex for bigquery alias (#75123)

* use unicode regex for bigquery alias

* trigger ci

* fix workspace e2e test

* trigger ci

---------

Co-authored-by: Riley Thompson <riley@metabase.com>
Co-authored-by: Riley Thompson <rileythompson99@gmail.com>
2026-06-05 12:08:17 -04:00
github-automation-metabase
3b53aeabe3 🤖 backported "GHY-3805: Make check-card dependency response order deterministic" (#75324)
GHY-3805: Make check-card dependency response order deterministic (#75300)

`broken-cards-response` built `:bad_cards`/`:bad_transforms` from
`(keys card)` and an `IN` query with no `:order-by`, so the response
order was nondeterministic. This flaked
check-card-hydrates-dashboard-and-document-test, which matches
`:bad_cards` positionally via `=?`.

Achieve the deterministic ordering by sorting `created_at` anchored by `id`.
---------

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ben Grabow <ben.grabow@metabase.com>
2026-06-05 16:05:57 +00:00
github-automation-metabase
8b1fc62c7c 🤖 backported "Default appdb checkoutTimeout to 30s" (#75320)
Default appdb checkoutTimeout to 30s (#75051)

Change c3p0 checkoutTimeout to 30s to avoid forever-waits for connections

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-06-05 15:55:22 +00:00
github-automation-metabase
98acda6931 🤖 backported "fix: pin commons-configuration2 2.15.1 in Databricks driver" (#75314)
fix: pin commons-configuration2 2.15.1 in Databricks driver (#75228)

databricks-jdbc-thin 3.3.1 transitively pulls commons-configuration2
2.11.0 which has CVE-2026-45205 (Uncontrolled Recursion, Medium).
Fixed in 2.15.0; 2.15.1 is the latest patch on that line.

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 15:05:49 +00:00
github-automation-metabase
f42985be1e 🤖 backported "fix: pin netty 4.1.135.Final in Snowflake driver" (#75315)
fix: pin netty 4.1.135.Final in Snowflake driver (#75229)

AWS SDK (transitive via snowflake-jdbc-thin) pulls io.netty 4.1.126.Final
which has SNYK-JAVA-IONETTY-16438930 (Data Amplification, High) and
SNYK-JAVA-IONETTY-16438322 (Resource Exhaustion, High). Both fixed in
4.1.133.Final.

Pin netty-codec, netty-handler, and netty-transport-classes-epoll to
4.1.135.Final to ensure the full netty family is on a consistent version.

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 14:01:30 +00:00
Mahatthana (Kelvin) Nomsawadi
7935904c04 Fix flaky theme editor main colors test on release-61 (#75100)
Fix super flaky test on 61 branch
2026-06-05 16:34:57 +08:00
github-automation-metabase
3b365ae045 🤖 backported "GHY-3804 require superuser for remote-sync current-task endpoints" (#75286)
GHY-3804 require superuser for remote-sync current-task endpoints (#75276)

The /current-task and /current-task/cancel endpoints were protected only
by +auth, letting any authenticated user read sync task details and cancel
active sync operations. Add api/check-superuser to both, matching the rest
of the remote-sync API.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-06-04 21:59:08 +00:00
github-automation-metabase
a4e1973267 🤖 backported "GHY-3801 preserve result_metadata for native cards on serdes export" (#75281)
GHY-3801 preserve result_metadata for native cards on serdes export (#75259)

* GHY-3801 preserve result_metadata for native cards on serdes export

Native (non-model) cards can't have their result_metadata re-derived at
import time the way MBQL cards can: query->expected-cols computes MBQL
columns statically from synced metadata, but a native query is opaque SQL
that can only be introspected by executing it. GHY-3339 dropped
result_metadata from all non-model exports on the assumption that import
re-derives it; that holds for MBQL but leaves native source cards with
nil metadata, which breaks downstream GUI questions that join them
("Error adding alias info to join...").

Export the full result_metadata for native non-model cards (mirroring the
pre-GHY-3339 transforms that import-result-metadata already inverts),
while keeping the lean model export and the MBQL non-model skip.

* Whitelist exported keys + assert column names (PR review)

- export-result-metadata: switch the native branch from a blacklist
  (dissoc :fingerprint) to an explicit keep-keys whitelist, so internal
  :lib/* and other computed keys aren't serialized — the bloat GHY-3339
  set out to remove. Keeps the structural type info native needs plus the
  display soft-keys and FK transforms.
- e2e test: assert the imported native card's result_metadata column names
  match the source, not just that metadata is non-empty.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-06-04 21:13:24 +00:00
github-automation-metabase
1b7243d89a 🤖 backported "GHY-3802 capture extract-phase logs in serialization export.log" (#75264)
GHY-3802 capture extract-phase logs in serialization export.log (#75255)

* GHY-3802 capture extract-phase logs in serialization export.log

The serialization export endpoint hoisted `extract/extract` out of the
`for-ns` log-capture block so invalid-input errors could still set a
non-200 HTTP status. But escape-analysis warnings (content referencing
entities outside the requested collections) are emitted eagerly during
`extract`, so they were logged outside the capture and never reached
`export.log` — leaving customers with a valid archive and an empty log.

Capture the eager extract logs into the same buffer that becomes
`export.log`, and write that buffer after the storage appender has closed
(and flushed). Add a regression test asserting the escape warning lands in
`export.log`.

* kick ci

* Rename serialization-log-nses to serialization-logger-prefixes

These are log4j2 logger-name prefixes, not loaded namespaces. Clarify the
name and docstring so it's obvious each prefix captures every logger nested
under it.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-06-04 19:56:40 +00:00
github-automation-metabase
065979b4c8 🤖 backported "docs: update metabase store UI paths" (#75272)
docs: update metabase store UI paths (#75136)

* docs: update metabase store UI paths

* fix links

* edits from jeff

Co-authored-by: Michael Eby <38187276+michaeleby1@users.noreply.github.com>
2026-06-04 19:31:51 +00:00
github-automation-metabase
32d2f6cf54 🤖 backported "docs: add drill-through page" (#75270)
docs: add drill-through page (#74922)

* docs: add drill-through page

* fix broken links

* edits from alex

* add note about drag to select

Co-authored-by: Michael Eby <38187276+michaeleby1@users.noreply.github.com>
2026-06-04 19:31:24 +00:00
github-automation-metabase
b47885d8ed 🤖 backported "Docs: backfill create_question + create_dashboard MCP tools" (#74660)
Co-authored-by: bryan <bryan.maass@gmail.com>
2026-06-04 12:28:38 -06:00
github-automation-metabase
530dfbd23e 🤖 backported "Optimize temporal expressions the same as filters" (#75241)
Optimize temporal expressions the same as filters (#74875)

* optimize expressions

* rename for accuracy

* group into one middleware

* include a between filter in test

* rename filter to clause

* improve check for day bucketing, add tests

* fix kondo and a test

* fix test and kondo

* fix test for drivers

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-06-04 15:05:57 +00:00
github-automation-metabase
c16f880add 🤖 backported "[perf] Don't perform redundant idempotent zone-id conversion in format-rows-xform" (#74725)
[perf] Don't perform redundant idempotent zone-id conversion in format-rows-xform (#74326)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-06-04 14:15:40 +00:00
github-automation-metabase
b03cabce20 🤖 backported "[perf] Optimize metabase.search.spec for wide tables" (#75018)
[perf] Optimize metabase.search.spec for wide tables (#74706)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-06-04 12:08:10 +00:00
github-automation-metabase
e231432adc 🤖 backported "Improve error messages for unsupported H2 functions" (#75191)
Improve error messages for unsupported H2 functions (#75149)

* Improve error messages for unsupported H2 functions

* regenerate sample db

Co-authored-by: Ben Grabow <ben.grabow@metabase.com>
2026-06-03 17:56:45 +00:00
github-automation-metabase
169ff8d29b 🤖 backported "[perms] Fix N+1 query in sandboxing when updating perms" (#74911)
[perms] Fix N+1 query in sandboxing when updating perms (#74734)

Even when sandboxing is not used, 1 read was being generated for each
group X table whose permissions were impacted by the update. In a sample
query from a user, that was 28 groups X 382 tables =  10,696 reads.

This change includes a unit test that synthesizes this same scale and
makes the same request. It did reproduce the same 10,700+ requests the
user was seeing initially, but with these changes to how sandboxes are
checked (one query per database) it now only makes 87 queries. That's
still a lot but not unreasonable for a complex request.

Co-authored-by: Braden Shepherdson <braden@metabase.com>
2026-06-03 13:48:35 +00:00
github-automation-metabase
520d4aee60 🤖 backported "Stop using temporary fork of cljfmt" (#75066)
* Stop using temporary fork of cljfmt (#75062)
# Conflicts:
#	bb.edn

* 🤖 backported "Revert formatting-breaking commits" (#74924)

Revert formatting-breaking commits (#74921)

* Revert "Use existing commit for cljfmt (#74885)"

This reverts commit d4d8c53d83.

* Revert "Fix cljfmt formatting  (#74892)"

This reverts commit ef186e4a60.

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
# Conflicts:
#	bb.edn

---------

Co-authored-by: Cam Saul <github@camsaul.com>
Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2026-06-02 10:33:42 -07:00
github-automation-metabase
a401fedffe 🤖 backported "Fix XLSX exports dropping the currency symbol for non-currency columns" (#75116)
Fix XLSX exports dropping the currency symbol for non-currency columns (#75026)

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-06-02 15:03:28 +00:00
github-automation-metabase
cfb1b5e3fd 🤖 backported "[GDGT-2421] field metadata sandboxing" (#75087)
[GDGT-2421] field metadata sandboxing (#74902)

Row & column security already trims columns in query results; this extends the same trimming to the read-only field listings (Data Reference / table metadata) so a sandboxed group sees a consistent column set everywhere. This is a QoL change for sandboxed viewers, OSS/non-sandboxed users and admins are unaffected.

Co-authored-by: YB <yeowboon@metabase.com>
2026-06-02 11:27:50 +00:00
github-automation-metabase
a4632ead7a 🤖 backported "Handle nil subtotal nodes" (#74844)
Handle nil subtotal nodes (#74605)

Closes #71220
Closes QUE2-460

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-06-01 19:48:19 +00:00
github-automation-metabase
b3c559d654 🤖 backported "Increase timeout for Eastwood test job to 45 minutes" (#75043)
Increase timeout for Eastwood test job to 45 minutes (#75041)

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-06-01 19:54:57 +02:00
github-automation-metabase
ff44ee3bd3 🤖 backported "Report synchronous bigquery errors as client errors" (#74847)
Report synchronous bigquery errors as client errors (#74684)

* Report synchronous bigquery errors as client errors

Closes QUE2-487

* Add missing type hint

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-06-01 16:55:07 +00:00
github-automation-metabase
329d63e6b0 🤖 backported "Prevent writing queries failing the schema checks in the DB" (#75030)
Prevent writing queries failing the schema checks in the DB (#74884)

Closes QUE2-622

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-06-01 15:12:00 +00:00
github-automation-metabase
56cd3fb3b6 🤖 backported "Only re-validate incremental checkpoint field when source changes" (#74899)
* Only re-validate incremental checkpoint field when source changes (#74752)

* Use existing commit for cljfmt

Backport of #74885 — the previous commit (05886be) is no longer
reachable from any branch tip in camsaul/cljfmt, breaking classpath
resolution for Kondo, Cljfmt, Mage, and lint-migrations CI jobs.

* Fix cljfmt: remove blank lines inside cond forms

The pinned cljfmt (camsaul/cljfmt@d7e7bb9, PR #420) strips blank lines
between cond clauses. Reformat the two offending files so
cljfmt-all --force-check passes.

---------

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-06-01 12:12:44 +00:00
github-automation-metabase
375ca4e576 🤖 backported "[match] Use metabase.util.match macros directly instead of driver-api aliases" (#75007)
[match] Use metabase.util.match macros directly instead of driver-api aliases (#73987)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-05-31 11:24:47 +00:00
github-automation-metabase
5fa6369d15 🤖 backported "[perf] Implement efficient insertion in add-pivot-grouping" (#75008)
[perf] Implement efficient insertion in add-pivot-grouping (#74325)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-05-31 10:29:33 +00:00
github-automation-metabase
87bb53c8c7 🤖 backported "Fix pivot setting comparison when migrating from refs" (#74974)
Fix pivot setting comparison when migrating from refs (#74972)

* Fix pivot setting comparison when migrating from refs

* fix

* fix

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-29 15:41:16 +00:00
github-automation-metabase
a31123a293 🤖 backported "Distinguish empty Metabot prompt regeneration from errors" (#74907)
Distinguish empty Metabot prompt regeneration from errors (#74406)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Chris Truter <chris@metabase.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-29 09:23:54 -04:00
github-automation-metabase
fec2e5a31a 🤖 backported "Make default embedding service base url match AI service" (#74842)
Make default embedding service base url match AI service (#74756)

* Set the default of embedding service base to ai service

* components

* test

* Apply suggestion from @crisptrutski



* review remarks

* review remarks

* test and trim

---------

Co-authored-by: lbrdnk <lbrdnk@users.noreply.github.com>
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-29 14:34:32 +02:00
github-automation-metabase
a21eb6382b Backport create_question permission checks to v61 [from #74458] (#74869)
Backport create_question permission checks to v61

The agent-api POST /v1/question (create_question) endpoint called
queries/create-card! directly without the pre-checks that the REST
POST /api/card/ endpoint runs. Add them so the agent-api path matches
REST behavior: the caller must be able to run the proposed query and
must have write access to the target collection.

Backports just the create_question checks from #74458 (29d703e9d2).
The rest of that PR is new feature work (execute_sql, read_resource,
write tools, update_question) and is not backported.

Adds test coverage for both 403 cases.

Co-authored-by: Bryan Maass <bryan.maass@gmail.com>
2026-05-29 06:28:18 -04:00
github-automation-metabase
72eabe67a4 🤖 backported "BOT-1524 Save tables used in a metabot conversation" (#74925)
BOT-1524 Save tables used in a metabot conversation (#74254)

* BOT-1524 Save data sources used in a metabot conversation

* Handle implicit joins

* Simplify query-tables-and-cards with lib/all-referenced-entity-ids

* Cleanup native-tables parsing, remove uneeded macaw-tables

* Use lib more. banish mbql destructuring and raw mbql in tests

* Remove MetabotUsedTable from module config model-exports since nothing external uses it

* banish deprecated mt/mbql-query in tests

* Clean up docstrings in used_tables.clj

* Calm down with "MBQL 5" references

* Improve description for metabot_used_table

* Push seq down into when

* whitespace cleanups

* Use with-temp rather than t2/insert! and t2/delete! in used table tests

* Small formatting cleanup in persistence tests

* Use t2/with-transaction :rollback-only true rather than mt/with-model-cleanup in persistence tests

* Cleanup finalize-records-used-tables-test

* Formatting cleanup in ->transform-sql-parts and ->transform-python-parts

* More persistent test cleanups

* Remove private mp defn for metadata provider

* Rename provider to mp

* Formatting cleanups in used tables tests

* Pass in metadata-provider to extract-used-tables defaulting to lib-be/application-database-metadata-provider

And update the tests to take advantage of not need real cards in the app db and mark tests ^:parallel

* Use mt/with-dynamic-fn-redefs and mark tests ^:parallel

* Remove explicit ^:sequential

* Improve testing description strings

* Improve combined tool calls used tables test

* Only visit cards shared between tool calls once in extract-used-tables

* Fix trailing whitespace

* Fix lint warning about with-redefs

* Fix race in app-mention-triggers-response-test

* Add prometheus metrics for used table extraction duration and failures

* Move used tables extraction to background thread

* review feedback: remove macaw mentions

* review feedback: composed transducer rather than lazy seq

* review feedback: add some debug logs with more context

* review feedback: add bounded queue and increase threads to 4 for the used tables extraction executor

* Add 15s timeout via u/with-timeout for used tables extraction

* Add separate worker and waiter executors to ensure work remains bounded

Don't use u/with-timeout which runs the extraction on the shared future thread pool. Instead, create a separate worker
pool to ensure worker threads remain bounded even if they are stuck in cpu-bound state where they ignore thread
cancellations.

* Rename extract-used-tables-with-timing to extract-used-tables-with-timing!

* Allow idle threads to be reclaimed after 5 minutes

* Add test for extraction timeout and improve test for queue saturation

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-05-28 19:22:46 +00:00
github-automation-metabase
4ff74eb4cd 🤖 backported "Revert formatting-breaking commits" (#74924)
Revert formatting-breaking commits (#74921)

* Revert "Use existing commit for cljfmt (#74885)"

This reverts commit d4d8c53d83.

* Revert "Fix cljfmt formatting  (#74892)"

This reverts commit ef186e4a60.

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2026-05-28 11:48:46 -07:00
github-automation-metabase
07c02fdf05 🤖 backported "bump qs" (#74915)
bump qs (#74898)

deps: bump qs ^6.14.2 -> 6.15.2

Fixes Snyk medium-severity advisory SNYK-JS-QS-16721866
(CVE-2026-8723, NULL pointer dereference in stringify with
arrayFormat=comma + encodeValuesOnly when arrays contain
null/undefined).

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
2026-05-28 16:39:10 +00:00
lbrdnk
0cb7730e09 Backport 61 "Fix ai proxied /embeddings" (#74526) (#74901)
* Backport "Fix ai proxied /embeddings" (#74526)

* Use existing commit for cljfmt (#74885)

* Fix cljfmt formatting  (#74892)

Fix formatting

---------

Co-authored-by: Romeo Van Snick <romeo@romeovansnick.be>
2026-05-28 17:44:02 +02:00
github-automation-metabase
8567988884 🤖 backported "Auto-add issues only to open milestones" (#74878)
Auto-add issues only to open milestones (#74871)

only auto-add issues to open milestones

Co-authored-by: Ryan Laurie <30528226+iethree@users.noreply.github.com>
2026-05-27 22:53:11 +00:00
github-automation-metabase
4608caa479 🤖 backported "docs - adds dedicated AI privacy page" (#74866)
docs - adds dedicated AI privacy page (#74669)

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-27 20:16:45 +00:00
github-automation-metabase
9f1cb513c1 🤖 backported "Add clj-surgeon config" (#74857)
Add clj-surgeon config (#74855)

Co-authored-by: bryan <bryan.maass@gmail.com>
2026-05-27 18:32:57 +00:00
github-automation-metabase
d9f0d6b186 🤖 backported "Don't strip values from impersonation validation" (#74834)
* Don't strip values from impersonation validation (#74440)

* dont strip values from impersonation validation

* add test

* strip values for graalpy, but return original values

* add tests that strip multi statement queries

* formatting

---------

Co-authored-by: Riley Thompson <riley@metabase.com>
Co-authored-by: Riley Thompson <rileythompson99@gmail.com>
2026-05-27 17:56:43 +00:00
github-automation-metabase
2168ec9fdd 🤖 backported "Bound pr-str when logging LLM errors" (#74830)
* Bound pr-str when logging LLM errors (#74796)

* Thou shalt not space things out

---------

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2026-05-27 16:19:15 +00:00
github-automation-metabase
d769eefc87 🤖 backported "docs - you no longer have to be store admin" (#74841)
docs - you no longer have to be store admin (#74837)

Refine Metabase AI settings documentation

Removed redundant information about the Metabase AI add-on visibility and the requirement for admin email matching. Clarified the process for enabling and disconnecting the Metabase AI Service.

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-05-27 15:56:22 +00:00
github-automation-metabase
8c2c8fedb8 🤖 backported "Fix NullPointerException in render-minibar with missing range stats" (#74708)
Fix NullPointerException in render-minibar with missing range stats (#74572)

* Fix NullPointerException in render-minibar with missing range stats

render-minibar assumed the column fingerprint always supplied min and max
values. When a minibar-enabled column lacks :type/Number fingerprint stats
(e.g. an unsynced or computed column), min and max are nil and (< min 0)
throws a NullPointerException that crashes the entire Slack/email notification
delivery silently. Guard against nil min/max and fall back to rendering the
plain formatted value, consistent with the existing non-numeric fallback.

* Isolate per-card render failures in dashboard subscriptions

A single card that fails to render (e.g. the minibar NPE in #74007, or any
future render error) could take down an entire dashboard subscription: the
per-card try/catch in render-pulse-card-body swaps in a :render-error
placeholder, but render methods build the body as lazy hiccup, so an
exception thrown while building it escapes that catch and only fires later
when the channel realizes the hiccup (email: hiccup/html; Slack: PNG
rendering) — outside any per-card boundary, aborting the whole notification.

Add explicit per-part isolation at the assembly boundary, where each part is
already rendered and realized one at a time:
- email :notification/dashboard reduce wraps each part's render+html
- slack :notification/dashboard mapcat wraps each part->sections!

On failure the part degrades to the existing error placeholder (new
channel.render/error-rendered-part for email; an equivalent section block for
Slack) and the remaining cards still deliver. This catches any part failure,
lazy or not, and leaves the happy path unchanged.

* Simplify min/max nil check in render-minibar

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-27 07:41:33 +00:00
Cam Saul
5e06fbcbc3 Enable cljfmt remove-blank-lines-in-forms? [61] (#74803) 2026-05-26 15:33:01 -07:00
github-automation-metabase
888fd6a6e9 🤖 backported "Surface upstream body in LLM provider error logs and messages" (#74793)
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
fix for the same gap addressed in PR #74356
2026-05-27 00:00:50 +02:00
github-automation-metabase
fb0577a074 🤖 backported "[Metabot] Show message feedback UI in OSS" (#74593)
[Metabot] Show message feedback UI in OSS (#74555)

* show feedback buttons in self-hosted instances

* clean up ishosted flag in setup fn

* clean up

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
2026-05-26 20:59:04 +00:00
github-automation-metabase
522257eef9 🤖 backported "Stop dropping old datasets from snowflake." (#74672)
Prevent snowflake tests from deleting old data and failing build.

Manual backport of https://github.com/metabase/metabase/pull/74663

Co-authored-by: Phil Hagelberg <phil@hagelb.org>
2026-05-26 20:17:48 +00:00
github-automation-metabase
c809dec3d7 🤖 backported "MCP: signal tool-list changes to clients (GHY-3661)" (#74787)
Co-authored-by: bryan <bryan.maass@gmail.com>
2026-05-26 20:11:36 +00:00
github-automation-metabase
280f65c6d7 🤖 backported "docs - bitbucket remote sync" (#74792)
docs - bitbucket remote sync (#74772)

bitbucket tokens

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-26 19:52:47 +00:00
github-automation-metabase
e92d71f2ae 🤖 backported "Allow Admins to purchase Metabot AI" (#74786)
Allow Admins to purchase Metabot AI (#74602)

* Allow Admins to purchase Metabot AI

* update unit tests

Co-authored-by: Ryan Laurie <30528226+iethree@users.noreply.github.com>
2026-05-26 13:16:07 -06:00
github-automation-metabase
8b77de7b30 🤖 backported "Fix search values remap" (#74771)
Fix search values remap (#74770)

* fix custom values with labels

* fix

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-26 16:23:43 +00:00
github-automation-metabase
c2ccfdb6ee 🤖 backported "Security center: send test notifications to unsaved recipients" (#74762)
Security center: send test notifications to unsaved recipients (#74339)

* feat: pass explicit config

* api: build config

* update FE

* Update enterprise/frontend/src/metabase-enterprise/security_center/hooks/use-notification-config.ts



* handle FE review comments

* extend test

---------

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2026-05-26 14:43:28 +00:00
github-automation-metabase
4b655c9bc5 🤖 backported "feat: ui to display data complexity color coding" (#74761)
feat: ui to display data complexity color coding (#74363)

Closes [BOT-1519: UI to display data complexity score and color coding](https://linear.app/metabase/issue/BOT-1519/ui-to-display-data-complexity-score-and-color-coding)

### Description

Adds UI to display data complexity score and color coding.

### How to verify

1. Go to AI usage analytics page.
2. Go to data complexity scores.

### Demo

https://www.loom.com/share/81901a478d1a45a3839d7dd42f3e4ae7?from_recorder=1&focus_title=1

### Checklist

- [x] Tests have been added/updated to cover changes in this PR
- [x] If adding new Loki tests: they pass [stress testing](https://github.com/metabase/metabase/actions/workflows/loki-stress-test-flake-fix.yml)

Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-05-26 14:40:33 +00:00
github-automation-metabase
cd95af1715 🤖 backported "Show collection picker when saving new question in metabase-browser" (#74757)
Show collection picker when saving new question in metabase-browser (#74570)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-26 13:54:47 +00:00
github-automation-metabase
be3af16eaa 🤖 backported "security center: only include mb email if send to all emails is on" (#74745)
security center: only include mb email if send to all emails is on (#74334)

only include mb email if send to all emails is on

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-05-26 09:18:30 +00:00
github-automation-metabase
46ddf6d99c 🤖 backported "Fix remapping with custom values" (#74722)
Fix remapping with custom values (#74698)

* fix remapping with custom values

* fix

* fix unit test

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-25 17:49:16 +00:00
github-automation-metabase
23fcfa5edf 🤖 backported "handle duplicate copies of gson on classpath" (#74696)
handle duplicate copies of gson on classpath (#74685)

* handle duplicate copies of gson on classpath

fixes https://github.com/metabase/metabase/issues/73736

The relevant error message for us:

```
java.lang.NoSuchMethodError:
  'com.google.gson.stream.JsonWriter com.google.gson.stream.JsonWriter.value(float)'
  com.google.api.client.json.gson.GsonGenerator.writeNumber(GsonGenerator.java:105)
  com.google.cloud.bigquery.BigQueryRetryAlgorithm.getErrorDescFromResponse(BigQueryRetryAlgorithm.java:227)
  com.google.cloud.bigquery.BigQueryRetryAlgorithm.shouldRetryBasedOnBigQueryRetryConfig(BigQueryRetryAlgorithm.java:115)
  com.google.cloud.bigquery.BigQueryRetryHelper.run(...:108)
  driver.bigquery_cloud_sdk$execute_bigquery(bigquery_cloud_sdk.clj:751)
```

```clojure
user=> (enumeration-seq (.. (Thread/currentThread) getContextClassLoader (getResources "com/google/gson/stream/JsonWriter.class")))
(#object[java.net.URL
         "0x3bc8ea4f"
         "jar:file:/Users/dan/.m2/repository/com/google/code/gson/gson/2.12.1/gson-2.12.1.jar!/com/google/gson/stream/JsonWriter.class"]
 #object[java.net.URL
         "0x50073d1c"
         "jar:file:/Users/dan/.m2/repository/com/vertica/jdbc/vertica-jdbc/23.4.0-0/vertica-jdbc-23.4.0-0.jar!/com/google/gson/stream/JsonWriter.class"])
```

- Vertica bundles: gson 2.8.9 (vendored, unshaded)
- BigQuery demands: gson 2.12.1 (via com.google.code.gson/gson in deps tree — needs JsonWriter.value(float) which was added in 2.9.0)

contents of vertica jar:
 71 com/vertica/io
 70 com/google/gson/internal/bind
 61 com/vertica/utilities/conversion

188 gson class files bundled inside vertica-jdbc, vs ~600 Vertica
classes. They just vendored the entire gson library unshaded into their
fat JAR. Only gson — nothing else third-party.

So mystery solved. We had two copies of gson and vertica could slip
through. Our fix is to ensure that we only accept (or overwrite) when it
comes from a gson lib

```clojure
(def ^:private gson-conflict-handler
  "vertica-jdbc (and potentially other fat JARs) bundle their own copy of gson classes.
   When these overwrite the correct version from com.google.code.gson/gson, BigQuery's
   error-handling path crashes with NoSuchMethodError on JsonWriter.value(float),
   introduced in gson 2.9.0. This handler ensures the pinned gson version always wins
   regardless of JAR processing order. See #73736."
  {"com/google/gson/.*"
   (fn [{:keys [lib path in]}]
     (if (= lib 'com.google.code.gson/gson)
       {:write {path {:stream in}}}
       nil))})
```

* add activation conflict handler as well

```clojure
uberjar-test=> (clojure.test/run-tests)

Testing build.uberjar-test
Clean
  Delete /Users/dan/projects/work/metabase/target/classes
  Delete /Users/dan/projects/work/metabase/target/uberjar/metabase.jar

FAIL in (class-file-conflicts-test) (uberjar_test.clj:55)
No unexpected class file conflicts in the EE uberjar
Unexpected class file conflicts in packages: #{"javax/activation"}
If benign, add to known-conflicting-prefixes with a comment. If dangerous, add a conflict handler in build.uberjar.
expected: (empty? unexpected)
  actual: (not (empty? #{"javax/activation"}))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
{:test 1, :pass 0, :fail 1, :error 0, :type :summary}
```

Here's the output when i removed the `activation-conflict-handler` from
the uberjar-er. It hits conflicts on these files.

It doesn't know where the conflicts come from. The api is aware of the
lib trying to write and there's already a conflict. So the order isn't
stable and we must match on the files being written, not the lib
writing. Because lib A might write first so lib B tries to write the
same class we think it's conflict on B. But then B is written first and
now it appears a conflict in A.

also an exclusion in databricks so it doesn't bring in another logger
and we will no longer get this:

```
❯ clj -J"$(llm-repl 6006)" -M:"$ALIASES":build:build-dev:llm-repl
Downloading: vlaaad/reveal/maven-metadata.xml from clojars
WARNING: Specified aliases are undeclared and are not being used: [:performance]
SLF4J(W): Class path contains multiple SLF4J providers.
SLF4J(W): Found provider [org.apache.logging.slf4j.SLF4JServiceProvider@122ea8dc]
SLF4J(W): Found provider [org.slf4j.jul.JULServiceProvider@6cb417fc]
SLF4J(W): See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J(I): Actual provider is of type [org.apache.logging.slf4j.SLF4JServiceProvider@122ea8dc]
```

* fixing it up from comments

Co-authored-by: dpsutton <dan@dpsutton.com>
2026-05-25 10:48:32 -07:00
github-automation-metabase
51fc3f3a0a 🤖 backported "Handle query cache update race" (#74647)
Handle query cache update race (#74587)

Closes #73770

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-05-25 17:44:11 +00:00
github-automation-metabase
5d102e7a94 🤖 backported "Fix Snowflake python transform e2e test: explicit target schema" (#74721)
Fix Snowflake python transform e2e test: explicit target schema (#74716)

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-25 17:07:18 +00:00
github-automation-metabase
56ad9a188b 🤖 backported "Pin jest-canvas-mock" (#74576)
Pin `jest-canvas-mock` (#74509)

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-05-25 08:28:34 +00:00
Cam Saul
3fdb3b50e0 Enable cljfmt comment indentation [61] (#74688) 2026-05-22 16:51:12 -07:00
github-automation-metabase
83c6606e06 🤖 backported "Enable SMTP debug logs with mail.debug JVM property" (#74667)
Enable SMTP debug logs with mail.debug JVM property (#67519)

drewr/postal uses Jakarta Mail, and supports a debug option when sending
messages. However, it requires callers of the library to manually pass the
debug option, and doesn't look at any JVM options.

This change makes it so that setting the mail.debug JVM property to true
enables debug logging for all outgoing SMTP messages. No credentials are
logged.

Co-authored-by: Rodrigo López Dato <rodrigo@metabase.com>
2026-05-22 18:18:50 +00:00
github-automation-metabase
b38c7d64c1 🤖 backported "BOT-1495 Trim all metabot tables to MB_AI_USAGE_MAX_RETENTION_DAYS" (#74659)
BOT-1495 Trim all metabot tables to MB_AI_USAGE_MAX_RETENTION_DAYS (#74035)

* BOT-1495 Trim all metabot append-only tables to MB_AI_USAGE_MAX_RETENTION_DAYS

As of BOT-1409 we now trim the ai_usage_log table to prevent unbounded growth. The same arguments for trimming the
ai_usage_log table apply to other metabot append-only tables, like metabot_conversation, metabot_message,
metabot_feedback, and metabot_source_feedback.

- Update the fk on `metabot_message` pointing to `metabot_conversation.id` to `ON DELETE CASCADE`
- Add a trimmer task for `metabot_conversation` to also respect `MB_AI_USAGE_MAX_RETENTION_DAYS`.

Note that if we don't do this, older conversations will not have any source or profile_id associated, since these come
from the ai_usage_log table. This is not the only cause, but one possible cause of issues like BOT-1415

See also

* #73400
* #73518

* Shorten the gap between the ai_usage_log and metabot_conversation trimmer tasks

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-05-22 17:38:20 +00:00
github-automation-metabase
e9d68630ce 🤖 backported "Allow custom source for ID params" (#74655)
Allow custom source for ID params (#74650)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-22 16:31:38 +00:00
github-automation-metabase
2e6679c27a 🤖 backported "[Metabot] Fix AI settings inputs flickering + losing focus" (#74540)
[Metabot] Fix AI settings inputs flickering + losing focus (#74527)

* fix model dropdown flicker

* fix provider dropdown flicker

* fix api key input flicker

* fix connect/disconnect/done button flicker

* fix terms checkbox flicker

* drop unused isLoading from MetabotSetupContext

* consolidate BOT-1429 regression tests into one

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
2026-05-22 09:35:52 -05:00
github-automation-metabase
347d0e3284 🤖 backported "[perf] Optimize fingerprinters/mode-stats" (#74641)
[perf] Optimize fingerprinters/mode-stats (#74327)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-05-22 13:52:27 +00:00
github-automation-metabase
ec78891200 🤖 backported "Use transform timeout for queries inside transforms" (#74626)
Use transform timeout for queries inside transforms (#72522)

* Use transform timeout for queries inside transforms

Previously, queries executed within a transform were subject to the lower
of MB_DB_QUERY_TIMEOUT_MINUTES (default 20 min) and MB_TRANSFORM_TIMEOUT
(default 4 h). This made MB_TRANSFORM_TIMEOUT effectively unusable for its
stated purpose: long-running transforms always hit the query timeout first.

Bind *query-timeout-ms* to the transform timeout inside
run-cancelable-transform! so transform queries respect MB_TRANSFORM_TIMEOUT,
while regular Metabase queries keep using MB_DB_QUERY_TIMEOUT_MINUTES.

Fixes GDGT-2173.

* Enforce query timeouts per-statement, not via pool leak-detector

The c3p0 unreturnedConnectionTimeout was doing double duty: leak
detection AND query-timeout enforcement. That pool-wide conflation
meant transforms running longer than db-query-timeout always got their
connection destroyed first, defeating the transform-timeout rebinding
introduced earlier in this branch.

This commit:
- Calls Statement.setQueryTimeout uniformly in prepared-statement*/
  statement* so every SQL-JDBC query carries its own server-side
  timeout derived from *query-timeout-ms*. Applied after the multimethod
  returns, so driver overrides that replace the base method also get it.
- Raises the pool's unreturnedConnectionTimeout default to
  max(db-query-timeout, transform-timeout) so the safety net does not
  undercut a legitimate long transform; per-statement timeouts remain
  the real query killer.
- Also rebinds *network-timeout-ms* inside run-cancelable-transform! so
  a single slow socket read does not kill a long transform earlier than
  its own deadline.
- Replaces the prior tautological test with two real ones: the pool
  setting default correctly maxes query-timeout and transform-timeout,
  and a real H2 statement created through statement-or-prepared-statement
  observes the rebound timeout via .getQueryTimeout.

GDGT-2173

* Revert "Enforce query timeouts per-statement, not via pool leak-detector"

This reverts commit 5e01c82f24.

* Reapply "Enforce query timeouts per-statement, not via pool leak-detector"

This reverts commit a9f294247f.

* Simplify: strip ticket refs from source, hoist transform-timeout let

* Stronger tests: proxy-based unit test + multi-driver sweep for setQueryTimeout

* Route transform-timeout into driver pool default via registry, not cross-module lookup

Replaces the requiring-resolve from driver/settings.clj into transforms with a
register-long-running-timeout-provider! registry that transforms self-populates.
Keeps the dependency direction correct (transforms -> driver) and silences the
:metabase/modules kondo check. Also drops ^:parallel from the set-statement-query-timeout!
unit test, which tripped the destructive-fn-in-parallel-test linter.

* Route transforms through a dedicated :transform connection pool

Replaces the cross-feature `register-long-running-timeout-provider!`
registry with a new `:transform` value on the existing `*connection-type*`
enum, so transforms reuse the pool-cache key `[db-id, connection-type]`
mechanism that already isolates `:write-data`. The `:default` pool's
`unreturnedConnectionTimeout` is now unchanged from pre-PR — only the
`:transform` pool sees the longer leak-detector window — addressing
reviewer concerns about widening the safety net for non-transform queries.

* resolve-connection-type: take connection-type as arg, drop dynamic-var read

Pure-function refactor — caller passes *connection-type* (or any value)
explicitly. Easier to test, no implicit dependency on the dynamic binding.

* :transform also merges write-data-details; restore catch Throwable

Transforms route through `with-write-connection` (query_impl.clj:30), so
they need write-data credentials when configured. My earlier refactor's
`with-transform-connection` rebind dropped those credentials by treating
`:transform` as a separate write-context from `:write-data`. Fixed by
having `effective-details` merge `:write-data-details` when *connection-type*
is either `:write-data` or `:transform` — the pool key still differs so
pool properties (unreturnedConnectionTimeout) can be tuned per workload.

Updated `transform-creates-write-pool-test` -> `transform-creates-transform-pool-test`
to assert the new `[db-id :transform]` cache key.

Restored `catch Throwable` in `set-statement-query-timeout!` to match the
original PR — Spark/Hive may throw `AbstractMethodError` (an `Error`,
not `Exception`); the previous PR's `catch Throwable` swallowed it.

* Gate Statement.setQueryTimeout behind :jdbc/set-query-timeout feature flag

SparkSQL's Hive JDBC driver appears to handle Statement.setQueryTimeout
by closing the Thrift transport on the server side; subsequent statement
close() then throws "Failed to close statement" with TTransportException:
Socket is closed by peer. The catch in set-statement-query-timeout!
silences the setQueryTimeout call itself, but the session is already
poisoned and any explicit-joins test errors on .close().

Adds :jdbc/set-query-timeout feature flag (defaults true on :sql-jdbc,
overridden false on :sparksql). set-statement-query-timeout! takes the
driver as an arg and skips the call entirely when the feature is off.

* Skip getQueryTimeout assertion for drivers that opt out of setQueryTimeout

Spark/Hive opts out via :jdbc/set-query-timeout=false, so getQueryTimeout()
returns the JDBC default (0), not the bound *query-timeout-ms*. Branch
the assertion: drivers that support setQueryTimeout still round-trip;
opt-outs only assert a Statement was created.

* Clarify :transform connection-type docstrings per review

The with-transform-connection docstring claimed :transform shares primary
:details with :default, but effective-details merges :write-data-details for
:transform just like :write-data. Reconcile *connection-type*,
with-transform-connection, and resolve-connection-type docstrings to that truth:
:transform resolves details like :write-data but routes through a separate pool
key so c3p0 can carry transform-tolerant properties.

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-22 17:39:16 +07:00
github-automation-metabase
bb21882948 🤖 backported "Prepare the MCP for ChatGPT app" (#74603)
Co-authored-by: Timofei <timofei@metabase.com>
Co-authored-by: Phoomparin Mano <phoomparin@gmail.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2026-05-22 12:38:25 +02:00
github-automation-metabase
2404067a81 🤖 backported "Fix /api/frontend-errors 404 in embedded contexts" (#74630)
Fix /api/frontend-errors 404 in embedded contexts (#74623)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-22 09:47:54 +00:00
github-automation-metabase
a4e62284b7 🤖 backported "Fix excessive padding in embed wizard" (#74506)
Fix excessive padding in embed wizard (#74488)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-22 05:28:13 +00:00
github-automation-metabase
d7e9a78a89 🤖 backported "Pass label_field for parameter card dropdown sources" (#74619)
Pass label_field for parameter card dropdown sources (#74597)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-22 04:15:43 +00:00
github-automation-metabase
a928ae3fe9 🤖 backported "Fix sandbox removed field references" (#74616)
Fix sandbox removed field references (#74537)

Closes QUE2-589

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-05-21 23:43:44 +00:00
github-automation-metabase
a7a314838d 🤖 backported "Guard v60 dependency_status drop changesets against missing source columns" (#74600)
Guard v60 dependency_status drop changesets against missing source columns (#74594)

Follow-up to #74463. That PR guarded the eight INSERT changesets
(T00:00:03–10) with columnExists/MARK_RAN, but left the dropIndex
(T00:00:11–16) and dropColumn (T00:00:17–24) changesets unguarded. On an
instance whose entity table never had dependency_analysis_version, the
INSERT now skips cleanly, the migration proceeds, and then fails on the
unguarded DROP — e.g. T00:00:23, ALTER TABLE segment DROP COLUMN
dependency_analysis_version, which errors because the column is absent.

Add the same columnExists precondition with onFail: MARK_RAN (plus
validCheckSum: ANY) to all fourteen drop changesets, so every changeset
that touches an entity table's dependency_analysis_version column is
guarded consistently.

The regression test previously ran only the single changeset T00:00:09;
it now runs the full file range T00:00:09–T00:00:24 against the
missing-column state and asserts both the INSERT and the DROP COLUMN are
recorded as MARK_RAN — which is what would have caught this gap.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-05-21 22:17:38 +00:00
github-automation-metabase
84c15b7e0f 🤖 backported "docs(collections): add updates for missing or undocumented patterns" (#74607)
docs(collections): add updates for missing or undocumented patterns (#74287)

* docs(collections): add updates for missing or undocumented patterns that could lead to customer confusion

1. Curate vs. View relationship was implicit. The View access section didn't
   mention that Curate access is a superset of View. Admins setting up groups
   had to infer this. Added a note to make it explicit.

2. The "Also change sub-collections" toggle was undocumented. The doc said
   granting access to a collection doesn't cascade to sub-collections, but
   never explained how to fix that or what the toggle actually does. Added
   the missing explanation, including that "existing" scopes the toggle to
   sub-collections present at the time permissions are saved — not future ones.

3. The mixed-access edge case was unaddressed. New sub-collections automatically
   inherit their parent's permissions at creation time, regardless of whether
   the toggle was used. This means skipping the toggle can leave a group with
   access to some sub-collections (newly created ones) but not others (existing
   ones that predate the permission grant). Without knowing this, admins have
   no way to diagnose the inconsistency. Added a sentence naming this state and
   implying the fix.



* Update docs/permissions/collections.md



---------

Co-authored-by: thackervishal <95879205+thackervishal@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-05-21 20:10:23 +00:00
github-automation-metabase
71ba9ae434 🤖 backported "Data Complexity Score rating bands" (#74470)
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2026-05-21 17:35:40 +02:00
github-automation-metabase
b8bfba7d75 🤖 backported "Remote sync upsell" (#74167)
* Add backport resolution script

* Backport

* Remove backport.sh

* Fix unit tests

---------

Co-authored-by: Metabase bot <metabase-bot@metabase.com>
Co-authored-by: losrebellos <ben@benoitvinay.com>
2026-05-21 16:05:45 +02:00
github-automation-metabase
ce4c838a32 🤖 backported "Uxw 3359 visualizer breaks internal dimension remapping for integer" (#74584)
Uxw 3359 visualizer breaks internal dimension remapping for integer (#74446)

* Fix visualizer dropping internal dimension remapping, make cards combineable

* Fix removing and re-adding integer-category dimensions in the visualizer

* PR Feedback, adjust helper function and variable names

Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
2026-05-21 13:57:57 +00:00
github-automation-metabase
ea6a2c35fe 🤖 backported "fix: add metabase_version to serdes extract" (#74579)
fix: add metabase_version to serdes extract (#74476)

* fix: add metabase_version to serdes extract

* feat: silence version mismatch when version is not present

* tests

* the existing test that would've caught it was broken!

* ignore in existing tests

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-05-21 13:18:42 +00:00
github-automation-metabase
8f5d07051b 🤖 backported "deps: exclude azure-core-http-netty from snowflake driver" (#73828)
deps: exclude azure-core-http-netty from snowflake driver (#73824)

* deps: exclude azure-core-http-netty from snowflake driver

* ci: trigger fresh run with break-quarantine-snowflake label

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-21 08:50:48 +00:00
github-automation-metabase
eef1dd3c26 🤖 backported "Reproduce and fix deadlock issue with remote sync" (#74558)
Reproduce and fix deadlock issue with remote sync (#74544)

`load-metabase!` wrapped the entire import in a single transaction, holding row-level locks for the duration. On a live instance, concurrent writers (e.g., view-count batching, result-metadata updates) could deadlock with the import.

I split the transaction into per-entity transactions. Each `serdes/load-one!` call gets one. If an error is thrown, I attempt to check for known transient errors and retry with exponential backoff.

Co-authored-by: Timothy S. Dean <timothy.dean@metabase.com>
2026-05-20 23:38:34 +00:00
github-automation-metabase
97472e47db 🤖 backported "[AI Analytics] Make conversation list sortable by more fields" (#74554)
[AI Analytics] Make conversation list sortable by more fields (#74380)

* impl

* pr feedback

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
2026-05-20 22:36:18 +00:00
github-automation-metabase
0400dc8dd3 🤖 backported "Strip redundant card fields from serdes output" (#74372)
Strip redundant card fields from serdes output (#72055)

* Strip redundant card fields from serdes output

Cards export table_id, database_id, query_type, and source_card_id, but
all four are derivable from dataset_query by populate-query-fields on
insert. Segments and Measures export table_id, derivable from their
definition via lib/primary-source-table-id on insert.

Strip these from export so the YAML is minimal and authoritative — the
query is the source of truth, not redundant denormalized fields.

database_id is kept when dataset_query is empty (no :database key to
derive from). Segment/Measure storage-path updated to derive the table
path from the definition when table_id is absent.

for reference:
```clojure
(mu/defn populate-query-fields
  "Lift `database_id`, `table_id`, `query_type`, and `source_card_id` fields
  from query definition when inserting/updating a Card."
  [{query :dataset_query, :as card} :- ::queries.schema/card]
  (merge
   card
   (when (and (seq query) (map? query))
     (merge
      ;; This used to be conditional on not nilling source-card-id, changed due to #68080.
      {:source_card_id (source-card-id query)}
      (when-let [{:keys [database-id table-id]} (query/query->database-and-table-ids query)]
        ;; TODO -- not sure `query_type` is actually used for anything important anyway
        (let [query-type (if (query/query-is-native? query)
                           :native
                           :query)]
          (merge
           {:query_type (keyword query-type)}
           (when database-id
             {:database_id database-id})
           (when table-id
             {:table_id table-id}))))))))
```

Closes GHY-3325

* unimport model/Table

* re-export with fewer columns

* fix last tests

* remove last reference

* grab table_id from query using a serdes fn

optionally write out table_id

table_id is only skipped when it is derivable from the query. this
should in general happen unless the query is broken or empty `{}`.

* handle measures/metrics that lack a definition

tests caught these but real stuff could totally have this

FAIL in metabase-enterprise.remote-sync.impl-test/export!-excludes-archived-measures-test (impl_test.clj:1025)
export! excludes archived measures from export (via skip-archived flag)
(with-model-cleanup "RemoteSyncObject" "Measure")

Setting :remote-sync-type = :read-write

with temporary :model/Database

with temporary :model/Collection

with temporary :model/Table

with temporary :model/Measure

with temporary :model/Measure

expected: :success
  actual: :error

FAIL in metabase-enterprise.remote-sync.impl-test/export!-excludes-archived-measures-test (impl_test.clj:1028)
export! excludes archived measures from export (via skip-archived flag)
(with-model-cleanup "RemoteSyncObject" "Measure")

Setting :remote-sync-type = :read-write

with temporary :model/Database

with temporary :model/Collection

with temporary :model/Table

with temporary :model/Measure

with temporary :model/Measure

Active measure should be exported. Keys: ("collections/M-Q4pcV0qkiyJ0kiSWECl_some_collection/M-Q4pcV0qkiyJ0kiSWECl_some_collection.yaml" "collections/M-Q4pcV0qkiyJ0kiSWECl_some_collection/cards/f1C68pznmrpN1F5xFDj6d_some_question.yaml" "collections/M-Q4pcV0qkiyJ0kiSWECl_some_collection/dashboards/Q_jD-f-9clKLFZ2TfUG2h_shared_dashboard.yaml")
expected: (some
           (fn* [p1__314668#] (str/includes? p1__314668# "active_measure"))
           file-keys)
  actual: (not
           (some
            #<Fn@2c1faff4 metabase_enterprise.remote_sync.impl_test/fn__314670[fn/fn/fn/fn/fn/fn/fn/fn/fn]>
            ("collections/M-Q4pcV0qkiyJ0kiSWECl_some_collection/M-Q4pcV0qkiyJ0kiSWECl_some_collection.yaml"
             "collections/M-Q4pcV0qkiyJ0kiSWECl_some_collection/cards/f1C68pznmrpN1F5xFDj6d_some_question.yaml"
             "collections/M-Q4pcV0qkiyJ0kiSWECl_some_collection/dashboards/Q_jD-f-9clKLFZ2TfUG2h_shared_dashboard.yaml")))

ERROR in metabase-enterprise.serialization.v2.extract-test/collection-export-includes-fields-and-segments-test (serialization.clj:536)
Uncaught exception, not in assertion.

clojure.lang.ExceptionInfo: Error extracting Segment 1
                    cause: "Error extracting Segment 1"
                entity_id: "FTAODs9FCLwPAsC-_7Nb3"
                       id: 1
                    model: "Segment"
                    table: :segment
    toucan2/context-trace: [["execute SQL with class org.h2.jdbc.JdbcConnection"
                             #:toucan2.jdbc.query{:sql-args ["SELECT * FROM \"SEGMENT\" WHERE \"ID\" IN (?)" 1]}]
                            ["resolve connection" #:toucan2.connection{:connectable org.h2.jdbc.JdbcConnection}]
                            ["resolve connection" #:toucan2.connection{:connectable nil}]
                            #:toucan2.pipeline{:rf #function[clojure.core/map/fn--5952/fn--5953]}
                            ["with compiled query" #:toucan2.pipeline{:compiled-query ["SELECT * FROM \"SEGMENT\" WHERE \"ID\" IN (?)" 1]}]
                            ["with built query" #:toucan2.pipeline{:built-query {:select [:*], :from [[:segment]], :where [:in :id [1]]}}]
                            ["with resolved query" #:toucan2.pipeline{:resolved-query {:where [:in :id [1]]}}]
                            ["with parsed args"
                             #:toucan2.pipeline{:query-type :toucan.query-type/select.instances,
                                                :parsed-args {:queryable {:where [:in :id [1]]}}}]
                            ["with model" #:toucan2.pipeline{:model :model/Segment}]
                            ["with unparsed args"
                             #:toucan2.pipeline{:query-type :toucan.query-type/select.instances,
                                                :unparsed-args (:model/Segment {:where [:in :id [1]]})}]
                            ...]
clojure.lang.ExceptionInfo: Error extracting Segment 1
        error: {:schema [:ref :metabase.lib.schema/query],
                :value {},
                :errors
                ({:path [0 0 0 :lib/type],
                  :in [:lib/type],
                  :schema

* Export field elision is conditional on derivability

* Revert conditional elision for table-id and source-card-id

* t2 hook is source of truth

* m.

* Use resolve

* Use mu/disable-enforcement, revert some previous changes

---------

Co-authored-by: dpsutton <dan@dpsutton.com>
Co-authored-by: Timothy Dean <timothy@timothydean.dev>
2026-05-20 16:11:12 -06:00
github-automation-metabase
610516752d 🤖 backported "[AI Analytics] Date filters respect retention period" (#74543)
[AI Analytics] Date filters respect retention period (#74441)

* min date

* max date

* util clean up

* clean up

* improved code comment

* more unit tests

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
2026-05-20 21:51:01 +00:00
github-automation-metabase
672d7b8c06 🤖 backported "docs: fix minor typo in SDK Bundle description" (#74548)
docs: fix minor typo in SDK Bundle description (#74541)

Fix minor typo in SDK Bundle description

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-05-20 21:05:33 +00:00
github-automation-metabase
4984e198c5 🤖 backported "Guard v60 dependency_status data migrations against missing source columns" (#74518)
Guard v60 dependency_status data migrations against missing source columns (#74463)

* Guard v60 dependency_status data migrations against missing source columns

Changesets v60.2026-04-02T00:00:03 through T00:00:10 copy
dependency_analysis_version from eight entity tables into the new
dependency_status table; later changesets in the same file drop those
source columns. If an instance ever enters a partial-migration state
where T00:00:23 (DROP COLUMN segment.dependency_analysis_version) has
run but T00:00:09 (the corresponding INSERT) has not, init crashes on
the next upgrade with "column dependency_analysis_version does not
exist".

Add a columnExists precondition with onFail: MARK_RAN to each of the
eight INSERT changesets, mirroring the guard already present on the
v58 changeset that adds the column. validCheckSum: ANY is included as
a defensive measure following the precedent in 059_update_migrations.

Adds a schema migration test that reproduces the partial-migration
state on H2 and Postgres and asserts T00:00:09 is recorded as MARK_RAN.

* Fix test changelog table-name lookup for MySQL/MariaDB

The hard-coded "databasechangelog" identifier in the SELECT works on
H2 and Postgres but not MySQL/MariaDB, where Liquibase creates the
table as uppercase DATABASECHANGELOG. Use the cross-DB
liquibase/changelog-by-id helper instead.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-05-20 13:56:38 -05:00
github-automation-metabase
d973005b34 🤖 backported "GRO-411 Corrects transform inspector upsell" (#74510)
GRO-411 Corrects transform inspector upsell (#74227)

Co-authored-by: Brad Anderson <brad@metabase.com>
2026-05-20 15:57:16 +00:00
github-automation-metabase
0d6c5e35ae 🤖 backported "docs: add docs for scatterplots and bubble chart" (#74513)
docs: add docs for scatterplots and bubble chart (#74298)

* docs: add docs for scatterplots and bubble chart

* Add images and examples for additional data shapes

* Additional edits from Alex

Co-authored-by: Michael Eby <38187276+michaeleby1@users.noreply.github.com>
2026-05-20 15:28:09 +00:00
github-automation-metabase
43b57768f7 🤖 backported "Add no writable databases state to transforms pages" (#74503)
Add no writable databases state to transforms pages (#72880)

* Add no writable databases state to transforms pages

* add manage database permissions check

* only show no writable screen on DB write check, not permissions

* oss e2e adjustment

* handle error and loading states

Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
2026-05-20 14:57:07 +00:00
github-automation-metabase
86382f04f9 🤖 backported "docs: transform addons" (#74504)
docs: transform addons (#74379)

* first draft

* almost there

* transformssss

* Optimized 3 images, saved 147.8 KB (46.2% smaller)

* links

* more links

* Apply suggestions from code review



* Update docs/data-studio/transforms/jobs-and-runs.md



* feedback

* make the top more of a tldr

---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-20 14:39:42 +00:00
github-automation-metabase
0f3f5aaa4e 🤖 backported "Add transform as a serdes dependency of table" (#74481)
* Add transform as a serdes dependency of table (#74473)

* fix: transform is a dep of table for serdes

* feat: add deser tests for transform tables

* Fix backport tests for transform-created table serdes (61)

Transform's before-insert on 61 calls upsert-target-table! (pre-#73741
behavior), so the metabase_table row already exists by the time the
tests try to create it. Adopt the existing row via select-one-pk +
update!, mirroring what sync-target! does after a real transform run.

---------

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-20 12:56:24 +00:00
github-automation-metabase
5698cb15cd 🤖 backported "Fix conditional formatting colors for BigDecimal cells in email renderer" (#74489)
Fix conditional formatting colors for BigDecimal cells in email renderer (#74475)

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-20 12:45:14 +00:00
github-automation-metabase
63b11e9190 🤖 backported "Allow serialization to import transform tables after sync" (#74479)
Allow serialization to import transform tables after sync (#74474)

fix: relax data source override none->transform during deser

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-05-20 10:53:23 +00:00
github-automation-metabase
5bce448e3a 🤖 backported "security-center prometheus metrics" (#74418)
security-center prometheus metrics (#73730)

* add prometheus metrics for security center

* fix: last-synced-at should only be set on successful sync

* add test for last-synced-at behavior on sync failure

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-05-20 09:21:16 +00:00
github-automation-metabase
5a2d5d8a11 🤖 backported "Skip H2 databases during serdes extract (GHY-3633)" (#74453)
Skip H2 databases during serdes extract (GHY-3633) (#74429)

* Skip H2 databases during serdes extract

The import path rejects H2 databases via assert-not-h2!, but the export
path included them, so a basic fresh-instance round trip failed
immediately on Sample Database / Internal Metabase Database. Filter
H2 out of extract-query "Database" so the export/import contract is
consistent.

Fixes GHY-3633.

* Gate H2-extract filter on dynamic var so H2-round-trip tests still work

The previous commit unconditionally filtered H2 from the Database
extract-query, which broke the v2.e2e / v2.load / v2.round-trip /
api-test suites that exercise H2 throughout (they already rebind
assert-not-h2! to allow it). Move the filter behind a dynamic var
*include-h2-in-extract?* that those fixtures can flip back on, and
give the API export test an explicit non-H2 database so it still
verifies the data-model export contains a Database entry.

* Drop H2-extract escape hatch from round-trip-test, force :postgres in a few setups

Round-trip-test now reads its baseline directly without bypassing
assert-not-h2! or flipping *include-h2-in-extract?* — the baseline DB
is :postgres, so neither hatch is needed. A handful of e2e/load
fixture databases that were defaulting to :h2 now declare :postgres
explicitly. The e2e and load test fixtures still need both redefs
because ~100 Card with-temps lean on the (data/id) test-data default
(H2); shrinking that surface is for another pass.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-05-19 21:39:44 +00:00
github-automation-metabase
152aa098ca 🤖 backported "Fix impersonated model caching " (#74451)
Fix impersonated model caching  (#74303)

* fix impersonated model caching

* fix kondo

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-05-19 21:32:23 +00:00
github-automation-metabase
3ae638de6c 🤖 backported "fix(sdk-bundle): host style not respected when navigating between dashboards in InteractiveDashboard" (#74434)
fix(sdk-bundle): host style not respected when navigating between dashboards in InteractiveDashboard (#74345)

* refactor: drop dead fullscreen wiring from SDK dashboard wrapper

SDK fullscreen UI was removed in #60198. The
`SdkDashboardStyledWrapperWithRef` variant existed solely to attach the
`fullscreenRef` from `DashboardContext` to the dashboard wrapper, but
nothing in the SDK paths triggers `onFullscreenChange` anymore, so the
ref was attached but never used.

Drop the WithRef variant and collapse `SdkDashboardStyledWrapper` to a
plain function component. Two call sites in `SdkDashboard.tsx` move to
the plain wrapper.

No behavior change — the inner `fullscreenRef` was dead-wired.



* fix: preserve user style when navigating between SDK dashboards

EMB-1746. When `<InteractiveDashboard enableEntityNavigation>` navigated
to another dashboard via custom click behavior, the navigated dashboard
lost the user-provided `style` / `className` (e.g. `height: 100dvh`),
breaking sticky filters and forcing full-width layout.

Root cause: `SdkInternalNavigationProvider` wraps navigated content in
an outer `SdkDashboardStyledWrapper` carrying user style, while
`SdkDashboard` always wraps its own render in another
`SdkDashboardStyledWrapper`. The provider explicitly stripped
`style`/`className` on the inner render to avoid double padding/borders
— but that also dropped the height, leaving the inner wrapper without
the scroll context the dashboard relies on for sticky pinning.

Fix: add `isInsideNavigationStack` to the navigation context. When set,
`SdkDashboard` skips its own styled wrapper, so the outer one (with
user style) stays the sole styled container. Drop the `style={undefined}`
and `className={undefined}` overrides from the provider — no longer
needed.

Verified with a new E2E test that asserts a single styled wrapper with
the user height after navigating to another dashboard and back.



* test(emb-1746): assert wrapper count, drop height assertion

The host-style-height assertion couldn't discriminate the fix from the
bug. The wrapper has `min-height: 100%` from its CSS module, so when
the mount container is taller than the host-provided `style={height: 400}`,
the computed height resolves to the container's height (800px in the
component-test viewport) in both pre-fix and post-fix scenarios.

The real structural invariant is the wrapper count: pre-fix, navigation
produced a second nested `SdkDashboardStyledWrapper`; post-fix, exactly
one. Assert `have.length === 1` at every step instead.



* Simplify comment

* Make test actually testing scrolling and assert the filters

* Address review

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:45:15 +00:00
github-automation-metabase
eb01f70bbd 🤖 backported "Metabot: read_resource navigation primitives" (#74430)
Metabot: read_resource navigation primitives (#74238)

read_resource becomes an instance navigation primitive instead of a
dead-end leaf reader. URI dispatch runs through metabase.util.match
for a unified shape — adding a new pattern is one match clause plus
one focused fetch fn, replacing the sub-resource / path-segments slot
branching.

New URIs:
- Navigation: metabase://databases, metabase://collections (+ ?tree=true),
  metabase://user/recent-items
- Database drill-down: /database/{id}, /tables, /models, /schemas,
  /schemas/{name}/tables
- Collection drill-down: /collection/{id}, /items, /subcollections
- Lineage: /table/{id}/derived, /model/{id}/sources,
  /question/{id}/sources, /transform/{id}/sources, /transform/{id}/target
- Dashboard items: /dashboard/{id}/items

List responses cap at 25 with truncated/total signals so the agent
knows to drill into specific URIs rather than page blindly.

Each branch is permission-checked: single-entity URIs error when
mi/can-read? denies, list endpoints silently filter unreadable items.
Notably, /transform/{id}/target now gates the target table by
mi/can-read? — previously the target was hydrated from the transform
read without a per-table check.

URI segments are URL-encoded by the metabase-uri helper and decoded
by parse-uri, so schema names containing '/' (allowed by Postgres,
Snowflake, Redshift, SQL Server) round-trip correctly.

Co-authored-by: Mihael Konjević <konjevic@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:33:44 +00:00
github-automation-metabase
e5812502d7 🤖 backported "docs: remove exp recommendation" (#74431)
docs: remove exp recommendation (#73763)

Co-authored-by: Michael Eby <38187276+michaeleby1@users.noreply.github.com>
2026-05-19 16:11:33 +00:00
github-automation-metabase
f2b13bb769 🤖 backported "Add CSP nonce to SAML login popup for embedding" (#74212)
* Add CSP nonce to SAML login popup for embedding (#73861)

* Add CSP nonce to SAML login popup for embedding

* Add tests

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>

* Port necessary missing csp change

---------

Co-authored-by: Rodrigo López Dato <rodrigo@metabase.com>
Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
2026-05-19 15:48:16 +00:00
github-automation-metabase
868cdf9de3 🤖 backported "Update Security Center promo card layout" (#74421)
* Update Security Center promo card layout (#73918)

* wip

* Fix linter

* Remove old icon

* Fix channels refetching when Slack is setup

* Update SecurityCenterPromoCard tests for new copy

* GDGT-2345: Add SecurityCenterPromoCard banner to Admin's SettingsNav

* GDGT-2345: Fix unit tests for Admin notifications

* Add flag check for security center banner in SettingsNav

* GDGT-2345: Add new icon typing

---------

Co-authored-by: Stas Gavrylov <stas@metabase.com>
Co-authored-by: Stas Gavrylov <stasgavrylov@gmail.com>
2026-05-19 18:22:14 +03:00
github-automation-metabase
401a457a0e 🤖 backported "Defensively support double-encoded search index rows" (#74414)
Defensively support double-encoded search index rows (#74402)

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-19 15:10:22 +00:00
github-automation-metabase
ec7263ea6a 🤖 backported "docs: ai updates" (#74426)
docs: ai updates (#74388)

* ai updates

* Apply suggestions from code review



* links

---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-19 14:54:46 +00:00
github-automation-metabase
8ef336a0c4 🤖 backported "Return client error when unable to acquire DB connection" (#74409)
Return client error when unable to acquire DB connection (#74088)

* return client error for bad db details

* add test

* fix kondo

* fix test

* ignore kondo

* fix h2 and sqlite

* mock getConnection error

* fix kondo unused params

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-05-19 14:50:21 +00:00
github-automation-metabase
ab3aae9c36 🤖 backported "docs: usage analytics updates" (#74424)
docs: usage analytics updates (#74386)

* usage analytics updates

* Apply suggestions from code review



---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-19 14:49:44 +00:00
github-automation-metabase
44d9a5f83d 🤖 backported "Skip API-key users when expanding notification recipients" (#74408)
Skip API-key users when expanding notification recipients (#73922)

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-19 14:11:33 +00:00
github-automation-metabase
0c64e27763 🤖 backported "Bot 1146 transform context is included twice " (#74382)
Bot 1146 transform context is included twice  (#74126)

* fix: transform context is included twice

* opinionated refactor

* unset dry run error on unmount

* richer feedback for the agent

---------

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: Maksym Yakubych <maksym@metabase.com>
2026-05-18 22:29:27 +00:00
github-automation-metabase
8b6020351c 🤖 backported "[AI Analytics] General improvements" (#74377)
[AI Analytics] General improvements (#74318)

* date filter changes

* fix group filter logic

* fix e2e tests

* fix title logic if data complexity is not enabled

* impl bg color feedback for data parts and tool calls

* copy button alerts with copied message on click for user and agent messages

* fix flaky unit test

* remove action message type and clean up the tool call and data part stylings

* do no make lines curve for the by day/hour chart

* adjust breadcrumbs

* correct unit test

* pr feedback

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
2026-05-18 21:58:07 +00:00
github-automation-metabase
50985454de 🤖 backported "Fix password resets for Helping Hand instance access account" (#74364)
Fix password resets for Helping Hand instance access account (#74024)

Fixes UXW-4037.

Previously, it was possible to have a grant for Helping Hand access by
the Metabase success team with a much longer validity period than the
token which gets emailed to the success team.

The tokens (like other password reset tokens) are only good for 48h.
However the grants of access default to 96 hours, and can be raised
further. Password resets were previously disabled for these accounts,
so you can end up with a valid grant and no way to access the instance.

That results in extra back and forth with customers to revoke the first
grant and generate a new one, etc.

With this change, it is now possible to request a password reset for the
magic success account, which will generate and email a new 48h token,
provided the underlying grant is still valid.

Co-authored-by: Braden Shepherdson <braden@metabase.com>
2026-05-18 18:32:35 +00:00
github-automation-metabase
d2494dc659 🤖 backported " introduce validate-db-details multimethod" (#74351)
introduce validate-db-details multimethod (#74343)

* feat: introduce validate-db-details multimethod

* include mysql

* add to changelog

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-05-18 15:28:28 +00:00
github-automation-metabase
45d6537554 🤖 backported "Upgrade @slack/web-api to remove forced resolutions" (#74336)
Upgrade @slack/web-api to remove forced resolutions (#74329)

Upgrade @slack/web-api

This removes three forced package resolutions:
  - axios
  - follow-redirects
  - form-data

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-05-18 10:56:01 +00:00
github-automation-metabase
759e20b0b2 🤖 backported "fix: keep save button right-aligned in notebook editor" (#74333)
fix: keep save button right-aligned in notebook editor (#74268)

In the SDK default question view, when the title and back button are
absent (new exploration / ad-hoc question with no name), the
RenderIfHasContent wrapper collapses to a hidden sentinel that drops out
of flex layout. With only one visible child, `justify="space-between"`
left-aligns the Save button. Force the button right with `ml="auto"` so
space is reserved on the left regardless of title presence.

Closes EMB-1712.

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:33:06 +00:00
github-automation-metabase
5c9a21534a 🤖 backported "better error message for java object error" (#74316)
better error message for java object error (#74315)

Co-authored-by: dpsutton <dan@dpsutton.com>
2026-05-16 02:45:42 +00:00
github-automation-metabase
4579392e44 🤖 backported "Rename tag @smoke to @prerelease" (#74307)
Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-05-15 23:09:42 +00:00
github-automation-metabase
7e19dedb69 🤖 backported "Fix page crash when setting up sandboxing with a SQL question via the UI" (#74301)
* Fix page crash when setting up sandboxing with a SQL question via the UI (#74297)

* Fix page crash when setting up sandboxing with a SQL question via the UI

* Make GroupInfo.member_count optional

* Fix unit test

---------

Co-authored-by: Anderson <ands.alves.nunes@gmail.com>
Co-authored-by: Anderson <anderson@metabase.com>
2026-05-15 22:22:43 +00:00
github-automation-metabase
52f0110c79 🤖 backported "Premium feature is sufficient for complexity scoring" (#74302)
Premium feature is sufficient for complexity scoring (#74222)

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-15 21:14:48 +00:00
github-automation-metabase
e835523e4b 🤖 backported "docs: add docs for waterfall chart" (#74299)
docs: add docs for waterfall chart (#74163)

* docs: add docs for waterfall chart

* Optimized 1 images, saved 17.2 KB (51.4% smaller)

* Edits from Jeff

* Edits from Alex

* Change axes options list

---------

Co-authored-by: Michael Eby <38187276+michaeleby1@users.noreply.github.com>
2026-05-15 19:41:58 +00:00
github-automation-metabase
7374d00be1 🤖 backported "[Metabot] remove MetaBot references in favor of Metabot" (#74281)
[Metabot] remove MetaBot references in favor of Metabot (#71777)

* remove MetaBot

* Update src/metabase/metabot/reactions.clj



---------

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-05-15 17:08:30 +00:00
github-automation-metabase
3769bf3551 docs(sdk): Update SDK version to 0.61.0 (#74282)
docs(sdk): Update Readme version references and published npm version to 0.61.0
2026-05-15 16:37:45 +00:00
github-automation-metabase
f53dad1015 🤖 backported "Prevent captured-branch race in remote-sync (GHY-3505)" (#74277)
Prevent captured-branch race in remote-sync (GHY-3505) (#73796)

## Summary

Closes GHY-3505

The likely cause of the bug was a race condition. If someone switched branches while an import was happening, this could have caused the branch to change. It is hard to reproduce, but there are many paths though the API that could have caused this.

This PR tries to clean up the API to prevent conflicting tasks to run simultaneously. It does this with a guard. If a task is running, the setting cannot be changed, blocked at the operation level.

## Changes

- **Operation-level guards.** New `metabase-enterprise.remote-sync.guards/ensure-no-active-task!` is called at the top of every mutating operation. If any task is in flight, the operation refuses with 400.  If a task is stale, it will be cancelled.
  - `async-import!`
  - `async-export!`
  - `create-branch!`
  -  `stash!`
  -  `bulk-set-remote-sync`
  -  `check-and-update-remote-settings!`
- **Fail-early defense in `import!`/`export!`.** `async-import!` and `async-export!` capture the setting at scheduling time (`pre-task-branch`) and pass it to the work function. If the setting has drifted by the time the work runs, the task aborts with `:error` so no load happens against a different branch.
- **`handle-task-result!` is robust against late completions.** The terminated-check and the result-write run in a single transaction with `SELECT ... FOR UPDATE`. If `ended_at` is already set when the row lock is acquired (admin cancel, supersession, etc.), it logs a warning and exits without writing the setting — closing the TOCTOU window where a concurrent cancel could slip between the check and the update.
- **Stale-task supersession.** New `supersede-stale-tasks!` marks any task whose `last_progress_report_at` is older than `remote-sync-task-time-limit-ms` as cancelled + terminated. Called from `ensure-no-active-task!` (so user-driven operations self-heal past stuck threads) and from `create-task-with-lock!` (so the auto-import Quartz job does the same). Brand-new tasks aren't touched — the DB column defaults to `current_timestamp` on insert.
- **Helpers extracted** for create-branch and stash so each can be guarded at the operation layer.

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
2026-05-15 16:36:52 +00:00
Mahatthana (Kelvin) Nomsawadi
457534b552 [release branch 61] Pre and post gold 61 (#74275)
* Pre gold

* post gold
2026-05-15 17:17:56 +01:00
github-automation-metabase
ea86f7525b 🤖 backported "Metabot configure popup when trying to use w/o configuration" (#74228)
#72298
  > [!IMPORTANT]
  > Manual conflict resolution is required.
  > Checkout the branch and run `./backport.sh` script. Force push your changes after cherry-picking. Check the box below when done.

  Conflicts:
  ```shell
  100644 efe262dd7d 1	frontend/src/metabase/metabot/components/MetabotChat/MetabotChat.tsx
100644 85be854f79 2	frontend/src/metabase/metabot/components/MetabotChat/MetabotChat.tsx
100644 7f4a4b2555 3	frontend/src/metabase/metabot/components/MetabotChat/MetabotChat.tsx
100644 bbbe217f43 1	frontend/src/metabase/metabot/components/MetabotQueryBuilder/MetabotQueryBuilder.tsx
100644 f7144439e5 2	frontend/src/metabase/metabot/components/MetabotQueryBuilder/MetabotQueryBuilder.tsx
100644 c860b20d9b 3	frontend/src/metabase/metabot/components/MetabotQueryBuilder/MetabotQueryBuilder.tsx
100644 b8283767ea 1	frontend/src/metabase/metabot/components/MetabotQueryBuilder/MetabotQueryBuilder.unit.spec.tsx
100644 ccc2d6aa3a 3	frontend/src/metabase/metabot/components/MetabotQueryBuilder/MetabotQueryBuilder.unit.spec.tsx
100644 0c210ea7d0 1	frontend/src/metabase/metabot/hooks/use-user-metabot-permissions.ts
100644 855c1c646d 2	frontend/src/metabase/metabot/hooks/use-user-metabot-permissions.ts
100644 479bce88cd 3	frontend/src/metabase/metabot/hooks/use-user-metabot-permissions.ts
100644 fa9d598a17 1	frontend/src/metabase/metabot/hooks/use-user-metabot-permissions.unit.spec.tsx
100644 f69fb67b50 2	frontend/src/metabase/metabot/hooks/use-user-metabot-permissions.unit.spec.tsx
100644 3d35d1f90e 3	frontend/src/metabase/metabot/hooks/use-user-metabot-permissions.unit.spec.tsx
  ```

  - [x] Conflicts resolved
2026-05-15 12:36:22 -03:00
github-automation-metabase
b52aa28b5d 🤖 backported "fix: hide empty Usage tracking section on Cloud Starter" (#74270)
fix: hide empty Usage tracking section on Cloud Starter (#74262)

* Fix cloud starter seeing a section with an empty body

* Add a comment to clarify the extra conditions on the only section in the page

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
2026-05-15 14:53:19 +00:00
github-automation-metabase
4fbbd6d7a8 🤖 backported "fix(sdk-bundle): handle same-dashboard and cross-dashboard tab click behavior in SDK" (#74255)
fix(sdk-bundle): handle same-dashboard and cross-dashboard tab click behavior in SDK (#74083)

* Add tests

* Add assertions for passing parameter values

* Fix same dashboard navigation

* Fix cross dahsboard tab navigation using effect

* Handle cross dashboard tab navigation

* Make the test clearer

* Fix linter

* Update the tests to always pass some parameter values

* Remove unused exports

* Address review feedback

- Type initialDashboardTabId selector via Partial<SdkStoreState>
- Document non-clearing rationale on SdkState slot
- Use entity_id aliases for SDK dashboardId in nav tests
- Extend tests to exercise repeated navigation

* Revert stale-tab test extensions

* fix linter again

* Add eajs tab navigation tests

* Clarify code intent

* test: drop modular embedding tab nav tests

Modular embedding wraps the SDK; tab navigation behavior already
covered by the SDK component spec. Remove duplicate eajs spec
additions for EMB-1714 and keep the SDK-side coverage as the
single source of truth.



* fix: merge filters and restore tab on SDK click-behavior

Address two PR review issues for same-dashboard/cross-dashboard click
behaviors in the SDK:

1. Same-dashboard parameter merge — switch from
   setParameterValuesFromQueryParams (full replace) to per-id
   setParameterValue dispatch, mirroring core DashboardClickAction so
   unrelated filters keep their values. Carry id-keyed
   parameterIdValuePairs through the ClickBehaviorTarget and the
   navigation stack entry to avoid slug<->id resolution at dispatch time.

2. Back navigation tab restore — capture the live selectedTabId onto
   the outgoing dashboard entry when pushing, and seed
   setInitialDashboardTabId on pop so the re-mounted dashboard lands on
   the tab the user was last viewing.



* Add an assertions to ensure tabbed-dashboard -> tabbed-dashboard -> back works

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 14:32:04 +01:00
github-automation-metabase
5180d2f487 🤖 backported "AppDb support for Data Complexity Score CLI" (#74206)
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-15 14:52:51 +02:00
github-automation-metabase
daf69b70f3 🤖 backported "docs - mcp paths" (#74265)
docs - mcp paths (#74250)

* mcp paths

* update screenshot

* Optimized 1 images, saved 103.1 KB (42.7% smaller)

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-15 12:37:03 +00:00
github-automation-metabase
3f1fc5da0e 🤖 backported "Fix click/hover behavior for "Install in Cursor" link" (#74263)
Fix click/hover behavior for "Install in Cursor" link (#74259)

Co-authored-by: Timofey Kachalov <timofei@metabase.com>
2026-05-15 11:13:46 +00:00
github-automation-metabase
f615aa6d5d 🤖 backported "fix: library shows up in the model path incorrectly" (#74248)
fix: library shows up in the model path incorrectly (#74185)

Closes [BOT-1514: `Library` shows up in the model path incorrectly](https://linear.app/metabase/issue/BOT-1514/library-shows-up-in-the-model-path-incorrectly)

### Description

It was using the wrong method, which was designed for tables in the library. Now it's using the correct one. 

### How to verify

1. Go to metabot.
2. Type a message referencing some model.


### Checklist

- [x] Tests have been added/updated to cover changes in this PR
- [x] If adding new Loki tests: they pass [stress testing](https://github.com/metabase/metabase/actions/workflows/loki-stress-test-flake-fix.yml)

Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-05-14 21:48:34 +00:00
github-automation-metabase
cf18b0763f 🤖 backported "[match] Remove old match implementations based on clojure.core.match" (#73984)
[match] Remove old match implementations based on clojure.core.match (#73209)

* [match] Remove old match implementations based on clojure.core.match

* Rename match-lite to match-one and replace-lite to replace

* Stop using clojure.core.match completely

* Move metabase.lib.util.match to metabase.util.match

* Remove clojure.core.match dependency

* Update docstrings

* Address comments

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-05-14 20:15:58 +00:00
github-automation-metabase
355847f453 🤖 backported "Pin com.google.code.gson/gson dependency at root deps.edn" (#74241)
Pin com.google.code.gson/gson dependency at root deps.edn (#74220)

pin gson dep at root

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-05-14 19:46:35 +00:00
github-automation-metabase
ae3379e7f7 🤖 backported "Improve Instance Analytics Upsell" (#74144)
Improve Instance Analytics Upsell (#73933)

* Consistent padding for upsell card

* Upsell gem added for insights tab

* Misc

* Unit tests for upsell gem for insights tab

* Fix for unit tests

* Test titles not changed

* UpsellCard updates

* Misc

* Props renaming

* MArgin fixed

* Update Loki Snapshots

* Misc

* Misc

---------

Co-authored-by: Benoit Vinay <ben@benoitvinay.com>
2026-05-14 19:20:30 +00:00
github-automation-metabase
b39984bbf2 🤖 backported "fix: switching between internal and embedded metabot scrolls" (#74233)
fix: switching between internal and embedded metabot scrolls (#72904)

Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-05-14 17:47:27 +00:00
github-automation-metabase
dead92c7cb 🤖 backported "Ensure Snowflake driver blocks GET and PUT commands." (#73790)
Ensure Snowflake driver blocks GET and PUT commands. (#73578)

* Ensure Snowflake driver blocks GET and PUT commands.

Apparently this is a thing you can do with Snowflake's JDBC driver for
some reason?

https://docs.snowflake.com/en/developer-guide/jdbc/jdbc-parameters#enableputget

* Review suggestions for snowflake enablePutGet options.

Co-authored-by: technomancy <technomancy@gmail.com>
Co-authored-by: Phil Hagelberg <phil@hagelb.org>
2026-05-14 17:42:51 +00:00
github-automation-metabase
2bab16bdce 🤖 backported "fix: label of run button in queries generated section of ai usage" (#74235)
fix: label of run button in queries generated section of ai usage (#74159)

Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-05-14 17:34:21 +00:00
github-automation-metabase
e3a72b3993 🤖 backported "[Metabot] Persist errors and aborts" (#74165)
[Metabot] Persist errors and aborts (#74056)

* TEMP COMMIT

* wip

* improved FE state

* fix persisted conversation not showing aborted messages

* better FE error handling

* TEMP COMMIT

* remove errorMessages state in favor of new agent message with error subtype

* small corrections

* FE self-review

* claude FE review

* fix failing e2e tests

* self be review

* move migration to v61

* fix failing BE test

* fix e2e test

* fix FE retry logic

* last pass at BE clean up

* fix FE types

* BE PR feedback

* placeholder message record to fix ordering semantics (#74105)

* placeholder message record to fix ordering semantics

* catch all throwables

* fix test

* Fix readers of metabot_message to tie break on id

* make finished nullable

* minor pr comments

* fix tests

* fix migration

---------

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-05-14 10:54:22 -05:00
github-automation-metabase
8d95c65b5f 🤖 backported "docs - file-based development updates" (#74223)
docs - file-based development updates (#73192)

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-14 15:14:27 +00:00
github-automation-metabase
4a0185dd51 🤖 backported "Call utils/slugify from mc.c.m.collection with a map as the second arg." (#73488)
Call utils/slugify from mc.c.m.collection with a map as the second arg. (#72130)

* Call utils/slugify from mc.c.m.collection with a map as the second arg.

This caused test failures on Debian that weren't reproducible elsewhere.

Change the `utils/slugify` function to a malli defn so that other calls with
the wrong arguments will be caught in the future.

Had to remove a call to `pprint-to-str` from `metabase.util.malli.defn` in
order to break a circular dependency; replaced it with `with-out-str`+`pprint`
since most of the point of `pprint-to-str` is to be cljs compatible, and
this is a .clj file which will not run in cljs anyway.

* Loosen up the spec for slugify.

This shouldn't be necessary but let's just see if it has any effect on CI.

* Reintroduce max-width argument; turns out this does matter.

Was causing test failures which were only manifesting in CI for some reason.

* Review suggestions: unicode schema can be maybe boolean, and tighten up test.

Having it be optional *and* maybe feels redundant, but it plays better
with nil punning.

The test was too forgiving and would pass even tho the max-length
limit was being ignored. Adding longer inputs makes it so that the
length limitation has to be applied for it to pass.

* Pull :format-string out of entity before trying to slugify it.

You can't slugify a map, but previous versions were sloppy about this
and allowed it anyway. As far as I can tell, all of the relevant
entities have a :format-string key that contains the string that needs
to be slugified, but I don't have a lot of context on this and would
appreciate a double-check.

Co-authored-by: technomancy <technomancy@gmail.com>
2026-05-14 15:02:18 +00:00
github-automation-metabase
06e8226435 🤖 backported "Fix race in initializeQB" (#74210)
Fix race in `initializeQB` (#74202)

* Fix data race in initializeQB overriding itself

* Handle hash-only changes in QB

* Fix possible race with resolveCards

* Add test for the new isStale check

* Check undefined as well as null to check if navigation is external

* Handle empty state

Co-authored-by: Romeo Van Snick <romeo@romeovansnick.be>
2026-05-14 14:20:42 +00:00
github-automation-metabase
4f2841cf0c 🤖 backported "Fix clj-kondo errors regarding Nippy" (#74203)
Fix clj-kondo errors regarding Nippy (#73985)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-05-14 10:31:33 +00:00
github-automation-metabase
bbf6120d86 🤖 backported "Swap join order when doing remapping field values" (#74196)
Swap join order when doing remapping field values (#74192)

Co-authored-by: dpsutton <dan@dpsutton.com>
2026-05-14 00:16:58 +00:00
github-automation-metabase
5e6811a60a 🤖 backported "docs: typos in AI overview" (#74198)
docs: typos in AI overview (#74195)

* typo

* more typos

* Apply suggestions from code review



---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: Robert Kosara <rkosara@me.com>
2026-05-14 00:06:42 +00:00
github-automation-metabase
6eec7cd2dc 🤖 backported "BOT-1506 Add pre-conditions on metabot_message.external_id migration in v61" (#74189)
BOT-1506 Add pre-conditions on metabot_message.external_id migration in v61 (#74127)

BOT-1506 Add pre-conditions on metabot_message.external_id migrations

Prevents migration failure when upgrading from v60 -> v61.

Context:

v61 (release-x.61.x) has this original migration:
  - resources/migrations/061/20260421_metabot_message_external_id.yaml
  - Changeset id v61.2026-04-21T10:00:00 by sloansparger
  - Adds external_id column to metabot_message — with no preConditions

v60 (release-x.60.x) has this backport:
  - resources/migrations/060/20260501_metabot_source_feedback.yaml
  - Changeset id v60.2026-05-01T10:00:00 by undrfined
  - Adds external_id column to metabot_message (and the unique constraint)
  - Came in via commit 3caa0125e9 — "🤖 backported '[Metabot] Data source feedback' (#73785)"

Users upgrading from v60 after 3caa0125e9 will therefore already have external_id and we need a guard on the v61
changesets to prevent running them. At the same time, we need a guard on the v60 migration to prevent downgrade
conflicts if someone happened to install from a version of v61 prior to these changes.

The specific fix here (empty rollback in v61, pre-conditions on both changesets) mirrors the strategy suggested for
the ai_usage_log backport in #72651

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-05-13 21:47:35 +00:00
github-automation-metabase
109cb825c2 🤖 backported "Better de-duping of items to index to avoid ON CONFLICT DO UPDATE error" (#74183)
Better de-duping of items to index to avoid `ON CONFLICT DO UPDATE` error (#74160)

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-05-13 20:54:07 +00:00
github-automation-metabase
ea331546af 🤖 backported " Optimize search personal-collection filter to use a single EXISTS subquery" (#74182)
Optimize search personal-collection filter to use a single EXISTS subquery (#74114)

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-05-13 20:36:23 +00:00
github-automation-metabase
10305e788f 🤖 backported "Cap dependency backfill trigger queue at one pending" (#74179)
Cap dependency backfill trigger queue at one pending (#74121)

* Cap dependency backfill/entity-check trigger queue at one pending

Previously, each invocation of the in-job self-reschedule called
`qs/add-trigger` with a UUID-suffixed trigger key, so the Quartz queue
could grow unbounded under repeated event-driven scheduling. Switch to
a single static trigger key and route through `task/schedule-task!`,
which uses Quartz's `rescheduleJob` to replace the pending trigger in
place. The queue stays capped at one pending trigger regardless of how
often scheduling is requested.

Add `task/with-scheduler` to encapsulate binding the scheduler dynamic
var on Quartz worker threads (test-only `binding` of
`*quartz-scheduler*` doesn't propagate to workers, causing
`schedule-task!` to silently no-op in tests).

Add a regression test that blocks the backfill job mid-execution,
spams 50 schedule attempts, and asserts the trigger count stays at
most one both during execution and after the self-reschedule fires.

* Thread scheduler explicitly through schedule-task!/reschedule-task!

Replaces the with-scheduler binding hack with explicit scheduler
threading. `task/schedule-task!` and `task/reschedule-task!` now
accept the scheduler as their first argument; the 2-arity overload
of `schedule-task!` falls back to `(scheduler)` for backwards
compatibility.

Each dependency job's `schedule-run!` takes the scheduler explicitly
and passes it through. The job's `defjob` body uses
`(.getScheduler ctx)`; external callers (`init!`, event handlers) use
`(task/scheduler)`. No more dynamic-var binding workaround required
on Quartz worker threads.

* Include active triggers count in BackfillDependencies job start log message

* Type hinting in the defjob arglist isn't allowed

---------

Co-authored-by: Eric Normand <ericwnormand@gmail.com>
Co-authored-by: Ben Grabow <ben.grabow@gmail.com>
2026-05-13 20:14:01 +00:00
github-automation-metabase
23a0476253 🤖 backported "docs - AI updates" (#74164)
docs - AI updates (#74122)

* draft

* ai edits

* complete the sentence

* Apply suggestions from code review



* Update docs/ai/mcp.md



* Update docs/ai/overview.md



* feedback

* Apply suggestions from code review



* Update docs/ai/mcp.md



---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-13 17:33:37 +00:00
github-automation-metabase
844e5d5e06 🤖 backported "Implement JWT handling in SSO: add request-jwt function, refactor JWT request handling, and update tests for POST requests with JWT in JSON body" (#74158)
Implement JWT handling in SSO: add request-jwt function, refactor JWT request handling, and update tests for POST requests with JWT in JSON body (#72798)

* Implement JWT handling in SSO: add request-jwt function, refactor JWT request handling, and update tests for POST requests with JWT in JSON body.

* Clarify request-jwt docstring: :params covers both query string and form body

Agent-Logs-Url: https://github.com/metabase/metabase/sessions/2a1d5321-9546-4272-bfcf-06fb3783be24



---------

Co-authored-by: da <paoliniluis@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-13 17:24:23 +00:00
Chris Truter
91625fb965 Add type hints to fix reflection in data-complexity-score cli_test (release-x.61.x) (#74152)
Add type hints to fix reflection in data-complexity-score cli_test

The reflection warnings on .getCanonicalPath were fixed on master in #73684 but
that PR was labelled no-backport. On release-x.61.x these surface as Eastwood
failures now that Eastwood (tests only) runs on release-* pushes (#74079).
2026-05-13 17:57:57 +02:00
github-automation-metabase
338d363ad7 🤖 backported "Fix created questions missing from recents" (#74149)
Fix created questions missing from recents (#73909)

Fix created cards missing from recents

Co-authored-by: Anderson <ands.alves.nunes@gmail.com>
2026-05-13 15:10:03 +00:00
github-automation-metabase
560bd2830e 🤖 backported "fix: move mcp and agent api to their own settings page" (#74112)
#73838

> [!IMPORTANT]
> Manual conflict resolution is required.
> Checkout the branch and run `./backport.sh` script. Force push your changes after cherry-picking. Check the box below when done.

Conflicts:

```shell
100644 afd9e3612f 1	frontend/src/metabase/admin/routes.tsx
100644 d0d4e3925b 2	frontend/src/metabase/admin/routes.tsx
100644 8d04833199 3	frontend/src/metabase/admin/routes.tsx
100644 61b2a00a30 1	frontend/src/metabase/metabot/components/MetabotAdmin/AISettingsPage.tsx
100644 9f1d295342 2	frontend/src/metabase/metabot/components/MetabotAdmin/AISettingsPage.tsx
100644 70a0005075 3	frontend/src/metabase/metabot/components/MetabotAdmin/AISettingsPage.tsx
```

- [x] Conflicts resolved
2026-05-13 10:38:34 -03:00
github-automation-metabase
39b430afa5 🤖 backported "Revert "Enable data complexity score by default in Staging (#73982)"" (#74143)
Revert "Enable data complexity score by default in Staging (#73982)" (#74141)

This reverts commit 2f70fc1c3c.

See: https://metaboat.slack.com/archives/C07SJT1P0ET/p1778666456559809

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-13 12:46:21 +00:00
github-automation-metabase
a9e8919317 🤖 backported "Run Eastwood against changed tests only" (#74079)
* Run Eastwood against changed tests only (#73951)

* empty

---------

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2026-05-13 10:31:36 +00:00
github-automation-metabase
1e06ce3330 🤖 backported "Place unsectioned chart-settings widgets in the highest-priority section, not the first inserted" (#74135)
Place unsectioned chart-settings widgets in the highest-priority section, not the first inserted (#73805)

* Place unsectioned chart-settings widgets in the highest-priority section, not the first inserted

* Address review: localize section priority, simplify defaults, refactor

* Add localized labels for all SectionIds and cover with i18n tests

* Simplify section priority by using localized labels directly

Co-authored-by: Andrei Chernykh <andrei.chernykh@metabase.com>
2026-05-13 04:46:35 +00:00
github-automation-metabase
311ad967c0 🤖 backported "[BOT-1503] Fix cache read/write cols in ai usage log UA model" (#74125)
[BOT-1503] Fix cache read/write cols in ai usage log UA model (#74120)

fix ai usage log usage analytics model cache read/write columns

Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-05-12 23:36:55 +00:00
github-automation-metabase
4119a3fa20 🤖 backported "fix: correctly handle invalid api key in settings" (#74110)
fix: correctly handle invalid api key in settings (#73761)

Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-05-12 21:58:21 +00:00
github-automation-metabase
c31256f4d0 🤖 backported "Report SQL errors with parameters the same as without parameters" (#74096)
Report SQL errors with parameters the same as without parameters (#74041)

Closes QUE2-487

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-05-12 21:40:29 +00:00
github-automation-metabase
d48b9037f5 🤖 backported "turn off db routing for dependencies analysis" (#74102)
turn off db routing for dependencies analysis (#74091)

BAD:
when we analyze dependencies of a card, we hit the db routing
middleware, there's no current user so it errors.

```
clojure.lang.ExceptionInfo: Error preprocessing query in #'metabase.query-processor.middleware.enterprise/attach-destination-db-middleware: Anonymous users cannot access a database with routing enabled.
{:fn #'metabase.query-processor.middleware.enterprise/attach-destination-db-middleware, :query {:lib/type :mbql/query, :lib/metadata (metabase.lib.metadata.invocation-tracker/invocation-tracker-provider (metabase.lib.metadata.cached-provider/cached-metadata-provider (metabase.lib-be.metadata.jvm/->UncachedApplicationDatabaseMetadataProvider 743))), :stages [{:lib/type :mbql.stage/native, :native "SELECT * FROM ORDERS"}], :database 743}, :type :qp}
 at metabase.query_processor.preprocess$fn__126698$preprocess126697__126699$fn__126700$fn__126701.invoke (preprocess.clj:149)
    clojure.lang.PersistentVector.reduce (PersistentVector.java:418)
    clojure.core$transduce.invokeStatic (core.clj:7025)
    clojure.core$transduce.invoke (core.clj:7012)
    metabase.query_processor.preprocess$fn__126698$preprocess126697__126699$fn__126700.invoke (preprocess.clj:123)
    metabase.query_processor.setup$fn__126674$do_with_qp_setup126673__126675.invoke (setup.clj:232)
    metabase.query_processor.setup$fn__126674$fn__126679.invoke (setup.clj:223)
    metabase.query_processor.preprocess$fn__126698$preprocess126697__126699.invoke (preprocess.clj:121)
    metabase.query_processor.preprocess$fn__126698$fn__126725.invoke (preprocess.clj:116)
    metabase.query_processor.compile$fn__126766$compile126765__126767$fn__126768.invoke (compile.clj:66)
    metabase.query_processor.setup$fn__126665$do_with_canceled_chan126664__126666$fn__126667.invoke (setup.clj:196)
    metabase.query_processor.setup$fn__126655$do_with_database_local_settings126654__126656$fn__126657.invoke (setup.clj:188)
    metabase.query_processor.setup$fn__126635$do_with_driver126634__126636$fn__126637$fn__126640.invoke (setup.clj:173)
    metabase.driver$do_with_driver.invokeStatic (driver.clj:54)
    metabase.driver$do_with_driver.invoke (driver.clj:49)
    metabase.query_processor.setup$fn__126635$do_with_driver126634__126636$fn__126637.invoke (setup.clj:172)
    metabase.query_processor.setup$fn__126622$do_with_metadata_provider126621__126623$fn__126624$fn__126625.invoke (setup.clj:141)
    metabase.query_processor.store$fn__105777$do_with_metadata_provider105776__105778.invoke (store.clj:200)
    metabase.query_processor.store$fn__105777$fn__105781.invoke (store.clj:186)
    metabase.query_processor.store$fn__105777$do_with_metadata_provider105776__105778.invoke (store.clj:193)
    metabase.query_processor.store$fn__105777$fn__105781.invoke (store.clj:186)

```

GOOD:
analyze the card's dependencies. DB routing uses identical shaped
databases so this is fine and desired.

It's also possible that this puts these tasks into weird error states
and then lots of them build up.

Co-authored-by: dpsutton <dan@dpsutton.com>
2026-05-12 20:02:17 +00:00
github-automation-metabase
166133a5d9 🤖 backported "Add environment variables for controlling the App DB connection pool" (#74099)
Add environment variables for controlling the App DB connection pool (#74025)

Part of QUE2-570

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-05-12 19:28:05 +00:00
github-automation-metabase
a76a79adee 🤖 backported "[BOT-1496] fix metabot feedback migration logical filepath" (#74082)
[BOT-1496] fix metabot feedback migration logical filepath (#74053)

Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-05-12 16:20:08 +00:00
github-automation-metabase
07bb229197 🤖 backported "Reset embedding SDK props store state in place on cleanup" (#74070)
Reset embedding SDK props store state in place on cleanup (#73993)

* Reset embedding SDK props store state in place on cleanup

* Document use-lazy-selector regression test

* Update e2e tests for new props-store cleanup semantics

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-12 09:36:58 +00:00
github-automation-metabase
bdbefcb161 🤖 backported "[BOT-1351] Anthropic prompt caching" (#74059)
[BOT-1351] Anthropic prompt caching (#72851)

* explicit cache breakpoint on tool list

* apply cache breakpoint to sys prompt as-is

* split sys prompts into two parts; apply break point to static part

* fix flaky adapter auth tests when MB_METABOT_DEBUG_LLM_REQUESTS is set

* add automatic caching

* track anthropic cache tokens in usage analytics

* test fix

* fix migration

* add cache cols to ai_usage_log view

* test fix

* fix migration to v61

* update migrations

Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-05-12 06:17:19 +00:00
github-automation-metabase
5aaa02cdf6 🤖 backported "Refactor the failing/broken test" (#74061)
Refactor the failing/broken test (#74023)

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-05-12 06:08:43 +00:00
github-automation-metabase
a899035397 🤖 backported "[UXW-3795] Setting visualization options on dashboard cards breaks error messages" (#74048)
[UXW-3795] Setting visualization options on dashboard cards breaks error messages (#73879)

* [UXW-3795] Show the permission error on visualizer dashcards

* Narrow visualizer error suppression to permission errors only

* Use isDashcardAccessRestricted helper instead of icon-string check

* Tighten visualizer permission-denied tests and trim comments

* Drop redundant comment on isDashcardAccessRestricted

Co-authored-by: Andrei Chernykh <andrei.chernykh@metabase.com>
2026-05-11 23:58:06 +00:00
github-automation-metabase
e6bc7f1f48 🤖 backported "Fix schema-less databases freezing /browse/databases/{id} page" (#74045)
Fix schema-less databases freezing /browse/databases/{id} page (#73977)

* Add unit test covering the issue

* Fix the issue

Co-authored-by: Anderson <ands.alves.nunes@gmail.com>
2026-05-11 23:21:30 +00:00
github-automation-metabase
fc8f8e49f3 🤖 backported "add background-secondary to setup settingspage" (#74044)
add background-secondary to setup settingspage (#74037)

* add background-secondary to setup settingspage

* fix bg color extending to bottom

Co-authored-by: Maz Ameli <maz@metabase.com>
2026-05-11 23:10:01 +00:00
github-automation-metabase
d5bb4cce64 🤖 backported "Pin gson dep for bigquery " (#73966)
* Pin gson dep for bigquery  (#73757)

* pin gson for bigquery

* trigger ci

* trigger ci

---------

Co-authored-by: Riley Thompson <riley@metabase.com>
Co-authored-by: Riley Thompson <rileythompson99@gmail.com>
2026-05-11 19:01:43 +00:00
github-automation-metabase
5b1b4a114d 🤖 backported "Upsells for AI analytics, controls, customization, and prompts sections" (#73695)
* Upsells for AI analytics, controls, customization, and prompts sections (#73515)

* feat: add ai-controls and metabot-analytics upsells

* fix: use better locations and campaigns

* test: extend unit tests

* fix: use full width + height on upsells

* fix: navigation after reload and layout

* fix: image

* fix: missing props

* fix: use correct layout

* fix: images

* fix: dated test

* fix: import

---------

Co-authored-by: Gabriel Moreno <gabriel@metabase.com>
2026-05-11 14:41:46 -04:00
github-automation-metabase
74b58db22f 🤖 backported "BOT-1414 Ensure that edit_sql_query and replace_sql_query are included in :query_count" (#74018)
BOT-1414 Ensure that edit_sql_query and replace_sql_query are included in :query_count (#73708)

* Ensure that edit_sql_query and replace_sql_query are included in :query_count

* Remove new-query-tool-names since it is now just an alias for metabot.tools/query-generation-tool-names

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-05-11 17:37:41 +00:00
github-automation-metabase
c62017120e 🤖 backported "Allow disabling transform jobs" (#74021)
Allow disabling transform jobs (#73573)

* API and Job Details page

* Updating the job list page to handle single job actions

* bulk enable / disable jobs

* wire :active flag to Quartz trigger lifecycle

Rename the new `disabled` column to `:active` (codebase convention) and
route activation/deactivation through model helpers that create or
delete the per-job Quartz trigger. Without this the column flipped but
"disabled" jobs still fired on cron — defeating the cost-control halt
this feature is meant to provide.

- task/init! now filters :active true so restarts don't rebuild triggers
  for inactive jobs
- bulk endpoint becomes PUT /api/transform-job/active and reuses the
  model helpers so triggers stay in sync
- POST /:job-id/run is intentionally unchanged so admins can force a
  run regardless of :active
- serdes copies :active so round-trips preserve halted state

* harden activate/deactivate orchestration

- Note the DB↔Quartz race in activate-job!/deactivate-job! docstrings
  so callers don't assume full atomicity.
- Make the bulk PUT /active loop tolerant of per-job failures: log and
  continue rather than aborting the batch; report :updated as the
  count of successful flips.
- Extend the schedule-edit-on-inactive test to assert the schedule
  persists in the DB and that reactivation picks it up in the trigger.

* disabled -> active

* e2e test

* moving migration

* address review: fix migration logicalFilePath, write-check on bulk active

* change bulk disable endpoint to require admin permissions, updated FE to remove actions for data analysts

* Address review: cluster-lock active flips, surface bulk skip/failure counts

- Wrap activate-job!/deactivate-job! in a per-job cluster lock so the row
  flip and Quartz trigger op can't interleave with another flip of the
  same job across the cluster. Replaces the documented "not safe under
  concurrent activate/deactivate" caveat with an actual guarantee.
- Bulk PUT /api/transform-job/active now reports :updated, :cannot-write,
  and :failed counts instead of silently dropping perm-skipped jobs and
  swallowing per-job exceptions into the success count.
- Test the failure path with mt/with-dynamic-fn-redefs + mt/original-fn.

* Use snake_case for new bulk-active response keys

* Drop dead can-write filter from bulk transform-job activation

The endpoint requires superuser, so mi/can-write? short-circuits to true
on every job. The filter and the :cannot_write count are structurally
0 today; remove them so the response shape matches what we actually
report. If this endpoint is ever relaxed back to data-analyst, the
filter and field can come back together.

* Defer Quartz writes until per-job PUT's DB txn commits

Previously, a transform-job PUT that supplied both :active and :tag_ids
could leave the scheduler in a state the DB never agreed with: if the
tag write hit an FK race (or the trailing hydrate read failed), the
outer with-transaction would roll back the :active row UPDATE while
activate-job!/deactivate-job! had already written to Quartz on a
separate connection pool. Same DB-vs-scheduler divergence the cluster
lock fixed for concurrent flips, only triggered by a single request.

Restructure the endpoint into two phases: all DB writes that can fail
commit first; Quartz side effects (the :active toggle, the schedule
reschedule) run only after the txn closes. The cluster lock inside
activate-job!/deactivate-job! still serializes concurrent flips of
the same job across the cluster.

* Tighten bulk-active test assertion to exact pending count

Previously asserted :updated pos?, which would silently pass if the
endpoint started re-flipping already-correct-state jobs (the count
would just be higher). Capture the count of jobs in the wrong state
upfront and assert the response matches; this is robust to migration-
seeded built-in jobs in the test DB while still catching the regression.

* review feedback

* add padding to menu on job list page

---------

Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
Co-authored-by: Kevin Kelleher <kevin.kelleher@metabase.com>
2026-05-11 17:32:28 +00:00
github-automation-metabase
3ab1ddfaa6 🤖 backported "BOT-1410 Add ai-usage-max-retention-days setting and use it in ai-usage-trimmer" (#74019)
BOT-1410 Add ai-usage-max-retention-days setting and use it in ai-usage-trimmer (#73518)

This ENV-var-only setting is similar in spirit to audit-max-retention-days, and allows self-hosted admins to configure their own retention. Default is 180 days (6 months) with a minimum of 30 days.

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-05-11 17:27:24 +00:00
github-automation-metabase
1b5b106385 🤖 backported "[Slackbot] Remove slash command" (#74015)
[Slackbot] Remove slash command (#74009)

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
2026-05-11 16:37:19 +00:00
github-automation-metabase
cf2136dd8a 🤖 backported "[GDGT-2385] block transform runs when trial quotas run out based on token check sync" (#74014)
[GDGT-2385] block transform runs when trial quotas run out based on token check sync (#73622)

When a customer exceeds their free transforms quota during a trial, this flips the is-locked attribute in the token-check call. This PR mirrors that is-locked attribute into the Metabase's instance's DB as a setting row, and adds a lock check alongside the existing feature check at the two transform-execution sites (scheduled job runner and manual-run API endpoint). We also expose the lock state through a new GET /api/transform/settings endpoint.

Lock state is mirrored from the token-check response into a json setting. We defensively prevent API writes to this :visibility :internal because this is billing-relevant logic.
The new update-locked-meters! that syncs the lock state is called from inside do-refresh!, which all refresh token paths converge on. 

Two key points:
1. Writer fires only when meters is present in a successful response. If the fetch throws / times out / the circuit breaker is open, the setting is left untouched. We do not want to accidentally change lock state on an intermittent failure.
2. Empty :meters {} is a legitimate "nothing locked" signal and is written through. This is also the default value, so it's backwards-compatible with existing customers.

---------

Co-authored-by: YB <yeowboon@metabase.com>
Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-11 16:16:46 +00:00
github-automation-metabase
9e458a93c5 🤖 backported "uxwest to graphy" (#74013)
uxwest to graphy (#73973)

Co-authored-by: Aleksandr Lesnenko <alxnddr@users.noreply.github.com>
2026-05-11 15:30:41 +00:00
github-automation-metabase
869f787790 🤖 backported "fix metadata endpoint http verb" (#74011)
fix metadata endpoint http verb (#73989)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-11 15:20:37 +00:00
github-automation-metabase
17b4e77724 🤖 backported "Enable data complexity score by default in Staging" (#74002)
Enable data complexity score by default in Staging (#73982)

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-11 13:35:59 +00:00
github-automation-metabase
71539c670c 🤖 backported "Fix content translation for multi-line markdown cards (EMB-1654)" (#73999)
Fix content translation for multi-line markdown cards (EMB-1654) (#73542)

* Fix content translation for multi-line markdown cards (EMB-1654)

* Report row number in CSV parse errors

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-11 12:49:17 +00:00
github-automation-metabase
96b32c87d2 🤖 backported "Add batch_id to data complexity events" (#73997)
Add batch_id to data complexity events (#73836)

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-11 11:30:48 +00:00
github-automation-metabase
885d80091d 🤖 backported "Use square and octagonal color harmonies in embedding themes" (#73991)
Use square and octagonal color harmonies in embedding themes (#73535)

* Persist color_harmony on embedding_theme

* Add color_harmony to EmbeddingTheme frontend type

* Add color harmony utility for brand-derived palettes

* Wire color harmony into theme editor hook

* Add color harmony picker to theme editor

* Drop color_harmony column from embedding_theme

* Drop color_harmony from EmbeddingTheme frontend type

* Rewrite color harmony utility with storybook demo

* Replace harmony mode with regenerate-from-brand and undo toast

* Replace harmony picker with regenerate-from-brand button

* Pre-seed draft theme with brand-derived harmony colors

* Add interactive color harmony storybook

* fixed e2e

* no loki

* Address review feedback

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-11 10:13:38 +00:00
github-automation-metabase
558223e0a9 🤖 backported "Allow impersonated set operations like union, intersect, and except" (#73965)
Allow impersonated set operations like union, intersect, and except (#73873)

* allow impersonated set operations

* fix test for clickhouse

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-05-08 19:54:44 +00:00
github-automation-metabase
2a59984169 🤖 backported "Fix Snowflake impersonation for roles that need to be quoted" (#73884)
* Fix Snowflake impersonation for roles that need to be quoted (#73800)

* Fix Snowflake impersonation for roles that need to be quoted

* Test fix 🔧

* Empty commit to trigger CI

* Test fix 🔧

* Test fix 🔧

---------

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
Co-authored-by: Cam Saul <github@camsaul.com>
2026-05-08 12:53:29 -07:00
github-automation-metabase
a34ea476d2 🤖 backported "docs: security center" (#73969)
docs: security center (#73773)

* security center

* add link

* Optimized 2 images, saved 259.0 KB (48.4% smaller)

* upgrade links

* Apply suggestions from code review



* edits

---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-08 19:51:47 +00:00
github-automation-metabase
5d042ad428 🤖 backported "revert metadata endpoint updates" (#73961)
revert metadata endpoint updates (#73950)

* metadata endpoint updates

* fix

* simplify

* fix config

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-08 18:34:20 +00:00
github-automation-metabase
6f86f3ada0 🤖 backported "fix(sdk-bundle): race condition in ContentTranslationsProvider setup" (#73958)
fix(sdk-bundle): race condition in ContentTranslationsProvider setup (#73929)

Fix race condition in when rendering ContentTranslationsProvider

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
2026-05-08 18:20:43 +00:00
github-automation-metabase
da499ec979 🤖 backported "Fix scatterplot visualization without color distinction" (#73953)
Fix scatterplot visualization without color distinction (#73903)

* Fix scatterplot visualization without color distinction

Closes QUE2-566

---------

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
Co-authored-by: Aleksandr Lesnenko <alxnddr@gmail.com>
2026-05-08 17:38:26 +00:00
github-automation-metabase
4ebaa65d40 🤖 backported "GRO-354 Show transforms as locked during trial" (#73947)
GRO-354 Show transforms as locked during trial (#73517)

Co-authored-by: Brad Anderson <brad@metabase.com>
2026-05-08 16:43:57 +00:00
github-automation-metabase
34dedd3279 🤖 backported "[perms] Refactor graph updates to remove costly N+1 query" (#73944)
[perms] Refactor graph updates to remove costly N+1 query (#73694)

Fixes UXW-3902.

The original architecture for updating the permissions resulted in
`O(schemas * groups * perm_types)` `SELECT` queries and similarly for
`DELETE` and `INSERT` queries to update them. This made graph updates
extremely slow, where "slow" means that we have observed them taking 30+
minutes.

The new approach uses 3 or 4 queries no matter how many permissions are
being updated.

Co-authored-by: Braden Shepherdson <braden@metabase.com>
2026-05-08 16:14:15 +00:00
github-automation-metabase
070919bbca 🤖 backported "Upgrade Cypress grep plugin" (#73942)
Upgrade Cypress grep plugin (#70244)

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-05-08 15:47:11 +00:00
github-automation-metabase
38964262fe 🤖 backported "fix: cannot create a chart using metabot in a document" (#73938)
fix: cannot create a chart using metabot in a document (#73750)

Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-05-08 15:33:02 +00:00
github-automation-metabase
432746cc10 🤖 backported "Fix document default selected entity picker collection on save" (#73935)
Fix document default selected entity picker collection on save (#73891)

Fix document save collection default

Co-authored-by: Anderson <ands.alves.nunes@gmail.com>
2026-05-08 15:18:46 +00:00
github-automation-metabase
137ff1fd76 🤖 backported "Toggle Collect User Data input based on audit_app feature" (#73926)
Toggle Collect User Data input based on audit_app feature (#73920)

Co-authored-by: Timofey Kachalov <timofei@metabase.com>
2026-05-08 13:57:34 +00:00
github-automation-metabase
6c789303c5 🤖 backported "fix: document the anthropic api key setting" (#73930)
fix: document the anthropic api key setting (#73881)

Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-05-08 13:44:50 +00:00
github-automation-metabase
1bcdffa33c 🤖 backported "fix(sdk-bundle): Fix New question breadcrumb dead-end after Visualize in metabase-browser" (#73925)
fix(sdk-bundle): Fix New question breadcrumb dead-end after Visualize in metabase-browser (#73842)

Fix "New question" button break post viz metabase-browser flow

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
2026-05-08 13:43:43 +00:00
github-automation-metabase
13aaf3946d 🤖 backported "test: add e2e regression for EMB-332 (#57028) multi-value contains chain filter" (#73924)
test: add e2e regression for EMB-332 (#57028) multi-value contains chain filter (#73830)

test: repro EMB-332 multi-value string/contains chain filter

Static embedded dashboard with a `string/contains` locked parameter passing
multiple values used to crash the editable chain-filter dropdown values
endpoint with a 400 (issue 57028). Add a regression test that visits the
embedded dashboard, opens the linked Email filter, and asserts the values
endpoint succeeds and the dropdown populates.

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:50:35 +00:00
github-automation-metabase
9e8d30e9f5 🤖 backported "docs - useMetabot hook " (#73923)
docs - useMetabot hook  (#73692)

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-08 11:43:50 +00:00
github-automation-metabase
e7fc44746b 🤖 backported "Use resources to give more detailed tool instructions" (#73919)
Use resources to give more detailed tool instructions (#73744)

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-08 10:22:35 +00:00
github-automation-metabase
a2c0563d10 🤖 backported "deps: bump netty to 4.2.13.Final in hive-like driver" (#73916)
deps: bump netty to 4.2.13.Final in hive-like driver (#73821)

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-08 08:54:29 +00:00
github-automation-metabase
aa4881d472 🤖 backported "deps: upgrade postgres to 42.7.11" (#73915)
deps: upgrade postgres to 42.7.11 (#73820)

Patch bump on the 42.7.x line.

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-08 08:54:14 +00:00
github-automation-metabase
db39a5edd9 🤖 backported "Add clear per-model slots for json-based serialization" (#73904)
Add clear per-model slots for json-based serialization (#73851)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-08 00:14:36 +00:00
github-automation-metabase
94bf82e62d 🤖 backported "docs - ai usage analytics" (#73895)
docs - ai usage analytics (#73662)

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-07 22:24:30 +00:00
github-automation-metabase
49ddc35f23 🤖 backported "docs: Extend invoice payment period to 30 days" (#73900)
docs: Extend invoice payment period to 30 days (#73898)

Extend invoice payment period to 30 days

Updated payment terms for invoices from 15 to 30 days.

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-05-07 22:15:42 +00:00
github-automation-metabase
6388a1d1d0 🤖 backported "docs: remove embedding webinar" (#73896)
docs: remove embedding webinar (#73797)

remove webinar

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-05-07 22:05:31 +00:00
github-automation-metabase
b8bea3815c 🤖 backported "Show "No results" empty state in Metric Viewer " (#73878)
Show "No results" empty state in Metric Viewer  (#73705)

* Show "No results" empty state in Metric Viewer when filters yield zero rows

* Update e2e test to assert new empty-state copy

* Stop scoping no-results assertion to visualization-root testid

* Use findByRole heading to satisfy lint rule

* Render compact NoResultsView for partial-empty cards in metric viewer grid

Co-authored-by: Andrei Chernykh <andrei.chernykh@metabase.com>
2026-05-07 19:34:54 +00:00
github-automation-metabase
926aad20ea 🤖 backported "Port sankey repro no. 65317 to Jest " (#73875)
Port sankey repro no. 65317 to Jest  (#73789)

Port sankey 'Visualize another way' regression test to Jest (metabase#65317)

Replaces the e2e repro with a unit test alongside the existing
visualizer-button assertions in Dashcard.unit.spec.tsx. Anchors on
'Show visualization options' so a layout regression that drops the
actions panel cannot mask the negative assertion.

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:53:36 +00:00
github-automation-metabase
c4f46d07a6 🤖 backported "Remove explicit escape spec from postgres LIKE filters" (#73871)
Remove explicit escape spec from postgres LIKE filters (#73765)

* Remove explicit escape spec from postgres LIKE filters

Closes QUE2-555

Postgres's default is already '\' and explicitly setting it breaks when the `standard_conforming_strings` setting is
turned off.

* Require namespace only as an alias

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-05-07 18:40:23 +00:00
github-automation-metabase
1761a72141 🤖 backported "Work around sqlglot #>> operator priority bug" (#73870)
Work around sqlglot #>> operator priority bug (#73802)

Closes QUE2-562

format-json-query in src/metabase/driver/postgres.clj emits (parent #>> array[?]::text[])::field-type.

Postgres' grammar binds :: tighter than #>>, so ::text[] correctly applies to array[?] only. But the impersonation
pipeline runs the SQL through validate-impersonated-query*, which round-trips through sqlglot for safety — and sqlglot
treats :: and #>> as equal-precedence column operators. It re-emits as
CAST((CAST(parent #>> array[?] AS TEXT[])) AS TEXT),
putting the cast around the result of #>> instead of the path. Postgres then tries to read the JSON text value
as an array literal and fails with malformed array literal: "value".

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-05-07 18:38:57 +00:00
github-automation-metabase
f2139e082d 🤖 backported "Handle query stages that have both :limit and :page" (#73868)
Handle query stages that have both `:limit` and `:page` (#73795)

* Handle query stages that have both `:limit` and `:page`

* Kondo + test fixes 🔧

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2026-05-07 18:36:36 +00:00
github-automation-metabase
374cecd12f 🤖 backported "Fix price displayed for advanced transforms in upsell modal" (#73867)
Fix price displayed for advanced transforms in upsell modal (#73855)

Co-authored-by: Brad Anderson <brad@metabase.com>
2026-05-07 18:20:44 +00:00
github-automation-metabase
f29aad1d2c 🤖 backported "Do not run e2e tests when uberjar build fails" (#73859)
Do not run e2e tests when uberjar build fails (#73845)

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-05-07 18:16:01 +00:00
github-automation-metabase
60cf94f78c 🤖 backported "Fix Dockerfile to ensure Clojure installation script runs" (#73863)
Fix Dockerfile to ensure Clojure installation script runs (#73857)

Fixing my own error

Co-authored-by: da <paoliniluis@gmail.com>
2026-05-07 16:56:35 +00:00
github-automation-metabase
c4d20e6563 🤖 backported "deps: bump netty to 4.1.133.Final in athena driver" (#73854)
deps: bump netty to 4.1.133.Final in athena driver (#73822)

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-07 16:17:10 +00:00
github-automation-metabase
bbadde58a8 🤖 backported "Handle nil additional options in mysql can-connect?" (#73846)
Handle nil additional options in mysql can-connect? (#73834)

handle nil additional options

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-05-07 15:48:14 +00:00
github-automation-metabase
3d6a01da8b 🤖 backported "Add titles to tools in API to match Claude Connector requirements" (#73844)
Add titles to tools in API to match Claude Connector requirements (#73725)

Co-authored-by: Timofey Kachalov <timofei@metabase.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2026-05-07 15:20:54 +00:00
github-automation-metabase
c265c475b5 🤖 backported "Handle UUID field filters" (#73839)
Handle UUID field filters (#73778)

Closes QUE2-561

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-05-07 15:01:05 +00:00
github-automation-metabase
2071d04d9f 🤖 backported "deps: upgrade dompurify to 3.4" (#73827)
deps: upgrade dompurify to 3.4 (#73823)

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
2026-05-07 10:29:21 +00:00
github-automation-metabase
192ad45060 🤖 backported "Avoid invalid chars from keeping appdb reindex to complete" (#73811)
Avoid invalid chars from keeping appdb reindex to complete (#73759)

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-05-07 07:38:09 +00:00
github-automation-metabase
c2e20c3c6e 🤖 backported "Show plain-text 4xx body in guest embed visualization" (#73808)
Show plain-text 4xx body in guest embed visualization (#73646)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-07 07:26:02 +00:00
github-automation-metabase
bbddcd4a7e 🤖 backported "fix(sdk-package): use v55+ JWT auth method in CLI Express snippet" (#73807)
fix(sdk-package): use v55+ JWT auth method in CLI Express snippet (#73716)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-07 07:03:47 +00:00
github-automation-metabase
c1c08a11cd 🤖 backported "[Metabot] Data source feedback" (#73784)
[Metabot] Data source feedback (#73497)

Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: Maksym Yakubych <maksym@metabase.com>
2026-05-06 22:09:27 +00:00
github-automation-metabase
798830c3ef 🤖 backported "docs: metric math" (#73794)
docs: metric math (#73745)

* metric updates

* typos

* Optimized 6 images, saved 455.3 KB (43.7% smaller)

* Apply suggestions from code review



---------

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-06 22:07:21 +00:00
github-automation-metabase
4ad22e5035 🤖 backported "Fix Metabase command syntax in mcp.md" (#73782)
Fix Metabase command syntax in mcp.md (#73779)

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-05-06 21:06:51 +00:00
github-automation-metabase
955d0f47e8 🤖 backported "[BOT-1406] Fix & backport metabot IP & embed_url" (#73760)
[BOT-1406] Fix & backport metabot IP & embed_url (#73613)

* make metabot PII collection consistent

* pr feedback

Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-05-06 18:24:25 +00:00
github-automation-metabase
41c4a8f4df 🤖 backported "Bot 1361 metabot fixes" (#73746)
Bot 1361 metabot fixes (#73304)

* Enumerate available fields in agent-lib field-resolution errors

When `resolve-field-in-query` cannot locate a field in the current query
stage, the ex-info now lists the candidate fields available in that
stage (name, id, table-id, join-alias) and includes a reference-shape
hint tailored to whether a join alias is required. The full candidate
set is preserved in `:available-fields`; the human-readable message caps
the list at 20 and appends an "and N more" suffix.

This lets the agent recover from a field-lookup miss in a single step
instead of re-exploring the query schema.



* Add query-shape rules to metabot NL-querying prompt

Teach the agent the MBQL shape expected for five common question
patterns:

- Scalar questions (average, percentage, total, ...) take no breakout,
  even when dimension words appear in the question or metric name.
- Superlative questions (most, highest, slowest, ...) use aggregate +
  breakout + `order-by` desc + `limit 1`.
- Filters reflect only what the user asked for; soft-delete flags,
  "active only" predicates, and ETL hygiene columns are not added by
  default. Filters baked into a metric or model definition stay.
- Stop re-searching once a search returns at least three candidate
  entities relevant to the business concept; widen only on zero /
  unrelated results.
- If a metric does not expose the needed dimension, pivot to the
  underlying table or model instead of re-inspecting the metric.



* Show related tables to the agent when it's reading table and table field metadata

* Simplify the NLQ prompt

* Scope metabot search tools to their allowed entity types and expose a limit parameter

Previously the per-profile search tool wrappers (nlq, sql, transform, generic)
only used their allowed-types for validating agent-supplied entity_types; when
the agent omitted the argument, the core search defaulted to every model
(dashboards, transforms, databases). NLQ could return dashboards/transforms,
which should never surface in that flow. Default entity-types to the tool's
allowed-types so each variant stays within its declared scope.

Also add an optional limit parameter (default 10, capped at 50) to every
variant, with prompt guidance to raise it for broad/generic queries. Expands
the NLQ tool to include saved questions.



* Preserve field options through agent_lib repair

Non-canonical field references from the agent — either an options map
in the third slot (`["field", id, {"temporal-unit": "month"}]`) or a
field-like map (`{"field_id": 13, "temporal_bucket": "month"}`) — were
silently dropped during repair, producing MBQL queries without the
requested bucketing or binning. Repair now rewrites them into the
canonical `with-temporal-bucket` / `with-binning` / `asc`-`desc`
wrappers so the runtime applies them as intended. The `with-binning`
helper also keywordizes its `:strategy` value so string inputs reach
`lib/with-binning` in the shape it validates.

* Produce a renderable pseudo-card from `query->url-hash`

Links generated via `metabase://query/...` previously base64-encoded
just `{:dataset_query ...}`, so the ad-hoc card the query builder
reconstructs from `/question#<base64>` had no `visualization_settings`
or `type`. Renderers that assume those fields exist (notably
`getAlertType` when a multi-row result auto-picks a line/bar/area
display) crashed on undefined lookups.

`query->url-hash` now emits a full pseudo-card with `:type "question"`
and `:visualization_settings {}`, and `pseudo-card->link` fills in a
default empty `:visualization_settings` so callers passing through
`query-and-viz-link` get the same guarantee.

* Expand `construct_query` MCP tool description

The tool previously shipped with a ~430-char description and a 12KB
auto-generated JSON schema. That's enough for the LLM to see what's
valid structurally, but not enough to know which idioms are canonical —
so agents routinely produced non-canonical shapes (field-options in a
third slot, bare aggregated sort refs, no `append-stage` before
filtering on aggregations).

Extract the description into a `construct-query-tool-description` def
that covers source types, every top-level operation, the nested
reference forms (including `with-temporal-bucket` and `with-binning`),
filter/aggregation/temporal operator names, and four worked examples
(top-N, metric with monthly bucket, filter-on-aggregated with
`append-stage`, named expression). Stops short of the full agent_api
reference; aims for enough structure to write valid programs without
guessing.

* Add workflow and pitfalls sections to `construct_query` description

Expand the `construct_query` MCP tool description with the guidance
agents consistently get wrong: the workflow (fetch entity detail →
construct → execute/visualize), an `offset` example where
`append-stage` must NOT be added, and a rules section sourced from the
capability catalog's `prompt-notes` covering stage boundaries,
ref/shape constraints (aggregation-ref for sorting, no third-slot
field options, bare `case` fallback, JSON objects only where helpers
explicitly call for them), when to prefer direct related-field refs
over explicit joins, and metric/date caveats.



* Remove obsolete test asserting table resources exclude related_tables

The metabot table read-resource tools now intentionally include related
tables in their output, so the assertion that they should be excluded no
longer reflects the desired behavior.

* Drop bogus join-alias hint from field-resolution errors

The structured-program field reference is a strict 2-tuple [\"field\", id]
with no opts map (per agent_lib/schema.clj), so suggesting an opts-map
form to the agent was misleading. Drop the hint branch (and the
:suggested-ref-shape ex-data); the candidate listing already exposes
table-id and join-alias inline. Also suppress the listing header when
no candidates are visible — the prior "use one of the ids below"
message was incorrect when the stage has no fields.

* Use mt/with-dynamic-fn-redefs for search-tool spies

Switch the inner search-core/search redefs in tool-default-entity-types-test
and tool-limit-test to the thread-safe macro so the spies don't race with
parallel test execution.

* Place saved questions in the NLQ curation hierarchy

The NLQ search tool now returns saved questions, but the prompt's
preference ordering only mentioned metric / model / table — leaving the
agent without guidance on whether to pick a question over a model or a
raw table. Slot saved questions between models and tables: more curated
than a raw table because someone hand-built and saved the answer, but
typically narrower and less reusable than a model.

* Reuse max-search-limit constant in search-tool :limit schemas

The four per-tool schemas had the cap hardcoded as 50, which would drift
from max-search-limit if the runtime cap ever changes. Reference the
def so the schema and the runtime min stay in sync.

* Use sorted-set for search-tool allowed entity types

Previously each per-tool variant declared its allowed types as a plain
set literal, so iteration order was unspecified — the "Allowed types:"
error message and the default entity-types fallback came out in
hash-bucket order. Use sorted-set at the definition sites so the
ordering is stable in the data itself, and drop the now-redundant
(sort allowed-types) call in do-search.

---------

Co-authored-by: Mihael Konjević <konjevic@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:38:45 +00:00
github-automation-metabase
4d885ef536 🤖 backported "Fix guest embed question downloads ignoring editable filters" (#73738)
Fix guest embed question downloads ignoring editable filters (#73553)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-06 13:29:02 +00:00
github-automation-metabase
41c06d6a5c 🤖 backported "collapse view permissions when apropriate" (#73731)
collapse view permissions when apropriate (#73675)

* collapse view permissions when apropriate

* PR Feedback

Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
2026-05-06 11:08:16 +00:00
github-automation-metabase
bbe8edf51d 🤖 backported "notify on timed out job" (#73630)
notify on timed out job (#73108)

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-05-06 10:28:11 +00:00
github-automation-metabase
1114d16c88 🤖 backported "Security center improvements" (#73718)
Security center improvements (#73634)

* Re-check acknowledged advisories after config changes

* clear ack status on reactivation

* add security-center escape hatch

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-05-06 10:01:15 +00:00
github-automation-metabase
bdb4f1d53e 🤖 backported "Adjusting other-sso-enabled? setting to not include metabot slack connection" (#73712)
Adjusting other-sso-enabled? setting to not include metabot slack connection (#73672)

Co-authored-by: Nick Fitzpatrick <nick@metabase.com>
2026-05-06 04:58:56 +00:00
github-automation-metabase
be282d4b1d 🤖 backported "PoC: Performance tests e2e refinement" (#73677)
PoC: Performance tests e2e refinement (#73595)

* Add write-path coverage to Schedule.unit.spec.tsx

Extend the existing read-path tests with field-render checks (presence
and absence of aria-labeled inputs per frequency) and 19 click-driven
write-path cases that cover the full UI → cron mapping including AM/PM
12-hour boundaries and monthly frame variants.

test-utils now exposes the onScheduleChange spy from setup() and adds
setupHarness() — a stateful wrapper that re-renders <Schedule> after each
onChange so multi-click flows behave correctly (<Schedule> derives its
settings from cronString on every render, so the parent must update
cronString between clicks for subsequent clicks to see the new state).



* Remove redundant cache-related e2e specs

These specs duplicated coverage that already exists in faster, more
deterministic backend or frontend unit tests.

Deleted specs:

- admin/performance/clock.cy.spec.ts — covered by
  metabase.testing-api.api-test/set-time-test.

- admin/performance/schedule.cy.spec.ts — covered by Schedule.unit.spec.tsx
  (UI -> cron) and cron.unit.spec.ts (transform), with cache-config
  endpoint acceptance verified in metabase-enterprise.cache.cache-test.
  The save -> launcher-label roundtrip is still covered by
  strategyForm.cy.spec.ts.

- admin/performance/dashboardsAndQuestions.cy.spec.ts — covered by
  metabase-enterprise.cache.strategies-test (TTL/duration/schedule with
  deterministic clock) and metabase-enterprise.cache.cache-test (full
  inheritance ladder including the dashboard tier).

- admin/performance/preemptiveCaching.cy.spec.ts — covered by
  metabase-enterprise.cache.task.refresh-cache-configs-test
  (refresh-duration-cache-card/dashboard with MBQL + native + parameterized
  fixtures); config save round-trip is covered by strategyForm.cy.spec.ts.

Trimmed cache-form configure and clear-cache tests from
question/caching.cy.spec.js and dashboard/dashboard.cy.spec.js. The
configure flows mirrored each other and were already covered by
strategyForm.cy.spec.ts. The four-path dirty-form-modal test is
preserved in both files (still mirror images of each other; left for a
follow-up).

Also drops the now-unused constants.ts, types.ts, and unused exports in
e2e-performance-helpers.ts.



* Move policy-options-count assertions from e2e to Jest

The "N policy options" tests were pure DOM count assertions on
StrategyEditorForDatabases — no real interaction or backend roundtrip,
just conditional rendering by edition and target type. They fit the
existing Jest harness for that component without any new infrastructure.

OSS: added "shows two policy options for the default policy" to the
existing StrategyEditorForDatabases.unit.spec.tsx.

EE: added "shows four policy options for the default policy" and "shows
five policy options for a database (adds 'Use default')" to the
enterprise StrategyEditorForDatabases.unit.spec.tsx.

Removed the corresponding e2e tests from strategyForm.cy.spec.ts:
- "there are two policy options for the default policy" (OSS)
- "there are N policy options for itemName" (EE, ran twice via the
  default-policy / Sample Database loop).



* Move preemptive-caching switch availability to Jest

The preemptive-caching switch is rendered conditionally in StrategyForm
based on (targetModel, strategyType). The e2e test exercising this
conditional was paying four cy.visit cold boots to verify presence/
absence of one DOM element across (root | database) × (duration |
schedule). Pure conditional rendering, perfect Jest fit.

Added a parameterized test in the enterprise StrategyEditorForDatabases
spec covering the four (target, strategy) combinations on root and
database — the switch must never appear for those targets, regardless
of strategy. Uses the existing test harness with `cache_preemptive`
enabled alongside `cache_granular_controls`.

Trimmed the e2e test in strategyForm.cy.spec.ts to the three remaining
question-target cases (adaptive / nocache / use-default → switch
absent), which would need a SidebarCacheForm Jest harness to migrate
fully — out of scope for this batch.



* Drop redundant adaptive-strategy input-binding e2e tests

Adaptive strategy input binding (typing into "Minimum query duration"
and "Multiplier" fields, then saving) is already exhaustively covered
by Jest:

- OSS: StrategyEditorForDatabases.unit.spec.tsx — "lets user change the
  default policy to 'Adaptive', then 'No caching'" types both inputs
  and saves.
- EE: enterprise/.../StrategyEditorForDatabases.unit.spec.tsx — "lets
  user change the default policy from 'Duration' to 'Adaptive' to
  'Don't cache results'" types min duration and multiplier on Adaptive
  and asserts the launcher reflects the change.

Removed seven redundant e2e it() blocks:

- OSS "can configure both a minimum query duration and a multiplier
  for the default adaptive policy".
- EE adaptive describe per itemName (×2 itemNames): "can configure a
  minimum query duration for X's adaptive policy", "can configure a
  multiplier for X's adaptive policy", and "can configure both...".

Kept "can set ${itemName} to adaptive" for both itemNames (and OSS) as
the save -> launcher-label-reflects round-trip with inheritance check
— that's the e2e-unique signal worth keeping until the smoke pass.



* Collapse schedule-strategy e2e to one save-roundtrip per itemName

The schedule sub-describe ran six it() blocks (hourly/daily/weekly per
itemName), with nested 6-iteration daily loops and 7-iteration weekday
loops re-saving and resetting between iterations. All of that was
exercising cron permutations that are now exhaustively unit-tested:

- Cron transform: cron.unit.spec.ts
- UI -> cron binding (28+ permutations including AM/PM 12-hour
  boundaries and monthly frame variants): Schedule.unit.spec.tsx
- Strategy -> launcher label mapping: enterprise/.../StrategyEditorFor
  Databases.unit.spec.tsx ("can abbreviate a 'Schedule' strategy")

What's left as e2e-unique is the save -> backend -> GET -> launcher
re-render round-trip. One representative schedule (weekly Tuesday
2 PM) per itemName is enough; the choice of frequency doesn't change
what the round-trip itself proves.

Net: 6 it()s + ~50 internal save/reset cycles -> 2 it()s, one save each.



* Consolidate caching e2e tests into a single smoke spec

Replace the scattered caching tests with one spec at
admin/performance/caching.cy.spec.ts containing five smokes that target
exactly what backend tests and Jest component tests cannot:

- OSS: save the default-policy strategy and reflect the saved state.
- EE: configure a database strategy, save, then clear the cache via the
  confirm-modal flow.
- EE: preemptive caching toggles persist across the question sidebar
  and the admin Dashboard-and-question-caching tab (RTK-query cache
  invalidation across distinct UI surfaces).
- EE: dirty-form modal guards all four close paths (× button, ESC,
  click outside, browser back).
- EE: clicking distinct entries on the admin tab opens each form
  initialized from its own row's config (per-row click-handler + form
  state isolation that only assembles correctly in the real app).

Spec docstring spells out what is covered elsewhere — strategy
resolution / TTL / refresh task in backend cache tests, cron transform
+ Schedule UI mapping in frontend Schedule unit specs, strategy form
input binding + radio counts + preemptive switch availability for
root/database in the StrategyEditorForDatabases unit specs — so the
boundary is explicit for future readers.

Removed:

- admin/performance/strategyForm.cy.spec.ts (replaced).
- question/caching.cy.spec.js (mirror of the dashboard caching block;
  consolidated).
- "scenarios > dashboard > caching" describe in dashboard.cy.spec.js
  (mirror; consolidated) plus its now-orphaned imports.
- admin/performance/helpers/modals-helpers.ts (no longer needed).



* Trim caching e2e helpers and move multi-consumer ones under H

Replace the e2e/test/scenarios/admin/performance/helpers directory with
a single global file at e2e/support/helpers/e2e-caching-helpers.ts
holding only the helpers used by more than one spec
(interceptPerformanceRoutes, cacheStrategySidesheet, durationRadioButton),
exposed via H. Single-consumer helpers are inlined into their spec.
Unused helpers are deleted.

In caching.cy.spec.ts, only abstractions used in three or more places
remain (adaptiveRadioButton, preemptiveCachingSwitch, saveCacheStrategyForm,
openSidebarCacheStrategyForm, cancelConfirmationModal). Everything else
inlined.

Updates to other consumers:

- permissions/impersonated.cy.spec.js: drops the wildcard import on the
  caching helper module, uses H.openQuestionActions("Edit settings"),
  H.cacheStrategySidesheet, H.durationRadioButton.
- admin/database-routing/{usage,admin}.cy.spec.ts: use H.interceptPerformanceRoutes.
- search/recently-viewed.cy.spec.js: inline advanceServerClockBy as a
  two-line local const (its only consumer).

Also drops the broken ./types import in the deleted helper module — the
types had no live consumers after earlier cleanups.



* Add Jest coverage for the gaps left by the caching e2e cuts

The e2e cuts moved most caching coverage to backend tests and Jest, but two
load-bearing paths ended up with no safety net at any layer:

1. ModelPersistenceConfiguration had zero unit tests. The deleted
   strategyForm.cy.spec.ts case "can enable and disable model persistence"
   was the only thing exercising the Switch -> /api/persist/{enable,disable}
   wiring and the refresh-interval picker -> /api/persist/set-refresh-schedule.

2. The Schedule strategy save round-trip via StrategyForm wasn't reachable:
   Schedule.unit.spec.tsx covers Schedule UI -> cron, the EE
   StrategyEditorForDatabases spec covered the other strategies, but no test
   drove "pick Schedule -> pick frequency/day/time -> save -> see launcher
   label flip to 'Scheduled: weekly'".

New: ModelPersistenceConfiguration.unit.spec.tsx (9 tests, ~5 ms each).
Added: one save-roundtrip case in the EE StrategyEditorForDatabases spec
that drives Schedule fields through Formik's setFieldValue and asserts on
the round-tripped launcher label.



* Simplify intercepts

---------

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 22:08:47 +00:00
github-automation-metabase
a40bb78a23 🤖 backported "Drop cljs from timezone tests" (#73696)
Drop cljs from timezone tests (#73691)

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-05-05 23:52:03 +02:00
github-automation-metabase
ed23c521e9 🤖 backported "Correctly serialize lists in SAML/JWS attributes" (#73666)
Correctly serialize lists in SAML/JWS attributes (#73605)

* Short circut permission group membership queries when no current-user to compute them for

* Fix serialization of lists in jwt/saml

Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-05-05 18:06:58 +00:00
github-automation-metabase
2f9e197123 🤖 backported "fix(sdk-package,sdk-bundle):Fix inconsistent SDK loader height" (#73656)
fix(sdk-package,sdk-bundle):Fix inconsistent SDK loader height (#73318)

Fix inconsistent loader height

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
2026-05-05 17:26:44 +00:00
github-automation-metabase
2a984c7e5d 🤖 backported "docs: make MCP snippet copyable" (#73654)
docs: make MCP snippet copyable (#73649)

Improve documentation for Claude Code MCP command

Updated the example command in the documentation to include code formatting for clarity.

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-05-05 14:49:46 +00:00
github-automation-metabase
394558febb 🤖 backported "Avoid temp files when reading JAR" (#73650)
Avoid temp files when reading JAR (#73638)

* Fix PermissionError reading python-sources from non-writable jar dir

GraalVM's bundled polyglot FileSystem wrapper routes newByteChannel
through provider.newFileChannel, which forces JDK ZipFileSystem to
extract deflated entries to a temp file beside the jar. When the jar's
parent directory isn't writable (Metabase Cloud / Kubernetes pods run
as non-root with /app/ owned by root), the temp create fails with
AccessDeniedException, surfaced as PermissionError on the source path.

Wrap the NIO zip FS with a custom polyglot FileSystem that delegates
newByteChannel to Files/newByteChannel — returns an in-memory
ByteArrayChannel for compressed entries, no temp file needed. The
polyglot interface only requires SeekableByteChannel.

Fixes #73541.

* Fix kondo warnings on python-sources branch

Drop unused java.nio.file imports (LinkOption, OpenOption, FileAttribute)
— they appear only inside fully-qualified array type-hint strings, so
the imports themselves were never referenced.

Inline the write-deflated-zip! helper into the regression test. It was
called once and the trailing ! tripped the parallel-test linter
(:metabase/validate-deftest), even though the side effect targets a
test-owned temp file.

* Clean up nio-polyglot-fs reify formatting

Put each method body on a new line and lift type hints from the bodies
into the method signatures. Reify needs a return-type hint on every
method once any arg is hinted, otherwise the body is inferred as Object
and fails to match the interface signature.

* Assert channel isn't a FileChannel in jar-python-fs repro test

Hardcoding `jdk.nio.zipfs.ByteArrayChannel` couples the test to a JDK
implementation class. The actual invariant we care about is that the
channel doesn't go through the temp-file-backed `FileChannel` path that
fails on read-only jar dirs, so check that directly.

* Address review on jar python FS wrapper

- checkAccess now delegates straight to the provider for both empty and
  non-empty mode sets. The earlier Files/exists shortcut collapsed
  permission errors into NoSuchFileException, masking the real failure
  mode for unreadable files. Provider.checkAccess with an empty mode
  array is the documented existence check and preserves IOException for
  access denials.
- Rename the regression test var to kebab-case to match project
  convention.

* Drop stale checkAccess comment

* Note why dropping LinkOption opts in checkAccess is safe

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-05 10:37:13 -04:00
github-automation-metabase
15503ffae4 🤖 backported "CLI for Data Complexity Score" (#73643)
CLI for Data Complexity Score (#72768)

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-05-05 13:13:20 +00:00
github-automation-metabase
1e0e162a03 🤖 backported "fix: gsheets syncing status no longer exists in HM" (#73635)
fix: gsheets syncing status no longer exists in HM (#73437)

* fix: gsheets syncing status no longer exists in HM

This status no longer exists, but MB hadn't been updated yet. This caused the FE to no longer display that a connection was being synchronized. Updated the BE code to derive the syncing status in the same way that HM does.

Fix https://linear.app/metabase/issue/CLO-4883/google-sheets-sync-can-temporarily-show-duplicated-data

* fix: only show gsheets status when initializing

The little bottom right status panel would also show every time the connecting was synchronizing.

* fix: poll gsheets while syncing

Co-authored-by: Filipe Silva <filipematossilva@gmail.com>
2026-05-05 10:06:58 +00:00
github-automation-metabase
25a3ed0d4b 🤖 backported "External metadata" (#73623)
External metadata (#73612)

Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-05-05 04:18:34 +00:00
github-automation-metabase
47f99e34e6 🤖 backported "Fix QUE2-523" (#73615)
Escape quotes in identifiers in set role statements (#73248)

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2026-05-04 17:40:43 -07:00
github-automation-metabase
bbdd1acc22 🤖 backported "docs - embed themes" (#73598)
docs - embed themes (#73409)

* draft

* link

* Optimized 3 images, saved 261.6 KB (43.8% smaller)

* format

* Update docs/embedding/appearance.md



* Apply suggestion from @jeff-bruemmer

* theme copy

* update screenshot

* heading/links

* Apply suggestion from @jeff-bruemmer

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-04 21:02:41 +00:00
github-automation-metabase
fbddf2b85b 🤖 backported "Replace flaky e2e repro no. 55637 with predicate unit test" (#73594)
Replace flaky e2e repro no. 55637 with predicate unit test (#73556)

Extract `infoPopoversDisabled` logic from TableInteractive into a pure
helper and pin its four branches (including the `clicked !== null`
clause that fixed issue no. 55637) with a unit test. Drop the e2e repro
that was flagged by Trunk as one of the flakiest specs.

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 20:33:20 +00:00
github-automation-metabase
ac4e02adba 🤖 backported "Fix issue with closed filesystem for python sources" (#73588)
Fix issue with closed filesystem for python sources (#73562)

Co-authored-by: dpsutton <dan@dpsutton.com>
Co-authored-by: Chris Truter <chris@metabase.com>
2026-05-04 20:13:07 +00:00
github-automation-metabase
2580343207 🤖 backported "put non-aot drivers into jar" (#73582)
put non-aot drivers into jar (#73566)

before we would build jars and load them at runtime. then we put
everything into the normal jar so we can not have our own first party
drivers have to go through these hoops. We have postgres, h2, mysql on
the classpath.

We strip our clojure source files and this was removing the non-aot'd
versions. We had two options to get them back in:

1. update the "remove" regex we use to strip these out
2. patch the jar after being built

the first was getting kinda gnarly with negative lookaheads. The uber
task we use treats these as an "OR", so if any regex matches it is
excluded from the jar. I would have favored this perhaps if we didn't
already patch up the jar, but we do.

We patch up the jar to include the version information, so it felt
natural to also patch it up to include all files in the excluded
directories for the drivers. Oracle and vertica are just simple
namespaces so they aren't complicated, but something like mongo that has
it's own query processor would have all files brought along

Co-authored-by: dpsutton <dan@dpsutton.com>
2026-05-04 18:58:50 +00:00
github-automation-metabase
6c97834f5a 🤖 backported "Check Mysql connection params for unsafe options" (#73574)
Check Mysql connection params for unsafe options (#73510)

* Check for malicious connection parameters in mysql.

* change wording

* add other cases

* dont log details

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-05-04 17:43:26 +00:00
github-automation-metabase
c195a5aa7b 🤖 backported "Link directly to docs about specific environment variables" (#73570)
Link directly to docs about specific environment variables (#73558)

* Link to specific docs when referencing individual env vars

* better link target

Co-authored-by: Rodrigo López Dato <rodrigo@metabase.com>
2026-05-04 16:35:06 +00:00
github-automation-metabase
23adf9edf5 🤖 backported "Fix content translation when embed locale is en (EMB-1683)" (#73554)
Fix content translation when embed locale is en (EMB-1683) (#73545)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-04 14:28:13 +00:00
github-automation-metabase
b18cccac89 🤖 backported "Fix content translation endpoint race on guest embeds (EMB-1478)" (#73537)
Fix content translation endpoint race on guest embeds (EMB-1478) (#73435)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-05-04 08:20:26 +00:00
github-automation-metabase
a2542b4817 🤖 backported "Read Python directly from uberjar" (#73528)
Read Python directly from uberjar (#73527)

Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2026-05-02 15:59:04 -07:00
github-automation-metabase
a97c11ea05 🤖 backported "Drivers on classpath" (#73522)
Drivers on classpath (#73519)

* Flatten drivers into the uberjar instead of extracting nested JARs at runtime

Previously, driver JARs were shipped as nested JARs inside the uberjar
under resources/modules/ and extracted to the plugins directory at
startup. This changes the build to include driver dependencies directly
on the uberjar classpath via the :drivers alias, eliminating the
extract-then-load step entirely.

Key changes:
- Build includes :drivers alias in uberjar basis, skips separate driver
  JAR build step
- Driver plugin manifests moved from resources/metabase-plugin.yaml to
  resources/metabase/<driver>/metabase-plugin.yaml to avoid collisions
  when flattened
- Plugin loading discovers manifests on classpath (glob in jar,
  Files/find in dev) rather than extracting from nested JARs
- resources/modules/ excluded from uberjar to avoid double-inclusion
- User-supplied plugins in ./plugins/ still work as before



* tasteful renames

* handle oracle ee/oss divide

* little cleaner

* remove build driver test

was mostly concerned about oracle dep presence. no longer an issue

* vertica follows oracle's pattern

* preserve clj files for vertica and oracle

these files are not AOT compiled because of licensing: the jdbc drivers
might be on the classpath (in ee) and might not (oss by default). So
let's just not AOT and let things resolve at runtime. Important to not
strip out the clj files in this case

* handle group in connection properties

---------

Co-authored-by: dpsutton <dan@dpsutton.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com>
2026-05-02 14:53:55 -07:00
github-automation-metabase
5feb2eb8cf 🤖 backported "Fix a couple reflection/performance warnings" (#73526)
Fix a couple reflection/performance warnings (#66920)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-05-02 07:25:56 +00:00
github-automation-metabase
5470055b88 🤖 backported "uxw-3931: check table perms with single CTE" (#73509)
uxw-3931: check table perms with single CTE (#73425)

for search, table perms were split between the CTE (which handled perms in
`data_permissions`) plus a separate clause for published tables.

this change puts the published table permissions into the CTE too, which
resulted in a 5x speedup for me locally with 1500 tables in a local
postgres (1 user, in 2 groups, with overlapping permissions on those tables).

Co-authored-by: John Swanson <john.swanson@metabase.com>
2026-05-01 23:59:59 +00:00
github-automation-metabase
8486c9e9b8 🤖 backported "Lighter prometheus analytics" (#73511)
Lighter prometheus analytics (#73456)

* Lighter prometheus analytics

There's a couple things about prometheus that have gotten a bit
unwieldy. This pain has recently been seen in Tamas's change that broke
startup in a very "action at a distance" way.

https://github.com/metabase/metabase/pull/72262 is _excellent_ work. But
prometheus starts up so late that it is hazardous. Principally it
requires app db support (for the setting system) and it loads
essentially last after app db is initialized, token checks, etc. And
this is the bad part. Many systems can log analytic events before the
system is initialized. There are calls that when incrementing a a
metric, if it's not already initialized, to initialize it. But in the
course of initializing, it would want to increment a metric, causing an
infinite loop.

This was exacerbated in that the safety valves of the token checker
kicked in and suppressed errors for 30 seconds, ending up with threads
locked up trying to initialize the collector.

Solution:
just initialize it. It's just a few atomic longs and maybe a small
webserver. One complication is from chris's work in
https://github.com/metabase/metabase/pull/52834 where they try to seed
initial values so that when something registers a number it is a jump
from 0 to 8 and therefore alarming, rather than just 8 with no
differential.

So i added an edge that these can be set later on in the process. One
thing to call out is that the initial value is mostly 0, but the search
ones use a value of 1 as a bit field to indicate it's true:

```
prometheus=> (initial-labelled-metric-values)
[
,,,
 {:metric :metabase-search/engine-default,
  :labels {:engine "appdb"},
  :value 1} ;; -> indicates appdb is the default engine
 {:metric :metabase-search/engine-default,
  :labels {:engine "semantic"},
  :value 0}
  ,,,
 {:metric :metabase-search/engine-active,
  :labels {:engine "in-place"},
  :value 1}
 {:metric :metabase-search/engine-active,
  :labels {:engine "appdb"},
  :value 1}
 {:metric :metabase-search/engine-active,
  :labels {:engine "semantic"},
  :value 0}
```

So when we call the initial observed values it's just setting an
affirmative 0 on the value, or an affirmative 1. we won't clobber the
number of emails sent or anything

* initialize prometheus in test runner

* remove tests that care about reentrant and auto start

* default no-op reporter

* remove setting up dynamic var. we don't setup for you any more

Co-authored-by: dpsutton <dan@dpsutton.com>
2026-05-01 18:27:25 +00:00
github-automation-metabase
72c10561b2 🤖 backported "Fix metabot UI settings not respecting appdb state" (#73512)
Fix metabot UI settings not respecting appdb state (#73498)

* fix UI settings not respecting appdb state

* make recently viewed injection respect verified only setting

* address review feedback on AISettingsPage tests

- Extract buildDefaultMetabots helper into metabot/tests/utils for reuse
- Add contentVerification option to setup, mirroring enableEmbedding
- Drop redundant setupEnterprisePlugins/mockSettings from verified-content test
- Add regression test asserting selected collection_id renders correctly



* tighten verified-content recents test assertions

- Wrap the "no metabot-id passed" case in mt/with-premium-features
  #{:content-verification} so the absence of metabot-id is what's actually
  being tested, not the absence of the premium feature.
- Identify recents items by [:type :id] pairs instead of bare :id, since
  card and dashboard ids occupy independent sequences and can collide,
  silently masking filter regressions.



* fix kondo

* fix api_test

---------

Co-authored-by: Tyler Plude <tsplude@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:00:10 +00:00
github-automation-metabase
3876b94f45 🤖 backported "fix(test) reflection error in metabot conv test" (#73494)
fix(test) reflection error in metabot conv test (#73491)

* fix reflection error in mb conv test

* fix mb messages calls too

Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-05-01 01:23:35 +00:00
github-automation-metabase
cd74d44289 🤖 backported "Record is_impersonated/is_db_routed correctly on failed queries" (#73493)
Record is_impersonated/is_db_routed correctly on failed queries (#73234)

Co-authored-by: bryan <bryan.maass@gmail.com>
2026-04-30 23:48:45 +00:00
github-automation-metabase
e0d1d679f2 🤖 backported "[equality] Comment out debugging statements in lib.quality/=" (#73492)
[equality] Comment out debugging statements in lib.quality/= (#65641)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-04-30 23:28:19 +00:00
github-automation-metabase
ea29d371a0 🤖 backported "Replace bigml Histogram and stream-lib with DataSketches" (#73485)
Replace bigml Histogram and stream-lib with DataSketches (#66642)

Co-authored-by: Oleksandr Yakushev <alex@bytopia.org>
2026-04-30 22:36:14 +00:00
github-automation-metabase
faaaf8c996 🤖 backported "Center the document loading spinner" (#73487)
Center the document loading spinner (#73481)

Co-authored-by: Andrei Chernykh <andrei.chernykh@metabase.com>
2026-04-30 22:36:02 +00:00
github-automation-metabase
e0d34dddcd 🤖 backported "Revert "BOT-1368 Drop metabase-enterprise.metabot.task.ai-usage-trimmer (#72926)"" (#73484)
Revert "BOT-1368 Drop metabase-enterprise.metabot.task.ai-usage-trimmer (#72926)" (#73400)

This reverts commit fc1af36539.

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-04-30 22:27:38 +00:00
github-automation-metabase
be75c439c6 🤖 backported "Skip event publishing during Document deserialization" (#73478)
Skip event publishing during Document deserialization (#72655)

* Skip event publishing during Document deserialization

Fixes: #72293

* Skip event publishing during Transform deserialization

Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
2026-04-30 22:26:25 +00:00
github-automation-metabase
76738dfe5b 🤖 backported "Fix XLSX export truncating decimal precision" (#73476)
Fix XLSX export truncating decimal precision (#72711)

* Fix XLSX export truncating decimal precision (#71299)

XLSX exports applied a 2-decimal-place cell format to all numeric values,
causing values like 0.001 to display as 0. This was a display format issue
(the underlying data was preserved) but users perceived it as data loss.

Two changes:
- Increase default float format from "#,##0.##" to "#,##0.##########"
  (10 optional decimal places) so formatted exports show full precision
- When format-rows? is false (unformatted export), skip numeric cell
  styles entirely so Excel uses its General format with auto-precision

* Update download export test expectations for new decimal precision

The pivot table test now correctly expects full precision for averages
(2.966666667 instead of 2.97). The format-rows=false test now expects
no thousands separators since unformatted exports skip numeric styling.

Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
2026-04-30 22:20:13 +00:00
github-automation-metabase
fdaeaa4f71 🤖 backported "Fix parsing timestamps with time zone in CLJS" (#73472)
Fix parsing timestamps with time zone in CLJS (#73466)

Closes QUE2-524

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-04-30 21:12:20 +00:00
github-automation-metabase
60652a4a12 🤖 backported "fix: flaky usage auditing tests" (#73471)
fix: flaky usage auditing tests (#73454)

Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-04-30 20:35:27 +00:00
github-automation-metabase
cf5b9e1463 🤖 backported "Allow impersonated writes for custom actions" (#73469)
Allow impersonated writes for custom actions (#73396)

* allow impersonated writes for actions

* update test

* add impersonated action test

Co-authored-by: Riley Thompson <riley@metabase.com>
2026-04-30 20:02:37 +00:00
github-automation-metabase
0689cc06a4 🤖 backported "Metrics Viewer bug fixes" (#73465)
Metrics Viewer bug fixes (#73190)

bug fixes

Co-authored-by: Kevin Kelleher <kevin.kelleher@metabase.com>
2026-04-30 17:33:39 +00:00
github-automation-metabase
6cd87abc7d 🤖 backported "docs - add prometheus env var" (#73458)
docs - add prometheus env var (#73450)

* add prometheus env var

* description

* Update src/metabase/cmd/resources/other-env-vars.md



---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-04-30 16:27:46 +00:00
github-automation-metabase
d60d1a5b99 🤖 backported "Added a loading indicator to static dashboard pdf export button" (#73451)
Added a loading indicator to static dashboard pdf export button (#73300)

Co-authored-by: Sébastien <sebastien@metabase.com>
2026-04-30 14:38:57 +00:00
github-automation-metabase
6ca424bdfd 🤖 backported "docs: fix mcp link" (#73441)
docs: fix mcp link (#73440)

fix link

Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-04-30 12:44:14 +00:00
github-actions
d4e3a33370 Cut release-x.61.x branch 2026-04-30 12:14:51 +00:00
Romeo Van Snick
0a60f2436f Move romeovs to devex team (#73429) 2026-04-30 14:07:50 +02:00
Fraser
4f6989171f Move auth utils inline (#73355) 2026-04-30 20:02:41 +12:00
Fraser
cfdfc7c35b update module config (#73354) 2026-04-30 19:46:28 +12:00
Fraser
732860d361 Move database utils into shared (#73353) 2026-04-30 18:48:41 +12:00
Fraser
49f8099a3c Clean palette violations (#73362) 2026-04-30 18:48:13 +12:00
Fraser
67f0fa7193 Move database actions into shared (#73356) 2026-04-30 18:48:01 +12:00
Fraser
f2cb46a307 Inject pdf export params (#72847) 2026-04-30 16:38:26 +12:00
Romeo Van Snick
7ff626e966 Clean up url utils (#73167) 2026-04-30 16:38:13 +12:00
Noah Moss
c8cf44d61d [Metabot] AI Usage Analytics v1 (#71864)
* Initial views + routes

* lint

* fix tests relevant to this branch

* throw down some FE vibes

* change date filter approach

* Persist estimated cost in ai_usage_log and surface in analytics

* Fix duplicate 'task' key in modules config.edn

* move views to 61

* improvements

* rework FE to use MBQL queries over audit views vs raw SQL and hardcoded IDs; ensure we resync the audit db on startup

* fix some tests, fix liquibase issues on staging instance

* format

* add a temporary transform deletion step to fix error blocking staging startup

* strip out cost estimation stuff from analytics

* make filters consistent across pages

* wip conversations impl

* lint

* fix modules test

* make views checksum mechanism work in a jar

* tweak test

* fix build

* whoops, fix parens

* fix another java interop issue

* lint

* strip out prior build workaround, add some api tests, refactor

* tweak queries to ignore deleted emssages, fix audit view checksum mechanism

* lint

* fix tests, misc cleanups

* abstract the directory checksum mechanism

* fix be tests

* api endpoint cleanup

* switch conversation list endpoint to use a proper batched hydration method

* remove unneeded comment

* tweaks, test fixes

* iniital stab at surfacing sql/mbql queries generated, + some test fixes

* lint

* lint again

* fix build

* simplify audit db sync approach

* more refactor/schema fixes

* break out metabot-analytics.conversations ns; some other tweaks

* simplify api surface based on what's actually being used

* lint

* return message count/total tokens/model from the BE to fix conversation loading

* be refactor

* fix reference

* Persist and expose Slack conversation links

* add an index on metabot_conversation.created_at, more refactor

* fix migration

* move slack link stuff to slackbot module

* fix tests

* ensure we capture failed slackbot conversations

* fix up migration attribution + timestamps

* fix broken FE references, I think broken from the rebase

* fix test

* model -> profile in usage view

* Revert "model -> profile in usage view"

This reverts commit 7d464f27a0.

* relayout stats page

* remove cost, fix loading state, convo page changes

* misc improvements

* make charts work on click through

* clean up skeleton loading experience

* fix debug mode on FE

* [AI Analytics] Track & report convo searches, queries, user IP (#72509)

* surface search counts

* track & report convo ip

* condense unit tests

* add ip migration; fix ip in views

* add query count

* render generated queries in conversation detail

* fix kondo

* fix migration

* fix migration

* nav/page refactor

* default date filter buttont to simpler view

* handful of UI quick wins

* Metabot conversation endpoints

* move conversation read perms to the model

* updated layout and breadcrumbs

* add run button

* bucket by hour when data filter is 24 hours long

* referer embed url tracking

* fix ai usage source/tag tracking; add tracking opts for slackbot source

* misc improvements

* [AI Analytics] Improved user feedback (#72999)

* TEMP COMMIT

* vibes

* vibed feedback endpoint rework

* vibed review

* makin this real code

* self-review pt 1

* self-review pt 2

* fix migration lint

* fix ci failures

* more ci fixes

* test fix

* pr feedback

* fix test

* TEMP COMMIT

* convo view shows group names

* feedback on my feedback ui

* fix padding

* other quick padding fix

* BOT-1339 Include tenant id and other info from ai_usage_log in v_metabot_conversations (#73015)

* BOT-1339 Include tenant id and other info from ai_usage_log in v_metabot_conversations

* Rename v_metabot_conversations.model to profile_id

It was returning a profile_id not a model, so the name was confusing and conflicts with the actual model in ai_usage_log.model, which we'd also like to return.

* Add ai_usage_log.model column to v_metabot_conversations view

* Remove user_qualified_id from metabot analytics views

* Fix alignment of user_display_name alias

* Add some tests for v_metabot_conversations view

* Convert tokens to long in tests to mask type differences based on db

* init new v_ai_usage_log view

* route Tokens tab to v_ai_usage_log view

* drop useless alias

* Fix get-conversation-detail-test

---------

Co-authored-by: Tyler Plude <tsplude@gmail.com>

* split out completion vs input tokens

* TEMP COMMIT

* view profile link improved

* [BOT-1384] Fix slackbot tool persistence format (#73078)

fix slackbot tool persistence format

* add tenant info

* TEMP COMMIT

* address recent feedback from maz

* icon change

* [AI Analytics] Persist and display data parts (#73107)

* first pass

* second pass

* clean up

* clean up test

* fix test

* fix unit tests

---------

Co-authored-by: Noah Moss <noahbmoss@gmail.com>
Co-authored-by: Tyler Plude <tsplude@gmail.com>

* Support slack threads with multiple metabase users for usage analytics (#72997)

* First cut of new data model to support multi-user slackbot conversations

* lint

* update list conversations docstring

* fall back to conversation user ID when message user ID isn't  set

* don't break conversation API schemama

* update api schema with better semantic names

* simplify, strip out participant filtering API chagnes that we don't need

* remove redundant test case

* clean up tests with better model defaults

* fix store-message, some alignment fixes

* fix whitespace

* fix(tests): tolerate extra keys on hydrated user in CI

The :user sub-map in analytics responses picks up :tenant_id in
enterprise CI runs but not locally. Compare only the keys we care
about so the assertion is stable across environments.

* support multiple feedback events for a single agent message

* fix slackbot ai_usage_log writes to have conversation id

* fix migrations lint

* fix unit

* split feedback table migration to avoid breaking staging

* fix up migrations for mysql

* try again to fix on mariadb

* add another defensive migratino to avoid breaking staging

---------

Co-authored-by: Tyler Plude <tsplude@gmail.com>

* [BOT-1373] Add AI usage analytics tables as UA models (#73155)

* new UA models from views v_metabot_usage & v_metabot_messages

* update messages & usage models

* add v_ai_usage_log and v_metabot_conversations models

* drop deprecated v_metabot_usage

* col align fix

* [AI Analytics] Add tenants support + refactor filters/charts code (#73195)

* TEMP COMMIT

* TEMP COMMIT

* TEMP COMMIT

* new group component w/ new approach

* conform other components

* by time chart

* move util

* clean up

* remove dead code, move code closer to where it is used

* move skeletons back into components, clean up types

* ConversationStatsPage clean up

* refactor toBreakoutRawSeries fn

* add tenants filter and chart

* clean up filter logic

* fix ci

* self-review

* last improvements

* kondo fix

* add back dots to by day chart

* [AI Analytics] Nicer profile + source aggregate names (#73268)

* move nice aggregate names into the SQL

* add fallthrough case

* pr feedback

* fix ci

* Fix fe-type-check after merge from master

* Fix deep linking for slack channel threads (#73333)

* fix deep linking for slack channel threads

* tweak test

* BOT-1401 Add some more BE tests for AI usage analytics (#73263)

* BOT-1401 Add tests for v_metabot_messages

* Add tests for v_ai_usage_log

* Add some slackbot/conversation-permalink tests

* Add metabot-stats tests for multi-user convos

* Add some tests for list-conversations edge cases

* Add some more persistence tests

* [AI Analytics] undrfined review feedback + self review feedback (#73326)

* undrfined review feedback + self review feedback, fix using the chart display type when running queries generated by metabot

* refactor chart approach

* move to just embedding

* Fix test failures

Commit 5368b0df35 ("AI Analytics review feedback") flipped the API schema from kebab-case (:sort-by, :sort-dir, …) to
snake-case (:sort_by, :sort_dir, …). That commit also updated the asc-sorting test and the invalid-sort-test to use
snake-case in the URL — but missed the two siblings:

  - list-conversations-sorting-desc-test (still sent sort-by=…&sort-dir=desc)
  - list-conversations-invalid-sort-dir-test (still sent sort-dir=sideways)

* fix backend unit test

* fix result metadata in new usage analytics models (#73350)

* fix result metadata in new usage analytics models

* fix missing field yamls

* updates after comparing with mage output

* [AI Analytics] Quick feedback (#73349)

* fix slack markdown rendering and center layout on large screens

* add historical source value to sql case

* more correct group filtering

* lil bit cleaner

* fix

* feat: surface complexity score in admin UI (#73115)

Co-authored-by: Tyler Plude <tsplude@gmail.com>

* [AI Analytics] FE unit tests for query building (#73398)

* simplify

* better summarizing

* update snapshots

* feat: ai usage analytics e2e tests (#73332)

* fix: feature gate data complexity (#73408)

* fix data complexity score tests from merge conflict

* fix testing api & kondo

---------

Co-authored-by: Sloan Sparger <sloansparger@gmail.com>
Co-authored-by: Tyler Plude <tsplude@gmail.com>
Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
2026-04-29 21:47:53 -04:00
technomancy
2871c86b3f Add a namespace to generate a JSON Schema for MBQL 5 queries. (#72381)
* Produce JSON Schema for MBQL queries based on Malli schema.

* Extend tests to generate queries and validate them.

* Remove empty branches in schema.

* Start chipping away at failures in example queries.

* Ensure IDs aren't integers for "external MBQL".

* Work around issues with Malli's JSON schema compiler.

Primarily the lack of support for :cat schemas, but also an issue with
UUIDs which doesn't really make any sense in JSON anyway.

This currently can validate all the queries from the "representations"
repository except one; joins.yaml fails because it's missing lib/type.
I believe this to be a bug in the query, not a problem with the schema.

* Define Malli external schema in terms of existing internal schema.

* Update external MBQL malli schema to replace dispatch type for clause field.

There's a clause in mbql.clause.field like this:

   [:multi {:dispatch      (...)
            :error/message "Invalid :field clause ID or name: must be a string or integer"}
    [:dispatch-type/integer ::field.id]
    [:dispatch-type/string ::field.literal]]

We need to make sure that sequences map to ::field-id, not just
integers. Previously we just conj'd `[:dispatch-type/sequence ::field.id]`
onto the end of the list, but when this gets compiled to JSON schema,
it turns it into a `oneOf` clause that has `field.id` in it twice. So
instead of adding, we should *replace* the integer dispatch.

* Address review comments.

* Use com.github.erosb/json-sKema instead of shelling out.
* Use included example queries instead of representations repo.

* Undo error/message change; the message describes the OK condition, not problem.

* Address review suggestions.

* Give example of bad :allOf behavior from Malli's JSON Schema
  compiler
* Use update-keys, not zipmap
* Rename some updater functions
* Use lib to create test query instead of hard-coding it
2026-04-29 18:46:38 -07:00
Denis Berezin
6ee919507a UXW-3779 Metric dropdown after picking one (#73142)
* UXW-3779 Metric dropdown after picking one

* Review fixes

* Review fixes
2026-04-30 01:19:55 +03:00
Ben Grabow
e352453344 Ghy-3466/relax the mysql minimum version constraint (#73331)
* Relax minimum supported mysql version to accommodate AWS Aurora users who have no upgrade option currently

* Use mysql 8.0 for CI tests of minimum mysql version

* Fix find-replace oversight
2026-04-29 14:37:44 -07:00
Chris Truter
7a86f5c203 Track Snowplow emit time as a publish phase in complexity scoring (#73283) 2026-04-29 17:09:28 -04:00
Jeff Bruemmer
06a95b71c0 docs - remove resizing dashboards section from components.md (#73338)
Remove resizing dashboards section from components.md

Removed section on resizing dashboards in components.md.
2026-04-29 13:55:51 -07:00
Jeff Bruemmer
cd47f707f7 docs- fix checkout command for Metabase version (#73310)
Fix checkout command for Metabase version

Updated the checkout command to remove the 'v0.' prefix from the version.
2026-04-29 20:55:33 +00:00
Jeff Bruemmer
41fee061a3 docs - mcp settings path (#73051)
* path

* Apply suggestion from @jeff-bruemmer
2026-04-29 20:52:18 +00:00
Chris Truter
01429bb7c5 Use STS model for synonym detection (#73165) 2026-04-29 22:50:32 +02:00
Jeff Bruemmer
b9e4827e22 docs - guest token refresh (#73340)
* draft

* edits

* edits

* notes on JSON string
2026-04-29 20:02:46 +00:00
Riley Thompson
6dd011110e Allow empty column aliases (#72996)
* allow empty column aliases

* add e2e test to click explore results
2026-04-29 21:47:27 +03:00
Nicolò Pretto
920ea341cb use parseAttributeValue on custom-context to parse stringified JSON values (#73390) 2026-04-29 17:05:54 +00:00
bryan
66118921ec Add create_question and create_dashboard MCP tools (#72742) 2026-04-29 10:54:31 -06:00
Riley Thompson
d6a45bdf99 Don't show run overlay on between field filter with no values (#71097)
* dont modify between clause

* consider ordered param values

* fix backend

* save wip

* add tests
2026-04-29 12:50:12 -04:00
Andrei Chernykh
06d543abe5 Show Metabot illustration preview next to the visibility toggle (#73346)
* Show Metabot illustration preview next to the visibility toggle

* Align icon label and helper with the AI agent rename

* Match icon tile height to upload button and fix e2e label
2026-04-29 09:21:04 -07:00
Andrei Chernykh
50a17d1240 Shorten AI usage controls nav item labels (#73325) 2026-04-29 09:04:27 -07:00
Andrei Chernykh
94341692eb Use generic AI agent wording on Metabot customization page (#73324) 2026-04-29 09:04:08 -07:00
Andrei Chernykh
a7d38f6e99 Rename Metabot chat admin label and page title to AI chat (#73323) 2026-04-29 09:03:38 -07:00
Tyler Plude
f8531f2301 fix(search): index batch failure when function-attr throws (#73352)
prevent ingestion batch failure when function-attr throws
2026-04-29 08:34:13 -07:00
Anderson
bb396a5c48 Simplify ai controls permission model (#72906)
* Implement backend part

* Simplify AI controls permission model

* Fix backend tests

* Make some frontend code improvements

* Fix BE test (attempt)

* Refactor AI Features table to use TreeTable

* Address review comments

* Implement UI improvements as per latest Maz' comments

* Add tests for the latest code updates

* Fix ai controls e2e test
2026-04-29 12:24:10 -03:00
Romeo Van Snick
c1ba28f7c0 Remove unused metabase lib code (#72335)
* Remove unused DATE_MBQL_FILTER_MAPPING

* Remove unused checkDatabaseCanPersistDatasets

* Remove unused functionsUsedByExpression

* Remove unused removeFilters

* Remove unused renameJoin helper

* Remove unused measureMetadata

* Remove unused tableOrCardDependentMetadata

* Remove unused columnUniqueKey

* Remove unused requiredNativeExtras

* Remove unused withDifferentTable

* Remove unused drop-stage

* Remove unused findDrillThru

* Remove unused createColumnClickObject

* Remove unused createRawCellClickObject

* Remove unused createAggregatedCellClickObject

* Remove unused createPivotCellClickObject

* Remove unused createLegendItemClickObject

* Revert "Remove unused columnUniqueKey"

This reverts commit 47c73aca0f.

* Revert "Remove unused drop-stage"

This reverts commit 44981fd0fe.
2026-04-29 17:20:18 +02:00
Oleh K
c948c17ea3 Do not fold SidebarCollectionLink on click if item is not active (#73320)
* Do not fold SidebarCollectionLink on click if item is not active

* Add tests
2026-04-29 17:19:19 +02:00
Kevin Kelleher
8b0f6d14f8 Use Mini Picker and Entity Picker in Metrics Viewer (#73137)
* add mini picker and entity picker

* cleanup dead code, fix tests

* add e2e test, fix tests, fix snowplow schema

* ci whack-a-mole

* review comments

* add search input to minipicker, scope to library when no query
2026-04-29 10:55:21 -04:00
Chris Truter
4a9210a4a5 Document :module flag in CLAUDE.md and add --drivers=LIST to bin/test-agent (#73372)
Two related changes for backend test ergonomics:

- CLAUDE.md gets a 'module-scoped runs' subsection documenting :module / :modules,
  the args to metabase.test-runner/find-and-run-tests-cli.
- bin/test-agent gets a --drivers=LIST flag that appends :drivers:drivers-dev to
  the alias set and exports DRIVERS=LIST in one step.

Together these let you run e.g.

  ./bin/test-agent --drivers=mysql,h2,postgres :module enterprise/workspaces

instead of dropping down to a raw clj -X invocation for driver tests.

Supersedes #73371.
2026-04-29 08:45:47 -05:00
github-automation-metabase
e08f1895cf Auto-update documentation for master (#73341)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-29 09:29:47 -04:00
Oleksandr Yakushev
23dab4ee58 [match] Update replace-in implementation to use replace-lite (#72843) 2026-04-29 15:44:15 +03:00
Oleksandr Yakushev
d33e435af0 [mage] Run Kondo in fix-unused-requires as a Babashka pod (#73208) 2026-04-29 15:42:29 +03:00
YB
ecd709fc70 GDGT-2077 chore: add observability to transform inspector (#72954)
Transform Inspector lacks Prometheus metrics and tracing spans. While audit events exist, we want to add more complete observability over the inspector endpoints for monitoring usage & performance.

We are adding the following:
* Prometheus counter inspector-discovery with label {status}
* Prometheus counter metabase-transforms/inspector-lens with labels {lens_type, complexity, status}
* Prometheus histogram metabase-transforms/inspector-query-duration-ms with labels {lens_type, status}
* Spans with the attributes `transform/id`, `inspector/source-type`, `transform/source-type` where they are relevant information

### Technical Considerations
1. I added the `source-type` scope for spans covering the `discover` and `lens` endpoints because their cost from the `build-context` may vary based on the transform's source type. The `query` endpoint should not require this as it's always generated MBQL against a target table regardless of source type - the query's target database is probably more relevant but requires an additional data fetch so I've left it out for now.

2. I noticed there's a repeated `try/catch` + prom.inc shape across the handlers but I've deferred extracting a helper for now, generally prefer to do so once we need that pattern across more than 1 request handler file. 

3. `lens-id` in the lens endpoint is only validated by `NonBlankString`, and it's possible for unregistered values to be input by a client which could lead to unbounded cardinality in Prometheus (since we are storing `lens-id` as `lens-type` label). I've added helpers to ensure these values are limited to registered lens types or `unknown` otherwise.
2026-04-29 19:54:47 +08:00
Nemanja Glumac
3baf516d7a Upgrade Cypress to v15.14.1 (#73365) 2026-04-29 13:27:12 +02:00
Chris Truter
e240900b78 Track actual data-complexity snowplow schema (#73368) 2026-04-29 10:45:59 +00:00
Sébastien
69ddfae14d Properly pass mantine-color-scheme (#73216) 2026-04-29 12:01:25 +02:00
Sébastien
58b6219cb7 Move v59 embedding_theme migration to v61 (#73359) 2026-04-29 11:49:39 +02:00
Nemanja Glumac
04490603ee Fix timing-data artifact name format (#73321) 2026-04-29 10:44:46 +02:00
Romeo Van Snick
24fa0fa8d0 Remove unused getColor helper on entities (#73299)
Remove unused getColor helper on entities
2026-04-29 07:45:29 +02:00
Edward Paget
cbeb2d4be6 Dimension and chart interestiningness calculation functions (#72630) 2026-04-29 00:07:03 -05:00
Aleksandr Lesnenko
adb659db32 hide usage analytics content from unreferenced items (#73348)
* hide usage analytics content from the dependency graph

* hide only from unreferenced?
2026-04-28 23:04:33 -04:00
Aleksandr Lesnenko
067352f19f drop metadata import api (#73264) 2026-04-28 23:47:12 +00:00
Romeo Van Snick
dbe73d1a36 Move metabase/utils/icon to metabase/common/utils/icon (#73294)
* Move metabase/utils/icon to metabase/common/utils/icon

* Simpify getIcon helper
2026-04-29 10:15:32 +12:00
Denis Berezin
32c8c2ade3 UXW-3754 add segments to Metric Viewer (#72820)
* UXW-3754 add segments to Metric Viewer

* Add segments into dimension table group

* Self review fixes

* Fix e2e

* Review fixes

* Fix tests

* Added segments for joined tables

* Fix CI

* Review fixes

* Remove joined-table segment support (moved to follow-up PR)

Joined-table support (FE dimension-group bucketing and lib_metric
segment pre-expansion with :source-field annotation) is split into
den/UXW-3754-segments-joined-tables so this PR stays focused on the
direct-source-table case.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Allow to replace applied segment filter by clicking on it

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 01:13:25 +03:00
Filipe Silva
db8b920989 fix: cloud migration error should allow restart (#73231)
Currently if the last migration attempt hits an error state it is not possible to cancel and start again.

This commit should fix that case by showing the the "Restart the process" link.

It also removes the "Try for free" panel on error state, it shouldn't be there.

Fix https://linear.app/metabase/issue/CLO-5361/migration-flow-gets-stuck-in-initialized-state
2026-04-28 21:06:34 +01:00
Riley Thompson
0358cd4d93 Increase e2e test timeout model actions test (#73322)
increase e2e test timeout
2026-04-28 14:43:03 -04:00
dpsutton
d0556eeccc Pre-parse VALUES stripping (#73314)
* Pre-parse VALUES stripping

Problem: GraalPy has ~50-100x memory amplification over CPython for AST
nodes. A query with ~19,800 VALUES tuples creates 2.26 GB of heap from
what would be ~50MB on CPython.

Fix: Added _strip_large_values() in sql_tools.py that runs before
sqlglot parsing. It:

1. Scans for VALUES (...) clauses using a character-by-character
parser (not regex for the tuples — handles nested parens, string
literals with commas/quotes)
2. Counts tuples — only activates above 100 tuples (configurable via
_VALUES_STRIP_THRESHOLD)
3. Replaces with VALUES (NULL, NULL, ...) preserving the column count
from the first tuple
4. Preserves everything else: CTEs, JOINs, table references, column
references, keyword casing

Applied to (analysis-only functions):
- referenced_tables
- referenced_fields
- validate_query
- simple_query
- field_references
- returned_columns_lineage
- is_single_select_stmt

NOT applied to (must preserve original SQL):
- replace_names
- transpile_sql
- add_into_clause

Performance: 258KB input (20K tuples) → 360 bytes output in 15ms. The
structural query is perfectly preserved for analysis.

Why not just bail? Bailing would mean we can't analyze these queries at
all. Stripping VALUES lets us still extract table references, validate
syntax, and trace field lineage — just without the irrelevant data
payload.

* Strip large VALUES clauses before SQL parsing to prevent GraalPy OOM

GraalPy has ~50-100x memory amplification over CPython for AST nodes
(~114KB per node vs ~1-2KB). Queries with thousands of VALUES tuples
(common pattern for inline lookup tables) cause OOM or 30s+ timeouts.

Adds JVM-side `strip-large-values` that replaces VALUES clauses with
>100 tuples with `VALUES (NULL, NULL, ...)`, preserving column count.
This runs before passing SQL to GraalPy since Python char-by-char
scanning of multi-MB strings is itself too slow on GraalPy.

Applied to all analysis functions (referenced-tables, field-references,
validate-query, etc.) but NOT to replace-names/transpile which must
preserve original SQL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove Python-side VALUES stripping (JVM-side only)

The stripping is done on the JVM side before SQL reaches GraalPy,
so the Python implementation was redundant. Single place, single
responsibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add try/catch safety and false-positive tests for VALUES stripping

- Wrap strip-large-values in try/catch so malformed SQL falls through
  to the parser unchanged rather than crashing
- Add tests for VALUES inside string literals, column named "values",
  and VALUES keyword not followed by a paren

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Simplify VALUES stripping code with smaller named functions

Break the dense strip-large-values* into readable pieces:
- skip-balanced-parens (was skip-tuple)
- count-top-level-commas (was count-columns)
- count-and-skip-tuples (was inline nested loop)
- make-null-placeholder, extract-values-keyword (was inline string ops)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove stray blank lines from sql_tools.py

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 13:25:32 -05:00
github-automation-metabase
bf1e86dab1 Update translations 2026-04-28 (#73273)
* Update translation sources 2026-04-28

* Update translations

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-04-28 18:16:08 +00:00
metamben
c8d4914484 Prevent infinite loop in Prometheus setup (#73230) 2026-04-28 13:24:36 -04:00
Nicolò Pretto
c512be61f5 fix(embedding usage analytics): show all values for auth method (#73306)
* Embedding Unknown -> Embedding (Unknown)

* remove auth_method value remapping

* fix typo
2026-04-28 15:37:41 +00:00
Nemanja Glumac
e264e0d37b Add cut-release-branch workflow (#73189)
Add cut-release-branch workflow (DEV-1862)

New workflow_dispatch workflow that:

- Reads vars.CURRENT_VERSION, computes next = current + 1.
- Fails if release-x.{next}.x already exists.
- Cuts the branch from master HEAD with an empty
  "Cut release-x.{next}.x branch" commit, pushed via
  METABASE_AUTOMATION_USER_TOKEN.
- Best-effort bump of vars.CURRENT_VERSION; logs failure to Slack
  if the token scope isn't sufficient instead of blocking the cut.
- Posts to the release channel tagging @releasemanagers, with a
  reminder line if versionRequirements[next] still needs to be added
  to release/src/version-helpers.ts.

The empty commit gives the new branch a tip SHA that differs from
master's, so release-branch CI can run a clean first build instead of
reusing master-cached check results for the same SHA.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 09:27:27 -06:00
Riley Thompson
ebaa93ef41 Reject invalid queries for admins on impersonated DBs (#73168)
* reject non select impersonated queries

* show error to user

* show error to user on failed impersonation parsing

* increase test timeout to account for graalvm cold start
2026-04-28 11:07:08 -04:00
metamben
c88f89d30a Avoid interpreting slash as namespace separator in honeysql (#73053)
* Avoid interpreting slash as namespace separator in honeysql

Closes QUE2-406
2026-04-28 14:47:40 +00:00
Jeff Bruemmer
6504fef394 docs - AI controls (#72998) 2026-04-28 10:31:27 -04:00
Stas Gavrylov
370a693a65 Bump axios and @slack/web-api to uncompromised versions (#73303)
* Bump axios and @slack/web-api to uncompromised versions

* Update lockfile
2026-04-28 17:10:07 +03:00
Nemanja Glumac
ddca736dd0 Fix flaky e2e browser navigation test (#73295) 2026-04-28 15:24:15 +02:00
Nicolò Pretto
0a36445da6 add remap for embedding-iframe -> "Embedding Unknown" (#73292)
* add remap for `embedding-iframe` -> "Embedding Unknown"

* add is_top_level filter to other charts
2026-04-28 14:01:33 +02:00
Nemanja Glumac
4674bf0022 Fix broken e2e reproduction 63416 (#73289) 2026-04-28 13:57:29 +02:00
metabase-bot[bot]
58c85e6295 [chore] Update E2E test timings with recent data (#73205)
Update E2E test timings with averaged data

Collected timing data from 7 days of test runs.
All specs with timing data were included (simple averages).

🤖 Generated automatically by update-e2e-timings workflow

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Actions <actions@github.com>
2026-04-28 11:18:47 +00:00
Oleh K
2999fa588d Add olehk-mb to team.json and github-slack-map.json (#73222) 2026-04-28 12:40:16 +02:00
Nemanja Glumac
95d5969b92 Turn off Cypress video compression (#73199) 2026-04-28 11:16:54 +02:00
Nicolò Pretto
0a97609405 test(e2e): use computed dashboard id in new question redirect assertions (#73206)
Replace hardcoded `/dashboard/12-new-dashboard` paths in the "creating a
new dashboard" flow with the id captured from the POST /api/dashboard
response, so the test no longer breaks whenever the seed shifts the id.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 10:14:41 +02:00
Ngoc Khuat
02d68b9cd4 Fix stale viz-settings in cached dashboard subscriptions (#73048) 2026-04-28 06:40:36 +00:00
Mahatthana (Kelvin) Nomsawadi
868a4ef23c docs(sdk): Update SDK version to 0.61.0-alpha.0 (#73227)
docs(sdk): Update Readme version references and published npm version to 0.61.0-alpha.0

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-04-28 08:32:21 +02:00
Romeo Van Snick
b0c09624ff Clean up metabase-lib (#73219)
* Move mbql clause definitions in to metabase-lib

* Make imports explicit

* Define a Metadata in Lib

* Remove Field import from metabase-lib

* Fix declare on class

* Remove v1 imports in metabase-lib tests

* Fix Field getPlainObject type

* Fix Field getPlainObject type

* Split up mlv1 and mlv2 modules

* Move mlv2 to lib level

* Enforce metabase-lib module boundaries

* Fix module boundaries precedence

* Remove console warning
2026-04-28 07:59:17 +02:00
Anderson
654ddc67d1 Show usage limit error in inline SQL prompt (#73065) 2026-04-28 02:35:40 -03:00
Nathan Voxland
158f108828 Do hold onto appdb transaction during database sync (#73246) 2026-04-27 21:10:47 -05:00
Andrei Chernykh
1179d5cd60 Fix missing synced/official icons on collection bookmarks (#72988)
* Fix missing synced/official icons on collection bookmarks

* Route official tooltip + color through getIcon instead of getCollectionIcon

* Update official collection icon test to match new color + tooltip

* Resolve palette keys to CSS vars inside Icon

* Update official collection icon test to match palette-key value
2026-04-27 16:33:26 -07:00
metamben
c1d93652b1 Fix joined nested field reference compilation (#73240)
Closes QUE2-535
2026-04-28 01:49:47 +03:00
metamben
f77eddd2ca Handle clickhouse dbnames containing commas (#73247)
Closes #73175
2026-04-28 01:48:47 +03:00
Fraser
5ac5c57af2 Fix embedding hub violations (#73076) 2026-04-28 10:16:21 +12:00
Nathan Voxland
685cc651c4 Materialize describe-fields values rather than leaving postgres connections open (#73121) 2026-04-27 16:47:01 -05:00
Fraser
fe07d948e3 Move DataPermission types into metabase-types (#73021) 2026-04-28 09:06:31 +12:00
Andrei Chernykh
3888390bb9 Migrate notifications components from Emotion to Mantine + CSS modules (#73068) 2026-04-27 10:53:09 -07:00
bryan
af9b662bb2 Handle API errors in ci-report (#72180) 2026-04-27 09:00:08 -07:00
bryan
bfe71297dc Enhance usage analytics for embedding (#71386)
* initial commit

* WIP: generated migration file

* Clarify remarks and comments

* analytics-pii-retension-enabled defsetting

* Fix tenant-create audit log: populate model and details

The create-tenant! function was passing a plain map (from present-tenant)
as the event object, which stripped Toucan model metadata. This caused
both the model column to be nil and details to be empty in the audit log.

- Pass raw Toucan instance to the event instead of the presented map
- Add :attributes to tenant model-details for audit logging
- Add test verifying audit log entry for tenant creation

* Bind embedding_client server-side for public, embed, metabot, and agent routes

Surfaces that don't send X-Metabase-Client (public links, signed embeds,
metabot, agent API) now get embedding_client set server-side via a
+embedding-client route wrapper. The server-side binding wins over any
client header, ensuring the endpoint determines the surface.

* Move metabase-client coalescing to embedding-mw

* Remove dev code

* Test all the important mapping cases

* Add GDPR-gated PII extraction helpers (EMB-1512)

Pure functions for extracting request metadata gated by the PII setting:
- extract-hostname: parses hostname from Origin header URL
- extract-path: parses path from Referer header URL (strips query/fragment)
- pii-request-info: takes request values, returns map for view_log/query_execution

These are called by EMB-1504 and EMB-1505 when populating the new columns.

* Add missing *warn-on-reflection* to analytics.sdk

* Add route-derived client and preview suffix tests (EMB-1503)

Document preview handling decision from team discussion: keep -preview
suffix for auditing, de-emphasize in usage analytics views. Add tests
for route-based client derivation and preview suffix behavior. Fix
include-analytics-is-idempotent to expect auth_method key.

* Wire GDPR-gated PII fields into view_log and query_execution writes (EMB-1512)

Extend `include-sdk-info` to merge PII fields (embedding_hostname,
embedding_path, sanitized_user_agent, ip_address) from the current
Ring request when `analytics-pii-retension-enabled` is true. Both
view_log and query_execution call `include-sdk-info` in their Toucan
before-insert hooks, so both tables are wired automatically.

Move `ip-address` from request.util to request.current so sdk.clj
can use it without circular deps (request.util -> analytics.core ->
sdk). request.util/ip-address now delegates to request.current.

Tests:
- Unit: include-sdk-info with PII enabled/disabled/no-request
- Integration: public card + dashboard card queries verify PII and
  embedding_client land in both view_log and query_execution rows

* Bind auth_method in session middleware and re-export from analytics.core (EMB-1512)

Derive auth_method ("session" or "api-key") in merge-current-user-info
where session vs API key authentication is resolved, then bind
*auth-method* in bind-current-user so it flows into view_log and
query_execution via include-sdk-info.

Also re-export with-auth-method! and get-auth-method from
analytics.core for consistency with other sdk vars.

Tests:
- Session-authenticated card query records auth_method "session"
  in both view_log and query_execution
- Unauthenticated public card query records nil auth_method

* Add embedding_client, surface, is_preview to analytics views (EMB-1535)

Expose embedding_client in v_view_log (v4) and v_query_log (v5), with
two derived columns:

- surface: flat CASE mapping from embedding_client to human-readable
  surface names (sdk, iframe, public-link, static-embed, metabot,
  agent-api, internal) with -preview variants
- is_preview: boolean derived from embedding_client LIKE '%-preview'

This lets the embedded analytics dashboard filter or de-emphasize
preview requests (admin embed wizard) while keeping them in the raw
data for auditing.

Also fixes stale v_query_log test schema (was missing transform_id,
transform_qualified_id, lens_id, lens_params from v4).

Verified against local Postgres:

  v_view_log:
  embedding_client |   surface   | is_preview
  -----------------+-------------+-----------
                   | internal    | f
  public           | public-link | f

  v_query_log:
  embedding_client |       surface       | is_preview
  -----------------+---------------------+-----------
                   | internal            | f
  public           | public-link         | f
  public-preview   | public-link-preview | t

* Populate tenant_id on view_log from current user (EMB-1504)

Read (:tenant_id @api/*current-user*) in generate-view so every
view_log insert carries the tenant when the request is authenticated
by a tenanted user. Nil-safe on public/embed/background paths.

Verified:
- card-read-ee-test: tenant_id nil for non-tenanted user
- card-read-tenant-id-test: tenant_id matches tenant for tenanted user
- Full view-log test namespace: 26 tests, 85 assertions, 0 failures
- SDK middleware tests: 13 tests, 69 assertions, 0 failures

* Populate tenant_id on query_execution from current user (EMB-1504)

* Populate is_db_routed, is_impersonated, parameters on query_execution (EMB-1505)

* Add tests for is_db_routed and parameters on query_execution (EMB-1505)

Unit tests for query-execution-info verifying:
- is_db_routed true/false based on destination-database/id
- parameters JSON-encoded when present, nil when absent

Verified: 29 tests, 92 assertions, 0 failures

* Add new columns to analytics views, create v_tenants and v_users v5 (EMB-1506)

- v_query_log v5: add embedding_version, auth_method, is_sandboxed,
  is_impersonated, is_db_routed, parameters, tenant_id, and PII columns
- v_view_log v4: add has_access, context, embedding_version, auth_method,
  tenant_id, and PII columns
- v_users v5: add tenant_id, tenant_qualified_id
- v_tenants v1: new view exposing tenant data
- Register v_tenants in audit-db-view-names

* JOIN auth_identity for real auth_method provider (EMB-1502)

Replace hardcoded "session" auth_method with the actual provider
from auth_identity table via LEFT JOIN on session.auth_identity_id.
Sessions now report "password", "jwt", "saml", "ldap", "google",
"oidc", etc. Legacy sessions without auth_identity_id fall back
to "session". Test helper authenticate! now sets auth_identity_id
on test sessions.

* Address PR review: simplify is_impersonated, fix formatting nits

- is_impersonated now derived from :impersonation/role in query-execution-info
  (pre-pipeline) instead of rff wrapping. Simpler, matches is_db_routed pattern.
- Remove rff metadata wrapping from apply-impersonation-postprocessing
- Fix are macro indentation in sdk_test.clj
- Fix indentation in process_userland_query_test.clj
- Compact analytics_dev_test.clj column set formatting

* Update src/metabase/analytics/core.clj

* Align surface values with product doc, add modular-embedding mapping

Renames surface labels in analytics views to match the product spec:
- public-link -> public-sharing
- static-embed -> guest-embedding

Adds previously unmapped embedding-simple -> modular-embedding.

* Drop :ai alias for auth_identity in session query

Per review feedback - :ai is overloaded. Use the table name directly.

* Fix review findings: typo, H2 user label, remove redundant some->, add ip_address test coverage

- Fix "retension" -> "retention" in setting name (before it ships to DB)
- Fix H2 v_users "Anonymous" -> "External" to match MySQL/Postgres
- Remove redundant some-> in merge-current-user-info (dissoc handles nil)
- Add :remote-addr to mock test client so ip_address flows through
- Assert ip_address in PII integration tests

* Actually sanitize user-agent via UA parsing instead of just truncating

Extract describe-user-agent into leaf ns metabase.request.user-agent
to break cyclic dep (analytics.sdk -> request.util -> analytics.core).

* Fix clj-kondo module boundary violations for analytics<->request cross-module requires

* Store raw user_agent instead of parsed/sanitized form

Rename column sanitized_user_agent -> user_agent and store the raw
User-Agent header (truncated to 512 chars) instead of parsing it via
describe-user-agent. Raw strings preserve more information for analytics.

Reverts the request.user-agent leaf namespace (created to break a cyclic
dep) since analytics.sdk no longer needs UA parsing.

* Separate embedding_client and embedding_route into independent columns

embedding_client was conflating SDK/client identity (from X-Metabase-Client
header) with API route surface (from URI matching), making it impossible to
distinguish e.g. React SDK vs browser hitting the same embed route.

Now embedding_client stores the header value and embedding_route stores
the URI-derived surface. SQL views use COALESCE(embedding_route,
embedding_client) for backward compatibility with pre-split data.

* Add sanitized_user_agent alongside raw user_agent in view_log and query_execution

Extract describe-user-agent into leaf namespace (request.user-agent) to
break the circular dep analytics.sdk -> request.util -> analytics.core,
then call it from pii-request-info to populate sanitized_user_agent - a
human-friendly parsed form like "Browser (Chrome/OS X)".

* Align analytics.core postrequire whitespace for consistency

* Add iframe variant surface mappings for upcoming embedding-iframe-static/full-app/public client values

* Document PII retention behavior in setting description

* Add test coverage for v_view_log surface CASE mapping

Tests all 24 branches of the SQL CASE expression that derives
surface and is_preview from embedding_client/embedding_route,
protecting against silent analytics data corruption from typos.

* Document why include-sdk-info is called twice for QueryExecution writes

* Add auth_identity LEFT JOIN test for all SSO provider strings

Verifies the session query LEFT JOIN on auth_identity correctly
returns the provider string for jwt, saml, google, ldap, oidc,
custom-oidc, slack-connect, and support-access-grant.

* Move migration from 060/ to 061/ to match current version

Master advanced past v60, so our migration needs to target v61.
Updated logicalFilePath and changeset IDs accordingly.

* Fix v_users rollback: DROP VIEW before CREATE OR REPLACE

PostgreSQL cannot drop columns from a view via CREATE OR REPLACE VIEW.
The rollback SQL for v_users needs to DROP the view first since v5 adds
tenant_id/tenant_qualified_id columns that v3 doesn't have.

* Add DROP VIEW IF EXISTS to v_users rollback SQL files

Match the pattern used by v_view_log and v_query_log rollback files.
Move the DROP from inline YAML into the SQL files themselves.

* Revert "Add DROP VIEW IF EXISTS to v_users rollback SQL files"

This reverts commit 703b9ae2aa.

* Fix 4 CI test failures from new analytics columns

1. surface-case-mapping-test: MySQL/MariaDB CASE returns 0/1 not
   true/false - coerce with (boolean ...) in test helper
2. auth-method-test: authenticate! picked arbitrary auth_identity -
   filter to provider="password" specifically
3. baseline-comparison-test: add analytics-pii-retention-enabled to
   serialization baseline settings.yaml
4. basic-query-test: add new query_execution columns to expected map

* code review responses

* Remove unused wrapper macros/fns that just delegated to the real impl

- Remove with-client!, with-route!, with-version! macros from analytics/sdk
  (middleware already binds the vars directly; keep with-auth-method! which
  is used in session.clj)
- Remove ip-address fn and describe-user-agent def from request/util
  (device-info now calls request.current/ip-address directly)
- Update request/core imports to point at actual source namespaces
- Tests bind dynamic vars directly instead of through removed wrappers

* Fix CI failures: MySQL returns 0/1 for boolean CASE, not true/false

- analytics_dev_test: handle numeric is_preview from MySQL JDBC driver
- view_log_test: add sql-false? helper for cross-DB boolean comparison
- api_test: add :auth_method to format-response boolean conversion set

* Match MySQL view SQL to Postgres/H2: use TRUE/FALSE not 1/0

* Remove MySQL boolean workarounds now that views use bit literals

* Fix CI failures: MySQL boolean handling, ->bool coercion, OSS AuthIdentity insert

- MySQL views: use TRUE/FALSE instead of b'1'/b'0' for is_preview column
- Add ->bool test helper to coerce MySQL Integer 1/0 to boolean in assertions
- Use raw table name for AuthIdentity insert to bypass OSS validation hooks

* fix modules

* Guard tenant tests with ee-available? to fix OSS CI failures

* Set auth_method to "guest"/"public" for unauthenticated embed/public requests

Previously auth_method was nil for requests without a session or API key.
The guest-embed JWT and public sharing links are authorization mechanisms,
so they should appear as auth methods. Falls back to route surface when
no traditional auth is present, mapping "guest-embed" -> "guest".

* Fix auth_method assertions in end-to-end analytics view tests

Two more tests were asserting auth_method=nil for public requests,
missed in the initial commit. Now correctly expect "public".

* Ungate embedding_hostname from PII retention setting

embedding_hostname is just the customer's domain (e.g. acme-corp.com),
not PII. Split pii-fields into hostname-fields (always collected) and
pii-fields (still gated behind analytics-pii-retention-enabled).

* fix route mapping

- see https://github.com/metabase/metabase/blob/master/src/metabase/api_routes/routes.clj#L202

* Fix preview-embed -> preview_embed route syntax

* Clarify some var names

* Clarifying comments regarding how the middleware stack works

* Move auth-method calculation to wrap-current-user-info mw

* Write spy-handler-middleware and fix wrap-current-user-info bug

* Prefer embedding-route over session/api auth method

* Add scenarios coverage for session/auth-method helper

Covers the full scenarios table agreed on for the auth_method column:
- session-based credentials on non-special routes return the provider
  string ("password", "saml", "jwt", "ldap", or "session" default)
- api-key on non-special routes returns "api-key"
- special routes (guest-embed, public, metabot, agent-api) win over
  credentials; notably api-key on /api/embed/* returns "guest"
- fully anonymous requests on non-special routes return nil

Locks in Ben's route-override fix (#173e863).

* Fix current-user-info-for-api-key-test after :embedding/auth-method rename

Ben's route-override fix (#173e863) renamed the request key from
`:auth-method` to `:embedding/auth-method` in merge-current-user-info,
but these two assertions still hardcoded the old key. Update them to
match the new namespaced keyword.

* Rename X-Metabase-Embed-Origin -> X-Metabase-Embed-Referrer

The header carries a full URL with path (like HTTP Referer), not just
scheme+host+port (like Origin). Rename to reflect actual semantics.

* Gate query_execution.parameters behind analytics-pii-retention-enabled

Filter values can contain PII (emails, names, etc). Only persist them
when PII retention is enabled. The parameterized boolean remains ungated.

* Remove surface CASE expression from IA SQL views

The surface derivation will be implemented in the IA model layer
instead. Removes the CASE from v_view_log and v_query_log (all 3
dialects), along with the surface-case-mapping-test and related
test assertions.

* collect new values in query-execution-statistics

- keep it backwards compatible too

* Add test coverage for new embedding_client header values

- Test new header values (embedding-iframe-full-app, embedding-iframe-static,
  embedding-public, embedding-simple) alongside legacy embedding-iframe
- Fix NULL handling in internal fall-through bucket with COALESCE
- Add simple_embed to Snowplow payload (was missing)
- Add mt/initialize-if-needed! to fix test in isolation

* Fix CI: modules config and parameters test

- Add metabase.analytics.settings to analytics module :api set
- Gate parameters test behind analytics-pii-retention-enabled setting

* remove client version header from EAJS (#72618)

* fix whitespace (#72757)

* fix whitespace

* remove "are" that's likely a typo?

* split `embedding-iframe` header into more specific values (#72455)

* split `embedding-iframe` into specific headers

* removed unused "x-metabase-guest-embed" header, that was also passed in public links

* refactor embed headers

* use getIsEmbedPreview

* add comment on full app embedding

* Add metabase_version column, rename embedding_version -> embedding_sdk_version

EMB-1577 + EMB-1578: Two changes to query_execution and view_log:

1. Add metabase_version (varchar 32) - stamps config/mb-version-info :tag
   at insert time so we can see which server version generated each row.

2. Rename embedding_version -> embedding_sdk_version to distinguish the
   SDK's npm package version from the new server version column.

Also fixes analytics-dev-test missing :db fixture (bare t2/select-one
against app DB needs explicit initialization when run in isolation).

* Update embedding-context? and track-sdk-response for new client headers (EMB-1588)

This branch split "embedding-iframe" into specific client header values
(embedding-iframe-full-app, embedding-iframe-static, embedding-public) and
added embedding-simple. But embedding-context? and track-sdk-response only
knew the two old values (embedding-sdk-react, embedding-iframe).

Without this fix:
- Prometheus metrics silently stop for all new-header embedding traffic
- Comments are not disabled in embedding contexts using new headers
  (regression: comments/api.clj uses embedding-context? to gate comments
  in embedded documents, added in #64098)

Changes:
- embedding-context?: replace two equality checks with a set of all 6 values
- track-sdk-response: add case branches for 4 new client types, each with
  its own prometheus counter (per Roman's preference for full granularity)
- Register 4 new prometheus counters: embedding-iframe-full-app,
  embedding-iframe-static, embedding-public, embedding-simple
- Remove now-unnecessary embedding-sdk-client / embedding-iframe-client defs
- Fix comments/api_test.clj reference to removed private def

Prometheus mapping:
  "embedding-sdk-react"       -> :metabase-sdk/response (existing)
  "embedding-iframe"          -> :metabase-embedding-iframe/response (existing)
  "embedding-iframe-full-app" -> :metabase-embedding-iframe-full-app/response (new)
  "embedding-iframe-static"   -> :metabase-embedding-iframe-static/response (new)
  "embedding-public"          -> :metabase-embedding-public/response (new)
  "embedding-simple"          -> :metabase-embedding-simple/response (new)

* add X-Metabase-Embed-Referrer (#72688)

* add X-Metabase-Embed-Referrer

* fix unit test

* use isEmbeddingEajs

* Apply suggestion from @npretto

* rewrap comment

* Update some outdated comments

* Remove unused refer

* Add Usage Tracking admin UI section (#73083)

* Add Usage Tracking admin UI section

* Improve setting from env logic

* Update environment-variables.md

* Update setting description

* Fix tracking

* Update GeneralSettingsPage.unit.spec.tsx test

* Cleanup events

* Fix type

* Update description

* fix wrong function name after merge

* remove fallback for embedding host and path (#72879)

* remove fallback for embedding host and path

* update test import path

* update settings copy as per figma (#73164)

* update copies as per figma

* update unit tests

* v1 of embedding usage and small change in analytics dev export (#73036)

* embedding dashboard, without field values remapping

add field value remapping

update to the dashboard

* fix columns by specifying only what we want to expose

* mage analytics:setup && mage analytics:export

* update dashboard

* remove accidental file

* restore result_metadata

* maybe this time

* revert unwanted change to usage analytics collection description

* added un-sanitized user agent to view and query models

* extract doc change to a separate pr

* initialize app-db in parameter-substitution-test

qp.compile/compile now runs apply-impersonation preprocessing, which
hits the app-db. Without a :db fixture, H2 has no schema and tests
error with "Table PERMISSIONS_GROUP_MEMBERSHIP not found".

* Drop hand-authored result_metadata from tenants_with_most_views.yaml

The block's column maps failed ::lib-or-legacy-column validation in
normalize-card (join-through-model shape with transient lib/qp keys and
portable id refs), the schema decoder silently nulled it, and
infer-metadata's u/ignore-exceptions fallback swallowed the followup
failure -- leaving the card with :result_metadata nil and tripping the
audit-db-installation-test assertion added in e8676763ff. Removing the
block lets populate-result-metadata route straight to inference, which
succeeds for this query.

* fix test

* test fix

* Fix merge conflict resolution error

* Fix merge conflict resolution error

* Add a missing test

* Fix some tests

* Fix expected key name

* Fix is_impersonated/is_db_routed timing in query_execution

`process-userland-query-middleware` was reading `:impersonation/role` and
`:destination-database/id` from the query map at the top of the around
middleware — before the preprocessing middleware that sets those keys had
run. As a result `is_impersonated` and `is_db_routed` were always recorded
as false, and `database_id` was the router DB id instead of the routed
destination DB id.

Move the reads to inside `rff*` (the postprocessing reducing function),
where the dynamic-var bindings established by `apply-impersonation-
postprocessing` and `swap-destination-db` are still in effect. Add a
parallel `*destination-database-id*` dynamic var for DB routing, and
three `defenterprise` accessors that OSS code can call.

The error path remains broken (the bindings are popped before our outer
catch fires) but that's a pre-existing issue, not a regression — will be
addressed in a follow-up PR.

Add end-to-end integration tests that drive a real impersonated/routed
query through the QP and assert the persisted QueryExecution row,
addressing @metamben's review on PR #71386. Remove the synthetic-query
unit tests that masked the bug by passing the keys pre-set.

---------

Co-authored-by: Ben Grabow <ben.grabow@gmail.com>
Co-authored-by: Ben Grabow <ben.grabow@metabase.com>
Co-authored-by: Nicolò Pretto <info@npretto.com>
Co-authored-by: Timofey Kachalov <timofei@metabase.com>
Co-authored-by: Roman Abdulmanov <roman.abdulmanov@gmail.com>
2026-04-27 17:25:37 +03:00
Mahatthana (Kelvin) Nomsawadi
4900a9a9f0 Support trusted publisher on the Embedding SDK release workflow (#73214)
* Support trusted publisher

* update permission
2026-04-27 15:12:07 +01:00
dwhitemv25
afbaba7e9f Update minimum app db versions in documentation (#73204)
* Update required app database versions

* Update min recommended mysql/mariadb version
2026-04-27 09:09:00 -04:00
Sébastien
a15d0594c8 fixed ResizeObserver regression (#73212) 2026-04-27 10:40:53 +02:00
Phoomparin Mano
7524d5d062 Integration branch for Embedding themes (#72147)
Co-authored-by: Sébastien <sebastien@metabase.com>
Co-authored-by: Phoomparin Mano <poom@metabase.com>
2026-04-27 07:40:51 +02:00
Aleksandr Lesnenko
72ba6a81c1 static viz typing (#73193)
* static viz typing

* rspack config
2026-04-26 16:29:54 +00:00
da
c31ad033e7 Update Apache POI dependencies to version 5.5.1 (#73148)
5.5.1
Upgrade bouncycastle dependency to 1.83
Upgrade commons-io dependency to 2.21.0
Upgrade commons-codec dependency to 1.20.0
Upgrade pdfbox dependency to 3.0.6

5.5.0
Upgrade batik dependency to 1.19
Upgrade bouncycastle dependency to 1.82
Upgrade commons-codec dependency to 1.19.0
Upgrade commons-collections4 dependency to 4.5.0
Upgrade commons-compress dependency to 1.28.0
Upgrade commons-io dependency to 2.20.0
Upgrade pdfbox dependency to 3.0.5
Upgrade graphics2d dependency to 3.0.5
Upgrade xmlsec dependency to 3.0.6
Upgrade JaCoCo code-coverage tooling to 0.8.13
2026-04-25 16:22:01 -03:00
github-automation-metabase
24491d51eb Auto-update documentation for master (#73191)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-25 14:43:00 -04:00
Mahatthana (Kelvin) Nomsawadi
673ee61589 feat(sdk-package, sdk-bundle): Introduce useMetabot hook (#73075)
* Expose query prop on StaticQuestion and InteractiveQuestion (#72713)

* Expose query prop on StaticQuestion and InteractiveQuestion (EMB-1585)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add tests for query prop on StaticQuestion and InteractiveQuestion (EMB-1585)

- Convert card.unit.spec.js → card.unit.spec.ts with typed getCard helper
- Add deserializeCardFromQuery unit tests (prefix stripping, dataset_query wrapping)
- Add InteractiveQuestion query prop render test and schema rejection test
- Add StaticQuestion query prop render test and schema rejection test
- Fix IQ/SQ schemas to accept query as an alternative to questionId

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* don't intercept props unnecessarily

* Remove unnecessary casting

* Keep `query` prop internal-only (EMB-1585)

Per team discussion, `query` should not be part of the public SDK API.
It stays as an internal channel for the upcoming `useMetabot` hook
(Layer 2 / EMB-1554).

- `SdkQuestionEntityPublicProps`: drop the `query` branch
- `SdkQuestionEntityInternalProps`: new internal type that adds `query`
- `InteractiveQuestion` / `StaticQuestion`: cast public export to the
  narrow props; expose `_InteractiveQuestionInternal` /
  `_StaticQuestionInternal` for tests and the Metabot hook
- Yup schemas: type against the internal shape so runtime still
  accepts `query`
- Unit tests: switch `query` specs to the internal component

* Clean up PR

* Fix unit tests

* cleaner API

* Remove embarassments

* Consolidate tests

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Expose currentChart from useMetabot (#72714)

* Expose currentChart from useMetabot (EMB-1561)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add useMetabot currentChart unit tests (EMB-1561)

- New use-metabot.unit.spec.tsx with StaticQuestion/InteractiveQuestion mocked
- Tests: currentChart is null before navigate_to, becomes a component after it fires
- Tests: routes to StaticQuestion (no drills) or InteractiveQuestion (drills=true)
- Tests: currentChart updates when a second navigate_to fires

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Clean up

* Defer chart message plumbing to EMB-1562

* Clean up pr

* Add missing tests

* Allow CurrentChart to be null because users need to check it

* Revise public API, drop irrelevant types.

* drop more public API unrelated to the SDK

* Fix merge conflict

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Expose chart in messages (#72383)

* Add Component to chart messages in useMetabot (EMB-1562)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add MetabotChatHistory filter tests and messages[n].Component tests (EMB-1562)

- New MetabotChatHistory.unit.spec.tsx: chart messages filtered from message list,
  non-chart messages render normally
- Extend use-metabot.unit.spec.tsx: messages[n].Component renders StaticQuestion
  or InteractiveQuestion based on drills prop; Component reference is stable
  (cached) after a second chart message arrives

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add clarity to code

* Clean up

* Rename messages[n].Component to messages[n].Chart (EMB-1562)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test: document untested passthrough props

* test: mock only internal hook

* docs: add JSDoc to isProcessing

* feat: warn on navigate_to overwrite

* test: document why we keep as any

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add `useMetabot` storybook stories (#71996)

* Add Component to chart messages in useMetabot (EMB-1562)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add MetabotChatHistory filter tests and messages[n].Component tests (EMB-1562)

- New MetabotChatHistory.unit.spec.tsx: chart messages filtered from message list,
  non-chart messages render normally
- Extend use-metabot.unit.spec.tsx: messages[n].Component renders StaticQuestion
  or InteractiveQuestion based on drills prop; Component reference is stable
  (cached) after a second chart message arrives

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add clarity to code

* Clean up

* Rename messages[n].Component to messages[n].Chart (EMB-1562)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test: document untested passthrough props

* test: mock only internal hook

* docs: add JSDoc to isProcessing

* feat: warn on navigate_to overwrite

* test: document why we keep as any

* Add useMetabot Storybook demos (EMB-1524)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test more useMetabot properties

* cleanup

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* expose `useMetabot` in the package + add E2E (#73047)

* WIP first pass on the useMetabot before verification

* Prepare for debugging

* Debug code

* add more tests

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Reconcile with master merge

- Rename authProviderUri -> jwtProviderUri (master renamed auth-config field).
- Filter locked MetabotErrorMessage variant from SDK public type.
- Rename emitted chart-message field Component -> Chart to match public type.
- Replace require() with jest.requireActual() in integration spec (lint rule).
- Widen test stubs for new SdkStoreState surface.
- Drop unused act wrap + silence exhaustive-deps on mock useLoadSdkBundle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* expose proper types

* Fix E2E

* Remove debug files

* Fix implementation from merging master

* Fix formatting

* Address review

* Fix useMetabot hook not working from merging master

* Make global state name consistent

* remove unused export

* Make type consistent

* Make tests more readable

* Make code more readable

* Polish E2E test

* Fix useMetabot not working under StrictMode

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix moved module paths on master

* Move useMetabot code to EE

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:24:20 +01:00
Anderson
7c8d37830b Fix dashboard queries re-executed when switching tabs (#73145)
* Fix dashboard queries re-executed when switching tabs

* Fix some e2e tests

* Improve unit test as per review
2026-04-24 21:40:24 -03:00
Jeff Bruemmer
c14aad5624 docs - ai service (#73187) 2026-04-24 13:45:24 -07:00
Oleksandr Yakushev
fcc50c8ab3 Bump clj-kondo (#72019) 2026-04-24 23:07:58 +03:00
Nathan Voxland
f0efbb36a8 Support env variables for remote-sync configuration (#68583)
* Support env variables for remote-sync configuration

* Merged from master

* Merged from master

* Merged from master

* Resolved conflicts

* Fixing merge issue

* branch switcher

* read-write

* some cleanup

* unit tests

* Branch is set by env var badge

* adjust y margin

---------

Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>
2026-04-24 19:14:15 +00:00
metamben
c0927e9c15 Introduce minimal dependency analytics interface (#72262)
* Implement mostly dependency free analytics interface
* Introduce analytics-interface module
2026-04-24 14:45:22 -04:00
Maksym Yakubych
055e52a014 fix: render tables in AI markdown (#73125) 2026-04-24 18:19:22 +00:00
Nemanja Glumac
d129fadd2b Fix release-tag.yml after release-channel removal (DEV-1864) (#73166)
* Fix release-tag.yml after release-channel removal (DEV-1864)

The `update-version-info` job in release-tag.yml required
`updateVersionInfoChannel`, which was removed alongside the rest of the
release-channel CI infra. `tag_name` is now effectively always `latest`,
so add a small async wrapper `updateVersionInfoLatest` that fetches the
live version-info.json and delegates to the surviving
`updateVersionInfoLatestJson`, and point the workflow at it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* only update for latest

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Ryan Laurie <iethree@gmail.com>
2026-04-24 19:32:37 +02:00
Brad Anderson
c0ee242e19 GRO-281 Enable transforms screen (#71658) 2026-04-24 13:28:56 -04:00
Jeff Bruemmer
ad53e24035 docs - notes on SDK version and upgrades (#72995) 2026-04-24 09:34:29 -07:00
Alexander Polyankin
e8676763ff Fix audit inferring result_metadata (#73169) 2026-04-24 15:58:08 +00:00
Timofey Kachalov
6c5b3fa5a3 feat(sdk,embed-js): Adjust mobile layout for SDK question topbar (#72853) 2026-04-24 19:40:03 +04:00
Romeo Van Snick
cf67b911e6 Clean up utils/redux and utils/entities (#72832)
* Remove star import

* Remove any

* Code style

* Move updateMetadata action to metabase/redux/metadata

* Use _.indexBy instead of custom resourceListToMap

# Conflicts:
#	frontend/src/metabase/utils/redux/utils.ts

* Move redux action utils into metabase/redux/utils

* Move redux utils into metabase/redux

# Conflicts:
#	enterprise/frontend/src/metabase-enterprise/metabot/components/MetabotAdmin/MetabaseAIProviderSetup.tsx
#	frontend/src/metabase/setup/components/LanguageSelector/LanguageSelector.tsx
#	frontend/src/metabase/utils/entities.js
#	frontend/src/metabase/utils/redux/utils.ts

* Move withAction to metabase/redux/utils

# Conflicts:
#	frontend/src/metabase/utils/entities.js
#	frontend/src/metabase/utils/redux/utils.ts

* Move handleEntities into metabase/utils/entities

# Conflicts:
#	frontend/src/metabase/utils/entities.js
#	frontend/src/metabase/utils/entities.unit.spec.ts

* Move withNormalize into metabase/utils/entities

# Conflicts:
#	frontend/src/metabase/utils/entities.js
#	frontend/src/metabase/utils/redux/utils.ts

* Move withCachedDataAndRequestState to metabase/utils/entities

# Conflicts:
#	frontend/src/metabase/utils/entities.js
#	frontend/src/metabase/utils/redux/utils.ts

* Use sdk api in sandbox components and remove unused selectors

* Remove withRouter hoc

* Move withRequestState to metabase/utils/entities

# Conflicts:
#	frontend/src/metabase/utils/entities.js
#	frontend/src/metabase/utils/redux/utils.ts

* Remove fetchData wrapper in fetchRevision as loading state is not used

* Move fetchData and updateData into metabase/utils/entities

# Conflicts:
#	frontend/src/metabase/utils/entities.js
#	frontend/src/metabase/utils/entities.unit.spec.ts
#	frontend/src/metabase/utils/redux/utils.ts
#	frontend/src/metabase/utils/redux/utils.unit.spec.ts

* Defer rtk instantiation in entities to break import loop

# Conflicts:
#	frontend/src/metabase/utils/entities.js

* Fix imports

* Fix import

* Move entities helpers to metabase/entities

* Handle loading and error states from useListUserAttributesQuery

* Fix withAction imports

* Import from internal modul

* Import from utils directly to avoid import loop

* Fix imports

* Fix types for gtap endpoint helpers

* Coalesce empty string to null
2026-04-24 13:38:47 +02:00
Romeo Van Snick
b50dbf8a65 Move utils/pulse to metabase/pulse since it contains only domain-related code (#73082)
* Move utils/pulse to metabase/pulse since it contains only domain-related code

* Use relative import in test
2026-04-24 09:28:40 +02:00
Romeo Van Snick
3b2fa353a2 Cleanup metabase/utils/analytics (#72981) 2026-04-24 16:29:55 +12:00
Fraser
7ef50e0a79 Move DataSelector into querying (#73071) 2026-04-24 11:05:48 +12:00
github-automation-metabase
f33090e8e6 Auto-update documentation for master (#73138)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-23 17:24:19 -04:00
Andrei Chernykh
b34d9ce017 Uxw 3658 nav misc (#73010)
* Migrate StoreLink to Mantine + CSS module

* Migrate NewItemButton to Mantine + CSS module

* Opportunistic fix: make Site URL input span full width

* Address review: use admin-navbar-inverse token for StoreLink hover; match Tools height

* Address review: use rem units and text-secondary-inverse token in StoreLink

* Migrate NewItemButton to Mantine Button
2026-04-23 13:47:28 -07:00
Andrei Chernykh
0c0de15849 Track weekly Emotion file count on stats.metabase.com (#73008)
* Track weekly Emotion file count on stats.metabase.com

* Inline the Emotion count in the workflow

* Comment the Emotion stats workflow

* Update .github/workflows/emotion-stats-update.yml

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>

* Strip trailing whitespace

* Catch bare @emotion side-effect imports and trim redundant comments

---------

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-04-23 12:29:51 -07:00
Andrei Chernykh
d1c9946092 Don't show required error in Create user modal before email is touched (#73056)
* Don't show required error in Create user modal before email is touched

* Add regression test for initial validation state in UserForm
2026-04-23 12:02:45 -07:00
Chris Truter
d51c5d5012 Data Complexity Score (#72681) 2026-04-23 18:07:13 +00:00
Nemanja Glumac
92d574667f Notify Slack when the auto-patch workflow skips a release (DEV-1854) (#73114)
Replace the silent console.error in .github/workflows/release-patch.yml
with a Slack post that names the specific reason (broken CI, no next
patch version, or already-released). Failures use ❌; the benign
already-released case uses ℹ️ so daily no-op runs
don't read as alerts.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:59:53 +02:00
Edward Paget
532470d786 Fix OAuth consent page breaking custom logo when logo URL is a data URI (#71835)
Fix OAuth consent page breaking custom logo when logo URL is a data URI (UXW-3524)

The `absolute-url` helper only checked for http/https prefixes, so data URIs
got mangled with the site URL prepended. Also adds proper CSS constraints on
the logo img to match the frontend LogoIcon component.
2026-04-23 12:25:08 -05:00
Andrei Chernykh
9dbfbb38c2 Uxw 3673 login history (#73050)
* Migrate LoginHistory from Emotion to Mantine style props

* Stack location and IP on separate lines, top-align right column
2026-04-23 08:58:39 -07:00
Andrei Chernykh
a5dcce0a28 Prevent MeasurementProvider detached root from flipping theme (#72931)
Prevent MeasurementProviders' detached Mantine root from flipping <html>'s color scheme
2026-04-23 08:55:49 -07:00
Ryan Laurie
e2ab959554 Remove release channel CI infra (#72292)
remove release channel code
2026-04-23 09:24:53 -06:00
Ngoc Khuat
942e35fd92 Rate-limit new-device login emails per user to protect SMTP budget (#73031) 2026-04-23 15:13:54 +00:00
Nemanja Glumac
98cb75d827 Improve the e2e group naming in the build matrix (#73099)
Improve the e2e group naming

GitHub's Actions UI orders workflow artifacts alphabetically
(lexicographically), not numerically. As a result, Cypress recording
artifacts produced per e2e chunk are interleaved in a confusing order,
making it difficult to locate the artifact for a specific failing group.

Example

Current ordering (alphabetical):

cypress-recording-10-ee

cypress-recording-2-ee

cypress-recording-34-ee

cypress-recording-5-ee

Intuitively we want 2 before 5 before 10 before 34.
2026-04-23 17:04:21 +02:00
Nick Fitzpatrick
97f04b57f1 Add a step to show fk tables in publish flow from Library (#72875)
* add a step to show fk tables in publish flow from Library

* e2e adjustment

* PR Feedback
2026-04-23 19:32:14 +05:30
Nick Fitzpatrick
4022e26336 Remove old segment icon and replace all usage with segment2 (#72893)
* Remove old segment icon and replace all usage with segment2

* remove unneeded icon

* there can only be one
2026-04-23 19:31:57 +05:30
Nick Fitzpatrick
98b7ae8adc Fix metabot button overlap on transform screen (#72727)
* Fix metabot button overlap on transform screen

* e2e adjustment
2026-04-23 18:32:48 +05:30
Braden Shepherdson
4c2318a5ae [mage] The serdes API changed; make mage analytics:finalize match it (#72980)
Mage calls some of the serdes functionality from Metabase to implement
the `analytics:*` tasks. The interface for `store!` changed as part of
the "Jekyll mode" project to use a more flexible `ExportWriter`
interface rather than explicit path names.

That caused the Mage task to throw an `IllegalArgumentException` since
one cannot call `ExportWriter` methods on a string.
2026-04-23 12:37:33 +00:00
Timofey Kachalov
b95cb046a8 Refactor breakout handling to utilize custom query handlers (#73093) 2026-04-23 15:03:33 +04:00
Romeo Van Snick
c795da13e0 Merge metabase-lib/v1/utils/card and metabase/utils/card into metabase/common/utils/card (#72609)
* Rename isTransientId to isTransientCardId for clarity

* Move metabase-lib/v1/utils/card into metabase/utils/card

* WIP

* Fix card comparison types

* Move metabase/utils/card to metabase/common/utils/card

* Fix lint

* Add ts expect error for unknown card usage

* Fix import order

* Allow passing other values to isTransientId

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix spacing issue

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Colocate isTransientCardId with Question to fix import loop

* Be more lenient as to what card can be

* Remove dead code branch

* Fix formatting

* Fix applyParameters helper

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-23 12:50:37 +02:00
Uladzimir Havenchyk
3b49b1a92b deps: upgrade follow-redirects (#72685) 2026-04-23 13:48:51 +03:00
Sébastien
ef4c07eae6 Changed copy of security center EMB-1622 (#73085) 2026-04-23 12:26:18 +02:00
Timofey Kachalov
02d2db0014 fix(sdk,embed-js): Refactor query handling to show aggregations/breakout for the last non-empty stage (#72219) 2026-04-23 13:44:49 +04:00
Nicola Mometto
0b1adbfdcc fix transform inspector regression on unaliased join tables (#73084) 2026-04-23 09:33:07 +00:00
Timofey Kachalov
dd6c1e01cb fix(sdk): scope SDK-themed Mantine CSS variables to .mb-wrapper to prevent leakage to :root (#72962) 2026-04-23 10:34:55 +04:00
Timofey Kachalov
02d588d3fc Add cursor deeplink button in admin UI for MCP setup (#72890) 2026-04-23 09:18:37 +04:00
YB
a94047288a GDGT-2087 validate localization strings (#72756)
Translation strings can be invalid (e.g. parsing error from java.MessageFormat, or have mismatched number of format args) which lead to incorrectly displayed english text when a user expects a translated text.

This PR introduces a step in the translations build that filters out such invalid translation strings and outputs an artifact that lists the relevant translation strings and information that can help with troubleshooting or feeding back into Crowdin.

We also lift up the autofixing logic as part of a new module to that translation build_artifacts pipeline instead of having it within create-artifact-for-locale! which happens later in the process. This allows us to more easily add other autofixes in future to the pipeline.

There's translation strings introduced both via Clojure and TS pipelines which are slightly different in terms of parsing, so the validator functions use MessageFormat parsing and regex respectively for source strings coming from either type of file.
2026-04-23 10:33:43 +08:00
github-automation-metabase
929b204a23 Auto-update documentation for master (#73063)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-22 20:50:19 -04:00
appleby
fc1af36539 BOT-1368 Drop metabase-enterprise.metabot.task.ai-usage-trimmer (#72926)
We want to retain this data for the AI usage analytics project.
2026-04-22 17:40:57 -07:00
Fraser
a6db0c0881 Move shared reference components into querying (#73066) 2026-04-23 10:18:39 +12:00
Ryan Laurie
b0097fbccb Update fork less often (#73062)
update fork less often
2026-04-22 15:24:23 -06:00
Denis Berezin
c8f5c5e6d9 UXW-3775 open Metric Viewer in the same tab (#73009)
* UXW-3775 open Metric Viewer in the same tab

* Fix e2e
2026-04-23 00:02:21 +03:00
Romeo Van Snick
f78efe0f4b Clean up metabase/utils/embed (#72615)
* Convert embed.js to embed.ts

* Move initializeEmbeddding to initializeInteractiveEmbedding in embedding/interactive-embedding

* Merge getIsEmbedPreview and IS_EMBED_PREVIEW into isEmbedPreview in metabase/embedding/config

* Improve prose

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Improve comment

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-22 22:35:58 +02:00
Fraser
55b9b24061 Fix dashboard qb action violation (#73019)
Fix dashboard qb qction violation
2026-04-23 08:35:44 +12:00
Romeo Van Snick
8b260b3b88 Fix the mlv2 module boundary (#73055)
Fix the mlv2 namespace
2026-04-22 22:35:28 +02:00
Anderson
13f17eab13 Convert metabase/utils files to TypeScript, part 2 (#72679)
* Convert metabase/utils spec files to TypeScript, part 2

* Remove some type castings

* Convert memoize.unit.spec.js > ts
2026-04-22 17:32:18 -03:00
Kevin Kelleher
43fa71b2a5 Add labels to scalar tab in Metrics Viewer (#72541)
* add labels to scalar, viz settings refactor

* design feedback

* fix tests
2026-04-22 16:25:10 -04:00
Kevin Kelleher
daf7af4525 Fix metric math formula editor bugs (#72896)
* better handle delimiters in getWordAtCursor

* only break words on delimiters or whitespace

* fix off by one error in delimiter loop
2026-04-22 15:58:31 -04:00
technomancy
d454fbda70 [MBQL Lib] Define ::external-query malli schema for External MBQL 5 (#72797)
* Define ::external-query malli schema for "External MBQL 5".

Using a nested :registry allows us to override the few differences
between internal and external MBQL without constructing an entire
separate tree of the whole ::query schema.

I've added tests to validate all the sample queries in the representations
repository. We have to do some normalization for these queries because
they're getting deserialized from yaml and thus end up having strings
where they should have keywords and lists where they should have vectors.

These tests are skipped if you don't have a representations checkout.
I've copied one of the queries into the test. Maybe we should just copy
all of them over into the test suite instead?

I've kept the normalization for these queries in the test suite
because it seems to be specific to the yaml deserialization process
and not really anything inherent in the external format, but it could
be argued that belongs in src/. This is run on top of the existing
lib.normalize process, which is intended specifically for queries
coming in from the frontend.

* Just use a couple sample queries instead of the representations repo.

* Tidy up external query schema as per review suggestions.

malli.utils/dissoc helps with the lib/uuid field, and it turns out
field IDs can have more strings for JSON-unfolded fields when you
need a nested path.

Turns out we can override mbql.clause/field without as much headache
as I thought because it's not actually part of a defmulti like it
originally looked.

* Add comments explaining the somewhat opaque external schema updates.

We have to do an `update` based on a numeric vector key, which is not
super obvious, so some comments would help guide someone if a future
schema update breaks this later down the line.
2026-04-22 11:22:33 -07:00
github-automation-metabase
05154519dd Update translations 2026-04-21 (#72934)
* Update translation sources 2026-04-21

* Update translations

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-04-22 16:32:30 +00:00
Nick Fitzpatrick
030b488224 add tracking for stack series (#72986) 2026-04-22 20:41:19 +05:30
Andrei Chernykh
0c06a10ff5 Add spacing above buttons in new collection modal (#72927) 2026-04-22 08:07:50 -07:00
Aleksandr Lesnenko
ec6e4baadb endpoint for exposting field values (#73003)
* endpoint for exposting field values

* review
2026-04-22 14:49:57 +00:00
Alexander Polyankin
b23bc51a1d Remove unused code (#73023)
* Remove workspace FE code

* remove unused code

* fix

* fix

* fix

* fix

* fix

* fixes

* fix

* fix

* fix
2026-04-22 09:17:51 -04:00
Chris Truter
9afc3c63dc Search-model mapping clean up (#73032) 2026-04-22 13:12:09 +00:00
Nemanja Glumac
47fe343ba5 Pin follow-redirects to a patched version (#73033) 2026-04-22 15:56:27 +03:00
Nemanja Glumac
7322a3ff57 Fix the deprecated set-output (#72945) 2026-04-22 14:23:49 +02:00
YB
1e81f6f4ae GDGT-1552 feat: i18n workspace permission error msgs (#72152)
When checking workspace permissions via check-isolation-permissions, the error messages returned are plain English strings built with format. These are displayed to users in the admin UI when workspace permission checks fail, but they are not localized.

There's also other such untranslated strings in workspace methods that we update in this PR to use the proper i18n string types so they are translated to the user's locale.

Since this is different from format, we also convert %s placeholders to {0}, {1} following Java MessageFormat syntax.
2026-04-22 16:57:21 +08:00
Romeo Van Snick
12d2583cfa Remove unused metabase/utils/click-behavior module (#72975)
Remove unused module
2026-04-22 09:33:07 +02:00
Romeo Van Snick
cc30d8331c Clean up metabase/utils/i18n (#72887)
* Move setUserLocale to LocalProvider, it's only user

* Fix formatting
2026-04-22 08:00:34 +02:00
Phoomparin Mano
ee5d17282f Fix admins unable to browse shared collections in question picker (#72086)
* Fix admins unable to browse shared collections in question picker

The QuestionPicker fetched collections without a namespace parameter,
so the backend only returned default-namespace collections. Added
useCollectionsWithTenants hook that fetches shared tenant collections
and merges them into the collection tree with correct breadcrumb paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add e2e tests for browsing shared collections in question picker sidebar

Tests that admins can browse shared tenant collections, see correct
breadcrumbs, and add questions from shared collections when editing
a dashboard. Also verifies shared collections are hidden when tenants
are disabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Avoid mutating cached collection objects in useCollectionsWithTenants

Replace the for-loop that mutated child.parent on objects from the query
cache with an immutable .map() that creates new child objects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* merge related tests

* Add top-level Collections node so Our analytics and Shared collections are siblings

Instead of nesting shared tenant collections under Our analytics (confusing),
introduce a "Collections" parent that contains both as siblings. The picker
still defaults to Our analytics on open, but a breadcrumb lets users navigate
up to see both namespaces.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

merge tests

* fix: should always show collections in breadcrumbs

* chore: update imports

* chore: update e2e tests

* chore: add comment on hiding at synthetic root

* chore: formatting

* chore: define synthetic collection nodes from scratch instead of spreading root

- Remove `?? undefined` fallback for SHARED_TENANT_NAMESPACE (always a string)
- Rename `topLevel` to `syntheticTopLevel` to signal it's a fake node
- Define syntheticTopLevel and sharedSyntheticRoot from explicit properties
  instead of spreading rootCollection/sharedRoot, avoiding inheritance of
  semantically incorrect fields (can_write, entity_id, etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: rename filter param `s` to `pathId` for clarity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: clarify why tenant collections are re-parented from root to sharedSyntheticRoot

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: test useCollectionsWithTenants hook directly using real providers

- Add useCollectionsWithTenants describe block using renderHookWithProviders,
  setupCollectionTreeEndpoint, and setupEnterprisePlugins instead of module mocks
- Cover tenants-disabled, no shared collections, and merging cases
- Refactor setup() to return fixtures alongside collectionsById so tests can
  reference collection IDs by variable instead of magic numbers
- Rename generic result to collectionsById, cast to ExpandedCollectionNode
  to avoid as any

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* add comment on why we do not reprocess our analytics subcollections

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 03:25:51 +00:00
metamben
d45d28ecbb Implement the changes requested in #71771's review comments (#72024)
Make the changes requested in #71771's review comments

Also fix an unrelated typo.
2026-04-22 00:34:51 +00:00
github-automation-metabase
b79f7fe6e5 Auto-update documentation for master (#73000)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-21 15:29:11 -07:00
Edward Paget
0dfe41db2a Exclude netty from main uberjar; split 4.1/4.2 netty pins by driver (#72787)
The Athena JDBC driver uses AWS SDK's async Netty client and requires
Netty 4.1.x — it is not binary-compatible with 4.2.x. Previously the
main uberjar pinned netty-codec-http{,2} at 4.2.9 via AWS SDK s3's
transitive deps, creating a version conflict with Athena's internal
4.1.x expectations.

Main uberjar: drop the netty-codec-http{,2} pins and exclude
netty-nio-client from awssdk/s3 and /sts. Metabase only uses sync
S3Client (Apache HttpClient) and S3Presigner, so the async Netty
client is unused — removing it eliminates transitive Netty from the
main classpath entirely.

Per-driver netty pins at latest patched versions:
- athena: 4.1.132.Final (latest 4.1.x; binary-compat with driver)
- hive-like (databricks): 4.2.9 -> 4.2.12.Final
- bigquery-cloud-sdk: 4.2.7 -> 4.2.12.Final
2026-04-21 22:10:24 +00:00
Denis Berezin
0a6b688d09 Provide custom name for Metric Math expression (#72586)
* UXW-3294 provide custom names for expressions

* Polish

* Add e2e tests

* Fix extra bugs

* Refactor expression rename UX

* UX changes

* Fix e2e

* Review fixes

* Add unit tests

* Address name assign to new expression added in the middle
2026-04-21 21:34:12 +00:00
metamben
aae297a317 Skip cancelation on closed statements (#72477)
Closes #72428

When calling .cancel on a statement, the only condition when the JDBC spec says a non-SQLFeatureNotSupportedException
should be thrown is when it's already closed. This PR makes sure we don't try to cancel closed statements and logs any
generic exception at info level instead of warning, since the symptom is benign.
2026-04-21 22:53:05 +03:00
Nathan Voxland
180bf74cda Stale appdb index drop improvements (#72910) 2026-04-21 14:34:54 -05:00
Aleksandr Lesnenko
dcbe8d30e0 fix metadata import oom (#72930) 2026-04-21 15:29:55 -04:00
metamben
74885b3416 Update and pin netty-codec dependencies (#72800)
* Update netty-codec dependencies

Closes QUE2-519
Closes QUE2-480

* Don't check for table existence if dataset-id or table-id are blank

* Remove duplicate NFC path in the bigquery driver
2026-04-21 21:46:41 +03:00
Maksym Yakubych
4af8ee4173 fix: enabling metabase ai requires multiple clicks (#72974) 2026-04-21 17:28:18 +00:00
Ryan Laurie
1fd62fa698 Convert formatting colors test to ts (#71425)
formatting colors test to ts
2026-04-21 11:00:28 -06:00
Ryan Laurie
9e349ef44f Convert browse tables to ts (#72810)
convert browse tables to ts
2026-04-21 11:00:02 -06:00
Andrei Chernykh
d8f44c281a Fix darker background behind disabled source input in table overview (#72915)
Fix darker background behind disabled source input in table overview sidebar
2026-04-21 09:24:04 -07:00
Andrei Chernykh
ae0385b198 Rename remaining 'library' reference to 'synced collection' (#72821)
Rename remaining 'library' reference to 'synced collection' in AllChangesView
2026-04-21 09:23:37 -07:00
github-automation-metabase
42ec9be03e Pin dorny/paths-filter to SHA (#72961)
ci: pin dorny/paths-filter to SHA in serialization.yml

Replace mutable tag @v3.0.0 with the full commit SHA
@0bc4621a3135347011ad047f9ecf449bf72ce2bd, matching the four other
workflows that already pin this action.
2026-04-21 15:46:14 +02:00
Nicola Mometto
6b879aad94 don't consider advisories active on dev (#72943)
* don't consider advisories active on dev

* update test
2026-04-21 14:45:23 +01:00
Sébastien
6d1b6b5da8 Copy and link update for security center banner (#72935) 2026-04-21 15:24:49 +02:00
Noah Moss
892d4468ef Fix issues with Codex MCP search + query pagination/row limit semantics (#72741)
* try to fix issues with mcp + codex

* tweaks, better error message

* cleanup

* simplify

* remove unnecessary whitespace change

* make sure we're using lib functions

* cleanups, new test

* switch to using lib/prepare-for-serialization

* use lib helpers for query serialization; drop extract-total-limit

Replace the JSON round-trip with lib/prepare-for-serialization, the
canonical lib helper for dropping :lib/metadata and other internal-use
keys before serializing. Collapse extract-total-limit into a pure
total-row-limit helper: stripping the user's :limit from the query was
never required for correctness, since remaining-page-rows already clamps
:page.items to the total budget each page.

* basic validation of continuation limit/page
2026-04-21 09:13:08 -04:00
Nemanja Glumac
f5e26c5cf8 Dial Cypress internal retries back to one (#72947) 2026-04-21 14:06:20 +02:00
Nemanja Glumac
f2a786237a Include the repo name in Slack alerts (#72941) 2026-04-21 14:02:08 +02:00
Nemanja Glumac
f0ccc0ab5a Fix broken e2e tests after the Mantine upgrade (#72944) 2026-04-21 13:14:12 +02:00
Nick Fitzpatrick
873ddd1bff Show Python Library even when we have 0 transforms (#72638) 2026-04-21 16:42:31 +05:30
Nemanja Glumac
7d1440502d Improve the resilience of data model (bulk editing) e2e tests (#72913) 2026-04-21 11:00:01 +02:00
Tyler Plude
5faf927109 Semantic search performance improvements (#72834)
* can-read fast path for model/Card

* paginate before hydration; unify can_write across models

* dedupe permission checks by collection_id

* use legacy_input instead of metadata->legacy_input

* fix unit; drop debug log

* pr feedback - drop inner timers; make t2 stub fast path safer

* fix kondo / unit
2026-04-20 18:26:20 -07:00
Maksym Yakubych
48d8fd49ff fix: pricing ui should reflect 1m free tokens (#72895) 2026-04-20 23:21:34 +00:00
Maksym Yakubych
7c1c238df0 fix: handle is-locked for metabase AI provider (#72646) 2026-04-20 21:37:17 +00:00
github-automation-metabase
5f5de3cb85 Auto-update documentation for master (#72919)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-20 14:28:41 -07:00
Anderson
c23fe2298a Fix fomatting in entities.ts (#72911) 2026-04-20 20:22:14 +00:00
bryan
431372205d Fix dev-time sqlglot installs for corrupted partial installs (#72654) 2026-04-20 20:08:56 +00:00
Anderson
17465e9c8d Convert metabase/utils files to TypeScript (#72314)
* Convert metabase/utils files to TypeScript

* More clean up / Resolve after-conflict problems

* fix unit test

* fix e2e tests

* Fix imports

* Avoid casting and other improvements

* Fix undefined function call

* Fix some unit tests

* More ts clean-up

* Fix reducer initialization

* A little bit more of improvements
2026-04-20 16:07:56 -03:00
Anderson
c36d20da51 Create pseudo locale to reduce E2E reliance on literal translated strings (#72328)
* Create pseudo locale to reduce E2E reliance on literal translated strings

closes UXW-3460: Reduce E2E reliance on literal translated strings

* Fix setup e2e tests

* Fix a few e2e tests + clean up

* Add missing data-testid

* Always generate en-ZZ pseudo-locale, hide from UI at runtime

* Clean up some comment blocks

* Add doc content
2026-04-20 15:20:42 -03:00
appleby
12665c051c BOT-1354 Bump retention of ai_usage_log data from 3 to 6 months (#72826)
We'd like to use the ai_usage_log table as our source for token usage analytics so that billing, ai usage controls,
and ai usage analytics are all pulling from the same table. Update the ai-usage-trimmer to retain 6 months of data so
we have more historical data for usage analytics. We can adjust the retention later if it turns out we need more or
less data.
2026-04-20 10:40:41 -07:00
Nemanja Glumac
eb80f4b139 Fix admin > people pagination race conditions (#72888) 2026-04-20 17:08:40 +00:00
Nick Fitzpatrick
9b4ddd2460 Updating to Mantine 8.3.18 (#72710)
* Updating to Mantine 8.3.18

* adjusting e2e

* green ci?

* fix stray failing test
2026-04-20 22:30:29 +05:30
Jeff Bruemmer
29c59aa988 docs - note about z-index (#72892)
note about z-index
2026-04-20 09:34:48 -07:00
Jeff Bruemmer
fbe9aa6380 docs - update docs workflow to 60 (#72882)
update docs workflow to 60
2026-04-20 18:19:06 +02:00
Braden Shepherdson
9d9280142f [devex] Add :test/pre and :test/post conditions to mu/defn (#72382)
These work like Clojure's built-in `:pre` and `:post` conditions, but
are only enabled in non-prod modes on both CLJ and CLJS.

Core `:pre` and `:post` are treated as assertions. Our shadow-cljs
configuration drops assertions in release mode, but Metabase on the
JVM runs with assertions enabled even in prod, since they're used to
check some important conditions.

That's weird and might change, but in the meantime `:test/pre` and
`:test/post for now give us "weightless", test-like pre- and
postconditions.
2026-04-20 14:15:46 +00:00
Alex Yarosh
33d9cfb809 docs: data studio updates (#72641)
* data studio updates

* Optimized 9 images, saved 652.1 KB (47.2% smaller)

* links

* link

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* Update docs/data-studio/dependencies/replace-data-sources.md

* typo

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-04-20 13:37:16 +00:00
github-automation-metabase
5a0ecdede4 Auto-update documentation for master (#72812)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-20 06:25:25 -07:00
Nicola Mometto
608cc81d21 GDGT-2310: use db-routing's own db-routing-enabled? (#72865)
* GDGT-2310: use db-routing's own db-routing-enabled?

* chore: test

* chore: remove import

* fix: test on ee enabled

* update test
2026-04-20 14:12:30 +01:00
Kevin Kelleher
6637fff38f Fix duplicated dimensions in Metrics Viewer tab picker and incorrect 'Multiple dimensions' label (#72637) 2026-04-20 08:46:36 -04:00
Nemanja Glumac
b3e64a7ccd Fix chart drill broken e2e test (#72864) 2026-04-20 12:11:30 +02:00
Nemanja Glumac
de2a5af435 Delete e2e reproduction for #57132 (#72862) 2026-04-20 11:49:40 +02:00
Timofey Kachalov
95c2dd47ec Update SDK version in package.template.json to 0.61.0-alpha (#72861) 2026-04-20 13:39:00 +04:00
Fraser
73e58e2135 Move api to shared (#72856) 2026-04-20 21:36:07 +12:00
metabase-bot[bot]
0141d69411 [chore] Update E2E test timings with recent data (#72835)
Update E2E test timings with averaged data

Collected timing data from 7 days of test runs.
All specs with timing data were included (simple averages).

🤖 Generated automatically by update-e2e-timings workflow

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Actions <actions@github.com>
2026-04-20 11:30:00 +02:00
Fraser
0f03828392 Simplify click actions (#72849) 2026-04-20 20:36:00 +12:00
Fraser
700315244f Move Text viz into dashboard (#72848) 2026-04-20 20:35:47 +12:00
Fraser
f0b01b187b Move card slowness into viz types (#72846) 2026-04-20 20:35:33 +12:00
Fraser
cbe82473d6 Pass qb into viz via props (#72844) 2026-04-20 20:35:21 +12:00
Nick Fitzpatrick
f7ead91137 Reduce bleeding-edge token usage (#72613)
* comment out all occurances of bleeding edge to see which ones are actually needed

* default to pro-self-hosted

* enable transforms where needed

* final adjustments?
2026-04-20 14:05:10 +05:30
Ngoc Khuat
655825076b Guard transform runner from being poisoned by unrunable transforms (#72149) 2026-04-20 08:21:33 +00:00
Aleksandr Lesnenko
0c493fd36e Prettier -> oxfmt (#70825)
* oxfmt config

* run oxfmt
2026-04-19 19:22:04 -04:00
appleby
0f819c0a9b BOT-1285 Restrict metabase managed AI to anthropic and claude-sonnet-4-6 (#72324) 2026-04-19 13:23:38 -07:00
Oleksandr Yakushev
fc7812a0e6 [match] Add :and syntax to match-lite (#70010) 2026-04-19 23:00:23 +03:00
Alexander Polyankin
82e868df4c Clarify permissions for fetching database metadata (#72837)
* small api fixes

* fix
2026-04-19 13:40:44 -04:00
Alexander Polyankin
be849d8ba6 Cleanup /api/database/metadata (#72831)
simplify
2026-04-18 19:33:00 -04:00
appleby
da05483604 BOT-1321 BOT-1230 Switch to AiUsageLog for token usage reporting in metabot-stats (#72662)
* BOT-1321 BOT-1230 Switch to AiUsageLog for token usage reporting in metabot-stats

The AiUsageLog table includes usage not only for chat conversations, but non-chat LLM usage like the example question
generator (a.k.a suggested prompts), so use it for reporting metabot-stats metering values to harbormaster.

This also has the advantage that we use the same table both for billing and ai usage limits enforcement.

* Add some test of example question generator token usage in metabot-stats

* Move metabot-stats tests into EE
2026-04-18 03:19:32 +00:00
Maksym Yakubych
c6babc336d fix: remove -tiered add-on on disconnect (#72807) 2026-04-18 01:58:54 +00:00
Aleksandr Lesnenko
3379b1be9f model to transform e2e specs (#72589) 2026-04-17 21:24:17 -04:00
appleby
ddc4e3604b BOT-1321 Add ai_usage_log migration to v60 (#72651)
* BOT-1321 Backport ai_usage_log migration to v60

* Add ai_proxied column to ai_usage_log table
2026-04-17 17:33:59 -07:00
Maksym Yakubych
1a5ba0a0a4 fix: cancel managed ai add-on when disconnecting (#72782) 2026-04-17 22:12:30 +00:00
appleby
fa3e7d34d4 Fix broken link fragment in guest-embedding.md (#72815)
lychee docs --config ./.lychee/config.toml --offline
Issues found in 1 input. Find details below.

[docs/embedding/guest-embedding.md]:
   [ERROR] /metabase/docs/embedding/guest-embedding.md#to-turn-off-a-locked-parameter-pass-an-empty-array-as-its-value | Cannot find fragment: Fragment not found in document. Check if fragment exists or page structure
2026-04-17 22:12:04 +00:00
Andrei Chernykh
16dd3b3fad Add spacing above buttons in new dashboard modal (#72755)
* Add spacing above buttons in New Dashboard modal (UXW-3739)

* Clean up CreateDashboardModal tests
2026-04-17 13:15:39 -07:00
Jeff Bruemmer
6586ca393e Docs locked params (#72657) 2026-04-17 20:11:12 +00:00
Jeff Bruemmer
272bcd4abc docs - MCP settings update (#72744) 2026-04-17 12:51:27 -07:00
Jeff Bruemmer
ac3d0d0c59 docs - various edits (#72185) 2026-04-17 12:50:53 -07:00
Alexander Polyankin
996d6b2140 Fix db filter in metadata endpoint (#72791)
* fix db filter

* fix filter order
2026-04-17 15:30:21 -04:00
Tyler Plude
119889d16f BOT - Debug opt-in helper for agent request & response JSON (#72780)
dump agent req to json
2026-04-17 11:47:55 -07:00
Anderson
6539732003 Show the MCP URL in the admin panel (#72737)
* Show the MCP URL in the admin panel

* Use CopyTextInput component
2026-04-17 15:39:56 -03:00
Robert Kosara
7dbdbe100c Docs: Fix dependency graph image (#72789)
Fix dependency graph image
2026-04-17 11:01:14 -07:00
Maksym Yakubych
bda6c83a12 fix: token stats (#72777) 2026-04-17 16:36:03 +00:00
Rodrigo López Dato
d3850dc628 Hide process UUID in log viewer if there is only one process (#67152) 2026-04-17 13:19:09 -03:00
Uladzimir Havenchyk
315d513f3f chore: cleanup resolutions (#72030) 2026-04-17 18:57:50 +03:00
Sloan Sparger
cb67a8ff47 [Metabot] Remove indicator for events (#71788)
* remove indicator for events

* fix unit tests
2026-04-17 15:50:55 +00:00
Nemanja Glumac
c16ec07edc Update the Sample Database by shifting CREATED_AT by three years (#72701)
* Shift CREATED_AT by three years

* Update tests

* Fix e2e group 8

* Fix e2e group 11

* Fix e2e group 12

* Fix e2e group 14

* Fix e2e group 47

* Fix e2e group 38

* Fix e2e group 32

* Fix e2e group 35

* Fix e2e group 36

* Fix e2e group 44

* Fix e2e group 42

* Fix repro 15331

* Fix e2e group 5

* Fix moar tests

* Fix more tests

* Moooooar

* Fix the timeline test

* Fix "extract shorcuts" (group 26)

* Fix extract actions (group 22)

* Fix e2e group 19

* Attempt to fix embedding tests
2026-04-17 08:53:33 -06:00
metamben
1e17223288 Clamp :max-results-bare-rows with pivot-limit if it's set (#72369)
Closes #72157
2026-04-17 17:37:49 +03:00
dpsutton
a497238a75 Improve checker (#72747)
* correctly analyze native queries

need to include aliases in column names. consider `select f.name as
field_name, t.name as table_name ...`. The columns referenced are both
`name`, but because of the aliases (field_name, and table_name) they are
disambiguated. The bug is we were using name and not the selected name

* fix sql wrapper issues:

```
 -- PASS before fix, PASS after
  SELECT datum FROM (SELECT 1 AS datum) DQ

  -- FAIL before fix (false positive: missing-column "datum"), PASS after
  SELECT datum AS b FROM (SELECT 1 AS datum) DQ

  -- FAIL before fix (same bug, multiple references), PASS after
  SELECT datum AS b, datum - 1 AS d FROM (SELECT 1 AS datum) DQ

  -- Real-world case: dim_dates transform had 1 false positive error, now 0
  SELECT TO_CHAR(datum, 'yyyymmdd')::INT AS date_dim_id,
         datum AS date_actual,
         EXTRACT(EPOCH FROM datum) AS epoch,
         datum - DATE_TRUNC('quarter', datum)::DATE + 1 AS day_of_quarter
  FROM (SELECT '2000-01-01'::DATE + SEQUENCE.DAY AS datum
        FROM GENERATE_SERIES(0, 20000) AS SEQUENCE (DAY)) DQ

  -- Real-world case: self_hosted_daily_stats had 47 false positive errors, now 0
  -- (CTE-defined columns like latest_month_above_goal_alerts were flagged
  --  as missing on the source table)
  WITH stats AS (
    SELECT ..., SUM(CASE WHEN x THEN v END) AS latest_month_alerts, ...
    FROM source_table
  )
  SELECT ls.latest_month_alerts  -- was falsely flagged as missing
  FROM stats ls
```

issues propagating out from ctes where there was an alias involved

* skip validation for queries with table-type template tags

Queries like `SELECT * FROM {{table_one}}` where `table_one` is a
table-type template tag can't be meaningfully validated — the actual
table (and therefore its columns) is dynamic and unknown at check time.

`native-result-metadata` already skipped these, but `validate-native-query`
did not, causing a syntax error when the unresolved table-id path vector
(e.g. `["DB" "public" "my_table"]`) was substituted into the SQL as a
placeholder name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* move function before declaration

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 09:22:56 -05:00
Filipe Silva
9571b2b4c3 fix: fix transform run metering aggregation (#72632)
* fix: fix transform run metering aggregation

Part of https://linear.app/metabase/issue/CLO-5307/investigate-double-billing-on-basic-advanced-plan-change

* fix: comment about transforms metering context
2026-04-17 14:00:53 +01:00
Mihael Konjević
b966e69c6f Metabot mbql improvements (#71524)
* Add agent-lib module for structured MBQL program repair, validation, and evaluation

Introduces the `agent-lib` module (team: Metabot), which provides the
core pipeline for processing structured MBQL programs produced by the AI
agent: deterministic repair/normalization of malformed operator forms,
schema validation, and safe evaluation against a trusted runtime.

Key components:
  - repair: idempotent normalization of operator forms, stage boundaries,
    and top-level operations
  - validate: schema and semantic validation with structured error reporting
  - eval: sandboxed evaluation of validated programs against lib/MBQL
  - runtime: trusted bindings exposing lib query-introspection helpers
  - capabilities/catalog: enumerates available operators, sources, and
    field types for the agent
  - mbql_integration: bridges agent-lib's structured programs to lib's
    MBQL query building API

The module is registered in clj-kondo config with `:uses #{lib util}`
and as a `:friend` of `lib` (for two internal functions not yet in the
public API: `lib.expression/maybe-resolve-expression` and
`lib.ref/expression-ref-name`).

* Wire up agent-lib for MBQL construction, remove synthetic field IDs

Replace the old construct_notebook_query approach (3 query types, synthetic
field IDs like `t42-3`, rigid filter schemas) with agent-lib's unified
program format (`{source, operations}`). Programs use real database field
IDs and support richer capabilities: expressions, joins, post-aggregation
filtering via `append-stage`, and custom calculations.

Key changes:

- construct.clj: rewritten to call `agent-lib/evaluate-program` instead of
  the old filters.clj pipeline. Tool now accepts `{program: {source, operations}}`
  instead of `{query: {query_type, source, filters, ...}}`

- agent-api construct-query endpoint: translates the existing REST request
  format into programs and evaluates via agent-lib

- Entity details, field stats, table utils: all switched from synthetic
  index-based field IDs (`t42-3`) to real database field IDs (integers)

- filters.clj: deleted — no longer needed by any caller

- Prompts: construct_notebook_query.md completely rewritten with program
  format documentation, operations reference, and usage patterns

- System prompts: removed outdated limitation references (no joins, no
  field calculations)

* Refine construct_notebook_query tool schema and fix metric breakouts

Tool schema now uses the source_entity/context-ref pattern: `source_entity`
at the top level selects the primary data source, while the program always
uses `{"type": "context", "ref": "source"}`. This separates entity selection
from program structure and enables `referenced_entities` for additional
context.

Strip default metric breakouts when the agent provides explicit ones.
Metrics include a default time dimension breakout in their query definition.
When the program contains explicit breakout operations, these now replace
(not augment) the metric defaults via `lib/remove-all-breakouts`.

Include `display` in question URL hash so the frontend renders the correct
visualization type (line, bar, pie, etc.) instead of defaulting to table.

Rewrite construct_notebook_query.md prompt with comprehensive rules:
context-ref pattern, single-transform-per-operation rule, temporal
extraction helpers, multi-value filter guidance, breakout ordering rules,
grouped ratio examples, join retry guidance, and field projection rules.

* Fix evaluation context and time-interval keyword coercion

Evaluation context improvements:
- Map "model" source type to "dataset" (matching agent-lib's
  context-allowed-ids which checks for "card" or "dataset")
- Compute surrounding tables for all source types, not just tables,
  enabling join context for models/questions/metrics
- Compute and pass measure-ids to the evaluation context, enabling
  measure ID validation in agent-lib's operator validator

Keywordize time-interval amount values ("current", "last", "next")
before passing to lib/filter, which expects keyword enum values
(:current, :last, :next). Integer amounts pass through unchanged.

* Fix tests

* Fix Eastwood warnings, update tests for real field IDs and new tool schema

* Fix cljfmt indentation in entity_details.clj

* Temp fix for transform table issues

* Build Uberjar on push to branch

* Tighten metabot prompt guidance for MBQL queries

- Add MBQL intro sentence to the construct_notebook_query tool prompt
- Drop references to the retired metabot query DSL and the redundant
  `relative-date` alias
- Correct the permission-error join guidance: implicit refs hit the same
  permission check, so surface the error instead of retrying
- Add a heuristic for choosing pie-chart vs calculated-percentage expressions
  in the percentage_question example
- Update the agent_api reference to describe field/segment/measure refs
  using the actual request-body keys instead of internal MBQL forms

* Use shared ::field-id schema in agent_api

Defines `::field-id` reusing `::lib.schema.id/field` (pos-int? with API
decoding) and a string branch for expression/aggregation aliases, then
replaces the repeated `[:or :int :string]` field-id shapes across request
and response schemas with that single alias.

Also tightens the `body->program` helper by removing blank lines inside
the `let`/`cond->` per the no-blank-lines-in-def-forms style rule and
fixes the `construct-query*` docstring to say MBQL 5 rather than pMBQL.

* Use vars for agent-lib capability bindings

Converts every catalog `:binding lib/foo` to `:binding #'lib/foo` so that
REPL inspection of a capability entry shows the var name instead of an
opaque function object.

Also aligns all seven catalog files on the standard `(def capabilities
"docstring" …)` form instead of the `^{:doc …}` metadata shape, adds a
namespace docstring to `catalog/aggregation.clj` explaining that the
catalog is the runtime source of truth while `construct_notebook_query.md`
is hand-curated prompt guidance, and brings the join / top-level prompt
notes in line with the corrected permission-error fallback guidance.

* Document the unsupported/repair split in agent-lib

Adds a namespace docstring to `capabilities.unsupported` explaining that
this catalog is prompt-time guidance distinct from the runtime repair
layer: some entries (temporal extractions, `count-if`, `variance`, etc.)
are silently rewritten before validation so the agent never sees an
error, while others (`avg-where`) have no safe mechanical rewrite and
only surface as retry guidance when the model gets it wrong.

* Simplify agent-lib coercion helpers

Rewrites `quarter-label->number` as a plain `case` dispatch over the
trimmed, upper-cased input instead of threading through a regex, and
makes `direction-string?` actually return a boolean so it matches its
`?`-suffixed name contract. Adjusts the direction-string test to assert
`true?`/`false?` instead of `some?`/`nil?`.

* Derive agent-lib aggregation operator names from lib hierarchy

Replaces the hard-coded set of aggregation helper names in
`literals/aggregation-operator-names` with a derivation from
`(descendants @lib.hierarchy/hierarchy ::aggregation-clause-tag)` so new
aggregation clauses added to the lib schema are picked up automatically.
Filters out `:aggregation` (the abstract wrapper tag) and `:offset`
(a windowed aggregation that also appears as an expression — keeping it
out of this set preserves the existing `aggregation-form?` semantics so
the repair layer still defers offset-inside-expression to lib.core).

* Inline private structured-helper-symbols in agent-lib syntax

`structured-helper-symbols` was a private single-use alias for
`capabilities/helper-symbols`. Inlining it at its one call site in
`recognized-operator-symbols` removes the indirection without changing
behavior. `top-level-operation-symbols` is left as-is because it is
consumed by other namespaces.

* Increase max allowed count for modules triggering drivers tests

* Make agent-lib operator repair idempotent for degenerate args

Several binary/ternary nested operators (`in`, `not-in`, `=`, `!=`, `is`,
`is-not`, `between`, `with-temporal-bucket`, `percentile`,
`with-join-conditions`) used to unconditionally destructure their args
and rebuild the form at a fixed arity, so a degenerate input like
`["in"]` would be padded to `["in" nil nil]`. When that same operator
was embedded inside `case`, the case normalizer would then classify the
3-element form as a `[pred then else]` triple and rewrap it into a
branches vector on the next pass — adding another `nil` each time
`repair-program` was called.

The `repair-is-idempotent` fuzz property caught this intermittently in
CI (~20% flake rate); a 10k-iteration local run now passes cleanly.
Each affected operator now returns its args unchanged when it does not
have enough of them to form a canonical shape, leaving rejection to
downstream validation instead of synthesizing a malformed canonical
form. A deterministic regression test pins the `["case" [op]]` case
for every affected operator.

* Scope agent-lib runtime metadata to entities the program references

Walk the validated program for referenced table/field/card/metric/measure
ids and load only those tables (and their fields) into the runtime, instead
of materializing every table in the database on each evaluate-program call.
Field/card/measure ids are resolved via lib.metadata/bulk-metadata so each
class collapses to a single SQL query regardless of count.

* Change how docstrings are attached to defs

* Drop unused agent-lib unsupported-rewrite-catalog

* Expose agent-lib programs directly in agent_api v2 endpoints

Replace the legacy POST /v1/construct-query and /v1/query endpoints —
which spoke a flat filters/aggregations/group_by body shape that an
in-process adapter translated into an agent-lib program — with new
POST /v2/construct-query and /v2/query endpoints whose request body is
the agent-lib program itself. The flat shape only ever exposed the
lowest-common-denominator subset of agent-lib's capabilities; programs
unlock joins, expressions, multi-stage queries, and the full operator
catalog for any HTTP or MCP client. Repair and validation continue to
happen inside agent-lib, so the loose-schema benefit of accepting raw
programs is free.

The request schema reuses metabase.agent-lib.schema/program-schema
(now re-exported from metabase.agent-lib.core), with a thin restriction
that the top-level source must be table/card/dataset/metric — context
and nested program sources are reserved for in-process callers. The
new endpoints derive the source-entity for evaluation from the program
itself via a new program-source->source-entity helper on
metabase.metabot.tools.construct, leaving execute-program's signature
unchanged so the slackbot caller is untouched. MCP picks up the new
shape automatically because its tool manifest is generated from
defendpoint metadata.

* Keep agent_api v2 program request schema MCP-friendly

The :and+:fn wrapper around agent-lib/program-schema confused the MCP
tools manifest's flatten-root-schema pass — it couldn't extract a clean
object root for construct_query and query, so their inputSchemas lost
their "type": "object" field and the tools became unusable through MCP.

Drop the :fn predicate from ::program-request and instead enforce the
top-level source-type restriction imperatively inside
evaluate-program-for-execution. The schema is now just program-schema,
which the manifest flattens cleanly into a single object. Update the
MCP execute-query test to send the v2 program shape.

* Make agent responses less chatty during work

Strip step-by-step narration from system prompts so the agent works
silently through tool calls, then presents results with a brief
query-translation sentence explaining how the question mapped to the query.

* Clean up prompt contradictions, redundancy, and misplaced guidance

- Fix identity/tone contradictions with brevity style ("conversational" →
  "concise, action-oriented", "expert data analyst" → "query builder")
- Remove banned narration pattern from example text
- Remove redundant "Data Source Types" section from NLQ prompt
- Scope proactive helpfulness to ambiguous/partial results only
- Move percentage question guidance from <why> blocks in examples to
  proper query construction sections in both prompts
- Fix chatty entity linking examples in NLQ prompt

* Fix tests

* Fix modules config

* Restore base_type and related field metadata in tool result columns

Keep the real-field-ID redesign and new :type enum, but restore
:base_type, :effective_type, :semantic_type (qualified), and
:coercion_strategy.

Also update construct-notebook-query-tool test to use the new
{:source_entity, :program} schema after the rebase.

* Remove model/Transform cleanup hotfix from database.clj
2026-04-17 14:04:24 +02:00
Nick Fitzpatrick
c2a0e3615d Uxw 3765 goal line on waterfall chart (#72650)
* feat: Add goal line to waterfall chart

* adjusting e2e test

* add storybook test for static viz

* adding loki screenshot, finxing e2e

---------

Co-authored-by: Brad W <bradleo99@gmail.com>
2026-04-17 10:12:09 +05:30
Nick Fitzpatrick
24d7e73495 update copy in Enable Transforms Page based on plan (#72625) 2026-04-17 10:11:39 +05:30
Aleksandr Lesnenko
3564f8662e import json metadata (#72729) 2026-04-17 01:07:47 +00:00
Kevin Kelleher
00497ab9f4 Fix Metrics Viewer "No compatible dimensions" error (#72731) 2026-04-16 19:21:33 -04:00
Andrei Chernykh
3619ec2e85 Added a new team member (#72647) 2026-04-16 15:34:10 -07:00
dpsutton
bcec823dba Better checker output (#72734)
* human and json output

* drop --format flag and JSON output

Removed for now — the human format is what we actually want, and we'll
add structured output (JSON or otherwise) when there's a real consumer
asking for it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* remove classloader/plugins calls

These belong to the driver-loading PR (#72732), not this output-formatting
branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:10:58 -05:00
Rodrigo López Dato
10972d2b35 Preserve original cause in serdes load errors when app DB tx is aborted (#72725)
* Preserve original cause in serdes load errors when app DB tx is aborted

path-error-data does a SELECT via serdes/load-find-local to populate
:local-id on the error map. When it is called from the catch block in
load-one!, the outer load transaction has often already been poisoned by
the real failure, so that SELECT throws "current transaction is aborted,
commands ignored until end of transaction block" and prevents ex-info
from being constructed. The original cause is lost and support engineers
see the aborted-tx symptom instead of the actual SQL error.

Wrap the lookup so a failure leaves :local-id nil and still lets us
chain the real cause.

* Update enterprise/backend/src/metabase_enterprise/serialization/v2/load.clj

Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>

* Don't run starting from empty app DB

---------

Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
2026-04-16 20:24:25 +00:00
dpsutton
e2fa24df94 plugins loaded for checker (#72732) 2026-04-16 15:00:24 -05:00
Maksym Yakubych
d495e0391c fix: do not disable agent api and mcp when not configured (#72730) 2026-04-16 19:56:18 +00:00
Maksym Yakubych
a734b119aa fix: allow use of legacy metabase AI (#72568) 2026-04-16 19:55:56 +00:00
dpsutton
74f715fa72 Concise schema source (#72667)
* concise format

shaves about a minutes off!

before:

```
clj -M:dev:drivers:ee -m metabase.core.bootstrap --mode checker --export ~/projects/work/stats-remote-sync --schema-dir /tmp/metadata/metadata/databases

took 1m55s
```

now with new format:

```
clj -M:dev:drivers:ee -m metabase.core.bootstrap --mode checker --export ~/projects/work/stats-remote-sync --schema-dir /Users/dan/projects/work/exports-root/metadata/new-format

took 58s
```

new format is one JSON file per database, loaded entirely into memory:

```javascript
{
  "name": "Sample Database",
  "engine": "h2",
  "tables": [
    {
      "name": "PRODUCTS",
      "display_name": "Products",
      "database_schema": "PUBLIC",
      "fields": [
        {"name": "ID", "base_type": "type/BigInteger", "semantic_type": "type/PK", "database_type": "BIGINT"},
        ...
      ]
    },
    ...
  ]
}
```

The CLI auto-detects the format: if --schema-dir contains .json files,
it uses concise; otherwise falls back to the serdes YAML format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* document SchemaSource and AssetsSource contracts

Each resolve-* method now documents the exact map shape it should return,
keys marked as required vs optional. The ns docstring lays out the broader
contract: sources return snake_case maps matching the serialized form,
provider.clj does all conversion to lib metadata format (kebab-case,
keywords, defaults, ID resolution, MBQL normalization).

Documenting this now (alongside adding the concise format) so future source
authors — REST API, git, LLM, app DB via toucan2 — know exactly what shape
to produce.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* concise format: single file with three flat lists

The concise schema format is now a single JSON file with three flat lists
keyed by integer IDs:

  {"databases": [{"id": 1, "name": "Sample Database", "engine": "h2"}, ...],
   "tables":    [{"id": 3, "db_id": 1, "name": "PRODUCTS", "schema": "PUBLIC"}, ...],
   "fields":    [{"id": 12, "table_id": 3, "name": "ID",
                  "base_type": "type/BigInteger", "semantic_type": "type/PK",
                  "database_type": "BIGINT"}, ...]}

At load time we join tables.db_id → databases.id and fields.table_id →
tables.id to build path-keyed indexes, so the rest of the checker still
sees portable references like [db-name schema table].

Format selection is now explicit via --schema-format (serdes|concise)
rather than guessed. Default remains serdes for backwards compatibility.

  java -jar metabase.jar --mode checker \
    --export /path/to/export \
    --schema-dir /path/to/metadata.json \
    --schema-format concise

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* loudly error when --schema-dir doesn't match --schema-format

Pointing --schema-format serdes (the default) at a single JSON file used
to silently produce a schema source with zero databases, which then
showed up as a flood of unresolved-reference errors. Same in reverse for
concise format pointed at a directory.

Now the CLI checks the path shape against the format and exits with a
helpful error suggesting the right --schema-format value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* rename format/concise to format/concise-schema

Mirrors the serdes-schema vs serdes-assets split — the namespace name now
makes clear which side of the protocol divide it implements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* use cheshire instead of clojure.data.json

clojure.data.json is available at dev time but not bundled into the
production jar, causing a FileNotFoundException when running the checker
from a built artifact. cheshire is a hard dep already on the classpath.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* use metabase.util.json instead of cheshire directly

Project convention is to go through metabase.util.json, which wraps
cheshire and adds custom encoders/decoders. Stream-decode via reader
to avoid slurping the whole file into a string before parsing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:08:45 -05:00
Ben Grabow
a0d8cc309d Ghy 3359/embedded postgres dev/test driver (#72635)
* Add embedded-postgres dep

* Basic integrant lifecycle wrapper around embedded pg
2026-04-16 12:29:48 -05:00
Edward Paget
6f03c07e98 Bump Bouncy Castle to 1.84 (#72712)
Updates the top-level pin from 1.82 to 1.84 and adds matching pins to
the Snowflake and Databricks driver deps.edn files so the bundled
driver JARs pick up the patched version during security scans.
2026-04-16 12:23:29 -05:00
Ben Grabow
ee847893ff Ghy 3370/remote sync phantom diffs (#72495)
* Use natural sort order for all nested model types.

* Update serialization baseline with new expected sort order
2026-04-16 11:37:38 -05:00
Edward Paget
737056cb93 Switch Athena driver from uber-jar to thin jar with pinned deps (#72539)
* Downgrade all netty pins to 4.2.9.Final

Netty 4.2.12 introduced a breaking change in HttpObjectEncoder that
causes AWS SDK netty-nio-client to fail with "unexpected message type:
LastHttpContent" errors. Revert to 4.2.9.Final which is compatible
with the AWS SDK while still being in the 4.2.x line.

Closes: #72532

* Switch Athena driver from uber-jar to thin jar with pinned deps

Use athena-jdbc-thin instead of the bundled uber-jar so transitive
deps are visible to security scanners. Pin netty at 4.2.12.Final to
match root deps.edn. Add athena-streaming from our private maven
repo (not available on Maven Central).
2026-04-16 11:32:55 -05:00
Edward Paget
a5a132c663 Publish both uber-jar and thin jar for Athena JDBC driver (#72480)
Update athena.bb to download and publish both the existing
with-dependencies uber-jar (athena-jdbc) and the lean thin jar
(athena-jdbc-thin) from AWS's Athena downloads bucket. The thin
jar enables pinning transitive deps for security scanning.
2026-04-16 11:16:18 -05:00
Aleksandr Lesnenko
bf1ddbe794 data source replacement e2e specs (#72409)
* data source replacement tests

* source replacement e2e sql (#72636)
2026-04-16 11:10:10 -04:00
Braden Shepherdson
57271be747 Fix a new broken test that snuck in while Snowflake was quarantined (#72703)
Fixes DEV-1818.
2026-04-16 15:04:11 +00:00
Alexander Polyankin
6a31828228 /api/database/metadata endpoint (#72683) 2026-04-16 10:44:17 -04:00
Braden Shepherdson
1437d04c8d [Viz] Row charts pass raw values to drill-thru, not formatted strings (#72659)
Fixes QUE2-97.

Row charts were passing formatting strings like "(empty)" as the `value`
in the drill-thru context map, rather than the proper value `null`. That
breaks at least the `underlying-records` drill and perhaps others too,
when the formatted string is different from the value's usual
representation.
2026-04-16 10:35:22 -04:00
Kevin Kelleher
fd2a3e0705 Metrics Viewer design improvements (#72252) 2026-04-16 10:07:57 -04:00
dpsutton
113ee31093 Split source (#72666)
* rename serdes to serdes assets in prep of split

* split serdes source into serdes-schema and serdes-assets

The previous commit renamed serdes to serdes-assets. This commit
extracts the schema (database/table/field) resolution into its own
serdes-schema namespace and source type (SerdesSchemaSource), keeping
assets (cards, dashboards, segments, etc.) in serdes-assets.

This is a no-op refactor — the checker behaves identically. The split
prepares for a new schema format that uses a single file instead of
the 500k-file directory tree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* kondo lints

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 09:07:01 -05:00
Kevin Kelleher
bbc6ca6a04 Metrics Viewer UX tweaks (#72553) 2026-04-16 07:35:34 -04:00
Romeo Van Snick
e0acf707bd Reduce metabase/utils violations by moving notifications helpers into shared (#72510)
* Move Question.alertType to utils/notifications as getAlertType

* Move metabase/utils/notifications to metabase/notifications

* Disable alert message in embedding sdk differently

* Fix type
2026-04-16 12:59:13 +02:00
Romeo Van Snick
2221a978a8 Move export formats to common instead of utils and merge duplicate definitions (#72611)
* Move export formats to common instead of metabase/utils/urls

* Fix types
2026-04-16 12:58:46 +02:00
Romeo Van Snick
2b9a4b6f9e Clean up metabase/utils/text-measurer (#72619)
* Move MeasureText types to the utils file that uses them

* Fix imports
2026-04-16 12:58:31 +02:00
Romeo Van Snick
821b0529fd Move uploads module into status where it is used (#72621) 2026-04-16 12:57:47 +02:00
lbrdnk
2d50c382ae Support questions in read_resource (#72469)
* Add question instructions into read_resource template

* Extend fetch to question

* support question entity in tools/field-values

* extend get-table-details to work also with questions

* refactor get-table-details usage

* refactor get-table-details tests

* Adjust fetch-model-or-card-resource to work with questions

* drop old get-table-details

* Adjust tests

* Extend question->xml with fields support

* Update llm representations question template with fields

* whitespace

* Update /v1/table/:id

* Update src/metabase/metabot/tools/entity_details.clj

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>

* Adjust test

---------

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-04-16 08:38:12 +02:00
Anderson
c42e6a7312 Show tooltip on disabled databases when creating transforms (#70987)
* Show tooltip on disabled databases when creating transforms

* Fix issues / Use better approach

* Add e2e tests / Remove unneeded prop

* Address review comments

* Fix test expectations to match validateDatabase messages

* Fix e2e test assertions to match validateDatabase messages
2026-04-16 01:23:27 -03:00
Fraser
3f89706bb7 Move dashboard-only components into dashboard (#72595) 2026-04-16 12:14:52 +12:00
Edward Paget
665c3bc807 deps: jackson-core 2.21.2 and httpcore5-h2 5.3.5 in databricks driver (#72474)
Pin jackson-core 2.21.2 and httpcore5-h2 5.3.5 in databricks driver
2026-04-15 17:07:08 -07:00
da
76f41437cf Add installation of Astral and Python3 pip (#72645)
* Add installation of Astral and Python3 pip

If you build Metabase with Docker it will fail unless uv and pip are installed

* Update Dockerfile to remove python3-pip installation

Removed installation of python3-pip from Dockerfile.
2026-04-15 20:41:20 -03:00
Edward Paget
961e63a622 deps: jackson-core 2.21.2 in snowflake, bigquery, and redshift drivers (#72468)
Pin jackson-core 2.21.2 in snowflake, bigquery, and redshift drivers

Each driver pulls a vulnerable jackson-core transitively:
- snowflake: 2.18.4.1 via snowflake-jdbc-thin
- bigquery: 2.18.2 via google-cloud-bigquery
- redshift: 2.17.2 via aws-java-sdk-core
2026-04-15 16:34:31 -07:00
Alexander Polyankin
ea831ed8d5 Revert "database mcp tool" (#72664)
* Revert "Fix database tools perf (#72633)"

This reverts commit 1526a1b26e.

* Revert "Fix database tools scope (#72617)"

This reverts commit 1523499820.

* Revert "database agent api (#71852)"

This reverts commit 5034e186b7.
2026-04-15 23:38:05 +01:00
bryan
9a2d08aec6 Implement current-user-table-privileges for Oracle and SQL Server to fix N+1 query (#72043) 2026-04-15 16:20:04 -04:00
Aleksandr Lesnenko
c76d1aa35d Allow metrics with numeric names (#71870)
* support metrics with numeric names

(cherry picked from commit 48cd551625792d9997206bb492e2e5ba21e0e0f6)

# Conflicts:
#	frontend/src/metabase/metrics-viewer/components/MetricSearch/MetricSearchInput/MetricSearchInput.tsx

* e2e test

(cherry picked from commit aebcd2a92eed6c818e32fb28e1fd5d47f7d8c7bb)

* Fix styling

* UXW-3294 fix e2e

* Revert not needed changes for dimensions mapping

* Fix issue with metric added to expression without dropdown list

* Fix e2e

* Fix e2e

* Review fixes

---------

Co-authored-by: Denis Berezin <denis.berezin@metabase.com>
2026-04-15 20:11:42 +00:00
Timofey Kachalov
a3b1e81cd8 Use fetch-artifact action for SDK bundle size checking to find artifact in other workflows (#72592) 2026-04-15 23:03:11 +04:00
Riley Thompson
430df2c389 Apply temporal bucketing on expressions (#72567)
* apply temporal bucketing to expression

* improve test

* fix test with formatted-rows

* format dates

* use normal driver select
2026-04-15 18:39:42 +00:00
Nick Fitzpatrick
65f313d35c fixing flakey tests (#72540)
flakey test fixes
2026-04-15 23:56:53 +05:30
Alexander Polyankin
1526a1b26e Fix database tools perf (#72633) 2026-04-15 18:01:08 +00:00
Romeo Van Snick
4730f8aa92 Move metabase/utils/click-behavior helpers into metabase/dashboard (#72512)
* Move metbabase/utils/click-behavior into metabase/dashboard

* Fix test name to avoid confusion
2026-04-15 19:39:37 +02:00
Timothy S. Dean
800db3ff13 GHY-3339: serdes export should drop most result_metadata (#72560)
**Simplify `result_metadata` serialization for serdes export**

For non-models, drop `result_metadata` entirely on export (it is recomputed by the QP on import anyway). For models, export only `:name` and the keys in `lib/model-preserved-keys` (used to match and apply model column overrides), discarding internal `lib/` and `qp/` properties that are unused after import. This reduces export noise and avoids needing to generate full metadata when editing cards externally.
2026-04-15 10:44:48 -06:00
Nicola Mometto
0c5b412af1 fix: re-evaluate active advisories even if they've been acked (#72610) 2026-04-15 17:18:42 +01:00
lbrdnk
6d5721c585 Fix create_chart tool and chart extraction (#72566)
* Fix create chart links

* Fix chart extraction

* Use pseudo-card fn

* Adjust test

* Integration test

* Instruction formatting

* Adjust chart link resolution

* Adjust test

* Adjust test

* Update test

* Adjust test
2026-04-15 17:29:43 +02:00
Eric Normand
92df3ec3e9 Fix null effective_type for sample database fields (GHY-3367) (#72571)
* Set effective_type for sample database fields (GHY-3367)

Every field entry in resources/sample-content.edn was missing :effective_type,
so CreateSampleContentV2 inserted metabase_field rows with null effective_type
on fresh installs. Default effective_type to match base_type for all 71 fields.

* Test that sample-db fields have non-null effective_type (GHY-3367)
2026-04-15 09:55:32 -05:00
Nathan Voxland
1eebfd0c37 Reduce memory usage of metabase.sync on postgres (#72128) 2026-04-15 09:24:32 -05:00
Alexander Polyankin
1523499820 Fix database tools scope (#72617)
* Fix database tools scope

* fix
2026-04-15 10:21:31 -04:00
Sébastien
f32612daa7 Updated UI for Security Center (#72518)
Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
2026-04-15 15:13:11 +02:00
Timofey Kachalov
80671e6643 Convert embedding SDK shared files to TypeScript (#72521) 2026-04-15 16:06:27 +04:00
github-automation-metabase
e66fff337a Use targeted fetch instead of full clone (#71350)
perf: replace full clone with targeted fetch in containerize jobs
2026-04-15 12:43:07 +02:00
YB
fd7d198955 GDGT-232 fix: dashboard subscriptions should work even if Slack channels are renamed (#72223)
Dashboard subscriptions store Slack channels by name (e.g. "#alerts"). Slack channel IDs are immutable, but names are not. Renaming a channel breaks all subscriptions targeting it with just the name.

This PR fixes this by storing the immutable Slack channel ID alongside the display name at subscription creation time. At send time, we prefer the ID over the name - this maintains backwards compatibility with legacy subscriptions. The UI remains entirely name-based - users never see or interact with channel IDs.
2026-04-15 17:47:35 +08:00
Alexander Polyankin
5034e186b7 database agent api (#71852)
* database api

* add tests

* add docs

* perms

* fixes
2026-04-15 05:07:37 -04:00
Nick Fitzpatrick
270e297f8b Force color on select item hover in question picker (#72353)
* Force color on select item hover in question picker

* move select item color into class instead of c prop
2026-04-15 14:35:51 +05:30
Romeo Van Snick
7619844a88 Colocate data_grid tests with code (#72513) 2026-04-15 07:20:59 +02:00
Romeo Van Snick
7de6c4db9d Colocate nativeDrillFeedback with component that uses it (#72514) 2026-04-15 07:18:50 +02:00
Romeo Van Snick
c24ea41f56 Move memoizeClass to metabase/utils (#72511) 2026-04-15 07:17:21 +02:00
dpsutton
e16a8d8a07 Deref schema model from a protocol not field access (#72588)
it's internal but we don't need to use field access on the type. I don't
want to export the type, etc. Just make a debug protocol and this won't
be exported so it's not for anyone else.

Before:

```
serdes-test=>
Reflection warning, metabase_enterprise/checker/format/serdes_test.clj:286:20 - reference to field schema_model can't be resolved.
Reflection warning, metabase_enterprise/checker/format/serdes_test.clj:292:20 - reference to field schema_model can't be resolved.
Reflection warning, metabase_enterprise/checker/format/serdes_test.clj:298:20 - reference to field schema_model can't be resolved.
Reflection warning, metabase_enterprise/checker/format/serdes_test.clj:315:20 - reference to field schema_model can't be resolved.
nil
```

After:

```
serdes-test=> (clojure.test/run-tests)

Testing metabase-enterprise.checker.format.serdes-test

Ran 28 tests containing 87 assertions.
0 failures, 0 errors.
{:test 28, :pass 87, :fail 0, :error 0, :type :summary}
```
2026-04-15 02:16:10 +00:00
Maksym Yakubych
b395d97868 feat: redesign AI settings (#72041)
* feat: redesign AI settings

* fix: invalidate llm proxy token cache

* fixup! fix: invalidate llm proxy token cache

* fix: review comments
2026-04-14 23:03:04 +00:00
Noah Moss
f869ac0811 Version slack secret and expire linked accounts on rotation (#72559)
* version slack keys and expire on rotation

* refactor around module boundaries

* fix upgrade path

* fix module config test
2026-04-14 18:00:23 -04:00
Nathan Voxland
e6c13bd0e0 Reduce memory usage of fingerprinting postgres (#72133) 2026-04-14 16:51:58 -05:00
Edward Paget
587b41e307 Grant admin access to Helping Hand support user (#72478)
fetch-or-create-support-user! now sets is_superuser=true on all paths
(new user, reactivation, and existing active user). On grant revocation,
is_superuser is set back to false. This ensures the support user has
full admin access as promised by the grant access dialog.

Fixes: UXW-3725
2026-04-14 21:28:54 +00:00
Alexander Polyankin
cf1ef102e7 Bridge the gap between agent/mcp field types and representations field types format (#72547)
bridge the gap between agent/mcp field types and representations field types
2026-04-14 17:26:24 -04:00
dpsutton
d4a861b3c9 Checker module (#70866)
* Checker module

```
❯ clj -X:dev:test:ee:ee-dev :module enterprise/checker
Finding tests took 80.0 ms.
Running 10 tests
Running before-run hooks...

10/10   100% [==================================================]  ETA: 00:00

Ran 10 tests in 0.645 seconds
39 assertions, 0 failures, 0 errors.
{:test 10, :pass 39, :fail 0, :error 0, :type :summary, :duration 644.51825, :single-threaded 10}
Ran 0 tests in parallel, 10 single-threaded.
Finding and running tests took 762.0 ms.
```

The number of public functions here is too high but will come down soon.

```
❯ tree export-dir -d -L 2
export-dir
├── collections
│   ├── BFCwHpVZqKMKqth3wVbGF_automatically_generated_dashboards
│   ├── HyB3nRtqb7pBPhFG26evI_examples
│   ├── cards
│   ├── hFKj55TbZN8uDDTNPiZgf_repro1
│   ├── transforms
│   ├── uj8O1_VYi4rPa4Y8ZGb_S_demo_day
│   └── wUkEfoXgg3dBTNCN2egsh_native_queries
├── databases
│   ├── Internal Metabase Database
│   ├── Sample Database
│   └── pg sample-dataset
├── python-libraries
└── transforms
    ├── transform_jobs
    └── transform_tags

17 directories
```

and

```
❯ time clj -X:dev:drivers:ee nocommit.ci/cli :export '"export-dir"' :output '"/tmp/from-cli-with-error.txt"'
Warning: environ value /Users/dan/.sdkman/candidates/java/current for key :java-home has been overwritten with /Users/dan/.sdkman/candidates/java/25.0.2-tem
2026-03-13 14:26:57,899 INFO metabase.util :: Maximum memory available to JVM: 8.0 GB
2026-03-13 14:27:04,139 INFO util.encryption :: Saved credentials encryption is DISABLED for this Metabase instance. 🔓
 For more information, see https://metabase.com/docs/latest/operations-guide/encrypting-database-details-at-rest.html
clj -X:dev:drivers:ee nocommit.ci/cli :export '"export-dir"' :output   34.10s user 1.68s system 228% cpu 15.634 total
```

the output is a big edn map:

```clojure
{"fdkSuB8zWEbjEq_vWMaI-"
 {:card-id 1,
  :name "Heavy-Duty Silk Chair trend",
  :entity-id "fdkSuB8zWEbjEq_vWMaI-",
  :file
  "export-dir/collections/HyB3nRtqb7pBPhFG26evI_examples/cards/fdkSuB8zWEbjEq_vWMaI-_heavy_duty_silk_chair_trend.yaml",
  :refs
  {:tables ["Sample Database.PUBLIC.ORDERS"], :fields ["Sample Database.PUBLIC.ORDERS.CREATED_AT"], :source-cards []},
  :unresolved nil,
  :bad-refs #{}}
  ...
;; card with unknown field
"rvxR7dn4wrKT9_7MHhy7i"
 {:card-id 222,
  :name "Distinct User ID",
  :entity-id "rvxR7dn4wrKT9_7MHhy7i",
  :file "export-dir/collections/uj8O1_VYi4rPa4Y8ZGb_S_demo_day/cards/rvxR7dn4wrKT9_7MHhy7i_distinct_user_id.yaml",
  :refs
  {:tables
   ["pg sample-dataset.public.products" "pg sample-dataset.public.people" "pg sample-dataset.public.demo_transform"],
   :fields [],
   :source-cards []},
  :unresolved [{:type :field, :path ["pg sample-dataset" "public" "demo_transform" "user_iddddd"]}],
  :bad-refs #{}}
  ;; card with unknown table:
 "srEGaobVwDUI177CowuLy"
 {:card-id 226,
  :name "Orders + People + Products model for real by Total",
  :entity-id "srEGaobVwDUI177CowuLy",
  :file
  "export-dir/collections/uj8O1_VYi4rPa4Y8ZGb_S_demo_day/cards/srEGaobVwDUI177CowuLy_orders___people___products_model_for_real_by_total.yaml",
  :refs
  {:tables ["pg sample-dataset.public.begone" "pg sample-dataset.public.products" "pg sample-dataset.public.people"],
   :fields ["pg sample-dataset.public.begone.total"],
   :source-cards []},
  :unresolved [{:type :table, :path ["pg sample-dataset" "public" "begoneeee"]}],
  :bad-refs #{}}
```

* handle foreign key refs in metadata, schemaless dbs like sqlite

checker=> (write-results! (check "export-dir-2") "/tmp/realized.txt")
Results written to: /tmp/realized.txt

```
❯ clj -X:dev:test:ee:ee-dev :module enterprise/checker

Ran 14 tests in 0.936 seconds
58 assertions, 0 failures, 0 errors.
{:test 14, :pass 58, :fail 0, :error 0, :type :summary, :duration 936.312333, :single-threaded 14}
Ran 0 tests in parallel, 14 single-threaded.
Finding and running tests took 1.1 s.
All tests passed.
```

* yaml structural checks:

```shell
❯ clj -X:ee metabase-enterprise.checker.structural/cli :export '"/Users/dan/projects/work/yaml-checked-files-v1/exports/sqlite-based"'
Warning: environ value /Users/dan/.sdkman/candidates/java/current for key :java-home has been overwritten with /Users/dan/.sdkman/candidates/java/25.0.2-tem
2026-03-17 17:16:18,363 INFO metabase.util :: Maximum memory available to JVM: 8.0 GB
Structural validation complete:
  Valid files: 444
  Invalid files: 1

Errors:
  /Users/dan/projects/work/yaml-checked-files-v1/exports/sqlite-based/collections/HyB3nRtqb7pBPhFG26evI_examples/cards/-1mRtcrAiZoe-GjYrQFHX_order_value_distribution_by_category.yaml (card)
    #ordered/map ([:name ["missing required key"]])
```

* report errors

```
❯ clj -X:ee metabase-enterprise.checker.structural/cli :export '"/Users/dan/projects/work/yaml-checked-files-v1/exports/sqlite-based"'
Structural validation complete:
  Valid files: 444
  Invalid files: 1

Errors:

  /Users/dan/projects/work/yaml-checked-files-v1/exports/sqlite-based/collections/HyB3nRtqb7pBPhFG26evI_examples/cards/-1mRtcrAiZoe-GjYrQFHX_order_value_distribution_by_category.yaml (card)
    - Missing required key 'name' - found 'nname' which may be a typo
```

```
❯ clj -X:dev:test:ee:ee-dev :module enterprise/checker
Running tests in ("enterprise/backend/test/metabase_enterprise/checker")
Looking for test namespaces in directory enterprise/backend/test/metabase_enterprise/checker
Finding tests took 208.0 ms.
Running 22 tests
Running before-run hooks...

22/22   100% [==================================================]  ETA: 00:00

Ran 22 tests in 0.948 seconds
104 assertions, 0 failures, 0 errors.
{:test 22, :pass 104, :fail 0, :error 0, :type :summary, :duration 947.840541, :single-threaded 22}
Ran 0 tests in parallel, 22 single-threaded.
Finding and running tests took 1.2 s.
All tests passed.
Running after-run hooks...
```

* Start to extract format from checker; add readme

ideally the runner persists and we can change the backends that power
it. Now that things are largely working on serdes, i'm extracting the
specifics about the format from the checker stuff so we can begin to
swap them out to simpler formats

* Refactor checker to use MetadataSource protocol

  Introduce protocol-based architecture for the checker module:

  - Add `source.clj` with `MetadataSource` protocol for entity resolution
  - Refactor `checker.clj` to use source protocol instead of global state
    - Session state is now per-check, not global (no more reset-state!)
    - Source and enumerators passed in, enabling different backends
  - Update `format/serdes.clj` to implement MetadataSource
    - Add `make-source`, `check`, `check-cards`, `make-enumerators`
    - High-level API now lives here, not in checker.clj
  - Update tests to use new API
    - In-memory sources via `reify` for edge case testing
    - No fixture cleanup needed between tests
  - Add DESIGN.md documenting two-protocol architecture:
    - EntityResolver: "does this reference exist?"
    - FolderLayout: "where are files in this directory?" (future)
  - Update README.md with current architecture and usage

  This prepares for supporting multiple file formats (serdes, simple LLM-friendly)
  and multiple backends (files, memory, app-db) with the same checker core.

```
❯ clj -X:dev:test:ee:ee-dev :module enterprise/checker
Running tests with options {:mode :cli/local, :namespace-pattern #"^(?:(?:metabase.*)|(?:hooks\..*))", :exclude-directories [".clj-kondo/src" "classes" "dev" "enterprise/backend/src" "local" "resources" "resources-ee" "src" "target" "test_config" "test_resources"], :test-warn-time 3000, :exclude-tags [:metabot-v3/e2e], :module enterprise/checker, :only ("enterprise/backend/test/metabase_enterprise/checker")}
Running tests in ("enterprise/backend/test/metabase_enterprise/checker")
Looking for test namespaces in directory enterprise/backend/test/metabase_enterprise/checker
Finding tests took 196.0 ms.
Running 22 tests
Running before-run hooks...

22/22   100% [==================================================]  ETA: 00:00

Ran 22 tests in 0.976 seconds
110 assertions, 0 failures, 0 errors.
{:test 22, :pass 110, :fail 0, :error 0, :type :summary, :duration 976.301375, :single-threaded 22}
Ran 0 tests in parallel, 22 single-threaded.
Finding and running tests took 1.2 s.
All tests passed.
Running after-run hooks...
```

* proper cli entries

```
❯ clj -M:ee -m metabase.core.bootstrap --mode checker --checker cards --export /Users/dan/projects/work/stats-remote-sync --output /tmp/output.edn
Warning: environ value /Users/dan/.sdkman/candidates/java/current for key :java-home has been overwritten with /Users/dan/.sdkman/candidates/java/25.0.2-tem
2026-03-19 12:27:47,656 INFO metabase.util :: Maximum memory available to JVM: 8.0 GB
2026-03-19 12:27:52,987 INFO util.encryption :: Saved credentials encryption is DISABLED for this Metabase instance. 🔓
 For more information, see https://metabase.com/docs/latest/operations-guide/encrypting-database-details-at-rest.html
Results written to: /tmp/output.edn
Card Check Results
==================
Total cards: 2
  OK: 0
  Errors: 2
  Unresolved refs: 0
  Issues: 0

Failures:
---------

=== Account [beKpAz4TM1cmVN_0HiAvi] ===
  Card ID: 1
  UNRESOLVED REFERENCES:
    - database: Analytics Data Warehouse
    ...
```

and structural:

```
❯ clj -M:ee -m metabase.core.bootstrap --mode checker --checker cards --export /Users/dan/projects/work/yaml-checked-files-v1/exports/sqlite-based --output /tmp/output.edn
Results written to: /tmp/output.edn
Card Check Results
==================
Total cards: 83
  OK: 83
  Errors: 0
  Unresolved refs: 0
  Issues: 0
```

```
❯ clj -X:dev:test:ee:ee-dev :module enterprise/checker
Running tests with options {:mode :cli/local, :namespace-pattern #"^(?:(?:metabase.*)|(?:hooks\..*))", :exclude-directories [".clj-kondo/src" "classes" "dev" "enterprise/backend/src" "local" "resources" "resources-ee" "src" "target" "test_config" "test_resources"], :test-warn-time 3000, :exclude-tags [:metabot-v3/e2e], :module enterprise/checker, :only ("enterprise/backend/test/metabase_enterprise/checker")}
Running tests in ("enterprise/backend/test/metabase_enterprise/checker")
Looking for test namespaces in directory enterprise/backend/test/metabase_enterprise/checker
Finding tests took 228.0 ms.
Running 22 tests
Running before-run hooks...

22/22   100% [==================================================]  ETA: 00:00

Ran 22 tests in 1.076 seconds
110 assertions, 0 failures, 0 errors.
{:test 22, :pass 110, :fail 0, :error 0, :type :summary, :duration 1075.697958, :single-threaded 22}
Ran 0 tests in parallel, 22 single-threaded.
Finding and running tests took 1.3 s.
All tests passed.
Running after-run hooks...
```

* whoops forgot to add new files

* hybrid format concise db schema, serdes assets

* clean up models.serialization

two things we are cleaning up:

1. protocol

there should be a real protocol that we can implement, rather than
knowing which five dynamic vars we need to override/bind a bunch of
dynamic vars, let's have a real protocol and a way to swap it out.

```
;; for yaml
(def ^:private checker-import-resolver
  (reify resolve/SerdesImportResolver
    (import-fk        [_ eid model]            (import-fk eid model))
    (import-fk-keyed  [_ portable model field] (import-fk-keyed portable model field))
    (import-user      [_ _email]               nil) ; checker doesn't resolve users
    (import-table-fk  [_ path]                 (import-table-fk path))
    (import-field-fk  [_ path]                 (import-field-fk path))))
```

note: future work will put the file system state inside of this and not
as some dynamic var spread throughout the code. But one step at a time.

Now to convert a dataset query we have:
```clojure
(defn- convert-dataset-query
  "Convert a dataset query from serdes format, resolving paths to IDs."
  [query]
  (when query
    (let [db-name (:database query)
          db-id (when (string? db-name) (resolve-db-name db-name))
          query (if db-id (assoc query :database db-id) query)]
      (binding [resolve/*import-resolver* checker-import-resolver]
        (resolve/import-mbql query)))))
```

instead of this:

```clojure
(defn- convert-dataset-query
  "Convert a dataset query from serdes format, resolving paths to IDs."
  [query]
  (when query
    (let [db-name (:database query)
          db-id (when (string? db-name) (resolve-db-name db-name))
          query (if db-id (assoc query :database db-id) query)]
      (binding [serdes/*import-field-fk* import-field-fk
                serdes/*import-table-fk* import-table-fk
                serdes/*import-fk* import-fk
                serdes/*import-fk-keyed* import-fk-keyed]
        (serdes/import-mbql query)))))
```

And in the future it will hopefully be something like this:

```clojure
(defn- convert-dataset-query
  "Convert a dataset query from serdes format, resolving paths to IDs."
  [yaml-store query]
  (when query
    (let [db-name (:database query)
          db-id (when (string? db-name) (resolve-db-name db-name))
          query (if db-id (assoc query :database db-id) query)]
      (binding [resolve/*import-resolver* (checker-import-resolver yaml-store)]
        (resolve/import-mbql query)))))
```

2. not require toucan in the checker.
Before this change, we had to require `metabase.models.serialization`
which brought in `[toucan2.core :as t2]`, models.interface, all kinds of
database specific stuff. We now have the protocol and can implement it
against the yaml store rather than the db. Hopefully startup is faster
now.

```
clj -M:ee -m metabase.core.bootstrap --mode checker --checker cards --export /Users/dan/projects/work/yaml-checked-files-v1/exports/sqlite-based-mixed-versions --output /tmp/mixed.output
Results written to: /tmp/mixed.output
Card Check Results
==================
Total cards: 83
  OK: 83
  Errors: 0
  Unresolved refs: 0
  Issues: 0
```

has gone from 11s to 9s. Small wins, but ~10% savings.

Also, it seems like the vast majority of time is spent in stuff we don't
care about which generates these logs:

```
Warning: environ value /Users/dan/.sdkman/candidates/java/current for key :java-home has been overwritten with /Users/dan/.sdkman/candidates/java/25.0.2-tem
2026-03-20 12:15:32,319 INFO metabase.util :: Maximum memory available to JVM: 8.0 GB
```

* clean up, build file interpreter rather than imperative

* lenient mode

writes out a manifest file of fields that are used

* lenient checker

* add serdes workflow and yaml-edit skill files, fix bootstrap fallthrough

Two new Claude skill files for the serdes demo workflow:
- serdes-workflow: export/validate/import pipeline with exact commands
- serdes-yaml-edit: editing YAML with portable refs, self-healing loop,
  checker error interpretation, and reference lookup guide

Fix bootstrap.clj to return false when no --mode flag is present,
allowing normal Metabase startup (import/export commands) to proceed.

* allow for separate db schema directory

checker: --schema-dir, --errors-only, sentinel IDs for unresolved refs

  --schema-dir lets database schemas live in a separate directory from the
  export. The directory is treated as the databases dir directly (contains
  db entries, not a databases/ subdirectory).

  --errors-only outputs just errors to stdout in a concise format for LLM
  consumption — no summary, no status lines.

  Unresolved field/table refs now get sentinel IDs instead of nil, so
  lib/query can construct the query and lib/find-bad-refs can flag the
  missing refs. Previously these blew up with "should be a positive int,
  got: nil".

  Without --lenient, missing schemas now throw instead of silently falling
  back to lenient mode.

  Want me to go ahead and commit with that?

* validate native sql

* filter false-positive :missing-column errors from SQL validation

Schema-less databases (e.g., SQLite) store tables with nil schema, but
sql-tools/validate-query assumes the driver's default schema ("public")
for unqualified table references. This mismatch causes valid columns to
be reported as missing.

Cross-check :missing-column errors against the store and suppress any
where the column actually exists for that database.

* extract the notion of the store

* down to just a single format

* cleanup, rename some args

* rename to semantic

* dashboard checker, extract a bit of common semantic checks

* check dashboard references

* extra schemas to bespoke file so we can move to a public repo

* Remove schema checker

we are semantic only. Now no reason to use a mode param

* Simplify checker module: remove formats, add dashboard/document, norm

  Major refactoring of the checker module:

- Remove format abstraction: Delete concise, lenient, and hybrid
formats. Serdes is the only format. Delete format/hybrid.clj,
format/concise.clj, format/lenient.clj and their tests.

- Remove structural checker: Schemas moved to separate repo. Delete
structural.clj, schemas.clj, structural_test.clj.

- Rename checker.clj → semantic.clj: The "cards" checker is now the
"semantic" checker. CLI no longer has a --checker flag.

- Simplify store: Replace enumerator thunks with direct file index. Add
fields-by-table reverse index for O(1) table→fields lookup.

- Remove requiring-resolve: checker.clj (now semantic.clj) directly
requires format.serdes. No circular dependency indirection.

- schema-dir is required: No auto-detection. Export dir and schema dir
are always separate arguments.

- Add CompositeSource: In source.clj, delegates db/table/field to one
source and cards to another.

- Index entities by model field: Collections tree indexed by reading
serdes/meta model from YAML content rather than path patterns. Supports
both traditional serdes layout and the new flat layout.

- Dashboard validation: Validate dashcard card_id refs, dashboard_tab_id refs, and grid bounds (24-column grid).

- Document validation: Walk ProseMirror content tree, validate cardEmbed card refs.

- collection_id and dashboard_id validation: Any entity with these fields is checked against the index.

- Duplicate entity_id detection: Detected at index build time, reported with file paths.

- MBQL normalization pipeline: keywordize string operators → resolve
portable refs → legacy normalize. Known temporal unit strings
keywordized. Malli enforcement disabled during checking so lib/query
accepts valid clauses the schema doesn't enumerate.

- Disambiguate internal naming: Accumulator atom renamed from unresolved
to !failures. Qualified keys renamed to ::resolution-failures and
::missing-database.

* segment and measure support

* compile sql, handle native query snippet

* implement transforms checks

* using binding to prevent running t2 middleware

* fixup merge failures

* directory agnostic schema dir; sql parsing fixes

realized we weren't keeping around db information. now we are so we
don't have to bind driver information

```diff
-            compiled    (binding [driver/*driver* driver
-                                  qp.i/*skip-middleware-because-app-db-access* true]
+            compiled    (binding [qp.i/*skip-middleware-because-app-db-access* true]
```

* use lib for native check

* parse transform sql, store tests, fixups

* rename check-cards → check-entities; add direct native validation tests

check-cards was misleading — it validates cards, dashboards,
collections, documents, transforms, and more. Renamed to check-entities
with an updated docstring that documents the three parameters.

Added 6 direct tests for native.clj functions (extract-sql-refs,
validate-native-sql) covering valid queries, missing columns, syntax
errors, unknown databases, and nil db-name.

* remove bespoke native checking, use deps analysis

* compute result metadata for processing:

change:

before:
```
=== Native Query - Card and Snippet References [HiBFSt0BNx5s5MxVDLLKB] ===
  Card ID: 94
  Tables: Sample Database.PUBLIC.ORDERS
  Fields: Sample Database.PUBLIC.ORDERS.CREATED_AT
  Source Cards: Basic Aggregations
  NATIVE SQL ERRORS:
    - {:type :missing-column, :name "total_revenue", :source-entity-type :card, :source-entity-id "h5F2EjHsRd73Dqqh8sAtd"}
    - {:type :missing-column, :name "created_attt", :source-entity-type :card, :source-entity-id "h5F2EjHsRd73Dqqh8sAtd"}
    - {:type :missing-column, :name "cooount", :source-entity-type :card, :source-entity-id "h5F2EjHsRd73Dqqh8sAtd"}
  Status: NATIVE SQL ERRORS
```

after:
```
=== Native Query - Card and Snippet References [HiBFSt0BNx5s5MxVDLLKB] ===
  Card ID: 94
  Tables: Sample Database.PUBLIC.ORDERS
  Fields: Sample Database.PUBLIC.ORDERS.CREATED_AT
  Source Cards: Basic Aggregations
  NATIVE SQL ERRORS:
    - {:type :missing-column, :name "created_attt", :source-entity-type :card, :source-entity-id "h5F2EjHsRd73Dqqh8sAtd"}
    - {:type :missing-column, :name "cooount", :source-entity-type :card, :source-entity-id "h5F2EjHsRd73Dqqh8sAtd"}
  Status: NATIVE SQL ERRORS
```

The total_revenue issue is related to not knowing that the card's
aggregation creates this column. Before it didnt' recognize it. Now with
metadata it does recognize it

* cleanup a bit

- println -> output! to silence the warnings and suppress in one place
- -main -> entrypoint so it doesn't appear as a proper entrypoint for
  the jar, we still go through bootstrap and dispatch

* some dumentation on checker shape

* cljfmt files

* fixup

* include resolver arg

* fix readme and docstrings

* clean things up, introduce provider

provider is what wraps a store into a metadata provider. it's very
qp. the store is what cares about how to load assets and schema from
disk (or anywhere else?)

* e2e tests, report all checked items, roadmap and readme

* fix lint errors

* Fix dependencies module dependency

was using deps.analysis for it's check-entity multimethod. I at first
put an entry in deps.core that passes through to this, but deps.core
brings in t2 which i don't want.

Future work should split this namespace so it has some protocol of how
to load up it's information for deps analysis and the checker can figure
out how to adapt it's store and index for these purposes. Then all the
t2 stuff goes away and we're golden

* sort the api namespaces

* fmt fixes

metabase on  yaml-backed-metadata-provider [$?] via 🥟 v1.3.8 via ☕ v25.0.2 on ☁️  metabase-query
❯ bin/mage cljfmt-files enterprise/backend/test/metabase_enterprise/checker
Reformatted enterprise/backend/test/metabase_enterprise/checker/semantic_test.clj

metabase on  yaml-backed-metadata-provider [$!?] via 🥟 v1.3.8 via ☕ v25.0.2 on ☁️  metabase-query
❯ bin/mage cljfmt-files enterprise/backend/src/metabase_enterprise/serialization/cmd.clj
Reformatted enterprise/backend/src/metabase_enterprise/serialization/cmd.clj

* Checker: unify entity checking, split protocols, extract provider

  Major refactoring of the checker module for clarity and consistency:

  - Split MetadataSource into SchemaSource (databases/tables/fields) and
    AssetsSource (cards/snippets/transforms/segments/dashboards/collections/
    documents/measures). All entity types now go through source protocols
    and the store — no more direct YAML file loading fallbacks.

  - Extract provider.clj from semantic.clj. The provider adapts the store
    into a lib MetadataProvider: reference resolution, MBQL normalization,
    data conversion. semantic.clj is now just validation + orchestration.

  - Replace custom keywordize-mbql-operators (~40 lines) with lib/normalize.

  - Unify entity checking: cards are no longer special-cased. check-entity
    dispatches by kind for all entity types. check-one works for any entity,
    not just cards. check-entities defaults to checking everything.

  - Store sources are now private. Provider creates resolvers internally.
    resolve-ref uses store load functions instead of reaching through to
    source protocols directly.

  - Add dashboard/collection/document/measure to source protocols, store
    load functions, and test helpers. Tests use in-memory sources throughout
    — no more temp YAML files.

  - Add e2e tests against test_resources/serialization_baseline with shape
    assertions for joins, snippets, measures/metrics/segments, and
    transitive ref propagation.

  - Add forward-declare wrapper for deps.analysis/check-entity.

  - Track snippets, measures, metrics, segments in card :refs, propagating
    transitively through source-card chains.

  - All non-card entities now appear in results (not just failures).

* handle multiple databases

* cleanup entrypoint a bit

* Checker: lazy on-demand indexing for schema directories

Index only database YAMLs (~40 files) at startup instead of parsing all
563k YAML files. Tables, fields resolved on-demand from directory structure.
Segments/measures indexed only from export dir, not schema dir.

Key changes:
- SchemaSource protocol gains enumeration methods (fields-for-table,
  tables-for-database, all-table-paths, etc.)
- db-name->dir mapping handles slugified directory names
- Provider scoped to current database (tables-for-database not all-table-paths)
- Store delegates to source instead of maintaining field index

Setup time: 64s → ~100ms for 40 databases, 22k tables, 541k fields.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Checker: handle table-type template tags, lightweight table listing

- Native queries with {{table_one}} (type: table) template tags now get
  placeholder substitution instead of passing through as raw mustache
  syntax that SQLGlot can't parse
- Filter out validation errors referencing table placeholder names
- Skip result-metadata for table-tag queries (columns unknown at check time)
- Provider uses ensure-table-id! for table enumeration (path-derived
  metadata, no YAML parse) instead of load-table! (full YAML parse)
- Remove debug instrumentation from semantic.clj

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Checker: update docs to reflect on-demand indexing architecture

- DESIGN.md: rewrite to reflect current architecture (SchemaSource/AssetsSource
  protocols, on-demand field/table resolution, db-name->dir mapping, parallelism
  readiness, performance characteristics)
- CHECKER_RESULTS.md: add analysis of 177-entity run (17 true errors, 2 remaining
  bugs, 1 fixed template tag bug)
- Fix stale docstrings in store.clj and serdes.clj

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Checker: consolidate docs into README, remove standalone md files

Move architecture, performance, and on-demand indexing docs into
the existing README. Remove DESIGN.md and CHECKER_RESULTS.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Checker: fix test helpers for new SchemaSource protocol methods

- Add fields-for-table, all-field-paths, all-database-names,
  all-table-paths, tables-for-database to test helper reify
- Update index assertion tests to check via source protocol
  instead of pre-indexed table/field counts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Merge branch 'master' into yaml-backed-metadata-provider

* remove bashing in place, unused function

* Checker: single-atom schema model with two-level lazy resolution

Replace the three separate cache atoms (indexed-schemas,
table-cache,field-cache) with a single schema-model atom that owns all
database, table, and field knowledge. Separate from the assets-index
which holds cards, dashboards, segments, etc.

Schema model shape:
  {db-name {:db-file "path"
            :schemas {schema ::not-indexed
                             | {table {:table-file "path"
                                       :fields ::not-indexed | {field "path"}}}}}}

Two levels of laziness:
- Schema → tables: populated when any table in that schema is resolved,
  or when tables-for-database is called. Walks the schema's tables/
  directory and reads each table's name: from YAML via fast regex.
- Table → fields: populated when resolve-field or fields-for-table is
  called for that specific table. Walks the table's fields/ directory
  and reads each field's name: from YAML via fast regex.

Schema name resolution handles the case where directory names differ
from real entity names (e.g. H2: dir=public, real=PUBLIC). This is
isolated in resolve-schema-name as the single point to change when
schema resolution improves.

Also: store paths as strings not Files, fix exists? for databases (now
checked via schema source not assets index), remove dead
has-card-template-tags?.

* inline util lower-case-en

don't want to pull in all the stuff so i just use the definition inline
here

---------

Co-authored-by: Bryan Maass <bryan.maass@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:10:01 -05:00
Jeff Bruemmer
a89d93d1f3 docs - file-based development (#72496) 2026-04-14 21:04:32 +00:00
Jeff Bruemmer
2f56929881 docs - serdes updates (#72391) 2026-04-14 20:34:57 +00:00
Cam Saul
1af01d8489 Pick random host when testing Redshift from MB_REDSHIFT_TEST_HOSTS (#72498)
* Pick random host when testing Redshift from MB_REDSHIFT_TEST_HOSTS

* Debugging

* Update CI config

* Oops

* Simplify connection details

* Kicc CI
2026-04-14 19:46:17 +00:00
Edward Paget
618d6f93dc Fix OIDC SSRF: validate all discovery-document URLs before HTTP requests (#72534)
* Fix OIDC SSRF: validate all discovery-document URLs before HTTP requests

The token_endpoint extracted from the OIDC discovery document was used
for HTTP POST requests without validating the host against
oidc-allowed-networks. A compromised OIDC provider could return a
token_endpoint pointing at internal hosts, causing Metabase to POST
client credentials to an attacker-chosen URL.

Centralizes all OIDC HTTP requests through a new metabase.sso.oidc.http
namespace that validates URLs against oidc-allowed-networks before every
request. Also adds defense-in-depth validation in get-endpoint so URLs
are checked at extraction time from the discovery document.

* Log blocked OIDC requests at WARN level for easier debugging

Adds an explicit log/warnf in validate-url! so SSRF blocks are visible
at the point of origin rather than only as wrapped causes downstream.
2026-04-14 13:40:28 -05:00
Edward Paget
0ac48cb972 Downgrade all netty pins to 4.2.9.Final (#72546)
Netty 4.2.12 introduced a breaking change in HttpObjectEncoder that
causes AWS SDK netty-nio-client to fail with "unexpected message type:
LastHttpContent" errors. Revert to 4.2.9.Final which is compatible
with the AWS SDK while still being in the 4.2.x line.

Closes: #72532
2026-04-14 15:39:31 -03:00
Nick Fitzpatrick
d93f7fecde Remove theme transition from dashbaord header, tabs, and parameters panel (#72370) 2026-04-14 23:40:44 +05:30
Braden Shepherdson
eb61194d47 [QP] Correctly handle "struct" fields on Athena and similar drivers (#71917)
Fixes QUE2-34.

These are nested fields (`:parent_id` is set) but not JSON nesting, so
the mechanism is slightly different from `:nfc_path`. This change should
include the necessary metadata on `:field` refs inserted by field filters
to correctly handle struct fields.
2026-04-14 13:48:19 -04:00
Mihael Konjević
828e1bf20c Align embedding profile with NLQ profile tools and system prompt (#72448)
* Align embedding profile with NLQ profile tools and system prompt

The embedding_next profile was a stripped-down version with only 3 tools
and a simpler prompt. This aligns it with the NLQ profile so embedded
metabot behaves identically: same system prompt content (kept as a
separate file for future divergence), same tool set (nlq-search,
read-resource, construct-notebook-query, navigate-user, create-chart,
edit-chart).

* Remove obsolete embedding_next tools test

This test was asserting the old tool set (including
list_available_data_sources). Coverage is now provided by
embedding-next-matches-nlq-tools-test in profiles_test.
2026-04-14 19:28:43 +02:00
Nick Fitzpatrick
2b5b417b1f Center buttons vertically in CreateOrEditQuestionAlertModal footer (#72366)
* center buttons vertically in CreateOrEditQuestionAlertModal footer

* Fix alert modal footer button sizing mismatch (#71945)

- Replace ActionButton with a Mantine Button for the Done/Save changes button
- Wire loading state to isCreating and isUpdating from RTK Query mutations
- Swap the wrapping div for a Flex with align="center" and gap="sm"
- Add align="center" to the outer footer Flex so all three buttons stay consistently sized and vertically centered at all viewport widths
- Remove throw result.error which only existed to drive ActionButton's internal error display

* handle error states

---------

Co-authored-by: Alexander <aquezadawa@gmail.com>
2026-04-14 22:38:45 +05:30
Rodrigo López Dato
82b07d4b28 Make CLJS dev port configurable in CSP via MB_CLJS_DEV_PORT (#72530)
The frontend dev port was already configurable via MB_FRONTEND_DEV_PORT,
but the CLJS REPL port (shadow-cljs) was still hardcoded to 9630. This
causes CSP violations when running multiple Metabase instances with
dynamic port allocation.
2026-04-14 13:14:31 -03:00
Anderson
175846d975 Address a few AI Controls UI follow-up items (#72482)
* Implement a few AI Controls UI follow-ups

* Fix token input suffix (pluralization)
2026-04-14 15:48:18 +00:00
metamben
52257c531f Fix BigQuery tests broken by #71771 (#72484) 2026-04-14 11:46:23 -04:00
Anderson
dc2bfa264b Address review comments from pr #69874 (#72500) 2026-04-14 11:41:20 -03:00
Noah Moss
af3da5c6b7 Strip bloat fields from :tool-output; add logging/metrics around stored metabot message size (#72388)
* strip bloat fields from :tool-output; add logging/metrics around stored message size

* switch to actually counting encoded byte size for prometheus

* address feedback

* clarify comment
2026-04-14 08:55:21 -04:00
Nick Fitzpatrick
50e490de81 Typescript common components (#72288)
* Entity Menu stuff, Expanding Content

* LastEditInfoLabel, ListItem

* buncha test files

* DebounceFrame, more spec files

* type adjustments
2026-04-14 16:11:27 +05:30
Nick Fitzpatrick
2d4279d596 Re-add new dashboard button in move modal and collection picker when dashboards are selectable (#72363) 2026-04-14 16:11:04 +05:30
Ngoc Khuat
64af0de83f GDGT-2144 Require transform target schema on schema-aware DBs (#72220) 2026-04-14 10:27:02 +00:00
Fraser
3403b34d00 Move store types and mocks to shared tier (#71400) 2026-04-14 22:06:00 +12:00
Fraser
d1fc89abff Update enforced modules (#72520) 2026-04-14 09:49:59 +00:00
Fraser
bcc9b9a15d Fix lib-css outgoing violations (#72437) 2026-04-14 21:34:08 +12:00
Sébastien
e21a007fbc Fixes ordering issue in parameters (#72083)
Co-authored-by: Braden Shepherdson <Braden.Shepherdson@gmail.com>
Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-04-14 10:21:05 +02:00
Ngoc Khuat
109257d6bc Don't send alerts to deactivated users (GDGT-1927) (#72444) 2026-04-14 14:59:30 +07:00
Fraser
522f803837 Fix breadcrumbs story violation (#72333)
Fix breadcrumps story violation
2026-04-14 18:31:30 +12:00
Fraser
2d26466250 Resolve MultiAutocomplete translation plugin (#72228) 2026-04-14 17:50:59 +12:00
Tyler Plude
70263943b5 [BOT-1316] Improve native agent API error logging (#72499)
* improve native agent error logging

* fix kondo
2026-04-13 22:16:18 -07:00
Anderson
25015ba318 [AI Controls] Show validation error for group limits above instance limit (#72205)
* Rename natural language query page to AI exploration page

* Show validation error for group limits above instance limit
2026-04-13 20:09:30 -03:00
Fraser
c417b95a44 Selectively enforce module boundaries (#72436) 2026-04-14 10:04:00 +12:00
github-automation-metabase
d493249403 Auto-update documentation for master (#72493)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-13 17:25:59 -04:00
Alexander Polyankin
baf1ebad78 fix table-id serialization (#72489) 2026-04-13 21:15:09 +00:00
bryan
1c92ac2bbc Fix models to transforms for transform output tables created with nil schema (#72139) 2026-04-13 14:42:06 -06:00
github-automation-metabase
a04458ffd8 Auto-update documentation for master (#72398)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-13 16:18:15 -04:00
Timothy S. Dean
d07c7a73ff Skip t2 hooks when updating dashboard's last-viewed-at (#72479)
Skip t2 hooks when updating dashboard last-viewed-at.
2026-04-13 14:01:57 -06:00
bryan
a0e764069c GHY-3378: Block source replacement when sandboxing policies are affected (#72473) 2026-04-13 13:33:32 -06:00
Nick Fitzpatrick
ee9e271af6 Update Checkbox Styling (#72375)
* Update Checkbox Styling

* fixing select all checkboxes

* Update Loki Snapshots

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-04-13 23:39:04 +05:30
Edward Paget
297421c564 deps: bump clickhouse transitive deps (#72470)
Fix ClickHouse driver security vulnerabilities

- Pin httpcore5-h2 to 5.3.5 to fix DoS vulnerability
- Replace discontinued org.lz4:lz4-java 1.8.0 with maintained fork
  at.yawk.lz4:lz4-java 1.10.2 to fix out-of-bounds read and
  sensitive data exposure vulnerabilities
2026-04-13 12:54:07 -05:00
Edward Paget
141ea91664 deps: bump jackson and related (#72460)
* Pin jackson-core/databind 2.21.2, bump clj-otel to 0.2.10, and bump httpcore5 to 5.3.5

Fixes transitive vulnerabilities:
- jackson-core 2.21.1 resource exhaustion (via cheshire)
- jackson-databind 2.13.3 DoS (via snowplow)
- kotlin-stdlib 1.8.21 info exposure (via clj-otel → okhttp, resolved by bumping clj-otel)
- httpcore5-h2 5.3.4 DoS

* Sort jackson-core/databind deps alphabetically in deps.edn
2026-04-13 12:26:22 -05:00
Edward Paget
f677b4524e deps: bump all log4j deps to 2.25.4 (#72461)
Bump all log4j deps to 2.25.4 to fix certificate validation and log injection vulnerabilities
2026-04-13 10:34:43 -05:00
John Swanson
dd59e7545a Safer conversion of the Data Analysts group (#71032)
Currently, stats has 5 "Data Analysts (converted)" groups. As far as I
can tell, this shouldn't happen in the happy path: after the database is
set up, we check for the necessary premium features before converting
the "special" Data Analysts group into a renamed "normal" group and
creating a new Data Analysts group.

But this is happening - intermittently - so I think we just need to be
more defensive here. If some transient network issue or something causes
us to temporarily be unsure what features are enabled, then normally we
just say "nope, no features here" and that's fine. In this case we need
to be sure, and should only do the Data Analysts swap if Metastore has
*told us* that the feature is not present.

So, this adds:

- a `:canonical? false` flag, when the result is not canonical

- and a function, `canonically-has-feature?` that returns `true` if the
  feature is canonically present, `false` if the feature is canonically
  absent, or `nil` if the feature's presence cannot be canonically
  determined

Then, when we start up, we check the feature using this function.

If the feature is canonically present, we do nothing.

If the feature is canonically absent, we do the swap.

If neither is true (network blip or some other issue caused us to not
receive a valid response from the metastore), we fire off a future that
will wait for a canonical response and then do the swap if necessary.
2026-04-13 08:22:50 -07:00
John Swanson
904a701e71 Skip cache auto-refresh attempts for routed dbs (#70082)
* Skip cache auto-refresh attempts for routed dbs

Currently we pollute the logs here, because we can't do anonymous
queries against a routed DB.

Just skip cards that are against routed databases to avoid failing
deeper down.

* Import :model/DatabaseRouter into enterprise/cache module
2026-04-13 08:22:04 -07:00
Edward Paget
a450c7db99 deps: bump zookeeper and guava in hivelike (#72454)
* Bump Zookeeper 3.8.4→3.8.6 and Curator 5.7.1→5.9.0 in hive-like driver

Fixes CVE-2026-24308 (config data leaked to INFO logs) and
CVE-2026-24281 (hostname verification bypass via PTR spoofing).

* Pin Guava 33.5.0-jre in hive-like driver to fix transitive 22.0 vulnerabilities

hive-standalone-metastore-common pulls Guava 22.0 transitively, which has
three known Snyk advisories (info disclosure, deserialization, insecure
temp files). Exclude it from the metastore dep and pin 33.5.0-jre directly,
matching the root deps.edn version.

* Pin Guava 33.5.0-jre in hive-like driver to fix transitive 22.0 vulnerabilities

hive-standalone-metastore-common pulls Guava 22.0 transitively, which has
known security advisories (info disclosure, deserialization, insecure
temp files). Exclude it from the metastore dep and pin 33.5.0-jre directly,
matching the root deps.edn version.
2026-04-13 10:19:52 -05:00
Anderson
5e6eb552e3 Use partial settings update when switching branches during Remote Sync conflict (#69874) 2026-04-13 10:15:08 -05:00
Alexander Polyankin
416a8cc63f Fix streaming with serdes (#72457)
* fix streaming

* fix comment

* fix
2026-04-13 10:23:32 -04:00
Nick Fitzpatrick
e1438f4c42 Update PATH_WITH_COLLAPSED_NAVBAR to not close on all routes with question (#72374) 2026-04-13 18:16:21 +05:30
Kevin Kelleher
027a2bede1 Time series axis label improvements (#70814) 2026-04-13 08:25:15 -04:00
Ngoc Khuat
10997b1090 Stop aborting Redshift test runs on concurrent schema-drop races (GDGT-2126) (#72441) 2026-04-13 11:09:51 +00:00
Nicola Mometto
3c8b941dce seed advisories on first run (#72446) 2026-04-13 10:42:13 +00:00
Fraser
9d68e94b06 Fix shared-common outgoing violations (#72438) 2026-04-13 20:46:21 +12:00
Fraser
8ea71a40e7 Move disable command palette hook into ui (#72432)
Move disable command palette hook into ui
2026-04-13 20:45:45 +12:00
Ngoc Khuat
7998a40a63 Show transform inspector as upsell when feature is not enabled (#71341) 2026-04-13 07:21:11 +00:00
Alexander Polyankin
28f1915be3 Trivial serdes export fixes for metadata (#72434) 2026-04-13 00:55:58 -04:00
Alexander Polyankin
011bbd3f98 Revert "Fix serdes perf on export" (#72416)
* Revert "Fix serdes breaking on IDs for db/table/fields for non-existent entit…"

This reverts commit 8b051f242f.

* Revert "Fix serdes export perf with database metadata - id lookup (#72326)"

This reverts commit e90c0ea24e.
2026-04-11 21:15:14 +01:00
Uladzimir Havenchyk
d95a444446 chore: do not transpile cljs in tests (#72248) 2026-04-11 20:25:59 +03:00
Edward Paget
84ebef5e3e Migrate hive-like driver from hive-jdbc$standalone to hive-jdbc (#72379)
Replace the standalone uberjar (which bundles and relocates dependency
versions with published security advisories) with the regular
hive-jdbc artifact plus explicit sub-module and third-party dependency
declarations at safe versions.

The published hive-jdbc 4.2.0 POM is broken (HIVE-28455) so all Hive
sub-modules must be declared manually. Each has exclusions to prevent
pulling in the massive Hadoop/Hive transitive dependency tree (YARN,
Jetty, Jersey, Spring, Shibboleth, etc.).

Key version pins for security scanning:
- Netty 4.2.12.Final (was bundled at 4.1.127.Final)
- commons-io 2.18.0 (CVE-2024-47554)
- commons-codec 1.17.1, commons-logging 1.3.5
- httpclient 4.5.14, httpcore 4.4.16

Fixes: GHY-1756:
2026-04-11 09:04:51 -03:00
Uladzimir Havenchyk
cb21657273 deps: upgrade lodash and lodash-es (#72336) 2026-04-11 08:20:14 -03:00
Ryan Laurie
04a520b658 Pin axios versoin (#72405)
* pin axios versoin

* pin axios version
2026-04-10 23:09:11 +00:00
Tyler Plude
257365531a [BOT-1260] Stub workflow for testing GH benchmark integrations (#72401)
stub workflow
2026-04-10 16:02:52 -07:00
Alexander Polyankin
8b051f242f Fix serdes breaking on IDs for db/table/fields for non-existent entities (#72399)
* fix broke FK fields

* fix

* fix

* fix

* fix

* fix

* fix

* fix
2026-04-10 15:15:56 -07:00
John Swanson
e88f6252d8 Don't document jwt-configured-and-enabled (#72244) 2026-04-10 15:14:46 -07:00
Denis Berezin
4c217bdb3c Metrics Math UX (#70733)
* feat: metric math - backend only

Squashed commit of lib_metric AST, compile, plan, filter, projection, and related backend changes.
Frontend and JS-export changes have been removed.

* support for constants

* fix tests

* kondo

* whitespace

* tests

* UXW-3264 Add metric math test UX

* Polishing, address edge cases

* FE changes to work with constants

Code may be bad but it seemed pretty simple to add support here.

* Add text view

(cherry picked from commit e7ad8536860ece87a84608b42a2e7a70ebc99ca0)

# Conflicts:
#	frontend/src/metabase/metrics-viewer/components/MetricSearch/MetricSearchInput/MetricSearchInput.module.css
#	frontend/src/metabase/metrics-viewer/components/MetricSearch/MetricSearchInput/MetricSearchInput.tsx
#	frontend/src/metabase/metrics-viewer/pages/MetricsViewerPage/MetricsViewerPage.tsx
#	frontend/src/metabase/metrics-viewer/utils/url-serialization.ts

* Add support for constants

* Add unit tests

* Small updates

* WIP - added syntax highlighting, validation

* Add syntax highlight and basic validation

* Metric viewer can use metrics based on cards

[UXW-3260: Metrics from models/table questions are not viewable in the Metrics viewer](https://linear.app/metabase/issue/UXW-3260/metrics-from-modelstable-questions-are-not-viewable-in-the-metrics)

* Add commit changes flow

* Allow using the same metric multiple times

* Temp

* Fixed reload after adding a new metric from dropdown

* Fix issue with single expression

* Polished version with metric text tokens

* scalar results tab

* show no compatible dimensions error rather than blank screen

* fix types

* Fix adding breakouts

* fix scalar and map breakouts

* rename to totals tab and allow adding back in

* scalar tab cleanup

* fix default tabs not reappearing

* fix error flashing

* UXW-3382 Fix metric names with commas and other symbols (#71318)

* UXW-3382 Fix metric names with commas and other symbols

* UXW-3382 Fix issue with searching metric with comma in it's name

* Self review

* allow adding tabs for expressions

* fix deserializing measures from URL

* add index to serialized metrics and expressions to preserve order on refresh

* fix tests

* UXW-3380 Fix metric dropdown to filter only self

* don't move cursor with arrow up/down when popover is open

* handle order of operations properly

* handle non-numeric metric error

* Allow metric math with non-exact temporal field matches (#71183)

* Allow metric math with non-exact temporal field matches

We can now do metric math between two metrics that both have temporal
dimensions, even if those temporal dimensions aren't identical! 🎉

* cljfmt

* fix clj tests

* UXW-3413 Support break out the same metric multiple ways (#71390)

* UXW-3413 Support break out the same metric multiple ways

* UXW-3413 Fix issues the last same metric instance overrides breakout, fix colors

* Fix removing breakout after changing formula

* Self review

* Review comments

* Remove rename that breaks IDE navigation

* Filtering specific instances of metrics (#71640)

* fix be tests

* llm review

* fix be test

* support negative numbers in metrics viewer expressions (#71790)

* support negative numbers

* review

* slight refac

* llm review - don't clear dimension filter on dimension change

* cleanup console.log

* revert accidental changes to pie files

* fix unit tests

* revert changes with invalid css variables

* remove unused CSS classes with bad CSS vars

* fix e2e tests

* Fix Metric Math URL serialization (#71769)

* reconile state on expressions change (#71795)

* add metric math e2e tests

* wip

* Revert "wip"

This reverts commit dadde5a90c.

* UXW-3292 Add dimension controls for every metric instance (#71791)

* UXW-3292 Add dimension controls for every metric instance

* Add expressions dimensions controls, but it doesn't really apply to the series

* Add fixes to pick default dimension, and support dimensions for expressions

* Add e2e tests

* Refactor

* Fix issue with not visible series

* Fix issue with missing MetricControls

* Fix issue selected metric dimension being preserve after reload

* Fix e2e

* Update dimension projection serialization for expressions

* Address some review comments

* Added more unit tests

* Self review

* Self review

* fix test, try to fix flaky metric identities

* revert failed attempt to fix flaky test

* Reuse reconciliation logic from MetricSearchInput (#72111)

---------

Co-authored-by: Kevin Kelleher <kevin.kelleher@metabase.com>

* Ensure we can always parse formulas (#72112)

* ensure we can always parse formulas

* review comments

* Revert changes to e2e to wait for metrics load

* Fix after merge

* fix modifiedDefinitions indexing for expressions

* update invalid expression handling (#71934)

* fix types in tests after merge

* fix test

* Review fixes

* more test fixes

* try to fix flaky test again

* Series refactor (#72060)

* add dimension pill icon for expressions

* Fix styles

* Fix styles

* Fixes after merge

* Review fixes

* Review fixes

* Fix e2e tests

---------

Co-authored-by: Edward Paget <ed.paget@gmail.com>
Co-authored-by: John Swanson <john.swanson@metabase.com>
Co-authored-by: Kevin Kelleher <kevin.kelleher@metabase.com>
Co-authored-by: Aleksandr Lesnenko <alxnddr@users.noreply.github.com>
2026-04-10 15:01:21 -07:00
appleby
95b6569503 BOT-1231 Remove user-intent tracking (#72199)
* BOT-1231 Remove user-intent tracking

https://metaboat.slack.com/archives/C0AP8MCUE4U/p1774895471205839?thread_ts=1774891049.263789&cid=C0AP8MCUE4U

* Remove trailing whitespace

* Remove unused require

* Remove unused defs
2026-04-10 14:43:17 -07:00
da
ed21fcad5a Update cheshire version to 6.2.0 (#72279)
* Update cheshire version to 6.2.0

https://github.com/dakrone/cheshire/commits/master/

patches the jackson-core dep issue

* Make JSON error test resilient to parser version changes

Use regex matching instead of exact string equality so the test
doesn't break when the JSON parser reports a different column offset.

---------

Co-authored-by: Edward Paget <ed.paget@gmail.com>
2026-04-10 15:49:38 -05:00
Edward Paget
e4d5acd850 deps: bump com.taoensso/nippy (#72295)
* deps: bump com.taoensso/nippy

resolves a reported issue with a transitive dependency

* Update deps.edn
2026-04-10 15:49:15 -05:00
John Swanson
f57e7585cc Fix metrics on models with custom column dims (#72164)
* Fix metrics on models with custom column dims

Just like with joins, a metric with a source-card needs to be compiled
to a two-stage query rather than querying the table directly, so that we
can query custom columns.

* Fix metrics on models with custom column dims

Just like with joins, a metric with a source-card needs to be compiled
to a two-stage query rather than querying the table directly, so that we
can query custom columns.
2026-04-10 12:41:06 -07:00
Timofey Kachalov
5ea5206719 feat(sdk,embed-js): Auto-scroll to the auto-opened dropdown for Join Step on touch devices (#71965) 2026-04-10 23:27:47 +04:00
appleby
ce78ecf14e BOT-1289 Send metabot-usage metering for both yesterday and today (#72378)
Send metering for today's usage under a new :metabot-rolling-usage key with corresponding :metabot-rolling-usage-date.

All other keys only send yesterday's values for backwards compat.

Keys are omitted if there is no usage for the corresponding day.
2026-04-10 11:37:40 -07:00
bryan
fa1f458a3d Fix flaky dataset-already-loaded?-test on Redshift (#72123) 2026-04-10 12:33:11 -06:00
appleby
d0570b318d BOT-1292 Do not convert metering values to string in send-metering-events! (#72377) 2026-04-10 11:05:39 -07:00
Alexander Polyankin
e90c0ea24e Fix serdes export perf with database metadata - id lookup (#72326)
* wip

* wip

* fixes

* fixes

* fix

* fix

* handle millions of fields

* fix

* fixes
2026-04-10 10:45:30 -07:00
Nick Fitzpatrick
59246fc093 dnd folder (#72264) 2026-04-10 21:54:53 +05:30
Ben Grabow
959a7a0e39 Ghy 3343/fix key sorting (#72192)
* Fix sorting of maps nested under double-layers of sequences

* Re-export after fixing nested map sorting

* Descend into all sequential nodes

* Re-export instance analytics collection

* Test against expected yaml string
Test that sorting is idempotent
2026-04-10 11:04:50 -05:00
Phoomparin Mano
4548c37b08 Fix flaky E2E test in connection impersonation step scenarios (#72341)
* fix flaky e2e

* disable next button until permission group query resolves
2026-04-10 22:10:57 +07:00
Eric Normand
5d1d7d4a49 GHY-3232: Make dependency graph calculation async on entity updates (#71037)
* Make broken dependency analysis async

This change make the synchronous (in the API request) part very simple and fast: Just mark the current entity stale,
trigger an async task, and return.

The async task will do the actual analysis and mark its direct dependents stale. The async task loop will then pick
those up and continue from there. In this way, all transient dependents will be analyzed as well.

Updated tests also to make sure chains of dependents do get updated.

* Async dependency backfill analysis

* Fix async dependency backfill: test fixes and error handling

- Fix NPE in mark-stale! when no existing entries found
- Add error handling in mark-stale-and-trigger! for empty test DBs
- Let dependency computation errors propagate for retry handling
- Fix downstream transform tests to persist target changes to DB
- Fix delayed-retry test scope for with-redefs
- Add run-backfill! calls to dependency model integration tests
- Remove with-empty-h2-app-db! from filtered-graph-dependencies-test

* Add DependencyStatus to model->namespace resolution map

Fixes failing all-models-are-accounted-for-test.

* Add backfill calls to replacement api tests

Events no longer synchronously compute dependencies, so tests
need to run the backfill before asserting on dependency data.

* Revert "Add backfill calls to replacement api tests"

This reverts commit 68b2835beb.

* Fix CI failures: add backfill calls, serialization exclusions, shared test helper

- Only process entities WITH dependency_status entries (inner-join
  instead of left-join) to avoid computing deps for entities that
  were never event-triggered
- Add DependencyStatus to serialization excluded-models and entity-id
  exclusion set
- Add deps.test/synchronously-run-backfill! shared helper in test_util
- Use shared helper consistently across all test files
- Add events/publish-event! calls in tests that create entities via
  mt/with-temp without firing dependency events
- Add backfill calls in replacement, usages, and workspace tests

* Remove trailing blank line in backfill_test.clj

* Add DependencyStatus to copy-test models-to-exclude

* Fix workspace test failures: run backfill before graph persistence

The workspace graph is cached when ws-done! runs. With async dep
computation, the backfill must run before ws-done! so that upstream
dependency edges exist when the graph is persisted.

Also add backfill call in validation_test which creates transforms
via mt/with-temp outside of create-resources!.

* Guard segment-delete dependency row delition

* Fix TOCTOU races in dependency_status and missing feature gate on segment-delete

- Replace select-then-insert patterns in mark-stale! and upsert-status! with
  app-db/update-or-insert! which handles cross-database conflict retry with
  savepoints (H2, MySQL, PostgreSQL).
- Add missing (when (premium-features/has-feature? :dependencies) ...) guard
  on ::segment-delete event handler, matching all other delete handlers.

* Use app-db.core instead of app-db.query to fix kondo module boundary warning

* Refactor backfill to use multimethods for per-entity-type behavior

- Move dashboard hydration from backfill case statement to hydrate-for-deps
  multimethod in dependency_status.clj, using standard t2/hydrate [:dashcards :series]
- Convert upstream-deps:* public functions to calculate-deps multimethod in
  calculation.clj with logic in defmethods, update all callers
- Extract post-deps-cleanup! multimethod in backfill.clj for transform-specific
  downstream dep cleanup
- Remove custom :series-card-ids hydration; extract IDs from existing :series
  hydration in calculate-deps :dashboard

* Move retry/broken state into dependency_status table, add Malli schema wrapper, migration fixes

- Add fail_count, next_retry_at, terminal columns to dependency_status table
- Remove in-memory ConcurrentHashMap retry tracking from backfill.clj
- record-failure! and clear-failure! persist state to DB, surviving restarts
- mark-stale! resets retry state so modified entities get a fresh chance
- instances-for-dependency-calculation filters terminal/not-yet-retryable in SQL
- Uses Java time (not DB time) for retry comparison so mt/with-clock works in tests
- Rename calculate-deps multimethod to calculate-deps*, add mu/defn wrapper with
  Malli schema validation on return value
- Add comment in events.clj explaining two separate stale tracking systems
  (dependency_status vs analysis_finding)
- Migrate ALL entities (not just version > 0), marking never-analyzed as stale
- Rename migration file to 20260330 for fresh DB compatibility
- Remove unused DashboardCard/DashboardCardSeries from module imports
- Add DependencyStatus to module exports

* Remove DependencyStatus from module exports — only used internally

* Add tests for error logging and graceful degradation in backfill

- backfill-error-logging-test: verifies that when calculate-deps throws,
  the entity remains stale, no deps are created, and fail_count is recorded
- backfill-partial-batch-failure-test: verifies that a failure on one entity
  doesn't prevent other entities in the batch from being processed

* Shorten comment separator lines to stay under 120 char limit

* Clean up dependency_status rows on entity delete, fold clear-failure into upsert-status

- All 8 delete handlers now also delete the dependency_status row to prevent
  orphaned rows accumulating over time
- upsert-status! now resets fail_count and next_retry_at, eliminating the
  need for a separate clear-failure! call and closing a gap where
  has-pending-retries? could loop indefinitely

* Remove trailing blank line in dependency_status.clj

* Harden error handling: catch failures in record-failure! path, fold clear-failure into upsert-status

- Wrap record-failure! and logging in a nested try/catch so a DB error
  while recording the failure doesn't abort the rest of the batch
- upsert-status! now clears fail_count and next_retry_at, eliminating
  the separate clear-failure! function and closing a gap where
  has-pending-retries? could loop on a processed entity

* kick ci

* Remove DependencyStatus from serialization models list

dependency_status is transient state (stale flags, retry counts) that
gets recomputed by the backfill job. No need to export/import it.

* Fix runner test: add synchronously-run-backfill! after card-create events

With async deps, the runner test needs to run backfill before attempting
source swaps that depend on the dependency graph being populated.

* kick ci

* Add DependencyStatus to serialization excluded-models list

The every-model-is-supported-test requires every Toucan model to be
accounted for in either exported, inlined, or excluded models.
DependencyStatus is transient state and should never be serialized.

* Fix replacement api test: add synchronously-run-backfill! after card-create events

* Improve docstring

Co-authored-by: Timothy S. Dean <timothy.dean@metabase.com>

* Improve docstring

* Use immediate dependents instead of transitive for all event handlers

- Transform run, sync complete, table/field metadata update handlers now
  mark only immediate dependents as stale, letting the entity-check job
  propagate in waves rather than computing the full transitive tree upfront
- Add mark-all-immediate-dependents-stale! for batch immediate-dependents
- Add missing trigger-entity-check-job! calls to table/field metadata handlers

* Add wave propagation test, fix check-entities! loop to drain all stale entities

- Add wave-propagation-via-entity-check-test: verifies that marking immediate
  dependents stale propagates through the entity-check job loop in waves
  (grandparent change → parent re-analyzed → child marked stale → child re-analyzed)
- Fix check-entities! loop condition: continue while has-stale-entities? is true,
  not only when the batch was full. analyze-and-propagate! creates new stale
  entities during processing that need to be picked up in subsequent iterations.

* Fix nested deftest: move query-transform-update-handles-downstream-dependencies-test to top level

Was accidentally nested inside python-transform-update-handles-downstream-dependencies-test
due to a missing closing paren. Now runs as its own test (18 tests, was 17).

* Add test for mark-stale! failure resilience in event handlers

Verifies that when mark-stale! throws (e.g., DB failure), the event
handler catches the error and doesn't propagate it to the API caller.

* Restore deleted analysis propagation tests adapted for async model

Adds back 15 tests that were removed when switching to async deps:
- Transaction rollback test: verify analyze-and-propagate! rolls back on failure
- Card update triggers native cards: wave propagation through entity-check job
- Card update stops on transforms: propagation boundary test with waves
- Transform update/run: with/without existing analyses, native transforms
- Segment update: with/without existing analyses, propagation to dependents
- Table metadata update: marks immediate dependents stale, no-analysis resilience
- Field metadata update: marks immediate dependents stale, no-analysis resilience

Tests adapted for async model: events mark stale, entity-check job propagates.

* Increase e2e dependency wait timeout from 10s to 30s

With async dependency computation, the backfill and entity-check jobs
need time to process entities. The 10s timeout was too short for e2e
tests that create multiple entities and wait for analysis findings.

* Move dependency_status migration from 060 to 061, update changeset IDs

The cross-branch migration test requires migrations to be in the correct
version directory. Since this PR targets v61, move the migration there
and update all changeset IDs from v60 to v61.

* Move dependency_status migration back to 060/, keep updated date

* Add preConditions with MARK_RAN to all dependency_status changesets

Databases that already ran the old v60.2026-03-17 changesets (from the
previous migration filename) will skip these changesets instead of
failing with checksum or duplicate errors.

* Revert preConditions — old changesets were never deployed

* Add preConditions onFail: MARK_RAN to all dependency_status changesets

* Revert "Add preConditions onFail: MARK_RAN to all dependency_status changesets"

This reverts commit f9f7ef1645.

* Add failing test: wave propagation through non-analyzable entities

Demonstrates that mark-immediate-dependents-stale! followed by
check-entities! doesn't reach cards that depend on non-analyzable
intermediaries (tables). The chain transform → table → card breaks
because the wave stops at the table (not analyzable).

* Fix wave propagation through non-analyzable entities, add 1s trigger delay

Two fixes:
- mark-supported-dependents-stale! now looks through non-analyzable entities
  (e.g., tables) to reach analyzable dependents beyond them. Previously the
  wave stopped at tables, so cards depending on a transform's output table
  were never re-analyzed when the transform changed.
- trigger-backfill-job! and trigger-entity-check-job! now use a 1-second
  delay instead of 0. Event handlers run inside Toucan2 after-update hooks
  (within a transaction), and delay-0 triggers fired the Quartz job before
  the transaction committed, so the job saw no stale entities.

* Remove trailing blank line in findings_test.clj

* Add waitForGraphDependencies helper for e2e tests

Polls /api/ee/dependencies/graph until the dependency data is available,
similar to waitForBreakingDependencies and waitForUnreferencedEntities.
Needed because dependency computation is now async.

* Add waitForGraphDependencies to visitGraphForEntity in e2e graph tests

Polls the graph API until dependency edges exist before navigating to
the graph page. This accounts for async dependency computation.
Also removes accidental .only() left from local testing.

* Remove test-mode delay-0 for backfill job init

The backfill job was starting with delay 0 in test mode, which caused
it to run during e2e snapshot creation. This monopolized the connection
pool and caused the data-studio-metrics e2e test to timeout when trying
to load the entity picker. Now uses the normal production initial delay.
Backend tests are unaffected — they call synchronously-run-backfill!
directly without relying on the Quartz scheduler.

* Appease the kondo

* Remove config from enterprise/dependencies module uses

The dependency module no longer uses the config module.

* Wait for async dependency computation in e2e tests

Dependencies are now computed asynchronously, so tests that check the
dependency graph need to poll until edges are populated before asserting.

* Wait for async dependency computation in unreferenced list e2e tests

Add waitForUnreferencedEntities calls before visiting the unreferenced
entities page to ensure async dependency computation has completed.

* Add backfill-status endpoint and use waitForBackfillComplete in e2e tests

Add GET /api/ee/dependencies/backfill-status endpoint that returns whether
dependency backfill has pending work. Replace per-entity graph polling in
e2e tests with a single waitForBackfillComplete() helper that polls this
endpoint, simplifying async dependency waits across all test files.

* Reload page before navigating to dependencies tab to refresh cache

---------

Co-authored-by: Timothy S. Dean <timothy.dean@metabase.com>
2026-04-10 09:47:44 -05:00
Braden Shepherdson
352798c484 [QP] Handle Snowflake tables with UUID columns more gracefully (#72034)
An upstream Snowflake JDBC driver issue causes an exception to be thrown
because the UUID column type is not present in an enum.

This adds logic to fall back to the more generic `SELECT *` sync mode if
this error occurs during a sync.
2026-04-10 13:35:32 +00:00
lbrdnk
e392e85ab4 Cleanup Metabot tools feature sentinels (#72113)
* Remove snippets sentinel

* Add appropriate feature to python transform tools

* Adjust oss transform tools sentinels

* Remove remnants of ee-feature-available?

* Drop features-transforms and adjust profiles code

* Use feature-transforms

* Adjust test

* Tests

* tests

* Ensure python tools not available when basic transforms are not available
2026-04-10 14:03:08 +02:00
Nicola Mometto
07325c8057 Security Center (#71661)
* security center data model (#71660)

* add security_center model

* wip: migration

* kondo

* Advisory security endpoints (#71704)

* Security center admin page and advisory list (#71707)

* Use :admin-security-center feature flag and restore premium-handler on route

* Reuse notification system components for security center channel config (#71723)

* Security center settings (#71807)

* feat: add security-center recipients settings

* feat(FE): add security-center settings

* tests

* feat(FE): hook to settings

* Security center matching engine (#71747)

* upgrade banner (#71800)

* added banner (#71816)

* moved the security center into the top bar (#71821)

* fixed an e2e test

* Gate Security Center for non-trial subscriptions (#71799)

* Gate Security Center for non-trial subscriptions

Trial subscriptions should not have access to Security Center to
prevent abuse via easily-obtainable trial enterprise tokens.

Adds `is-trial?` and `assert-not-trial` to the premium features
token check, and applies a `+require-non-trial` middleware to the
security-center route that returns 402 for trial tokens.

* Handle check-token exceptions in is-trial?

Fail open (return false) if the token status check throws, matching
the same pattern used by *token-features*. Prevents a 500 when
MetaStore is unreachable.

* Use token-status setting instead of check-token directly

Follow the existing pattern used by billing and cloud-add-ons.

* Use -token-status instead of the setting getter

* Gate Security Center for non-trial subscriptions

Trial subscriptions should not have access to Security Center to
prevent abuse via easily-obtainable trial enterprise tokens.

Adds is-trial? and security-center-enabled? to premium features
token check, and a +check-not-trial middleware on the security
center API routes that returns 402 for trial tokens.

* Security center notification (#71814)

* feat: add last_notified_at column

* feat: add security-advisory-match event

* feat: security advisory email template

* feat: add slack renderer

* feat: notify

* email fixes

* kondo

* fix: hydrate

* chore: quartz

* test

* send notification sync

* fix: guard against no smtp config

* fix: bcc

* more tests

* only on has feature

* switch to util

* move url builder

* feat: use all-admin-recipients

* Security center: fetch and scheduled evaulation (#71830)

* addressed stas feedback

* all-admins and recipients are no longer exclusive (#71895)

* Fix read only connection for security center (#71898)

* added sync button (#71905)

* Security center unify tasks + CI green (#71894)

* pull notify task into sync task

* make CI green

* chore: exclude security-advisory

* fmt

* require settings in init

* kondo

* set wor

* fixes

* exclude is-hosted

* check-enabled with open-api wrap

* change error

* missing

* fix: emails

* fix use an user-id

* fixes

* init test-users

* enable on dev

* rollback to enforce no writes

* fix: add index

* index before fk

* may not error

* Security center test notification (#71915)

* test notifications

Closes GDGT-2174

* test

* fix

* addressed PR feedback (#71919)

* fix parsing published_at timestamp durign fetch

* fix fetch test to use string published_at matching real API response

The test was using #t tagged literals for published_at which bypassed
the string→timestamp coercion path, masking the bug we just fixed.

* added tests for recipients settings (#71963)

* make recipients non nullable (#71967)

* Treat error match_status as affected (fail-open) (#71961)

* treat error match_status as affected (fail-open)

Per the matching engine spec, when a matching query fails, the advisory
should be treated as potentially affected. The isAffected check was only
looking for "active" status, missing the "error" case.

* fix type errors in createAdvisory mock

Add return type annotation so string literals narrow to the Advisory
type instead of widening to string when spread with Partial<Advisory>.

* Security center: force sync endpoint (#71899)

* Security center cursor (#71969)

* feat: add updated_at

* feat: cursor on updated_at

* feat: since tests

* fix fetch tests

* appease the linter

* fix test

* remove seed

* small fixes

* fix

* feature token properly propagates (#71970)

* added polling for sync button (#71971)

* fix

* fix

* fix

* Add Snowplow tracking for Security Center (#71972)

* Add Snowplow tracking for Security Center

Track 3 events using the existing simple_event schema:
- security_center_page_viewed (FE, on page mount)
- security_advisory_acknowledged (BE, with severity)
- security_advisory_notification_sent (BE, with channel/result/trigger)

* Derive notification channels from handlers instead of config

Instead of re-checking configured-channels at tracking time, derive
the channel list from the notification's actual handlers.

* Move last_notified_at update inside try block

* add :admin-security-center to excluded features in stats test

* kondo

* update schema (#72031)

* hook full feature logic to premium features exposed to the FE (#72026)

* hook full feature logic to premium features exposed to the FE

* tests

* fix test

* chore: error in FE

* fix

* fix e2e test

* lint

* tighten schemas

* fix hydration

* chore: defensively check-superuser

* tighter schemas

* fix test

* chore: simpler schema as we'd need to duplicate with :api/regex

* e2e test should only run on ee

* fix: recipients default (#72035)

* Add testing endpoint for security advisories (#71977)

* fix model boundary violations for SecurityAdvisory

* relax schema

* move to 45 (#72073)

* chore: migrations in 001 file for backporting to 45

* mark-ran preconds

* reorder

* consolidate

* add set null

* Mobile improvements (#72071)

* move to 55

* move severity->repeat-days into an overridable setting

Closes GDGT-2208

* reformat

* typo

* deal with nits

* switch to select-fn->fn

* explicit map building

* more nits

* align

* nit

* add not-empty

* use custom executor instead of future

* use semver4j

* fix test

* restrict queries to selects (#72162)

* reorder

* Security center no h2 (#72215)

* disable security-center for h2 appdbs

* tests

* fix last-checked at, store in setting

* fix tests

* enable for e2e tests

* fix merge conflicts

* Revert "Security center no h2 (#72215)"

This reverts commit 41dc93794d.

* fix: persist last-synced-at in a setting

* fix test

* disable on h2 (enabled for tests) (#72263)

* UI feedback on Security Center (#72214)

* fixed imports

* chore: max 2 mins for advisory query

* kondo

* chore: use sql-jdbc.execute

* kondo

---------

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
Co-authored-by: Sébastien <sebastien@metabase.com>
2026-04-10 12:28:48 +01:00
Mahatthana (Kelvin) Nomsawadi
a57cff4dd4 Fix drilling Metabot quesitons hides drilled questions (#72233)
* fix(sdk): push adhoc question to internal navigation stack

* Add tests

---------

Co-authored-by: Nicolò Pretto <info@npretto.com>
2026-04-10 11:20:00 +01:00
Chris Truter
9b86604078 Throttle FE error API (#72232) 2026-04-10 08:35:02 +00:00
Romeo Van Snick
773692eaf3 Move metabase/lib to metabase/utils and move domain-related utils to other modules (#71467)
* Rename metabase/lib to metabase/utils

* Move metabase/lib to metabase/utils

* Move getCrumbs to getCollectionBreadcrumbs in common/utils

* Fold compat into react-compat

* Remove composeEventHandlers as it is too simple

* Move fields types to metabase/common/utils/fields

* Always go through getCspNonce

* Move jwt helpers into jwt file

* Remove unused scroll helpers

* Use helper function instead of constant

* Remove unneeded removeAllChildren helper

* Move data url helper to separate file

* Move lib tests inline with modules

* Remove unused clipPathReference

* Remove unused getMainElement helper

* Move canDownloadResults to metabase/common

* Merge metabase/utils/engine and metabase/databases/utils/engine

* Clean up fetchWithTimeout

* Move formatting into formatting folder

* Split up formatting tests into formatting folder

* Allow null for date_style

* Remove need for eslint comments

* Remove unused i18n-debug package

* Move iframe utils into metabase/utils/iframe

* Remove unneeded getObjectValues helepr

* Move sortObject into metabase/utils/object

* Import delay from the delay module

* Rename utils/security to utils/password

* Remove unused foreignKeyCountsByOriginTable helper

* Move fields helpers to metabase/common/utils

* Move entities helpers to metabase/entities

* Move passwordComplexityDescription out of Settings

* Remove unused string utils

* Move tests to have same name as module under test

* Merge time-dayjs tests

* Move timelines to metabase/common/utils

* Use crypto.randomUUID

* Replace copy() with builtin structuredClone

* Remove single-use isEmpty helper

* Remove unused propertiesEqual helper

* Move version helpers to metabase/utils/version

* Move numberToWord into passwords file where it is used

* Remove simple _.isEqual wrapper

* Remove isEEBuild wrapper

* Fix new lib imports after merge

* Fix missing import

* Fix whitespace linter exclusions

* Remove utils/utils imports

* Fix import

* Fix noop imports

* Fix more aliases

* Fix formatting

* Fix lint

* Allow utils/promise in embedding tests

* Fix imprts after merge

* Fix lint

* Remove delay confusion

* Fix lint

* Revert to clone implementation

* Add fallback for crypto.randomUUID

* Fix lint

* Revert jspdf

* Do not coerce nonce to empty string

* Fix types

* Fix lint
2026-04-10 06:42:38 +00:00
Romeo Van Snick
32aedee1c7 Move InsightsUpsellTab to metabase/common/components/upsell (#72155) 2026-04-10 07:07:27 +02:00
Sébastien
8b4f96092a Rename "New exploration" to "New question" in <metabase-browser> (#72145) 2026-04-10 06:44:36 +02:00
Fraser
8322959c37 Make simple events extensible (#71631) 2026-04-10 16:25:25 +12:00
Aleksandr Lesnenko
6b5ce715dc fix breakouts axis split in metrics viewer (#72141)
* fix breakouts axis split in metrics viewer

* post-rebase fixes
2026-04-10 02:03:04 +00:00
Aleksandr Lesnenko
1b31cc6521 add stack series button to metrics viewer (#71694)
* add stack series button to metrics viewer

* change ui

* better approach

* update spec

* review
2026-04-10 00:59:27 +00:00
Cam Saul
2fd986a3f7 Support FKs for SQLite in tests (#72142)
* Support FKs for SQLite in tests

* Kondo

* 🔧

* Code cleanup

* Test fix 🔧
2026-04-09 17:26:10 -07:00
Copilot
12a66b2abe Add ESLint rule to restrict analytics imports to analytics files (#66780)
* Add ESLint rule to restrict analytics imports to analytics files

Co-authored-by: iethree <30528226+iethree@users.noreply.github.com>

# Conflicts:
#	.eslintrc.js
#	frontend/lint/eslint-plugin-metabase/rules/no-analytics-import-outside-analytics-files.js

* Fix test regex patterns to match full error message

Co-authored-by: iethree <30528226+iethree@users.noreply.github.com>

* formatting

* add lint rule

* fix use-table-crud

* fix violations

* remove dead exports

* type fixes

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: iethree <30528226+iethree@users.noreply.github.com>
Co-authored-by: Ryan Laurie <iethree@gmail.com>
2026-04-09 17:33:09 -06:00
Fraser
113f908408 Clean ui theming violations (#71629) 2026-04-10 11:01:29 +12:00
Edward Paget
4a2a143e15 deps: bump redshift to latest release (#72294) 2026-04-09 19:25:14 -03:00
Timofey Kachalov
fafb054e57 feat(sdk,embed-js): Replace PointerSensor with MouseSensor and add TouchSensor for improved drag-and-drop support (#72032) 2026-04-10 02:09:56 +04:00
Timofey Kachalov
5b53f1f8dc fix(sdk,embed-js): Don't close ChartSettingsWidgetPopup on touch events (#71979) 2026-04-10 02:08:39 +04:00
da
17f0cbe480 Update commons-lang3 version to 3.20.0 (#72278)
https://commons.apache.org/proper/commons-lang/changes.html#a3.20.0
2026-04-09 18:20:11 -03:00
da
c3bffc2610 Update Netty dependencies to version 4.2.12.Final (#72274)
https://github.com/netty/netty/releases
2026-04-09 18:17:45 -03:00
Edward Paget
3392486525 Enforce session timeout server-side with last_active_at tracking (#71866)
* Enforce session timeout server-side with last_active_at tracking

Add a last_active_at column to core_session and use it to enforce
session inactivity timeouts on the server side (behind the
session-timeout-config premium feature). Activity updates are throttled
via an atomic compare-and-set cache in session.core to avoid a DB write
on every request. The session cleanup task prunes idle sessions and
expired cache entries.

* Move EE-dependent session timeout tests to enterprise test file

Session timeout tests that require the :session-timeout-config premium
feature fail in OSS mode because enable-session-timeout-config? checks
config/ee-available? which is always false without enterprise code.

* Remove trailing blank lines from test files
2026-04-09 15:48:18 -05:00
Edward Paget
71e5ad58dc Fix nested WHERE clause in remove-unsynced! for TransformTag (#72286)
Fix nested WHERE clause in remove-unsynced! for TransformTag (UXW-3710)

Mixing a {:where ...} HoneySQL map with kv-pair conditions in t2/delete!
caused toucan2 to misinterpret the map as a condition key, producing invalid
SQL with a WHERE inside a WHERE. Convert other-conditions into the {:where}
clause directly and remove unreachable scoped-path code that had the same bug.
2026-04-09 15:47:40 -05:00
Alex Yarosh
a96f3bc9d5 docs: Update links in CONTRIBUTING.md to latest version (#72300)
Update links in CONTRIBUTING.md to latest version
2026-04-09 16:35:49 -04:00
Alexander Polyankin
46e35e97ba Fix serialization export perf with no content models (#72290) 2026-04-09 12:26:36 -07:00
Cam Saul
629c10e733 Replace 'MLv2' with 'Lib' or 'MBQL 5' in docstrings and function names (#72121)
Replace usages of 'MLv2' with 'Lib' or 'MBQL 5'
2026-04-09 12:26:17 -07:00
metamben
5d0d3c47c0 Enable commented out drill-thru tests (#71845)
Closes QUE2-140
2026-04-09 21:54:05 +03:00
metamben
5f5d472fe7 Use locale independent formatting to get the day of the week (#71620)
* Use locale independent formatting to get the day of the week

Closes #57794

* Use MBQL 5 in the test
2026-04-09 21:53:49 +03:00
Aleksandr Lesnenko
afe5a73498 Increase stacked series gap, make it dynamic (#72118)
* increase stacked series gap, make it dynamic

* Update Loki Snapshots

* review

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-04-09 13:59:32 -04:00
github-automation-metabase
b9e2e96185 Update translations 2026-04-07 (#72070)
* Update translation sources 2026-04-07

* Update translations

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-04-09 17:47:56 +00:00
Timothy S. Dean
8d5bd8ebcb Don't trigger result-metadata write based on non-default parameters (#72007)
If you have a parameterized card with computed columns, there is no Field from which to pull fingerprints, and the query-processor runs a fingerprint process based on the results of the query. Because the query is parameterized, the result is specific to whatever random parameter was used. `record-metadata!` saw this difference and updated the result-metadata on the card.

The fix: we will no longer persist `result_metadata` when non-default parameterized values are present.
2026-04-09 11:41:51 -06:00
Kevin Kelleher
2e71a55212 Fix colors in metrics viewer (#71874) 2026-04-09 17:19:16 +00:00
Edward Paget
fccf343325 Fix dashboard PUT response (#72173)
Extract shared `apply-card-permission-filters` helper used by both GET and
PUT paths to ensure consistent card visibility filtering. Also fix
`set-download-perms-on-dashcards` to handle empty dataset_query maps
gracefully.

Fixes: UXW-3571
2026-04-09 11:11:18 -05:00
Nicolò Pretto
2a8821b6a7 Address lack of continuity after admin completes database connection and x-ray dashboard steps (#72072)
* wip modal

* change button text

* polish

* update copy
2026-04-09 18:04:36 +02:00
John Swanson
6c65769bc5 fix n+1s in db, table, group creation (#72087)
* fix n+1s in db, table, group creation

this batches permissions changes to fix cluster lock timeouts with large numbers of groups, database, and tables.

When we created a new group, table, or database, we obtained O(NxM) fine-grained cluster locks, and did NxM separate inserts into data permissions.

This does batch operations instead. Obtains a single cluster-wide shared lock for batch operations (which actually might not be necessary at all for database and group creation - think it's still needed for creating a table though), then does the insert in batches.

Reduces the total number of queries dramatically:

- group creation: scaling with NxM -> ~10

- db creation: scaling with NxM -> ~10

- table creation: scaling with NxM -> ~8

* Fixup tests, improve perf more

* comment cleanup

* fix test

* remove dead code + unnecessary api epxansion

* Fix cluster lock contention

This is a fairly big change on its own, but it's fairly straightforward
and an additional performance improvement that should help with
concurrent processes trying to modify permissions at the same time.

Essentially, I realized that currently our cluster lock system provides
exactly one type of lock, an exclusive lock. But that's not the only
type of potential lock! Both Postgres and MariaDB/MySQL support locking
a row as *shared*. This conflicts with exclusive locks (`FOR UPDATE`)
but does not conflict with other shared locks.

This way, we can design a two-level lock system in which:

- most operations require:
  - a *shared* lock on `::batch-permissions-update`, and
  - an *exclusive* lock on `::batch-permissions-update-<db>`

- some operations require only an *exclusive* lock on
  `::batch-permissions-update`.

As one example, modifying permissions with the graph API requires an
exclusive lock on `::batch-permissions-update`. We don't want sync
adding a table at the same time that someone is modifying permissions,
e.g. moving from granular -> coalesced or vice versa.

On the other hand, sync itself just requires a *shared* lock on
`::batch-permissions-update`, because it can happily coexist with other
syncs that are for different databases. But they'll all take exclusive
locks on `::batch-permissions-update-<db>`, so any permissions changes
they're making to the database will be serialized.
2026-04-09 08:37:14 -07:00
Kamil Mielnik
42c4a2f3e9 Remove accidental semicolon in JSX (#72242) 2026-04-09 15:18:59 +00:00
Chris Truter
58916bb3ce Fix module update method to insert missing :model-X entries (#72247) 2026-04-09 17:10:20 +02:00
lbrdnk
50e9fe7a7a Adjust format-native-query to accept legacy mbql (#72217)
* Adjust format-native-query to accept legacy mbql

* Add test

* Restore permissive extraction form

* assertion

* Docstring
2026-04-09 16:53:58 +02:00
Ngoc Khuat
d6ccee4d52 GDGT-2143 Use subscription creator's perms for attachment check (#72156) 2026-04-09 14:25:36 +00:00
Aleksandr Lesnenko
d3ba36d66d Change config of js execution context for static viz (#72196)
* static viz bundle should be sandboxed

* update color selector

* split execution contexts
2026-04-09 14:05:01 +00:00
Maksym Yakubych
3b995f986f fix: sql edit tools creating new questions instead of editing (#70255) 2026-04-09 14:03:44 +00:00
Nathan Voxland
6a7e27195f Fix: allow moving dashboard questions to their parent collection (#71550)
Allow moving dashboard questions to their parent dashboard's collection

The MoveModal's isSameDestination() was comparing the question's
collection_id to the target collection, blocking the move. But for
dashboard questions, the real "location" is the dashboard — moving to the
collection is a valid operation that detaches the question from the
dashboard. Added \!movingItem.dashboard_id to the collection check.
2026-04-09 19:26:47 +05:30
Nathan Voxland
dc38720b20 Add keyboard shortcut (g s) to navigate to Data Studio (#71328)
* Add keyboard shortcut (g s) to navigate to Data Studio

Closes UXW-3155

* adding permissions check, e2e test coverage

* e2e adjustment

---------

Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>
2026-04-09 19:25:03 +05:30
Timofey Kachalov
6078e48144 feat(sdk,embed-js): Improve EntityPicker mobile UX (#71815) 2026-04-09 17:54:23 +04:00
Denis Berezin
605968c6e9 Fix styling for chart png download in Firefox (#72198) 2026-04-09 16:24:59 +03:00
Uladzimir Havenchyk
77fca900be deps: upgrade yaml (#71988)
chore: upgrade yaml
2026-04-09 14:57:30 +03:00
Nick Fitzpatrick
f31336e113 Uxw 3547 add analytics events for metrics viewer and metrics homepage (#72091)
* Adding events

* tests

* remove page view events

* re-adding comment since it's relevant again
2026-04-09 16:40:29 +05:30
Phoomparin Mano
75582f7983 fix(sdk-bundle): only render SDK components when EE plugins are loaded (#70737)
* only render children if plugin is ready

* fix: wait for ee plugins to initialize before rendering children

* add unit tests for race condition

* fix: avoid module cache and always re-dispatch plugin ready

* fix: initialize EE plugins synchronously during render instead of useEffect

Moves plugin initialization out of useEffect into the render body,
eliminating the extra frame delay. Uses reduxStore.dispatch directly
following the same pattern as use-init-data-internal.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:23:37 +07:00
Chris Truter
e54b1d968c Delete agent APIs for Workspaces (#71827) 2026-04-09 08:02:24 +00:00
Chris Truter
398840c0a1 OAuth token cleanup task (#72081) 2026-04-09 09:33:58 +02:00
Chris Truter
70552d31d8 Add OAuth and MCP throttling (#71884) 2026-04-09 09:33:45 +02:00
Timofey Kachalov
a2d99db064 Mock i18n FE translations to use EN texts (#72044) 2026-04-09 10:58:24 +04:00
da
f09bc46311 Fixes the find and replace menu from dissapearing (#71780)
Fixes the find and remove menu from dissapearing
2026-04-08 21:36:36 -03:00
appleby
80af5a2b55 BOT-1282 Report combined in + out tokens in :metabot-usage metering (#72186)
* BOT-1282 Report combined in + out tokens in :metabot-usage metering

* Add some additional tests for :metabot-usage reporting
2026-04-08 22:21:56 +00:00
github-automation-metabase
7382721f95 Auto-update documentation for master (#72195)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-08 17:35:05 -04:00
Maksym Yakubych
ee7663ccd1 feat: metabase as an AI provider (#71655) 2026-04-08 20:39:30 +00:00
Uladzimir Havenchyk
a5cf9c43c5 bundle: exclude lazy loaded libs from vendor bundle again (#72061) 2026-04-08 23:02:42 +03:00
bryan
db672b4043 Copy model metadata overrides to Fields during models-to-transforms (#72129) 2026-04-08 13:51:19 -06:00
Aleksandr Lesnenko
bc41b0d2c3 remove data studio link from the metrics viewer (#72122)
* remove data studio link from the metrics viewer

* update spec
2026-04-08 19:44:31 +00:00
Edward Paget
910a4b3e09 Require authentication for eid-translation API endpoints (#72174)
The /api/eid-translation routes were unauthenticated despite only being
used by frontend routes behind the IsAuthenticated guard. Wrap with +auth
and remove the stale "public facing" comment.
2026-04-08 13:41:01 -05:00
John Swanson
a224d29129 Don't allow pausing JWT if it's on via env var (#69386)
* Don't allow pausing JWT if it's on via env var

This separate the `jwt-enabled` setting into two parts:

- `jwt-enabled`, which is settable via an env var, and means "do I want
JWT to run or not"?

- `jwt-enabled-and-configured`, which is not settable at all, and simply
computes `(and (jwt-enabled) (jwt-configured))`. This one means "JWT is
running".

This way, if you set MB_JWT_ENABLED via the env, we don't give you the
option to pause or disable JWT anymore. If it's configured, it's on.

In some ways `MB_JWT_ENABLED` feels useless here, when set alone - it's
not really doing anything other than blocking the user from pausing JWT.
But I think in context it makes sense: you want to be able to configure
JWT solely via the env, but if you do that partially, it makes sense
that we just don't let you change the env-configured bits.

* kondo

* fix
2026-04-08 11:37:17 -07:00
Jeff Bruemmer
2867945fd4 docs - fix anchor links (#72176)
fix anchor links
2026-04-08 13:23:26 -04:00
technomancy
657669dcc5 Implement table privileges support for SQLite. (#71950)
* Implement table privileges support for SQLite.

This is based on code from https://github.com/metabase/metabase/pull/57000
but breaks it down into more manageable chunks.

* Set SQLite insert/update/delete privileges to true.

Unclear why the original PR hard-coded these to false; SQLite has no
permissions system which could prevent writes.

Getting this wrong doesn't affect anything in the backend; it's not
used anywhere outside the frontend, so I suppose it's somewhat
unrealistic to expect it to cause a test failure.
2026-04-08 10:22:03 -07:00
John Swanson
177a6515b7 Query parameter cleanup (#72048)
* Normalize query parameters in preprocessing

* Filter metadata results by user permissions

* Simplify permission checking logic

* Use server-controlled query context

* Validate query parameters at API boundaries

* Code review improvements

* Fixed failing test

* Fixed failing test

* Revert SQL driver to read persisted-info from query object

Instead of having the SQL driver load persisted-info from the metadata
provider, retain the old behavior where it reads :persisted-info/native
from the query map (set by fetch-source-query middleware).

---------

Co-authored-by: Edward Paget <ed.paget@gmail.com>
Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-04-08 12:04:28 -05:00
Alexander Polyankin
25969cb31e Fix source replacement with null effective_type (#72124) 2026-04-08 16:29:14 +00:00
Ben Grabow
6d45a39d94 Ghy-3343 Re-export instance analytics content (#72063)
* Re-export instance analytics in new serdes format
2026-04-08 11:00:42 -05:00
Alex Yarosh
97f95e40cd docs: more typos (#72168)
more typos
2026-04-08 11:49:12 -04:00
Nick Fitzpatrick
53a13043ae Hide Regenerate Key button if form field is readonly (#72105)
* Hide Regenerate Key button if form field is readonly

* adjust unit test
2026-04-08 20:59:07 +05:30
Edward Paget
241181c7f8 Add README for built-in MCP server (#71834)
Documents the MCP server capabilities, endpoint, client setup
(Claude Code, Claude Desktop, Cursor), OAuth authentication,
scopes, available tools, JSON-RPC methods, and architecture.
2026-04-08 10:11:22 -05:00
John Swanson
5dde87ebf7 Fix search filters for published tables (#71323)
When:

- a table was published,
- a user had data permissions for that table, and
- the user did NOT have permissions on the collection the table was
  published to

Then the search endpoint would not return that table, even though it's
queryable and viewable via data permissions.

This fixes the filtering, so that the table is discoverable via search.
2026-04-08 08:04:15 -07:00
Anderson
31d3ebe6fb [AI Controls] Add tooltip explaining most-permissive group access (#72132)
Add tooltip explaining most-permissive group access
2026-04-08 11:59:18 -03:00
Ngoc Khuat
9d50b86f16 Fix validation in public dashboard action endpoint (#72088) 2026-04-08 13:21:47 +00:00
YB
fdd2d558ba GDGT-26 Fix discard-setting-changes helper for settings that use init (#72069)
The issue reported that discard-setting-changes is broken for settings that use :init (e.g. query-analysis-native-disabled). The report predicted that calling the setting getter inside the macro would return nil, and that the value would be corrupted afterward.

do-with-discarded-setting-changes! used setting/read-setting to capture the "original" value before the thunk. read-setting binds *disable-init* to true, so for settings whose value comes from an :init function (and which haven't been initialized yet), it returns nil. This nil is then passed as both:

(outer capture) The "temporary" value to set during the thunk (via do-with-temporary-setting-value!)
(inner capture) The value to restore in the finally block (via the :skip-init? flag)
The key fix here is to use setting/get instead of setting/read-setting. We also remove the :skip-init? flag since it was there to match the semantics of read-setting and suppress the init call.
2026-04-08 17:23:40 +08:00
Romeo Van Snick
fc07abb93c Fix race condition in test and reset mocks (#72150) 2026-04-08 11:23:27 +02:00
Chris Truter
8a869177f4 Standardize where models live (#72096) 2026-04-08 08:06:38 +00:00
Romeo Van Snick
ed21122ef5 Content translations for parameters values without custom labels (#72025)
* Translate parameter values too

* Allow translating arrays of values

* Add e2e test for translations without label

* Fix types
2026-04-08 07:36:55 +02:00
Anderson
c989e01952 Metabot AI Usage Limits page (#71699)
* Implement static UI for Usage controls page

* Added initial ai-controls tables

* Set sidenav menu items / Add placeholders for new pages

* Implement Metabot Customization page UI

* - Added metabot/permissions GET and PUT apis
- Added settings for metabot name/tone/icon

* Fixed migration ids

* fixup! bad merge

* Add :scope metadata to all Agent Loop tools and create metabase.metabot.scope namespace

- Create metabase.metabot.scope with *current-user-scope* (defaults to #{}),
  scope-matches? with hierarchical wildcard support, and unrestricted set
- Add :scope to all 38 Agent Loop tool-fns with specific action scopes
  (e.g. "agent:sql:create", "agent:viz:edit") — wildcards are grant-side only
- Propagate :scope through wrap-tools-with-state in tools.clj
- :capabilities remain unchanged (orthogonal data-permission gating)
- Add tests for scope-matches?, default scope, and stub

Done: tool-scope/phase-1-tool-scope-declarations

* Add scope-based authorization middleware for Agent Loop tools

- Filter tools by scope in get-tools-for-profile (profiles.clj)
- Check scope before tool invocation in wrap-tools-with-state (tools.clj),
  returning a generic denial message (scope details logged, not leaked)
- Bind *current-user-scope* to unrestricted in run-agent-loop (temporary
  until phase 4 wires real permission resolution)
- Add scope enforcement tests covering filtering, denial, wildcards,
  no-scope passthrough, and default empty scope behavior

Done: tool-scope/phase-2-authorization-middleware

* Gate profile system prompts by metabot permission types

- Add *current-user-metabot-permissions* dynamic var to scope.clj
- Bind all-yes permissions in run-agent-loop (temporary, phase 4 wires real resolution)
- Derive has_sql_generation, has_nql, has_query_tools, has_other_tools
  boolean flags from permissions and pass to Selmer template context
- Gate internal.selmer sections: critical constraints, verification protocol,
  query building steps, SQL construction, SQL anti-patterns, SQL examples,
  NLQ guidance, dashboard routing, and query-focused common scenarios
- General sections (identity, context grounding, communication style, search
  example, entity linking) remain ungated for all permission levels
- 7 tests, 43 assertions covering all permission combinations

Done: tool-scope/phase-3-profile-prompt-gating

* Wire user-metabot-perms->scopes to metabot_permissions data model

- Implement resolve-user-permissions: looks up user's groups, queries
  MetabotPermissions, resolves most-permissive value across groups
- Implement real user-metabot-perms->scopes: maps :yes permissions to
  wildcard scope sets, always includes always-granted-scopes
- Replace temporary unrestricted/all-yes bindings in run-agent-loop with
  real permission resolution from api/*current-user-id*
- Superusers get scope/unrestricted and all-yes-permissions
- Permission resolution done once at reify construction, not per-reduce
- Agent API scope binding deferred (tracked in rdm task agent-api-scope-binding)
- 38 new test assertions covering permission→scope mapping, single-group,
  multi-group most-permissive-wins, and default scenarios

Done: tool-scope/phase-4-permissions-to-scopes

* Update permissions expected request format

* Include metabot and semantic-search tool options on the backend

* Connect access controls UI to the backend

* Wire up the backend for the Metabot Customize page

* Polish access controls page / Create MetabotSystemPromptsPage

* Added metabot-system-prompt

* Connect System prompt page to the backend

* Remove model selection + clean up

* Use custom metabot name

* Use metabot-icon in the UI + added metabot-show-illustrations toggle

* Removed metabase custom prompt

* Add defscope macro, scope registry, and api-scope module

Introduce a `defscope` macro that registers scopes with translatable
descriptions in a global registry, replacing inline scope strings across
all tool and endpoint metadata with typed var references.

Key changes:
- New `metabase.api-scope.core` module with defscope, scope registry,
  scope-matches?, and public API (all-scopes, scope-description)
- All 25 agent scopes declared via defscope in metabase.metabot.scope
- 3 EE workspace scopes declared in metabase-enterprise.workspaces.scope
- validate-tool-var! rejects tools with unregistered scopes at startup
- defendpoint accepts var references for :scope metadata (not just strings)
- enforce-scope logs dev-time warnings for unregistered endpoint scopes
- MCP scope enforcement now handled by defendpoint middleware (removed
  redundant pre-dispatch check); enforce-authentication preserves existing
  token-scopes on synthetic requests
- Scope enforcement error messages no longer leak required scope names
- Consolidated duplicate scope-matching logic in mcp/tools.clj

Done: tool-scope/phase-5-defscope-registry

* Make semantic_queries optional in search tool schemas

When semantic search is not enabled, the feature gate strips
semantic_queries from the tool definition sent to the LLM, but Malli
validation still required it — causing an "Invalid input" error.

* Add missing agent:query scope declaration

The agent API query endpoint requires "agent:query" but it was never
registered via defscope, only its sub-scopes were.

* Block agent profiles by permission and add prompt gating to slackbot

Add profile-level permission checks in run-agent-loop that throw 403
when users lack required permissions for specialized profiles (:sql,
:nlq, :transforms_codegen, :document-generate-content). Add explicit
denial messages to internal.selmer and slackbot.selmer templates when
features are disabled, directing users toward available capabilities
like search instead.

* Enforce per-group metabot on/off switch and remove semantic-search permission

Add base permission/metabot check in check-metabot-access! that blocks
all agent profiles when the group-level metabot permission is :no.
Remove permission/metabot-semantic-search from the permissions model
and frontend. Fix permissions API test request body format and update
all-perm-types to match current permission set.

Done: task/enforce-metabot-group-onoff

* Integrate custom system prompts into internal, SQL, and NLQ profiles

Read the metabot-system-prompt admin setting in build-system-message-content
and pass it as a custom_instructions Selmer variable. Add {% if %} blocks
at the end of internal, sql-querying-only, and natural-language-querying-only
templates to render custom instructions when set. Use |safe filter to
avoid HTML-escaping admin-provided content.

Done: task/integrate-custom-system-prompts

* Added AI usage backend APIs and tables

* Add new system prompts pages

* Update the ai feature access page

* Update side nav for Usage controls menu item + Clean up

* Make the page headings margin top consistent

* Update Customization page / Remove metabot-tone

* Add default metabot permissions for magic user groups

Insert default metabot permission rows (all yes/medium) for the four
magic groups: Administrators, All Users, Data Analysts, and All Tenant
Users. Also fix missing remarks on group_id column in metabot_group_limit
table to pass migration linter.

* Add GET /api/metabot/permissions/user-permissions endpoint

Return the current user's resolved metabot permissions (most permissive
across all their groups). Superusers get all-yes permissions. Response
uses simplified key names without the permission/ namespace prefix.

Done: frontend-ai-permission-gating/phase-1-backend-user-permissions-endpoint

* Add useUserMetabotPermissions hook for granular AI permission checks

Add RTK Query endpoint for GET /api/metabot/permissions/user-permissions
and a useUserMetabotPermissions() hook that returns granular permission
booleans (canUseMetabot, canUseSqlGeneration, canUseNlq, canUseOtherTools)
combining the global metabot-enabled setting with per-user group permissions.

Done: frontend-ai-permission-gating/phase-2-frontend-permissions-hook

* Gate metabot UI entry points with granular user permissions

Replace useMetabotEnabledEmbeddingAware with useUserMetabotPermissions
in 6 primary metabot UI entry points: sidebar button (canUseMetabot),
AI exploration menu (canUseNlq), Fix SQL button and inline prompt
(canUseSqlGeneration), chart analysis and data studio buttons
(canUseMetabot). Update tests to mock the permissions API endpoint.

Done: frontend-ai-permission-gating/phase-3-gate-ui-entry-points

* Migrate remaining components from useMetabotEnabledEmbeddingAware to useUserMetabotPermissions

Replace useMetabotEnabledEmbeddingAware with useUserMetabotPermissions
in all remaining metabot UI components: Metabot shell, MetabotQueryBuilder
(canUseNlq), MetabotQuickLinks, AppBarLarge, CodeMirrorEditor
(canUseSqlGeneration), query builder context registration, MetabotEmbed,
and CommandSuggestion. Add permissions API mock to shared test utils and
all affected test files. SelectEmbedExperienceStep intentionally kept on
the old hook (uses forceEmbedding for embedding feature config check).

Done: frontend-ai-permission-gating/phase-4-migrate-remaining-embedding-aware-uses

* Remove usage limits menu item

* Fix migration to work with mysql

* Fix clj-kondo warnings on feat/ai-controls branch

- Add api-scope to mcp module and users to metabot module allowed deps
- Add response schema to /user-permissions defendpoint
- Suppress aliased-namespace-symbol warnings on potemkin import-vars
- Remove unused make-tool-var helper and prefix unused bindings with _
- Rename make-tool-var to make-tool-var! (uses alter-meta!)

* Address code review findings from clojure-review

- Add response schemas to GET / and PUT / permissions endpoints
- Add ^:parallel to 22 pure tests across 3 test files
- Move check-metabot-access! guard out of let binding for clarity
- Fix indentation, alignment, :refer :all, and stale kondo ignores

* Route agent API scope references through metabase.metabot.core

Re-export scope vars from metabase.metabot.core so external modules
use the public API namespace instead of metabot.scope directly.

* Address PR review comments

- Fix indent in api-scope module config
- Re-export workspace scopes from workspaces.core instead of using
  workspaces.scope directly in agent_api/workspace.clj
- Extract wrap-with-scope-check into its own function in tools.clj
- Fix comment indentation in core_test.clj

* Add 'Settings and general limits' section on Usage limits page

* Add 'Group and tenant limits' section

* Tighten user-permissions response schema with exact permission fields

Use keyword keys and enum values matching the defined permission types
instead of loose [:map-of :string :string]. Also fix simplify-permissions
to produce keyword keys/values instead of strings.

* Separate foreign keys and indexes into distinct migration steps

Extract inline foreign key constraints from createTable into their own
changesets so FKs can be dropped independently of indexes/unique
constraints. Affects metabot_permissions, metabot_instance_limit, and
metabot_group_limit tables.

* Added api_usage_log tracking table and populate it through usage

* Add api-scope to driver-affecting-overrides

api-scope is a simple scope-registry utility that only depends on util.
It was triggering driver tests via its enterprise/workspaces dependent.

* Connect the backend API / Remove dollar limit type

* Initial llm limit checks

* Fix CI failures from AI controls + master merge

- Add MetabotPermissions/GroupLimit/InstanceLimit to copy entities
- Fix module config: sort api-scope, remove extraneous APIs/uses,
  add PermissionsGroup to metabot model-imports
- Fix MCP scope-matches? returning false instead of nil on non-match
- Remove :capabilities gate from autogen dashboard tool
- Bind scope to unrestricted in profiles capability tests
- Update prompt gating tests to match master's template wording
- Update prompts test to match metabot_name template variable
- Update scope resolution tests for migration defaults (yes/medium)

* Remove usage limit code from AI controls branch

Usage limits (instance, tenant, and group) are being developed on a
separate branch. Remove all related backend code: models, API endpoints,
settings, migrations, and tests.

Done: ai-controls-to-enterprise/phase-1-remove-usage-limits

* Fix copy entities: deduplicate MetabotPrompt, move AI models to core section

* Move permission resolution and MetabotPermissions model to enterprise

Introduce the :ai-controls premium feature flag and restructure metabot
permission resolution for the OSS/EE split:

- Register enable-ai-controls? in premium_features/settings.clj
- Convert resolve-user-permissions to defenterprise (OSS returns defaults)
- Create EE implementation with full DB-backed group resolution
- Move MetabotPermissions Toucan2 model registration to EE namespace
- Keep pure permission type definitions and defaults in OSS
- Split tests: OSS verifies defaults, EE verifies group-based resolution

Done: ai-controls-to-enterprise/phase-2-defenterprise-permission-resolution

* Move permission admin APIs to enterprise with input validation

Move GET/PUT /api/metabot/permissions admin endpoints to
/api/ee/ai-controls/permissions, gated by :ai-controls feature.
Keep user-permissions endpoint in OSS. Add schema validation on
PUT perm_type/perm_value fields, fix 402 feature-gate test to use
correct assertion pattern, and add ^:parallel to all test defs.

* Move AI controls pages into new plugin

* Add error toast for instance limit update

* Remove unused permission/metabot-model (model size) feature

The metabot-model permission (large/medium/small) was defined
speculatively but never wired into model selection and had no UI.
Remove it from permission definitions, defaults, API schemas,
migration seed data, frontend types, and all tests.

Done: ai-controls-to-enterprise/phase-4-move-model-loading

* Gate metabot customization settings behind :ai-controls feature

Add :feature :ai-controls to branding settings (metabot-name,
metabot-icon, metabot-show-illustrations) and system prompt settings
(metabot-chat-system-prompt, metabot-nlq-system-prompt,
metabot-sql-system-prompt). Without the feature, reads return defaults
and writes are rejected.

Done: ai-controls-to-enterprise/phase-5-move-customization-settings

* Refactor MetabotPermissions: move model to OSS, replace deprecated APIs

- Move Toucan2 model registration from enterprise to
  metabase.metabot.models.metabot-permissions (OSS)
- Replace mdb/update-or-insert! with t2/exists?+update!/insert!
- Replace user/group-ids with a subquery join through
  PermissionsGroupMembership
- Update model resolution to point to OSS namespace

* Move MetabotPermissions model to EE, fix test failures for ai-controls feature

- Move MetabotPermissions Toucan2 model registration from OSS to
  enterprise (metabase-enterprise.metabot.models.metabot-permissions)
- Move permission defs (metabot-permissions, perm-types, perm-type-defaults)
  into metabase.metabot.scope alongside related permission/scope code
- Add :ai-controls to snowplow stats ping and session token features test
- Add MetabotPermissions to serialization excluded-models and entity-id
  not-exported lists
- Update serialization baseline with new metabot settings
- Fix modules config: add api routes, model imports/exports for
  enterprise/metabot; remove stale users dep from metabot module

* Fix kondo warnings in settings_test: use true?, remove ^:parallel

* Add usage limit models and API as enterprise-only endpoints

Restore the usage limit code (instance, tenant, and group limits) that
was removed in 2db9f363, now scoped under enterprise modules and the
/api/ee/ai-controls/usage/ route prefix with :ai-controls feature gate.

* Address clojure-review findings: per-type perm validation, privacy, metadata

- Validate perm_value per perm_type in PUT endpoint (not just flat enum)
- Make format-native-query private
- Add ^{:added "0.61.0"} to enable-ai-controls?
- Add clarifying comment on enforce-scope dev-time warning

* - Add AI usage limits enforcement, logging, and cleanup
- Rename metabot-limit-type to metabot-limit-unit
- Add scheduled task to trim ai_usage_log rows older than 3 months

* Move ai controls RTK endpoints to enterprise folder

* Fix input debounced state issue

* Clean up on Group and tenant limits tabs

* - Add AI usage limits enforcement, logging, and cleanup
- Rename metabot-limit-type to metabot-limit-unit
- Add scheduled task to trim ai_usage_log rows older than 3 months

* Cleaned up ai_controls migrations

* Merged from ai-controls

* Fix CI test failures caused by ai-controls permission system

The new check-metabot-access! gate in run-agent-loop rejects
non-superusers whose permissions default to :no. Honor pre-bound
*current-user-metabot-permissions* so tests can bind permissions
directly. Also fix scope error message expectation, gate custom
prompt settings behind :ai-controls feature, increase poll timeout
for closing-connection test, and split large deftests into smaller
parallel tests.

* Move EE-dependent permissions test to enterprise, ignore metabot_permissions in H2 dump comparison

The user-permissions test that creates :model/MetabotPermissions belongs
in EE tests since the model is EE-only. The metabot_permissions table is
created by migration in all editions but only included in H2 dumps when
EE is available, so add it to the ignored tables in the dump comparison
utility.

* Fixed namespace reference

* Make cache ttl be 10 seconds

* Fix FE test failures by mocking metabot user-permissions endpoint

The new useUserMetabotPermissions hook calls /api/metabot/permissions/user-permissions
in many component trees. Tests that render these components were failing due to
unmocked API calls. Added setupUserMetabotPermissionsEndpoint helper and called it
in each affected test's setup.

* Mark update-permissions-test as non-parallel to fix MariaDB CI failures

* Fix prettier formatting in use-user-metabot-permissions test

* Fix TypeScript errors: missing settings defaults, stale token feature refs, type mismatches

* Fix eslint: remove unused createMockTokenFeatures imports, fix import ordering

* Compute token max in millions of tokens

* If any groups have a null limit, return null as the limit

* Improve state handling on group and limits components

* Add unit tests

* Fix metabot/new quicklink / don't call user-permissions if metabot is globally disabled

* Remove test flakiness

* Address PR review comments: remove scope re-exports, use enums, split tests

- Remove potemkin re-exports from metabot.scope; consumers now use
  api-scope directly for scope-matches?, unrestricted, registered-scope?,
  all-scopes, and scope-description
- Use proper malli enums for perm_type/perm_value in EE permissions
  response schema instead of bare :keyword
- Sort required-feature->message alphabetically in EE routes
- Fix api-scope indent in kondo config
- Delete unused keep-me comment in EE metabot routes
- Split large deftests into focused tests in permissions_test,
  scope_resolution_test, and profiles_test
- Remove unnecessary blank lines between testing blocks in api_test

* Exclude new AI usage limit models from serialization and update module config

Add AiUsageLog, MetabotGroupLimit, MetabotInstanceLimit, and
MetabotPermissions to serialization excluded-models, entity-id
not-exported, and copy-test exclusion lists. Update enterprise/metabot
module config with routes API, app-db/users dependencies, and
PermissionsGroup model import. Add new metabot settings to
serialization baseline.

* Fix kondo warnings: sort namespace requires alphabetically

* Fix namespace require ordering in agent core

* Fix add missing init module

* Fix module config for AI usage limit models

Update enterprise/metabot module: remove stale app-db and users deps,
add AiUsageLog model import. Export AiUsageLog from metabot module.
Remove stale internal-stats.metabot from internal-stats API.

* Add e2e tests for basic ai controls

* Fix serialization baseline: rename metabot-limit-type to metabot-limit-unit

* Fix e2e tests

* Add e2e tests for ai usage limits

* Polish tests

* Fix e2e tests

* Fix bug on custom quota-reached message input

* Fix issues with auto-saving and debounced requests

* Address further review comments

* Set max limit number on inputs

* Fix some tests

* Fix flaky MetabotFeatureAccessPage test by awaiting permission state

* Fix unit test

* Add anthropic api mock to test group quota limit more properly

* Remove stale metabot dependency from testing-api module config

* Fix nql→nlq typo in permission type and OSS default permissions

The permission type `permission/metabot-nql` was misspelled throughout
the codebase (should be `nlq` for Natural Language Query). Also fixed
the prompt file reference `nql_search.md` → `nlq_search.md` which was
a runtime bug, and updated the OSS fallback for resolve-user-permissions
to return all-yes (permissive) instead of all-no defaults.

* Move DB query helpers from permissions API into MetabotPermissions model

Move permissions-for-group and all-permissions from the API namespace
into the model namespace where they belong as data-access functions.

* Fix flaky resolve-user-permissions-nil-test

Return all-yes-permissions for nil user-id in both OSS and EE paths
(no user means no restrictions). Wrap test with with-premium-features
to make it deterministic regardless of token presence.

* Fix defenterprise-schema nil return schema causing OSS test failures

defenterprise-schema for log-ai-usage! had no return schema, which
produced nil and crashed malli validation in OSS mode. This broke the
report-token-usage-xf transducer pipeline, preventing snowplow events
from firing in downstream transducers.

* Renamed "conversations" to "messages"

* Add tenant limits e2e test / Address a few other review comments

* Fix more review comments

* Clean up on other pages to reuse new hook

* Move translation maps outside their functions / Fix config.edn issue

* Do not reset limits when switching limit types

* Fix e2e test

---------

Co-authored-by: Nathan Voxland <nathan@metabase.com>
Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
Co-authored-by: Edward Paget <ed.paget@gmail.com>
2026-04-07 22:13:53 -03:00
Ryan Laurie
0b237d0918 Fix release permissions (#72140)
* more permission constriction

* more permission constriction
2026-04-07 17:07:57 -06:00
Ryan Laurie
b1819f54f4 update release workflow permissions (#72138) 2026-04-07 16:59:49 -06:00
Timothy S. Dean
69a5020367 Adjust CLAUDE.md backend test instructions (#72136) 2026-04-07 16:58:48 -06:00
Ryan Laurie
e01f66612a update release workflow permissions (#72137) 2026-04-07 16:33:15 -06:00
github-automation-metabase
69a776ea2d Auto-update documentation for master (#72131)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-07 17:23:02 -04:00
Oleksandr Solovyov
3671d3bd80 metabot: ability to make requests via ai proxy w/o having api key configured (#71714)
* metabot: ability to make requests via ai proxy w/o having api key configured

* modernize logic: BYOK is more important than ai proxy

* dry error messages and tests

* metabot token reporting

* byok setting

* track that ai-proxied messages are marked in the db

* only count ai_proxied in tokens reported

* fix ci concerns

* metabase/ as indicator of requests going through ai-proxy

* Port resolve-auth and test fixes from #71655

* Add claude-list-models-auth-preferences-test similar to claude-auth-preferences

* Add conventional -test suffix to auth-preferences tests

* BOT-1262 Add :feature :metabase-ai-provider guard to llm-proxy-base-url defsetting

Rather than moving the defsetting into ee add a :feature :metabase-ai-provider guard and leave it in oss.

* Fix trailing whitespace

* Fix logic in llm-provider-configured?

Logic now matches metabase.metabot.self.core/resolve-auth, i.e. no proxy fallback if llm-metabot-provider is unconfigured.

* Remove unused func

* Add more tests for metabase proxied models

Add tests for:
- validate-metabot-provider!
- /list-models endpoint provider resolution for both direct and metabase/ prefix
- slackbot streaming ai-proxy? propagation to store-message!

* Add metabot.provider-util namespace for parsing model-and-provider strings

And use it instead of ad-hoc parsing with str/split in various places.

* Remove unused metabot.core/routes

Re-exported from metabot.api/routes but nothing references it.

* Prefer with-redefs over mt/with-temporary-setting-values in tests to override env vars too

Specifically for tests that set llm-anthropic-api-key to ignore any value set in the env.

* Fix token usage reporting for openai

OpenAI returns token usage in :input_tokens and :output_tokens, map those back to out internal :promptTokens and
:completionTokens, respectively and also drop any nested token usage *_details that get returned to prevent an
exception in accumulate-usage-xf when attempting to (merge-with +).

    java.lang.ClassCastException: class clojure.lang.PersistentArrayMap cannot be cast to class java.lang.Number (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')
	at clojure.lang.Numbers.add(Numbers.java:155)
	at clojure.core$_PLUS_.invokeStatic(core.clj:994)
	at clojure.core$_PLUS_.invoke(core.clj:986)
	at clojure.core$merge_with$merge_entry__6053.invoke(core.clj:3087)
	at clojure.core$reduce1.invokeStatic(core.clj:946)

* Review feedback: use str/replace-first

* Review feedback: ensure metabot-provider-schema matches supported-metabot-providers

---------

Co-authored-by: Mike Appleby <86076+appleby@users.noreply.github.com>
2026-04-07 13:25:37 -07:00
Tyler Plude
742b2190cd [BOT-1272] Fix missing metabot-id in streaming request (#72114)
pass metabot id to native streaming; unit test
2026-04-07 13:22:05 -07:00
Alex Yarosh
9b76829304 docs: the great detypoing (#72100)
* typos

* embedding
2026-04-07 13:15:59 -04:00
Jeff Bruemmer
0ffb9c891b docs - clarify one dashboard per page (#72098)
* clarify one dashboard per page

* questions
2026-04-07 13:14:34 -04:00
lbrdnk
a0a787ea54 Convert legacy into mbql5 on API level (#71944)
* Convert legacy into mbql5 on API level

* tests

* idempotence

* whitespace

* Review comments
2026-04-07 19:08:10 +02:00
Nathan Voxland
9f7c0c3a27 Search indexing memory and CPU improvements (#71999) 2026-04-07 11:30:01 -05:00
Cam Saul
4ae75fc5e6 Replace leftover usages of 'pMBQL' with 'MBQL 5' (#72066)
* Replace the term 'pMBQL' with 'MBQL 5'

* Restore deleted file
2026-04-07 09:29:27 -07:00
John Swanson
b1734f09ae No "login from new device" emails for to_session (#69854)
* No "login from new device" emails for `to_session`

Prevent the /api/sso/to_session endpoint from sending "login from new
device" emails. This endpoint is used for programmatic JWT-to-session
token exchange, so new device notifications are noise. Login history is
still recorded for audit purposes.

* kondo fix

* fix tests
2026-04-07 09:10:17 -07:00
Jeff Bruemmer
84329d9f08 Fix typo in settings documentation (#72074) 2026-04-07 11:30:04 -04:00
Timofey Kachalov
acfdee530b fix(sdk,embed-js): Fix wrong join on dropdown items styles (#71993) 2026-04-07 19:20:18 +04:00
Nick Fitzpatrick
d029b634ae Metric Viewer Menu Dividers (#71906) 2026-04-07 17:34:38 +05:30
Nick Fitzpatrick
122500f230 Update strings in metric view (#71912) 2026-04-07 17:34:27 +05:30
Nick Fitzpatrick
277d629317 Update Measure Icon in Data Studio and Metric Explorer (#71924) 2026-04-07 17:34:17 +05:30
Nick Fitzpatrick
05a0cd9db2 Hide the separator in breakout menu if not needed (#71937) 2026-04-07 17:34:05 +05:30
YB
12e3515671 GDGT-1431 Delete notifications orphaned from deleted cards (#71897)
When a card with an alert is permanently deleted, the Notification record and its Quartz cron trigger survive.
The trigger keeps firing and crashing every time, clogging logs and task history indefinitely.

The root cause is that Card's before-delete hook cleans up ParameterCards, ModerationReviews, and Revisions — but not Notifications. The notification_card table has a deleteCascade FK to report_card, so the NotificationCard payload row gets cascade-deleted, but the Notification row itself survives with a dangling payload_id.

There's 2 fixes in this PR:

Add notification cleanup to Card's before-delete hook. Materializes notification IDs first because Notification's own before-delete deletes the NotificationCard, which would invalidate a delete that depends on a subquery by the time the actual DELETE executes.

For notifications orphaned before this fix is deployed, guard in send-notification-sync! checks if the NotificationCard payload still exists before hydration. If not, it deletes the notification and throws a clean error.
We fully delete rather than deactivate (setting :active false) because an orphaned notification with a deleted card has no value — its handlers, subscriptions, and recipients would just be dead rows. This matches what happens when users manually delete an alert via delete-alert-and-notify!.
2026-04-07 10:02:06 +08:00
technomancy
69c17552f7 Add technomancy (me) to slack and GH team map json. (#71698)
Add technomancy (me) to slack map json file and github team map.

This is one of the steps of the onboarding guide for day 1.

[ci skip]
2026-04-06 16:36:27 -07:00
github-automation-metabase
3dc0f3eb08 Auto-update documentation for master (#72064)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-06 17:19:12 -04:00
Uladzimir Havenchyk
fcde592db4 deps: upgrade markdown-it (#71984) 2026-04-06 23:59:54 +03:00
Uladzimir Havenchyk
05fd40e2fb deps: upgrade dompurify (#71986) 2026-04-06 23:35:06 +03:00
bryan
351e4be6c8 Bundle airgap public key in standard Metabase JAR (#72046) 2026-04-06 19:39:54 +00:00
bryan
3cba66ca73 Guard export-viz-click-behavior-link against nil link models (#72003) 2026-04-06 15:54:46 +00:00
John Swanson
85de150a57 Increase debounce delay to 500ms (#70083)
[UXW-3154: Increase debounce delay for search API calls](https://linear.app/metabase/issue/UXW-3154/increase-debounce-delay-for-search-api-calls)

The current delay (300ms) is a bit too short. Increasing it to 500ms
should still be a snappy UX but send fewer requests while the user is
typing.
2026-04-06 15:16:11 +00:00
Braden Shepherdson
d4821e78e3 [MBQL lib] Add user docs for :fields lists (#71444) 2026-04-06 14:02:19 +00:00
Uladzimir Havenchyk
b34aa1c692 chore: upgrade qs (#70785)
deps: upgrade qs
2026-04-06 14:49:53 +03:00
Uladzimir Havenchyk
27e2412d69 deps: upgrade jspdf (#71987) 2026-04-06 13:04:36 +03:00
Uladzimir Havenchyk
f414178f3f deps: upgrade diff (#71981) 2026-04-06 13:03:19 +03:00
Uladzimir Havenchyk
492c1f3ae6 deps: upgrade lodash and lodash-es (#71982) 2026-04-06 13:02:57 +03:00
Uladzimir Havenchyk
5489604670 chore: replace vulnerable underscore with a util (#70784)
* chore: replace vulnerable underscore with a util

* missed another function

* remove underscore
2026-04-06 12:56:55 +03:00
metamben
cdcf995f5f Use effective/base type info for casts if database-type is missing (#71771)
* Use effective/base type info for casts if database-type is missing

Closes #68065
Closes #71551
2026-04-06 09:52:19 +03:00
Romeo Van Snick
9db030e42c Fix flaky test in unreferenced entries (#71134)
* Fix flaky test

* Fix flaky test

* Fix flaky test

* Add reference for table too
2026-04-05 09:20:57 +02:00
Anderson
672bd07e35 Metabot AI Controls (#71387)
* Implement static UI for Usage controls page

* Added initial ai-controls tables

* Set sidenav menu items / Add placeholders for new pages

* Implement Metabot Customization page UI

* - Added metabot/permissions GET and PUT apis
- Added settings for metabot name/tone/icon

* Fixed migration ids

* fixup! bad merge

* Add :scope metadata to all Agent Loop tools and create metabase.metabot.scope namespace

- Create metabase.metabot.scope with *current-user-scope* (defaults to #{}),
  scope-matches? with hierarchical wildcard support, and unrestricted set
- Add :scope to all 38 Agent Loop tool-fns with specific action scopes
  (e.g. "agent:sql:create", "agent:viz:edit") — wildcards are grant-side only
- Propagate :scope through wrap-tools-with-state in tools.clj
- :capabilities remain unchanged (orthogonal data-permission gating)
- Add tests for scope-matches?, default scope, and stub

Done: tool-scope/phase-1-tool-scope-declarations

* Add scope-based authorization middleware for Agent Loop tools

- Filter tools by scope in get-tools-for-profile (profiles.clj)
- Check scope before tool invocation in wrap-tools-with-state (tools.clj),
  returning a generic denial message (scope details logged, not leaked)
- Bind *current-user-scope* to unrestricted in run-agent-loop (temporary
  until phase 4 wires real permission resolution)
- Add scope enforcement tests covering filtering, denial, wildcards,
  no-scope passthrough, and default empty scope behavior

Done: tool-scope/phase-2-authorization-middleware

* Gate profile system prompts by metabot permission types

- Add *current-user-metabot-permissions* dynamic var to scope.clj
- Bind all-yes permissions in run-agent-loop (temporary, phase 4 wires real resolution)
- Derive has_sql_generation, has_nql, has_query_tools, has_other_tools
  boolean flags from permissions and pass to Selmer template context
- Gate internal.selmer sections: critical constraints, verification protocol,
  query building steps, SQL construction, SQL anti-patterns, SQL examples,
  NLQ guidance, dashboard routing, and query-focused common scenarios
- General sections (identity, context grounding, communication style, search
  example, entity linking) remain ungated for all permission levels
- 7 tests, 43 assertions covering all permission combinations

Done: tool-scope/phase-3-profile-prompt-gating

* Wire user-metabot-perms->scopes to metabot_permissions data model

- Implement resolve-user-permissions: looks up user's groups, queries
  MetabotPermissions, resolves most-permissive value across groups
- Implement real user-metabot-perms->scopes: maps :yes permissions to
  wildcard scope sets, always includes always-granted-scopes
- Replace temporary unrestricted/all-yes bindings in run-agent-loop with
  real permission resolution from api/*current-user-id*
- Superusers get scope/unrestricted and all-yes-permissions
- Permission resolution done once at reify construction, not per-reduce
- Agent API scope binding deferred (tracked in rdm task agent-api-scope-binding)
- 38 new test assertions covering permission→scope mapping, single-group,
  multi-group most-permissive-wins, and default scenarios

Done: tool-scope/phase-4-permissions-to-scopes

* Update permissions expected request format

* Include metabot and semantic-search tool options on the backend

* Connect access controls UI to the backend

* Wire up the backend for the Metabot Customize page

* Polish access controls page / Create MetabotSystemPromptsPage

* Added metabot-system-prompt

* Connect System prompt page to the backend

* Remove model selection + clean up

* Use custom metabot name

* Use metabot-icon in the UI + added metabot-show-illustrations toggle

* Removed metabase custom prompt

* Add defscope macro, scope registry, and api-scope module

Introduce a `defscope` macro that registers scopes with translatable
descriptions in a global registry, replacing inline scope strings across
all tool and endpoint metadata with typed var references.

Key changes:
- New `metabase.api-scope.core` module with defscope, scope registry,
  scope-matches?, and public API (all-scopes, scope-description)
- All 25 agent scopes declared via defscope in metabase.metabot.scope
- 3 EE workspace scopes declared in metabase-enterprise.workspaces.scope
- validate-tool-var! rejects tools with unregistered scopes at startup
- defendpoint accepts var references for :scope metadata (not just strings)
- enforce-scope logs dev-time warnings for unregistered endpoint scopes
- MCP scope enforcement now handled by defendpoint middleware (removed
  redundant pre-dispatch check); enforce-authentication preserves existing
  token-scopes on synthetic requests
- Scope enforcement error messages no longer leak required scope names
- Consolidated duplicate scope-matching logic in mcp/tools.clj

Done: tool-scope/phase-5-defscope-registry

* Make semantic_queries optional in search tool schemas

When semantic search is not enabled, the feature gate strips
semantic_queries from the tool definition sent to the LLM, but Malli
validation still required it — causing an "Invalid input" error.

* Add missing agent:query scope declaration

The agent API query endpoint requires "agent:query" but it was never
registered via defscope, only its sub-scopes were.

* Block agent profiles by permission and add prompt gating to slackbot

Add profile-level permission checks in run-agent-loop that throw 403
when users lack required permissions for specialized profiles (:sql,
:nlq, :transforms_codegen, :document-generate-content). Add explicit
denial messages to internal.selmer and slackbot.selmer templates when
features are disabled, directing users toward available capabilities
like search instead.

* Enforce per-group metabot on/off switch and remove semantic-search permission

Add base permission/metabot check in check-metabot-access! that blocks
all agent profiles when the group-level metabot permission is :no.
Remove permission/metabot-semantic-search from the permissions model
and frontend. Fix permissions API test request body format and update
all-perm-types to match current permission set.

Done: task/enforce-metabot-group-onoff

* Integrate custom system prompts into internal, SQL, and NLQ profiles

Read the metabot-system-prompt admin setting in build-system-message-content
and pass it as a custom_instructions Selmer variable. Add {% if %} blocks
at the end of internal, sql-querying-only, and natural-language-querying-only
templates to render custom instructions when set. Use |safe filter to
avoid HTML-escaping admin-provided content.

Done: task/integrate-custom-system-prompts

* Added AI usage backend APIs and tables

* Add new system prompts pages

* Update the ai feature access page

* Update side nav for Usage controls menu item + Clean up

* Make the page headings margin top consistent

* Update Customization page / Remove metabot-tone

* Add default metabot permissions for magic user groups

Insert default metabot permission rows (all yes/medium) for the four
magic groups: Administrators, All Users, Data Analysts, and All Tenant
Users. Also fix missing remarks on group_id column in metabot_group_limit
table to pass migration linter.

* Add GET /api/metabot/permissions/user-permissions endpoint

Return the current user's resolved metabot permissions (most permissive
across all their groups). Superusers get all-yes permissions. Response
uses simplified key names without the permission/ namespace prefix.

Done: frontend-ai-permission-gating/phase-1-backend-user-permissions-endpoint

* Add useUserMetabotPermissions hook for granular AI permission checks

Add RTK Query endpoint for GET /api/metabot/permissions/user-permissions
and a useUserMetabotPermissions() hook that returns granular permission
booleans (canUseMetabot, canUseSqlGeneration, canUseNlq, canUseOtherTools)
combining the global metabot-enabled setting with per-user group permissions.

Done: frontend-ai-permission-gating/phase-2-frontend-permissions-hook

* Gate metabot UI entry points with granular user permissions

Replace useMetabotEnabledEmbeddingAware with useUserMetabotPermissions
in 6 primary metabot UI entry points: sidebar button (canUseMetabot),
AI exploration menu (canUseNlq), Fix SQL button and inline prompt
(canUseSqlGeneration), chart analysis and data studio buttons
(canUseMetabot). Update tests to mock the permissions API endpoint.

Done: frontend-ai-permission-gating/phase-3-gate-ui-entry-points

* Migrate remaining components from useMetabotEnabledEmbeddingAware to useUserMetabotPermissions

Replace useMetabotEnabledEmbeddingAware with useUserMetabotPermissions
in all remaining metabot UI components: Metabot shell, MetabotQueryBuilder
(canUseNlq), MetabotQuickLinks, AppBarLarge, CodeMirrorEditor
(canUseSqlGeneration), query builder context registration, MetabotEmbed,
and CommandSuggestion. Add permissions API mock to shared test utils and
all affected test files. SelectEmbedExperienceStep intentionally kept on
the old hook (uses forceEmbedding for embedding feature config check).

Done: frontend-ai-permission-gating/phase-4-migrate-remaining-embedding-aware-uses

* Remove usage limits menu item

* Fix migration to work with mysql

* Fix clj-kondo warnings on feat/ai-controls branch

- Add api-scope to mcp module and users to metabot module allowed deps
- Add response schema to /user-permissions defendpoint
- Suppress aliased-namespace-symbol warnings on potemkin import-vars
- Remove unused make-tool-var helper and prefix unused bindings with _
- Rename make-tool-var to make-tool-var! (uses alter-meta!)

* Address code review findings from clojure-review

- Add response schemas to GET / and PUT / permissions endpoints
- Add ^:parallel to 22 pure tests across 3 test files
- Move check-metabot-access! guard out of let binding for clarity
- Fix indentation, alignment, :refer :all, and stale kondo ignores

* Route agent API scope references through metabase.metabot.core

Re-export scope vars from metabase.metabot.core so external modules
use the public API namespace instead of metabot.scope directly.

* Address PR review comments

- Fix indent in api-scope module config
- Re-export workspace scopes from workspaces.core instead of using
  workspaces.scope directly in agent_api/workspace.clj
- Extract wrap-with-scope-check into its own function in tools.clj
- Fix comment indentation in core_test.clj

* Tighten user-permissions response schema with exact permission fields

Use keyword keys and enum values matching the defined permission types
instead of loose [:map-of :string :string]. Also fix simplify-permissions
to produce keyword keys/values instead of strings.

* Separate foreign keys and indexes into distinct migration steps

Extract inline foreign key constraints from createTable into their own
changesets so FKs can be dropped independently of indexes/unique
constraints. Affects metabot_permissions, metabot_instance_limit, and
metabot_group_limit tables.

* Add api-scope to driver-affecting-overrides

api-scope is a simple scope-registry utility that only depends on util.
It was triggering driver tests via its enterprise/workspaces dependent.

* Fix CI failures from AI controls + master merge

- Add MetabotPermissions/GroupLimit/InstanceLimit to copy entities
- Fix module config: sort api-scope, remove extraneous APIs/uses,
  add PermissionsGroup to metabot model-imports
- Fix MCP scope-matches? returning false instead of nil on non-match
- Remove :capabilities gate from autogen dashboard tool
- Bind scope to unrestricted in profiles capability tests
- Update prompt gating tests to match master's template wording
- Update prompts test to match metabot_name template variable
- Update scope resolution tests for migration defaults (yes/medium)

* Remove usage limit code from AI controls branch

Usage limits (instance, tenant, and group) are being developed on a
separate branch. Remove all related backend code: models, API endpoints,
settings, migrations, and tests.

Done: ai-controls-to-enterprise/phase-1-remove-usage-limits

* Fix copy entities: deduplicate MetabotPrompt, move AI models to core section

* Move permission resolution and MetabotPermissions model to enterprise

Introduce the :ai-controls premium feature flag and restructure metabot
permission resolution for the OSS/EE split:

- Register enable-ai-controls? in premium_features/settings.clj
- Convert resolve-user-permissions to defenterprise (OSS returns defaults)
- Create EE implementation with full DB-backed group resolution
- Move MetabotPermissions Toucan2 model registration to EE namespace
- Keep pure permission type definitions and defaults in OSS
- Split tests: OSS verifies defaults, EE verifies group-based resolution

Done: ai-controls-to-enterprise/phase-2-defenterprise-permission-resolution

* Move permission admin APIs to enterprise with input validation

Move GET/PUT /api/metabot/permissions admin endpoints to
/api/ee/ai-controls/permissions, gated by :ai-controls feature.
Keep user-permissions endpoint in OSS. Add schema validation on
PUT perm_type/perm_value fields, fix 402 feature-gate test to use
correct assertion pattern, and add ^:parallel to all test defs.

* Move AI controls pages into new plugin

* Remove unused permission/metabot-model (model size) feature

The metabot-model permission (large/medium/small) was defined
speculatively but never wired into model selection and had no UI.
Remove it from permission definitions, defaults, API schemas,
migration seed data, frontend types, and all tests.

Done: ai-controls-to-enterprise/phase-4-move-model-loading

* Gate metabot customization settings behind :ai-controls feature

Add :feature :ai-controls to branding settings (metabot-name,
metabot-icon, metabot-show-illustrations) and system prompt settings
(metabot-chat-system-prompt, metabot-nlq-system-prompt,
metabot-sql-system-prompt). Without the feature, reads return defaults
and writes are rejected.

Done: ai-controls-to-enterprise/phase-5-move-customization-settings

* Refactor MetabotPermissions: move model to OSS, replace deprecated APIs

- Move Toucan2 model registration from enterprise to
  metabase.metabot.models.metabot-permissions (OSS)
- Replace mdb/update-or-insert! with t2/exists?+update!/insert!
- Replace user/group-ids with a subquery join through
  PermissionsGroupMembership
- Update model resolution to point to OSS namespace

* Move MetabotPermissions model to EE, fix test failures for ai-controls feature

- Move MetabotPermissions Toucan2 model registration from OSS to
  enterprise (metabase-enterprise.metabot.models.metabot-permissions)
- Move permission defs (metabot-permissions, perm-types, perm-type-defaults)
  into metabase.metabot.scope alongside related permission/scope code
- Add :ai-controls to snowplow stats ping and session token features test
- Add MetabotPermissions to serialization excluded-models and entity-id
  not-exported lists
- Update serialization baseline with new metabot settings
- Fix modules config: add api routes, model imports/exports for
  enterprise/metabot; remove stale users dep from metabot module

* Fix kondo warnings in settings_test: use true?, remove ^:parallel

* Address clojure-review findings: per-type perm validation, privacy, metadata

- Validate perm_value per perm_type in PUT endpoint (not just flat enum)
- Make format-native-query private
- Add ^{:added "0.61.0"} to enable-ai-controls?
- Add clarifying comment on enforce-scope dev-time warning

* Cleaned up ai_controls migrations

* Fix CI test failures caused by ai-controls permission system

The new check-metabot-access! gate in run-agent-loop rejects
non-superusers whose permissions default to :no. Honor pre-bound
*current-user-metabot-permissions* so tests can bind permissions
directly. Also fix scope error message expectation, gate custom
prompt settings behind :ai-controls feature, increase poll timeout
for closing-connection test, and split large deftests into smaller
parallel tests.

* Move EE-dependent permissions test to enterprise, ignore metabot_permissions in H2 dump comparison

The user-permissions test that creates :model/MetabotPermissions belongs
in EE tests since the model is EE-only. The metabot_permissions table is
created by migration in all editions but only included in H2 dumps when
EE is available, so add it to the ignored tables in the dump comparison
utility.

* Fix FE test failures by mocking metabot user-permissions endpoint

The new useUserMetabotPermissions hook calls /api/metabot/permissions/user-permissions
in many component trees. Tests that render these components were failing due to
unmocked API calls. Added setupUserMetabotPermissionsEndpoint helper and called it
in each affected test's setup.

* Mark update-permissions-test as non-parallel to fix MariaDB CI failures

* Fix prettier formatting in use-user-metabot-permissions test

* Fix TypeScript errors: missing settings defaults, stale token feature refs, type mismatches

* Fix eslint: remove unused createMockTokenFeatures imports, fix import ordering

* Add unit tests

* Fix metabot/new quicklink / don't call user-permissions if metabot is globally disabled

* Remove test flakiness

* Address PR review comments: remove scope re-exports, use enums, split tests

- Remove potemkin re-exports from metabot.scope; consumers now use
  api-scope directly for scope-matches?, unrestricted, registered-scope?,
  all-scopes, and scope-description
- Use proper malli enums for perm_type/perm_value in EE permissions
  response schema instead of bare :keyword
- Sort required-feature->message alphabetically in EE routes
- Fix api-scope indent in kondo config
- Delete unused keep-me comment in EE metabot routes
- Split large deftests into focused tests in permissions_test,
  scope_resolution_test, and profiles_test
- Remove unnecessary blank lines between testing blocks in api_test

* Fix kondo warnings: sort namespace requires alphabetically

* Fix namespace require ordering in agent core

* Add e2e tests for basic ai controls

* Fix e2e tests

* Fix issues with auto-saving and debounced requests

* Address further review comments

* Fix some tests

* Fix flaky MetabotFeatureAccessPage test by awaiting permission state

* Fix nql→nlq typo in permission type and OSS default permissions

The permission type `permission/metabot-nql` was misspelled throughout
the codebase (should be `nlq` for Natural Language Query). Also fixed
the prompt file reference `nql_search.md` → `nlq_search.md` which was
a runtime bug, and updated the OSS fallback for resolve-user-permissions
to return all-yes (permissive) instead of all-no defaults.

* Move DB query helpers from permissions API into MetabotPermissions model

Move permissions-for-group and all-permissions from the API namespace
into the model namespace where they belong as data-access functions.

* Fix flaky resolve-user-permissions-nil-test

Return all-yes-permissions for nil user-id in both OSS and EE paths
(no user means no restrictions). Wrap test with with-premium-features
to make it deterministic regardless of token presence.

---------

Co-authored-by: Nathan Voxland <nathan@metabase.com>
Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
Co-authored-by: Edward Paget <ed.paget@gmail.com>
2026-04-03 16:42:29 -07:00
Alex Yarosh
c05bc7d17b docs: stacked series and sticky columns (#71952)
* v60 viz improvements

* Optimized 4 images, saved 715.3 KB (43.9% smaller)

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* Update docs/questions/visualizations/line-bar-and-area-charts.md

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-04-03 22:19:50 +00:00
github-automation-metabase
eb3ef366ad Auto-update documentation for master (#72004)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-03 21:16:12 +00:00
Edward Paget
d1d42b9a22 Fix missing :lib/uuid in join metric projection tests (#72000)
The two tests added in #71741 were missing required :lib/uuid keys
in their projection maps, causing API schema validation failures.
2026-04-03 20:03:00 +00:00
John Swanson
c49a43c8fe support expr with multiple instances of same metric (#71930)
support expr with multiple instances of same metric
2026-04-03 09:57:23 -08:00
John Swanson
1f90a2172f Fix metric explorer for metrics with joins (#71741) 2026-04-03 09:56:45 -08:00
Nemanja Glumac
cad0841958 Fix Trivy sarif failing to upload (#71994) 2026-04-03 17:02:22 +00:00
Nemanja Glumac
69fad10cda Fix unsafe inline expression interpolation in CI workflows (#71991)
* fix: extract 20 unsafe expressions from run blocks to env vars

Move ${{ }} expressions used in shell `run:` blocks into step-level
`env:` mappings to prevent script injection. Affected workflows:
athena, drivers, e2e-stress-test, frontend-unit-test-stress-test,
perf-test, pr-env, pr-env-destroy, release-embedding-sdk, repro-bot,
tag-for-release.

This is part 2 of a split from #71610 as requested.

* fix: use C-style for loop for burn_in variable expansion

Bash brace expansion ({1..N}) happens before variable expansion, so
{1..$VAR} doesn't expand as a range. Use a C-style for loop instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: extract inline PGPASSWORD secret to env var

The PR_ENV_DB_PASSWORD was left inline in run blocks while other
secrets in the same steps were already extracted to env mappings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: rename REF_NAME env var to IS_MASTER_OR_RELEASE in drivers.yml

The variable holds a boolean expression result, not a ref name. The
new name matches the CLI flag it's passed to (--is-master-or-release).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: extract remaining inline workflow_dispatch inputs to env vars

Extracts spec, grep, burn_in, version, and issue_number inputs that
were still interpolated directly in run blocks. Also simplifies env
var names by dropping the redundant INPUT_ prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: place env block before run block in athena.yml

Consistent with the convention used in all other steps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Chris Nyhuis <cnyhuis@vigilantnow.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:35:30 +02:00
Mahatthana (Kelvin) Nomsawadi
e82a4d027c Add token renewal mechanism for guest embeds (#71075)
* An empty commit for the integration branch

* Extract JWT utilities into dedicated lib/jwt.ts module (#71515)

* Extract JWT utilities into dedicated lib/jwt.ts module

Moves `isJWT`, `decodeJwt`, `extractResourceIdFromJwtToken`, and `redactJwt`
from `lib/utils.ts` into a new `lib/jwt.ts`. Improves JWT decoding with safer
UTF-8 handling via `TextDecoder`. Updates all import sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Rename utils.unit.spec.ts to jwt.unit.spec.ts after moving extractResourceIdFromJwtToken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Remove redactJwt and show full error strings in JWT error messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix AI slop

* Fix unit test

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix SdkError too wide (#71569)

* Extract JWT utilities into dedicated lib/jwt.ts module

Moves `isJWT`, `decodeJwt`, `extractResourceIdFromJwtToken`, and `redactJwt`
from `lib/utils.ts` into a new `lib/jwt.ts`. Improves JWT decoding with safer
UTF-8 handling via `TextDecoder`. Updates all import sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Rename utils.unit.spec.ts to jwt.unit.spec.ts after moving extractResourceIdFromJwtToken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Remove redactJwt and show full error strings in JWT error messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix AI slop

* Fix unit test

* Add Storybook stories for SdkError component

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix mage error

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add token renewal infrastructure for guest embeds (#71516)

* Add token renewal infrastructure for guest embeds

Implements the full token renewal mechanism across three layers:

- embed.ts: adds _callGuestTokenProvider(), _fetchInitialGuestToken(), and
  _refreshGuestToken() to MetabaseEmbedElement; fetches tokens from
  guestEmbedProviderUri and serves refresh requests via postMessage

- request-session-token.ts + Redux store (auth.ts, guest-embed.ts): SDK-side
  postMessage protocol for requestGuestTokenRefresh / submitRefreshedGuestToken;
  getOrRefreshGuestSession thunk checks JWT expiry and refreshes transparently

- Plugin hooks (api.ts, embedding-sdk.ts, api.js, override-requests-for-embeds):
  wires refresh handler into the API request pipeline; adds x-metabase-guest-embed
  header and :entityIdentifier URL substitution

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* clean up AI slop

* Fix another AI slop

* Fix more slop

* fix embed wizard parameter not affecting the preview for 1 render

* Fix string custom-context not working

* Address review feedback

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Wire token renewal into dashboard/question components and add E2E tests (#71517)

* Wire token renewal into dashboard/question components and add E2E tests

- SdkDashboard and SdkQuestionProvider now dispatch setInitialGuestToken on
  mount, storing the raw JWT in Redux so the request interceptor can refresh it
  transparently when it expires
- use-load-question and use-common-dashboard-params updated for guest embed mode
- SdkIframeEmbedRoute: new route component for the iframe embed SDK
- guest-token-refresh.cy.spec.ts: comprehensive E2E Cypress suite covering
  token expiry, automatic refresh, error paths for both iframe and SDK modes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix _fetchInitialGuestToken to omit dashboardId/questionId from settings

In the guest embed token flow the JWT already encodes the resource, so
dashboardId and questionId should not be forwarded to the iframe via
setSettings. Pass them as undefined to override the values that
_updateSettings merges in from this.properties.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix Kelvin slop

* Make E2E tests consistent

* Make test more complete

* add custom context assertions

* address review feedback

* Address review

* Fix failed E2E tests

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix that one test that's always failing

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 16:00:46 +01:00
Mahatthana (Kelvin) Nomsawadi
7f785d6c98 Fix flaky metrics-editing.cy.spec.js test (#71983)
Fix that one test that's always failing
2026-04-03 15:08:36 +01:00
Timofey Kachalov
207548a864 Handle multi-touch events to improve pinch-to-zoom functionality on mobile devices for visualizations (#71932) 2026-04-03 15:52:44 +04:00
Sébastien
2a7c8331be fix(sdk-bundle): <metabase-question/> in guest embed no longer calls pulseform input (#71885) 2026-04-03 11:17:04 +02:00
dagecko
c27db353ba fix: pin 87 unpinned actions to full commit SHAs (#71913)
Pin all third-party GitHub Actions to immutable 40-character commit
SHAs to prevent supply chain attacks via tag rewriting. Version
comments added for readability.
2026-04-03 10:47:55 +02:00
Timofey Kachalov
63d5eb845a fix(sdk,embed-js): Adjust disabled state opacity for EditableText component (#71836) 2026-04-03 12:44:34 +04:00
Eric Normand
cbc40606ef Block database routing when transforms already exist (#71931)
Prevent enabling database routing on a database that already has transforms, since running transforms on databases with DB routing is not supported
Backend: added validation check in PUT /api/ee/database-routing/router-database/:id that returns 400 if transforms exist for the database
Frontend: disables the routing toggle in the UI when transforms exist, with a descriptive message
Also wired through the existing but unused database-id filter param in the transforms list API
2026-04-03 00:07:14 +00:00
github-automation-metabase
a7ca6bbca8 Auto-update documentation for master (#71949)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-02 18:06:12 -04:00
Alex Yarosh
a63333ac04 docs: metrics (#71778)
* first draft

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* viewer

* metrics edits

* metrics explorer

* data studio

* Update docs/data-modeling/metrics.md

* typo

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* edits

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-04-02 22:04:37 +00:00
John Swanson
93db322a84 Hide dimensions and filters without access in the metric explorer (#71692) 2026-04-02 13:50:36 -08:00
Alexander Polyankin
940c3e343f fix escape logic in serdes (#71940)
* fix escape logic in serdes

* fix

* fixes
2026-04-02 21:11:30 +00:00
Edward Paget
9f5aaf4a81 Add GitHub Action to auto-resolve backport conflicts with Claude (#71158)
* Add GitHub Action to auto-resolve backport conflicts with Claude

Adds a new workflow triggered by the `auto-resolve-conflicts` label (or
manual dispatch) that uses Claude Code to automatically resolve cherry-pick
conflicts on backport PRs. This eliminates the manual step of checking out
the branch, running backport.sh, and hand-resolving conflicts.

* Add kondo and typescript checks

* Add syntax validation and formatting to backport conflict resolver

- Prepare backend (JDK/Clojure) and frontend (Node/Bun) environments
- Run cljfmt and eslint --fix to auto-format resolved files
- Run clj-kondo and tsc to validate syntax before completing cherry-pick
- Claude loops on errors until files are clean before committing

* Make Claude max turns configurable in backport conflict resolver, default to 50

* Fix Bash permission pattern for cljfmt in backport conflict resolver

Use colon-star syntax (cljfmt:*) to match other Bash tool permission
patterns, fixing permission denial for ./bin/mage cljfmt-files.

* Detect yarn vs bun for older release branches in backport resolver

Release branches ≤58 use yarn, ≥59 use bun. Detect the package manager
by checking for bun.lock after checkout, then use it for the CLJS build
step, allowed_tools permissions, and Claude prompt instructions.

* Set NODE_VERSION=22 for claude-code-base-action step

The action internally runs setup-node with 18.x by default, overriding
the Node 22 installed by prepare-frontend. This caused yarn engine
check failures on older release branches. The action respects a
NODE_VERSION env var, so set it to 22.

* Improve backport resolver: detect max turns, add commit details to success message

- Bump default max turns from 50 to 75
- Detect when Claude exits due to max_turns exceeded and post a specific
  comment explaining the issue with a link to re-run the workflow
- Include a commit link and collapsible changed files list in the success comment
2026-04-02 13:45:21 -07:00
Oreon Lothamer
0eef3193ce Use assume role for AWS credentials in workflows (#71608) 2026-04-02 22:15:16 +02:00
Jeff Bruemmer
6dbb28bb3e docs - Fix formatting of Agent API link in documentation (#71941)
Fix formatting of Agent API link in documentation
2026-04-02 19:23:35 +00:00
Jeff Bruemmer
7034d54542 docs - upload edits (#71684) 2026-04-02 14:49:10 -04:00
Riley Thompson
bdda38ff17 Reject multi statement queries (#71573)
* reject multi statement queries

* add dialect

* update pr

* use multimethod

* mark driver tests

* remove join for clickhouse

* kebab case

* fix e2e test
2026-04-02 18:24:02 +00:00
Aleksandr Lesnenko
293b99e321 fix unnecessary data grid row remounting (#71877)
* fix unnecessary data grid row remounting

* spec
2026-04-02 18:21:26 +00:00
John Swanson
1c26f9d31c Fix document visibility in trash (#71903) 2026-04-02 09:17:25 -08:00
Phoomparin Mano
71b8317b65 Add MCP apps CORS settings page and settings (#71763)
* feat: add MCP apps CORS settings page at /admin/metabot/mcp

Add a new settings page where admins can enable CORS for popular MCP
app clients (Claude, ChatGPT, Cursor/VS Code) via checkboxes, and
add custom domains for self-hosted clients like LibreChat.

Backend: new `common-mcp-apps-cors-origins` (CSV) and
`custom-mcp-apps-cors-origins` (string) settings in mcp/settings.clj.
Security middleware merges these with embedding SDK origins in
`access-control-headers`. Special handling for vscode-webview:// prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address design review — use toggles, fix casing, remove sublines

- Switch from Checkbox to Switch toggles for MCP client selection
- "MCP Apps" → "MCP apps" in sidebar and page title
- Remove redundant description sublines from client options

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: extract MCP CORS tests to separate security_mcp_test file

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add unit tests for mcp apps

* rename: common-mcp-apps-cors-origins → mcp-apps-cors-enabled-clients

The setting stores client keys (claude, vscode, chatgpt), not origins.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* rename: custom-mcp-apps-cors-origins → mcp-apps-cors-custom-origins

Consistent mcp-apps-cors- prefix for both settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add mcp to driver-affecting-overrides

MCP settings are infrastructure, not driver logic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add cors origins parsing improvements from @crisptrutski

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>

* move mcp-apps-sandbox-origin? from security middleware to mcp.settings

Encapsulates MCP sandbox origin logic in the mcp module, uses condp
for extensibility when more non-standard schemes are added.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add oauth-server to mage modules list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add mcp.core as public API, keep mcp.settings internal

External consumers (e.g. security middleware) now go through mcp.core
instead of reaching into mcp.settings directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* move application logic from mcp.settings to mcp.core

mcp.settings now only contains pure config (defsettings + cors-origins).
vscode-webview-enabled? and sandbox-origin? live in mcp.core.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* remove useEffect state sync from MCP apps settings

Local state is kept for optimistic UI updates, but the useEffect sync
from savedValue is unnecessary since these settings won't change from
an external source concurrently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* rename "MCP app clients" to "MCP clients" in descriptions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* rename "vscode" client key to "cursor-vscode"

Aligns the key with the label "Cursor and VS Code" across frontend,
backend domain mapping, and tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* expand test to toggle all MCP clients

Use findByLabelText instead of testIds, and verify each toggle
accumulates the enabled clients list correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add init for mcp

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-04-02 16:29:09 +00:00
Sébastien
f01f753ff2 fixed translation issue with funnels (#71810) 2026-04-02 17:45:16 +02:00
Timofey Kachalov
a6453de48d feat(sdk,embed-js): Add touch device support for collection name description icon (#71762) 2026-04-02 19:42:56 +04:00
Aleksandr Lesnenko
a3ccc35af6 hides overview tab of the metrics pages if no dimensions (#71675)
* hides overview tab of the metrics pages if no dimensions

* hide caching tab if no write access to metric

* update specs based on latest changes from master
2026-04-02 10:28:56 -04:00
Chris Truter
d07b1772b8 Move table_transform_target_fk migrations from v60 to v61 (#71717) 2026-04-02 16:14:49 +02:00
Phoomparin Mano
05ec8451c5 Move CORS settings to OSS and show Security tab in Embedding (#71648)
* move the cors settings to oss

* add quick test for cors on oss

* test: update security middleware tests for OSS CORS behaviour

Add a dedicated test verifying CORS headers are sent when origins are
configured even without embedding features enabled (OSS path).

Update existing cases to reflect that removing the :embedding-sdk
feature flag from embedding-app-origins-sdk means the getter now
returns the actual stored value in all contexts (previously returned ""
without the premium feature active in tests).

Remove 16 redundant test cases where sdk=true with non-empty origins
produced identical results to sdk=false with non-empty origins — now
that CORS is enabled whenever origins are configured, the sdk flag is
only meaningful when origins are empty.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* restore tests

* fix: remove redundant enable flags from CORS check and add OSS CORS test

The enable-embedding-sdk and enable-embedding-simple flags are no longer
needed to gate CORS headers — having configured origins is sufficient,
and localhost is always allowed via the existing passthrough. This
addresses PR review feedback to simplify the logic and add missing
frontend test coverage for CORS settings in OSS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: update e2e tests for OSS/Starter CORS settings visibility

Security tab and CORS settings are now available on all plans,
not just EE. Update assertions to expect them to exist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:05:52 +00:00
Chris Truter
6f850e24db Clean up workspace schema and user-name code (#71808)
* Clean up workspace schema and user-name code

1. Single source of truth for the prefix — "mb__isolation" is defined once in metabase.driver.util (private), not three places
2. Matching methods live next to the constant — workspace-isolated-schema? (in-memory) and workspace-isolated-schema-clause (HoneySQL) so callers don't need to know the prefix or construct patterns themselves
3. Removed the requiring-resolve hack in the redshift test
4. Removed duplicated name generation — enterprise workspaces/util had identical copies of isolation-namespace-name, isolation-user-name, and instance-uuid-slug; callers now use driver.u directly

Closes [GDGT-2157: redshift tests require enterprise alias](https://linear.app/metabase/issue/GDGT-2157/redshift-tests-require-enterprise-alias)

* Avoid dynamic string building

* Use escape characters since _ is a wildcard

* Tighten up a bit more

* Remove stale include
2026-04-02 08:17:52 -04:00
github-automation-metabase
c08d322874 Update translations 2026-04-01 (#71841)
* Update translation sources 2026-04-01

* Update translations

* skipping offending test

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Katya Voropaieva <110612633+KorowkaMu@users.noreply.github.com>
Co-authored-by: Sébastien <sebastien@metabase.com>
2026-04-02 11:48:13 +00:00
Chris Truter
33a9177696 Test to catch incorrect migration versions (#71743) 2026-04-02 11:12:44 +00:00
Stas Gavrylov
beed12ea36 GDGT-2089: Adjust viz settings sidebar styling (#71472)
* GDGT-2089: Adjust settings sidebar styling

* GDGT-2089: Fix PR comments. Adjust styling
2026-04-02 13:51:56 +03:00
Sébastien
23eaffd14a add new-native to mb-question doc (#71890) 2026-04-02 09:33:04 +00:00
Stas Gavrylov
96f8785d12 GDGT-2047: Add ability to proxy /api requests to remote server (#71090)
* Add ability to proxy /api requests to remote server

* Tighten security. Add tests

* Move proxy code to dev/. Add mention in docs
2026-04-02 11:48:20 +03:00
Nick Fitzpatrick
0642892b42 Adding Details tab to table section (#71722)
* Adding Details tab to table section

* e2e adjustments

* remove grow on tab list

* fix e2e tests
2026-04-02 12:42:57 +05:30
Timofey Kachalov
0336557161 feat(sdk,embed-js): Improve touch device support in dropdown components in Notebook Editor (#71562) 2026-04-02 11:12:47 +04:00
Timofey Kachalov
8c2ca60ccb Fix duplicated identifiers error (#71883)
Fix duplicated identifiers
2026-04-02 01:38:10 -04:00
Timofey Kachalov
a3340c17a6 feat(sdk,embed-js): Improve mobile range selection experience for visualizations (#71374) 2026-04-02 08:45:00 +04:00
Alexander Polyankin
bd82202177 Representations baseline (#71879)
* Omit UUIDs in SerDes MBQL 5 export unless required

* Test fixes 🔧

* Final test fixes 🔧

* cljfmt

* fixes

* fixes

* fx

* fixes

* es

* fix

---------

Co-authored-by: Cam Saul <github@camsaul.com>
2026-04-02 00:22:41 -04:00
Cam Saul
22a21845f1 Omit UUIDs in SerDes MBQL 5 export unless required (#71862)
* Omit UUIDs in SerDes MBQL 5 export unless required

* Test fixes 🔧

* Final test fixes 🔧

* cljfmt
2026-04-01 21:43:06 -04:00
metatyb
e75d0484e8 chore: add metatyb to team and github-slack-map (#71803) 2026-04-02 09:30:45 +08:00
Nemanja Glumac
43db728f71 Clean up two module violations in metabase api (#71786)
* Move DataSegregationStrategy type to metabase-types/api/permissions

This is a permissions concept, not an embedding one. Moving it to
metabase-types resolves a module boundary violation (basic/api importing
from shared/other) and breaks a circular dependency between metabase/api
and metabase/embedding/embedding-hub.


Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 00:39:48 +02:00
Nemanja Glumac
9537481bb2 Improve sync-fork workflow ergonomics (#71875) 2026-04-02 00:38:49 +02:00
Eric Normand
74a08a7a2d Fix usage_info IN clause exceeding 65K parameter limit (GHY-2413) (#71856)
- Fixes `GET /api/database/:id/usage_info` failing when a database has more than 65K tables
- The segment count query was passing all table IDs as parameters to an `IN` clause, which exceeds PostgreSQL's 65,535 parameter limit
- Replaced the `IN` clause with a subselect (`SELECT id FROM metabase_table WHERE db_id = ?`), eliminating the parameter explosion
- Removed the now-unnecessary `select-pks-set` call and `table-ids` argument from `database-usage-query`
2026-04-01 20:42:52 +00:00
Alex Yarosh
db1f2bbe75 Update description for logarithm value parameter (#71851) 2026-04-01 19:37:57 +00:00
Aleksandr Lesnenko
6d190f1f62 do not suggest city as a geo dimension for tabs with map viz (#71768)
* do not suggest city as a geo dimension for tabs with map viz

* update e2e spec
2026-04-01 19:32:56 +00:00
appleby
4ff8b2722e BOT-1223 Fix zero-arity tool call schemas for anthropic provider (#71618)
* BOT-1223 Fix zero-arity tool call schemas for anthropic provider

Anthropic is stricter than openrouter about schemas for tool calls with no arguments. A schema like `[:maybe :map]`
produces a JSON schema like `{"oneOf": [{"type": "object", ...}, {"type": "null"}]}` but Anthropic's API requires
input_schema to be `{"type": "object", ...}` at the top level. A oneOf with a null variant is invalid.

* Handle nil arguments in metabase.metabot.self.core/run-tool
2026-04-01 12:05:14 -07:00
Jeff Bruemmer
75befd8877 docs - links (#71853)
links

Frontload links to ref docs.
2026-04-01 14:04:00 -04:00
Filipe Silva
34a7791680 fix: accept metered transforms product types in cloud-add-ons endpoint (#71664) 2026-04-01 18:56:42 +01:00
Nemanja Glumac
6a72335427 Allow test quarantine in Metabase internal forks (#71837) 2026-04-01 19:15:37 +02:00
Nick Fitzpatrick
a86098bc57 Rename Data Structure to Tables (#71744) 2026-04-01 22:12:00 +05:30
Eric Normand
23410f5c24 Fix sync_schema silently dropping when executor is busy (GHY-3254) (#71756)
* Add failing test reproducing GHY-3254: sync_schema silently drops when executor is busy

The quick-task executor uses a single thread. If any task gets stuck
(e.g., a hanging JDBC connection), all subsequent manual sync requests
queue forever while the endpoint returns 200.

* Add per-task timeout to quick-task executor

Each task submitted to the quick-task executor now runs on a child
thread with a configurable timeout (*task-timeout-ms*, default 2h).
If a task exceeds the timeout, it is cancelled and the executor moves
on to the next queued task. This prevents a single stuck task from
blocking all subsequent manual sync requests.

The stuck thread's stack trace is logged for diagnostics.

* Use MB_QUICK_TASK_TIMEOUT_MINUTES env var for task timeout

Replace the dynamic var with a real env var (default 120 minutes).

* Use config/config-int for MB_QUICK_TASK_TIMEOUT_MINUTES

Read the env var via metabase.config.core instead of System/getenv,
consistent with how other config values are read in the codebase.
2026-04-01 11:39:44 -05:00
Timofey Kachalov
2aae5c688b feat(sdk,embed-js): Enhance getEventTarget to support TouchEvent for improved mobile compatibility when showing a drill popup (#71636) 2026-04-01 20:36:55 +04:00
Nick Fitzpatrick
6af2a17af7 Subtract scrollbar with from pivot table header when apropriate (#71654) 2026-04-01 21:39:41 +05:30
Nick Fitzpatrick
6c297776ee Skipping GridMapPdfExport loki test (#71833) 2026-04-01 21:38:01 +05:30
dpsutton
ee5d80d008 middleware to prevent middleware that hits app-db (#71775)
currently it is just two middleware: impersonation and db routing

this is necessary as we want to move into a world where we can compile a
query from yaml files without an app db file. These middleware are not
impportant for our purposes here.

This is also temporary work. This is slated to be fixed up in

[QUE2-488: Introduce an app-db protocol for QP
middleware](https://linear.app/metabase/issue/QUE2-488/introduce-an-app-db-protocol-for-qp-middleware)
2026-04-01 09:29:07 -05:00
Edward Paget
1038b9d7bd Remove metabot describe API, openai client, and unused deps (#71774)
The metabot describe endpoints (card and dashboard autodescription) are
no longer needed. This removes the API routes, task implementations,
OpenAI client wrapper, tests, and the now-unused openai-clojure and
lambdaisland/uri dependencies.
2026-04-01 08:46:13 -05:00
Nick Fitzpatrick
a4d498d247 add data image check to urlTransform (#71588)
* add data image check to urlTransform

* de-duplicate regex
2026-04-01 19:03:47 +05:30
Nick Fitzpatrick
3b4897a341 show error message on remote sync pull changes or branches error (#71668)
* show error message on remote sync pull changes or branches error

* test adjustments
2026-04-01 19:03:31 +05:30
Jeff Bruemmer
134065e95e docs - images and notes (#71773)
* images and notes

* Optimized 2 images, saved 118.8 KB (41.4% smaller)

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-04-01 13:01:48 +00:00
Nick Fitzpatrick
fdb8d17d50 Revert April Fools Joke (#71801)
* Add CI check

* Revert "Fix metabot padding (#71680)"

This reverts commit 5886b7b7c6.
2026-04-01 05:38:24 -06:00
Timofey Kachalov
efc354420f feat(SDK, EmbedJS): Add touch support for Popover targets to improve mobile usability (#71484) 2026-04-01 12:59:39 +04:00
Tyler Plude
878fc365a3 [BOT-1233] Fix get-table-details arg flag typos (#71679)
* fix get-table-details arg flags

* new unit test
2026-03-31 22:59:18 -07:00
Alexander Polyankin
9ada59a804 Serdes fixes for python transforms (#71735)
* fixes

* fixes
2026-03-31 21:43:55 -04:00
appleby
4544ff5e6b BOT-1221 Resolve metabase://transform links to new data-studio path (#71613) 2026-04-01 00:30:55 +00:00
appleby
734dba5964 BOT-1226 Only run metabot internal tasks when the llm-metabot-provider-lite is configured (#71667)
* BOT-1226 Only run metabot internal tasks when the llm-metabot-provider-lite is configured

The agent loop was already checking llm-metabot-internal-tasks-enabled? before running internal background tasks like
user intent classification. Switch the default value for llm-metabot-internal-tasks-enabled? from true to false and
update the :getter to check that the corresponding llm-metabot-provider-lite is configured.

* Review feedback: ensure llm-provider-configured? returns boolean
2026-03-31 23:10:11 +00:00
Cam Saul
b6a9236b1d Remove legacy MBQL conversion steps from SerDes (#71614)
* SerDes use MBQL 5 [WIP]

* Update serialization baselines

* Test fixes 🔧

* More test fixes 🔧

* Test fixes 🔧
2026-03-31 19:03:43 -04:00
github-automation-metabase
ef14caecf1 Auto-update documentation for master (#71781)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-31 18:27:33 -04:00
appleby
9080614933 BOT-1199 ORDERS table link points to an invalid path (#71543)
* BOT-1199 Add table-specific link examples to the internal metabot prompt

* Fix resolve-table-link to resolve to valid ad-hoc question link

The docstring mentioned avoiding a db lookup for the database id, but a link like `/table/<id>` is not a valid
metabase link.

* Cache metabase.metabot.agent.links/resolve-table-link result for 10 minutes

* Remove regex guard around parse-long in resolve-table-link

parse-long already returns nil if the parse fails.

* Fix require for application-database-metadata-provider

* Fix resolve-entity-link-test for tables and split it out into a new test
2026-03-31 15:17:02 -07:00
Cam Saul
da8bc93939 Fix driver-related code scanning alerts (#71666) 2026-03-31 18:00:30 -04:00
Oleksandr Solovyov
9c23e9d35a [metabot] add repr for measures and segments (#71656)
* [metabot] add repr for measures and segments

* Wire up measures and segments correctly

* Improve measures and segments representation

---------

Co-authored-by: Mihael Konjevic <konjevic@gmail.com>
2026-03-31 21:20:17 +00:00
Sloan Sparger
686087f2ec [Metabot] Fix Admin AI sidebar disappearing (#71597)
reorganize page components so sidebar does disappear while loading metabot settings pages
2026-03-31 20:36:42 +00:00
Tyler Plude
e0ba625be4 [BOT-1247] Fix empty string content blocks in anthropic API (#71765)
init
2026-03-31 12:57:49 -07:00
Nemanja Glumac
9038c23075 Run cross-version script only on the main repo (#71754) 2026-03-31 21:55:10 +02:00
Nemanja Glumac
2e28c3f930 Ask for explicit input to override fork changes (#71753) 2026-03-31 21:52:30 +02:00
Fraser
0f42846b53 Clean UI utils and component violations (#71465) 2026-04-01 08:47:17 +13:00
Fraser
afc7fdbeb6 Add subset of files to app-misc module and make schema a lib module (#71700) 2026-04-01 08:46:56 +13:00
Noah Moss
adc3c025c5 Let Metabot write a brief response after using autogen_dashboard (#71758)
remove final-response? from autogen dashboard tool to allow for a brief description
2026-03-31 15:33:51 -04:00
Ryan Laurie
5886b7b7c6 Fix metabot padding (#71680)
* avante garde monetization

* fetch market prices

* limited fun

* obey the linter
2026-03-31 12:50:00 -06:00
Sloan Sparger
aa84a83fba [Metabot] Remove metabot v3 token feature (#71600)
remove metabot v3 token feature
2026-03-31 18:21:13 +00:00
Sloan Sparger
6c14871bae [Slackbot] Make slackbot auth req messages in channel an ephemeral message (#71603)
make slackbot auth req messages in channel an ephemeral message
2026-03-31 12:55:08 -05:00
Edward Paget
9e576f007b Fix git sync to remove stale YAML files from managed export directories (#71739)
Exports are always full, so any .yaml file in a managed directory that isn't
in the current write set should be removed. This fixes stale files persisting
after entity renames, moves between collections, or deletions.

The git source now accepts a set of managed top-level directories. During
write-files!, existing files in managed dirs not in the write set are cleaned
up automatically. This replaces the previous path-prefix-based collection
scoping and the explicit build-all-removal-paths machinery, which couldn't
handle renames or cross-collection moves.
2026-03-31 12:48:56 -05:00
Sloan Sparger
400a33e4ef [Metabot] Hide embedded settings if embedding is not available (#71594)
hide embedded metabot settings if embedding is not available
2026-03-31 12:17:12 -05:00
Noah Moss
201824c6b2 Remove capability check for create-autogenerated-dashboard-tool; clarify system prompt (#71589)
* fix autogen_dashboard tool by removing capability checck, clarify system prompt around dashboards

* fix tests

* fix test
2026-03-31 13:13:30 -04:00
Edward Paget
fa31def1bc Fix support access grant password reset failing for deactivated users (#71598)
* Fix support access grant password reset failing for deactivated users

When a support user was deactivated (e.g. after a previous grant expired),
creating a new grant would leave the user inactive. The password reset
token authentication would succeed, the :after method would consume the
token and change the password, but create-session! would then reject the
login because is_active was false. with-fallback would swallow this and
fall back to emailed-secret-password-reset which also failed, resulting
in an opaque "Invalid reset token" error with the token irreversibly
consumed.

Fix: fetch-or-create-support-user! now reactivates deactivated users.

Also adds tests for the deactivated user flow, pre-existing auth identity
cascade, with-fallback error swallowing behavior, and direct login! calls
for easier debugging.

* fixup! cljfmt
2026-03-31 11:00:45 -05:00
Nicola Mometto
c134074716 Dedupe slack channels (#71653)
dedupe slack channels
2026-03-31 16:39:30 +01:00
lbrdnk
237c3fe056 Remove stale archived_message instructions (#71715) 2026-03-31 16:17:00 +02:00
Nick Fitzpatrick
01af8eb57f updating styling for Database Destination Connection Modal (#71586) 2026-03-31 19:38:54 +05:30
Edward Paget
e60356ffd7 Fix notification send test retry config key (:max-attempts → :max-retries) (#71689)
The test overrode :max-attempts but diehard uses :max-retries, so the
override was silently ignored and all 6 retries ran with exponential
backoff (~31s). With the correct key the test finishes in ~1s.

Done: task/fix-notification-retry-key
2026-03-31 14:08:49 +00:00
Kevin Kelleher
d825c4dab3 Metrics home page and viewer cleanup (#71514) 2026-03-31 13:41:56 +00:00
lbrdnk
bee5aa840a Native query context formatting (#71566)
* Extend the schema for context received in requests

* Fix native question context formatting

* Adjust context schema

* Tests

* test

* Tests

* extract dialect

* remarks
2026-03-31 15:12:05 +02:00
Kevin Kelleher
214f776ad4 Metrics explorer dimension picker and tab creation fixes (#71536) 2026-03-31 07:13:57 -04:00
Timofey Kachalov
a4c66d0626 Update permissions in embedding SDK tests to avoid permission errors in some cases (#71705) 2026-03-31 13:56:11 +04:00
Nemanja Glumac
cc7506cc6f Allow Metabase forks (including private ones) to sync automatically (#71663)
Add a new sync-fork workflow that automatically syncs metabase internal forks
with upstream metabase/metabase (master + 5 most recent release branches).
Force-syncs from upstream as source of truth, skips push when no diff to
avoid unnecessary CI runs. Only runs on forks, never on the public repo.

Guard all scheduled workflows with repository check so they only run on
schedule for metabase/metabase, not on forks (manual dispatch
still works on both).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 06:05:11 +00:00
Alexander Polyankin
8f62ee5f3a Serdes skip default values (#71544) 2026-03-31 03:42:08 +00:00
Fraser
f922a58520 Clean dead code (#71463) 2026-03-31 16:02:41 +13:00
metabase-bot[bot]
1179147e3d [chore] Update E2E test timings with recent data (#71621) 2026-03-31 14:03:04 +13:00
Fraser
b2a016926e Push shared redux actions down (#71130) 2026-03-31 13:53:24 +13:00
Maz Ameli
a9f7e0504a Change label for stacked series setting (#71541)
change label for stacked series setting
2026-03-30 23:01:28 +00:00
Edward Paget
4b032951ad Include lock name in cluster lock log and error messages (#71677) 2026-03-30 21:09:03 +00:00
Braden Shepherdson
43ab4ba9d3 [MBQL lib] Add API docs for returned-columns and other metadata (#71443)
Part of QUE2-366.
2026-03-30 21:03:49 +00:00
Nemanja Glumac
610ff28b80 Fix a collection picker flake in cross-version tests (#71599) 2026-03-30 21:26:39 +02:00
Alexander Polyankin
4bbdb37c66 Serdes file and folder human readable names (#71583) 2026-03-30 18:52:39 +00:00
Nick Fitzpatrick
b56c33b6be adjust styling on parameter placeholders (#71577) 2026-03-30 23:40:08 +05:30
Nick Fitzpatrick
a14c0b00b9 don't recompute sensible chart groupings while sidebar is mounted (#71563) 2026-03-30 23:39:46 +05:30
Eric Normand
94cdeffdab Fix field filter dropdowns for users without create-queries permission GHY-1605 (#71046)
## Summary

Field filter dropdowns on native query cards show as text inputs ("Enter some text") for users who have view-data permission but lack create-queries permission on the underlying table.

**Root cause:** Two issues combined to break this:

1. **Frontend couldn't determine dropdown type** — The frontend needs `has_field_values` from field metadata to decide whether to show a dropdown vs text input. This metadata came from `GET /api/database/:id`, which returns 403 for users without `create-queries` permission. Without it, the widget falls back to a text input.

2. **Backend param values endpoint was too restrictive** — Even if the frontend showed a dropdown, the card param values endpoint (`GET /api/card/:id/params/:key/values`) would fail because `search-values-from-field-id` does `api/read-check` on the Field (which requires `create-queries`), and the QP permission check also required `create-queries`.

**Fix:**

- **Hydrate `param_fields` on Card API response** (matching existing Dashboard pattern) — includes `has_field_values` so the frontend can show the correct widget type without needing `GET /api/database/:id`
- **Populate metadata store with `param_fields`** on card load in the query builder
- **Bind `*param-values-query*`** in card param values endpoints (like dashboards already do) so the QP only checks `view-data` permission
- **Skip `api/read-check` on Field** in `search-values-from-field-id` when `*param-values-query*` is true
2026-03-30 12:55:14 -05:00
da
480ef19d1f Document reverse tunneling for SSH connections (#71628)
Added section on reverse tunneling for SSH connections.
2026-03-30 15:54:09 +00:00
Sébastien
65d66d2887 No request to pulse if user canot manage subscriptions (#71575) 2026-03-30 15:36:05 +02:00
Mahatthana (Kelvin) Nomsawadi
a1c3983037 Fix flaky test (#71623)
Attempt to fix flake
2026-03-30 14:26:33 +01:00
Jeff Bruemmer
424541b425 docs - mcp and AI updates (#71601) 2026-03-30 09:04:43 -04:00
Fraser
cc486a476e Lift dashboard only visualizations (#71271)
* Extract Dashboard-only visualizations

  Moved 4 dashboard-only visualization types from visualizations/visualizations/ to dashboard/visualizations/:
  - Heading/ — imports dashboard context, selectors, parameter components
  - IFrameViz/ — imports dashboard selectors
  - LinkViz/ — imports dashboard selectors
  - DashCardPlaceholder.tsx — imports dashboard actions

  Created dashboard/visualizations/register.js — registers these 4 viz types separately from the main visualizations/register.js

  Updated app.js — calls both registerVisualizations() and registerDashboardVisualizations()

  Updated 3 external imports to use the new paths:
  - dashboard/actions/utils.ts — IFrameViz utils
  - parameters/utils/mapping-options.ts — IFrameViz utils
  - PublicOrEmbeddedDashboardView-card-filters.stories.tsx — Heading

  Fixed 1 relative import in DashCardPlaceholder.tsx to use absolute path for VisualizationProps type.

  Violation progression: 157 → 111 → 86 → 78

* List DashboardChartSettings up

* Fix test setup

* Register dashboared viz in embedding
2026-03-30 22:01:47 +13:00
Fraser
e9b56f49b5 Allow Enterprise module to import from feature modules (#71468) 2026-03-29 16:26:56 +13:00
Fraser
307b2906b7 Fix module stats upload script (#71385) 2026-03-29 16:26:43 +13:00
lbrdnk
b8d3ac0869 Adjust search schemas (#71572) 2026-03-27 16:13:42 -07:00
Alexander Polyankin
ad4dfb41b1 Fix typo in transform database validation messages (#71596) 2026-03-27 22:34:25 +00:00
github-automation-metabase
38e28e0ec3 Fix pagerDurty workflow edge case (#71574)
Fix PagerDuty alert broken by single quote in issue title

Use jq --arg to safely construct the JSON payload instead of
interpolating user-controlled data directly into a shell string.
This prevents shell syntax errors when issue titles contain single
quotes or other special characters.

Removes the now-unnecessary 'Setting title' step that only escaped
double-quotes but left single quotes unhandled.
2026-03-27 20:52:32 +01:00
Edward Paget
655a3f6ac7 Move agent API tests from enterprise to OSS (#71456) 2026-03-27 18:30:00 +00:00
Chris Truter
3c968befff Improve model encapsulation with serialization bypass (#71553) 2026-03-27 20:11:07 +02:00
Sloan Sparger
334ed1fe42 [Metabot] Hide metabot feedback buttons on non-hosted instances (#71539)
hide metabot feedback buttons on non-hosted instances
2026-03-27 12:53:02 -05:00
Sloan Sparger
8e79105b9b [Metabot] Temporary removal of analyze chart button on dashboard page (#71540)
temp removal of analyze chart button on dashboard page
2026-03-27 12:52:53 -05:00
Ryan Laurie
92cd3f6cbf Converts some tests to ts (#71520)
converts tests to ts
2026-03-27 11:23:51 -06:00
Noah Moss
7089932b89 Move slack connect tests over to oss + fix API routes (#71525)
* move slack connect tests over to oss and remove some references to the token

* remove EE routes, always use OSS

* simplify

* undo some of that simplification

* fix tests / lint

* Move one test back to EE since it tests slack-connect's interaction with SAML
2026-03-27 10:50:48 -04:00
Nicolò Pretto
c4a27f18d3 embedding-hub: preserve "Select data" state + reset next steps after changing data segregation strategy (#71368)
* move up the state to persist it across steps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: reopen create tenants step when changing segregation strategy

When the user changes the data segregation strategy after already
creating tenants, the create tenants step now correctly reopens
instead of skipping to summary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* address feedback

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 15:24:32 +01:00
Phoomparin Mano
811e4466b2 Update in-app JWT onboarding to use snippets instead of iframes (#70475)
* share the same snippets for onboarding flow

* use sdk-docs-snippets namespace

* sync snippets

* fix rspack config

* rspack config: use path

* rspack: simplify resolve aliases

* remove node_modules from react refresh

* add copy button

* cleanup codemirror styling

* future proof: allow importing tsx files as well

* fix eslint pattern

* chore: move eslint ignore

* test to simplify aliases

* add context to ttag strings

---------

Co-authored-by: Nicolò Pretto <info@npretto.com>
2026-03-27 15:24:18 +01:00
Jeff Bruemmer
48c41ec6c8 Docs metabot slack (#71526) 2026-03-27 10:02:47 -04:00
Oleksandr Solovyov
b93319ab57 metabot llm calls: tools are optional/maybe (#71555) 2026-03-27 13:25:04 +00:00
lbrdnk
31905eaba4 Add 60 to version-helpers.ts (#71480)
Update versionRequirements
2026-03-27 11:02:22 +01:00
Nicolò Pretto
a0a14a42d3 scroll to top on page change in admin settings (#71369)
* scroll to top on page change in admin settings

* make tests green

* switch to useRouter instead of hoc
2026-03-27 09:55:04 +01:00
lbrdnk
839784b50c Fix transform writing tools (#71100)
* Add needed capabilities

* Partly fix transforms-write ns

* Partial fix to python transforms

* Add tool prompts

* Cleanup native transforms

* Add parameter schema descriptions

* Python schemas adjustments

* Adjust tool prompts

* Cleanup

* Tests

* Add capabilities test

* tests

* cleanup

* cleanup

* mbql5 and tests

* tests

* tests

* tests

* feature sentinel

---------

Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-03-27 08:44:57 +01:00
Nemanja Glumac
e9bc4ac447 Notify Slack on cross-version test failures (#71491) 2026-03-27 08:16:10 +01:00
Chris Truter
7180c6ae4b Optimize transform target table backfill migrations (#71487)
Better safe than sorry, given recent problems with expensive migrations.
2026-03-27 06:49:07 +02:00
Nick Fitzpatrick
a57a1c0ab8 preserve navbar status in the url update at the end of handleQBInit (#71505) 2026-03-27 09:59:12 +05:30
Nick Fitzpatrick
bee30e6423 Uxw 3355 page titles not in sync with data studio tabs (#71401)
* Fixing page titles in Data Studio

* shuffling Library files around. Adding library page title to all library subpages
2026-03-27 09:50:52 +05:30
Nick Fitzpatrick
281aa0dc12 decrease font size in click behavior sidebar (#71519) 2026-03-27 09:48:40 +05:30
Nick Fitzpatrick
14bd0853f6 set line height to 'normal' for textareas (#71498) 2026-03-27 09:48:20 +05:30
Romeo Van Snick
8be374db14 Convert query builder redux store to ts (#71063)
* Convert query_builder reducers to js

* Convert query_builder selectors to ts

* Fix types

* Fix types

* Fix types

* Fix isResultDirty selector

* Fix selector

* Move selector tests to TS

* Move reducers-typed into reducers

* Fix types

* Fix types

* Fix object id parsing

* Fix more types

* Fix PKRowIndexmap types

* Make name for EDIT_SUMMARY consistent

* Fix hanging test
2026-03-27 13:59:05 +11:00
Alexander Polyankin
b7e1d1eff8 Serdes for cards, segments, measures, transforms - allow deserializing only with minimal required properties without computed ones (#71464) 2026-03-26 18:51:58 -04:00
github-automation-metabase
3dd4c46871 Auto-update documentation for master (#71533)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-26 18:49:20 -04:00
Noah Moss
e7ae8072ca Reorganization + consolidation of metabot system prompts (#71506)
* Reorganization + consolidation of metabot system prompts

* More reorganization

* More shared prompt files

* revert search changes included accidentally

* more organization

* Fix dialect resource paths in module drivers

Update llm-sql-dialect-resource defmethods to use correct metabot/prompts/dialects/ path.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* share personality prompt for nlq profile

* bump ci

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 17:27:26 -04:00
Riley Thompson
483f2086dd Bump databricks jdbc driver to 3.3.1 (#71513)
* bump databricks jdbc

* trigger ci
2026-03-26 21:17:38 +00:00
Edward Paget
cb9c042404 Fix MCP tool dispatch to pass query params correctly per Ring spec (#71502)
invoke-agent-api-with-params was encoding GET/DELETE query params into
the URI string, but defendpoint reads from :query-params (a parsed
map) on the Ring request. The synthetic request never passes through
wrap-params middleware, so query params in the URI were silently
ignored.

Fix by moving the method-based dispatch into deliver-agent-api-response:
POST params go to :body, GET/DELETE params go to :query-params.
Remove invoke-agent-api-with-params which is no longer needed.
2026-03-26 15:49:09 -05:00
Anderson
1b26236cf1 Do not show JWT shared secret as plaintext after it is saved (#71165)
* Handle JWT shared secret as write-only after initial generation (#UXW-3300)

- Thread generated token through wizard state so AddEndpointStep displays
  the plaintext key without re-reading it from session properties
- FormSecretKey now recognises the backend obfuscated format (**********XX)
  and renders an empty input with 'Already set' placeholder and a
  'Regenerate key' button, rather than showing the masked value

* Add unit tests for FormSecretKey obfuscated value handling (#UXW-3300)

- Tests covering no-value, plaintext, and obfuscated backend secret states
- Verifies 'Already set' placeholder and button labels are correct per state
- Verifies confirmation modal uses caller-supplied title/body
- Verifies token regeneration clears obfuscated value

* Fix prettier formatting in FormSecretKey.tsx

* Update the way we obfuscate the key and handle it on the frontend

* Add be test, improve alert

* Fix ts

* discard unnecessary changes

* Remove section that displayed the JWT secret in embedding set up

* Fix e2e test

* Show dedicated modal when generating new JWT shared key

* Fix unit test

* Fix e2e tests

* Fix latest UI changes

* Address review comments

* Add loading state to secret key modal and assert obfuscated key in E2E test

* Read jwt-shared-secret from /api/setting instead of session-properties / Revert unneeded obfuscated? implementation

* Fix tests

* Replace reads of session properties with api settings to simplify component logic
2026-03-26 16:34:54 -03:00
Chris Truter
14ff22021e Support non-datetime migration ids in tests (#71471)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:03:02 +02:00
Nick Fitzpatrick
55df67b632 Update Value normalization / collapsing in ChartSettingInputNumeric.tsx (#71440) 2026-03-27 00:14:41 +05:30
Edward Paget
53b678bcd6 Flatten root-level :or/:and/:multi into flat object schemas for MCP clients (#71494)
* Flatten root-level :or/:and/:multi into flat object schemas for MCP clients

Only the outermost composite schemas are flattened — nested anyOf/allOf
within properties are preserved since LLM clients handle them fine.

- :and → :merge then deref (flattens the [:map {:encode/...}] pattern)
- :or → :union with optional-keys, then deref (merges branches)
- :multi → same as :or, extracts schemas from dispatch triples

Also updates construct_query and query tool descriptions to explain
which fields are mutually exclusive (table_id vs metric_id vs
continuation_token).

* Refactor flatten-root-schema to worklist with cycle detection

Replace the recursive flatten-root-schema with a two-phase approach:
collect-root-maps walks the root composite tree using an iterative
worklist, then flatten-root-schema merges the collected leaves.

This eliminates stack recursion (:or/:multi no longer call
flatten-root-schema on children) and replaces the fuel counter with
a seen set that precisely detects deref cycles. On cycle detection,
emit an empty :map (-> {"type":"object"}) as a safe fallback.

The worklist also fixes :and wrapping :or — both layers now flatten
correctly since all composite nodes are expanded uniformly.

Adds tests for deeply nested composites, nested composites within
properties (preserved as anyOf/allOf), and :multi root flattening.
2026-03-26 17:48:52 +00:00
Cam Saul
6a15fee1c5 Test to prove #32068 is fixed 🔧 (#71382)
* Test to prove #32068 is fixed 🔧

* Whoops revert deliberate test breakage

* Simplify test
2026-03-26 16:43:44 +00:00
Nicolò Pretto
92c07ddea1 hide sample db from embedding onboarding picker (#71304)
* hide sample db from embedding onboarding picker

* simplify e2e

* let's see if ci is green

* remove useless line, this is always pg
2026-03-26 17:23:19 +01:00
Sébastien
3dd99b6494 Fixed flaky custom elements api <metabase-question> test (#71500) 2026-03-26 17:20:53 +01:00
Riley Thompson
edb5eaad63 Handle non-dates in sqlite date columns (#71492)
* fix sqlite non date handling

* kondo
2026-03-26 12:14:00 -04:00
Chris Truter
77afdaafc0 MCP further nits (#71478) 2026-03-26 16:08:18 +00:00
metamben
56f04b92ca Render |> as a single operator when formatting SQL (#71435)
Closes #70447
2026-03-26 16:06:37 +00:00
Braden Shepherdson
8020b65546 [MBQL lib] Add user docs for joins to lib.core (#71442)
Part of QUE2-366.
2026-03-26 16:06:19 +00:00
Sébastien
0cc35ac876 Sprinkled upsell gems in embed wizard (#71076) 2026-03-26 16:59:33 +01:00
Kevin Kelleher
617ca0f5f0 Fix boolean cartesian charts defaulting to timeseries axis (#71486) 2026-03-26 11:13:37 -04:00
John Swanson
607fb9ba58 Hide destination DBs from permissions (#71395)
* FE: do not show destination DBs in permissions editor

If you add a destination database, don't show it in the perms editor.

This was a temporary bug (disappears on refresh) BUT if you edit permissions
for the destination database then that is persisted, which is not great. I
will fix that in a separate PR.

* Don't add data permissions rows for destination DBs

- Exclude destination databases from the permissions graph

- Don't add permissions for destination DBs when creating a new group
2026-03-26 06:46:59 -08:00
Ngoc Khuat
74c8811981 Fix silent data loss when importing unparseable YAML files (#71421)
* Surface ingestion errors instead of silently skipping unparseable YAML files

The serdes ingestion layer was silently dropping YAML files that failed
to parse (introduced in #41887 as defensive coding for editor temp
files). This caused remote sync imports to silently lose entities when
YAML files were malformed or exceeded SnakeYAML's nesting depth limit,
with no error reported to the user.

Add `ingestion-errors` to the Ingestable protocol so the ingestion
layer collects parse failures and surfaces them through `load-metabase\!`.
When `continue-on-error` is false (default, used by remote sync), the
import fails immediately on ingestion errors. When true, errors are
collected and reported alongside load-phase errors.

The dotfile filter from #41567 is preserved — editor temp files are
still silently skipped before parsing is attempted.

Fixes #71257

* Rename ingestion-errors to ingest-errors, extract populate-cache\! helper, add e2e tests

- Rename protocol method to ingest-errors for consistency with ingest-list/ingest-one
- Extract populate-cache\! helper to DRY up duplicated cache-population logic
- Add e2e test for continue-on-error true/false with ingestion errors

* Add tests for ingest-errors on IngestableSnapshot and wrapper delegation

* Fix log/warnf misuse, tighten tests, remove stale comment

- log/warnf -> log/warn to prevent MissingFormatArgumentException
  when error messages contain % characters
- Add tests for ingest-errors ordering contract and multi-file corruption
- Tighten continue-on-error assertion to check exact error count
- Remove stale comment describing pre-fix behavior

* Include file names in ingestion error messages

Wrap parse/read exceptions with ex-info containing the file path,
so the error message shows which files failed, e.g.
"Failed to read 1 file(s) during ingestion: bad_card.yaml"
2026-03-26 23:29:58 +09:00
Oleksandr Solovyov
ce15b2d9f0 fix metabot sql tools incorrectly checked query type (#71428) 2026-03-26 14:06:13 +00:00
github-automation-metabase
e5db491df4 Auto-update documentation for master (#71454)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-26 09:54:44 -04:00
Chris Truter
fdffe7dc82 Add db fallback for renamed settings (#71470) 2026-03-26 15:42:32 +02:00
lbrdnk
3bd3bf5b6a Edit chart fix (#71424)
* Add edit_chart.md instructions

* Ensure ids of state elements are stringified

* Cleanup

* Test

* add test and do cleanup

* Review comments
2026-03-26 14:00:04 +01:00
Chris Truter
c940bb3935 Add FKs from transforms back to tables (#70617) 2026-03-26 13:40:22 +02:00
Timofey Kachalov
366c74c02e Support entityId for guest embedded questions (#71246) 2026-03-26 14:44:00 +04:00
Chris Truter
0303419907 Increase transform test timeout (#71453) 2026-03-26 12:26:05 +02:00
Sébastien
1511899a7e Implement proper column width measurement for custom font sizes (#71407) 2026-03-26 11:08:04 +01:00
lbrdnk
b1a2673e1a Inject context in user message (#71045)
* Add context injection to user message

* Remove context from internal template

* whitespace

* Adjust tests

* Apply suggestion from @appleby

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>

* use lib

---------

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>
2026-03-26 10:42:20 +01:00
Nicolò Pretto
7074e0236a fix frontmatter in add-tracing skill (#71438) 2026-03-26 09:38:16 +01:00
Nemanja Glumac
ddba65c368 Add E2E (questions) tests on top of the cross-version automation (#70190) 2026-03-26 09:13:57 +01:00
Edward Paget
d8bb291939 Inline malli refs and flatten root anyOf in MCP tool schemas (#71457)
* Inline malli refs and flatten root anyOf in MCP tool schemas

MCP clients (Claude API, OpenAI, Gemini, AWS Bedrock) reject or
mishandle $ref and top-level anyOf in tool input schemas. Fix by:

- Walking malli schemas with ::walk-refs and ::walk-schema-refs to
  replace all registered-schema refs with their dereferenced content
  before JSON Schema generation, so mjs/transform never emits $ref
- Flattening root-level anyOf into a merged object schema for
  construct_query (table vs metric request union)
- Expanding construct_query tool description to explain table vs
  metric query differences since the schema no longer structurally
  separates them

Removes all $ref/$defs sanitization code and the defs accumulator
atom that are no longer needed.

* Replace :or/:and with :merge at malli level to eliminate anyOf/allOf

Instead of post-processing JSON Schema to flatten anyOf/allOf, handle
it at the malli schema level: in inline-malli-refs, replace :and with
:merge (eliminates allOf from the encode-map pattern) and :or with
:merge after making all entries optional via mut/optional-keys
(eliminates anyOf, all properties become optional in the merged
output).

This produces clean flat {type: "object", properties: {...}} schemas
that all MCP clients can handle. The construct_query tool now has all
table and metric properties in a single flat schema with none
required, and the description explains which to use.

Removes flatten-any-of and clojure.set dependency.
2026-03-26 06:21:33 +00:00
Aleksandr Lesnenko
1132bfd402 New Metric Home page (#71055) 2026-03-26 03:05:51 +00:00
Maksym Yakubych
37f3a01838 [Metabot] Bring your own keys (#70714)
* move ee/llm into oss

* lots of file movement

* move slack connect to opensource

* ability to have enterprise-only tools

* move tests

* Fix stale metabot-v3 aliases, dangling module refs, disabled linting, broken test paths, and typo

* start using deprecated-name, more ns path fixes

* move llm transforms code to oss

* fix namespaces

* whitespace fix

* checkpoint: switch tools to use mu/defn or defenterprise directly

* compilation fix

* lint fixes

* move around some code to break drivers dependency

* fix tests

* fix tool calls

* sort routes

* fix openapi

* fix module config

* lint

* fix clj-kondo

* fix test

* fix token check tests

* cljfmt

* fix slack test

* maybe fix dashboard tests?

* cljfmt

* is this a fix for dashboard tests

* move metabot models to oss

* untangle module config after rebase

* fix module order

* cleanup

* add back ai service-related settings that are still used

* move some more code to more appropriate places

* more code movement

* better schema names

* clean up mu/defn function name formatting

* update module config

* remove unneeded ai service-related test

* fix module config again

* fix auth in tests by removing more ai service stuff

* enable native agent by default

* test transforms over normal function calls

* requires

* pass through metabot-id to tool calls instead of hardcoding embedded-metabot-id, to fix answer sources test

* fix wrap-tools-with-state calls

* try to fix failing field/dashboard tests by matching the code on master (might have been from a bad rebase)

* Revert "try to fix failing field/dashboard tests by matching the code on master (might have been from a bad rebase)"

This reverts commit cd5ec8b487.

* hard revert parameters.dashboard-test to the version on master after the latest rebase

* mark snippet tests as sequential to try to fix perms test issues, remove a couple stray metabot feature checks

* move entity analysis + sql fixing code to oss

* wrap dashboard tests with with-full-data-perms-for-all-users to isolate the failure

* move ai-entity-analysis and ai-sql-fixer under the metabot module

* fix module test

* remove now-unneeded ai feature falgs

* disable perms cache for these failing dashboard tests

* feat: self-hosted metabot provider settings

* test: patch is-hosted temporarily

* fix: grouping

* move files to the frontend/ folder

* move MetabotProviderSection into MetabotSetup to accomidate design changes

* make anthropic the only allowed provider, updat sonnet 4.6 as default model, small layout change to api key input

* remove PLUGIN_METABOT usage, remove purchase pages, consolidate new connections settings ui

* remove oss impl of sql gen on the FE

* remove MetabotSettingUpModal, add alert for slackbot requiring metabot be setup, remove PLUGIN_METABOT, PLUGIN_METABOT.isCloudManaged checks now check llm-metabot-configured? which breaks things for enterprise for now

* add mock Metabase provider

* fix build

* impl copy feedback

* type-fix

* clean up

* fix unit tests

* fix metabot e2e tests

* type fix

* more test fixes

* fix semantic search tests

* more test fix

* kondo fix

* more test fixes

* copy change

* more kondo fixes

* app db test failure fixes

* fix failing BE tests

* fix e2e tests

* more e2e failure fixes

* fix typo

* remove mocked out endpoint definition

* fix last failing e2e test

---------

Co-authored-by: Oleksandr Solovyov <o@solovyov.net>
Co-authored-by: Noah Moss <noahbmoss@gmail.com>
Co-authored-by: Sloan Sparger <sloansparger@gmail.com>
2026-03-25 23:49:07 +00:00
Chris Truter
87dc660191 Minor improvements to MCP server (#71406)
* Fix token scope logic to match middleware

* Add :secure to CSRF and DRY up

* Add comment to magic number for clarity

* Clean up complex threading

* Don't clobber token-scopes

* Optimize and improve no user-id failure mode

* trim comment fluff

* Simplify and avoid double str call

* Use database time for revoke-token for robustness

* Use long for user-id actually

* Enable refresh token rotation

A stolen refresh token was reusable for its entire 30-day TTL with no
way to detect theft. The oidc-provider library supports rotation — on
each refresh grant the old token is revoked and a new one issued.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Sanitize brand-color on consent page to prevent CSS injection

The brand-color setting is interpolated via h/raw into a <style> block on
the consent page. A non-hex value could break out of the CSS rule.
Validate defensively at the rendering site rather than changing the setting
setter, to handle already-persisted invalid values gracefully.

* HMAC-bind consent params to CSRF token and sanitize error messages

Two security improvements to the OAuth server:

1. The authorization decision endpoint re-parsed OAuth parameters from
   the POST body without verifying they matched what the user saw on
   the consent page. A modified form could request broader scopes than
   shown. Now the authorize endpoint HMAC-signs the parsed params using
   the CSRF token as key, and the decision endpoint verifies the
   signature against the re-parsed params before proceeding.

2. Error catch blocks on /authorize, /authorize/decision, and /token
   returned raw exception messages that could leak internal details
   (client IDs, redirect URIs, expected vs actual values). Now returns
   generic RFC-compliant error descriptions and logs details server-side.

* Truncate long client name. Show client id

* Non-functional nits

* Clean up

* Align and extract helper

* DRY OCD

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 18:04:40 -04:00
Chris Truter
40691f70d8 Faster search index sync when loading snapshots (#71410) 2026-03-25 21:42:40 +00:00
Noah Moss
f4b0e5946b Consolidate metabot-enabled checks and add them to a number of endpoints (#71366)
* consolidate metabot-enabled checks and add them to a number of endpoints

* switch describe_tests to use mbql-query helper for cards

* Remove metabot.settings namespace import
2026-03-25 21:17:14 +00:00
Noah Moss
f3a2cfab7f Query pagination for MCP (#70971)
* Combined /v1/query endpoint with pagination and continuation tokens

- Add /v1/query that combines query construction and execution in one step
- Implement continuation_token-based pagination for large result sets
- Cap agent query rows at 200 per page; pagination stops at userland limits (2k/10k)
- Refactor construct-query into shared construct-query* helper
- Move agent-api dispatch behind defenterprise seam in EE MCP tools
- Add streaming JSON-RPC support for tool results in MCP layer
- Add json-string-escaping OutputStream for embedding streamed JSON in JSON-RPC
- Move /agent route out of naughty-routes into intentional top-level-routes
- Fix SSO test_setup to handle nil server port in CLI test runner

* Remove streaming JSON-RPC, buffer all tool responses in memory

Strip the streaming code path from the MCP layer so this branch
focuses purely on the query tool and pagination. Buffering ~120KB
for 200 rows is fine for the limits we enforce.

- Remove json-string-escaping OutputStream and streaming-tool-response
- Revert MCP api.clj to synchronous Ring form (no SSE streaming)
- Move StreamingResponse materialization into EE MCP tools layer
- Simplify invoke-agent-api to handle only plain map responses
- Remove streaming-specific tests (escaping, SSE streaming, error)

* a bunch of other changes to try get the rebased build working

* Roll back a bunch of fixes from the oss branch that don't need to be fixed here

* restore code accidentally lost via multiple rebases

* update modules + lint

* strip out dead ee code

* update tool  count test
2026-03-25 16:47:10 -04:00
Nathan Voxland
817eb1d787 Roll back transform perms (#71094) 2026-03-25 15:27:25 -05:00
Egor Grushin
8b5224136b Cypress bail (#71420)
* add bail feature to the e2e stress test flake workflow

* add bail feature to the e2e stress test flake workflow

* add bail feature to the e2e stress test flake workflow

* Update e2e/support/cypress.js

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>

* renamed BAIL to FAIL_FAST, replace Cypress.env with Cypress.expose

---------

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
2026-03-25 20:11:22 +00:00
Braden Shepherdson
051c1a6b25 [Params] Always return [] for param /values requests (#71302)
Fixes #71164.

On the BE, always return an empty list rather than nil. Old models with `:id` different from `:field_ref` in their metadata could result in `{:values nil ...}` being returned, which breaks the schema and FE. See #71164 for full details.
2026-03-25 20:11:07 +00:00
Jeff Bruemmer
c3c7feeddf docs - OIDC (#70892) 2026-03-25 13:54:47 -04:00
appleby
274b7de269 Remove dangling reference to use-native-agent setting in e2e tests (#71437)
This defsettings was removed in 311b735ae5
2026-03-25 16:48:29 +00:00
Stas Gavrylov
a0bea40dab GDGT-1755: Migrate common components from deprecated Popover to Mantine Popover (#70503)
* GDGT-1755: Migrate RightClickPopover to use Mantine

* GDGT-1755: Migrate DropdownSidebarFilter to Mantine Popover

* GDGT-1755: Migrate ParameterTargetWidget to Mantine Popover

* GDGT-1755: Make DropdownSidebarFilter close on Esc press

* GDGT-1755: Migrate GroupSelect to Mantine Popover

* GDGT-1755: Migrate deprecated Select to Mantine popover to remove deprecated Popover

* GDGT-1755: Cleanup. Delete unused deprecated components

* Fix prettier

* fix: fix test failures after Popover cleanup

- Remove jest.unmock for deleted Popover module in ChartSkeleton tests
- Add missing data-testid="popover" to DropdownSidebarFilter dropdown

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* GDGT-1755: Cleanup

* GDGT-1755: Address code review comments

* Clean up Popover.module.css: keep only tippy.js global styles

Remove unused module-level classes and obsolete tether-related classes.
Import the remaining global styles in TippyPopover.tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-25 18:33:12 +02:00
Nicolò Pretto
2576505465 fix flaky test in embedding hub (#71434) 2026-03-25 17:04:32 +01:00
Nick Fitzpatrick
9f5298fa9a Update TablePicker when users publish/unpublish tables, change name (#70954)
* update TablePicker when users publish/unpublish tables, change name

* refetch tables in useTableLoader

* update table owners
2026-03-25 21:27:23 +05:30
Kevin Kelleher
07f25c0cc7 Allow changing conditional formatting to color range when rule condition is set (#71079) 2026-03-25 10:59:01 -04:00
Nicolò Pretto
cfb3ab406c Implement SSO setup checklist locking for steps 2 and 3 until JWT is configured (#71345)
* Implement SSO setup checklist locking for steps 2 and 3 until JWT is configured

* remove e2e for something already covered by unit

* update checklist default values after merging master
2026-03-25 15:52:01 +01:00
Egor Grushin
a4db64b01b GDGT-2017 write watermark to the DB before setting "succeed" status t… (#71334) 2026-03-25 14:21:55 +00:00
Noah Moss
efcefcad96 Avoid sending only whitespace in a slack API request (#71391)
Don't send only whitespace to the Slack API
2026-03-25 14:09:29 +00:00
Braden Shepherdson
38b071e39d [MBQL lib] Add user docs for filter functions in lib.core (#71035)
Part of QUE2-366.
2026-03-25 14:04:28 +00:00
Braden Shepherdson
46902911c8 [QP] Change the nested query alias from source to __mb_source (#70955)
closes #70224
Fixes QUE2-396.

### Description

This is necessary because `source` is a reasonably common name for a
column, and databases diverge on how they treat a reference like
`"source"."source"`:

- Postgres et al see the relation `source` in scope, and access its
  column called `source`, and all is well.
- Athena et al:
  - See that there is a **column** `source` in scope that doesn't require a prefix.
  - Assume it's a nested column with `ROW` type, and try to access the
    nested `source` field.
  - Type mismatch! It's a string or whatever, not `ROW`.

This PR doesn't *eliminate* the problem, but there's not really anything
we can do to fix Athena's honestly pretty odd choice of how to resolve this
ambiguous parse tree. But by changing our hard-coded alias for all
nested queries from `source` to `__mb_source`, we can make it extremely
unlikely that it will collide with a column that's in scope.
2026-03-25 13:53:00 +00:00
Noah Moss
23f686e698 Include conversation history in slackbot feedback (#71359) 2026-03-25 09:47:42 -04:00
Oleksandr Solovyov
311b735ae5 clean up references to ai-service (#71353) 2026-03-25 15:33:26 +02:00
Ngoc Khuat
e2f1a1a315 Fix flaky schema-change integration test (follow-up to #70863) (#71339) 2026-03-25 21:51:33 +09:00
Alex Yarosh
1f7e4b12f7 docs: update embedding skills (#71415)
Remove development note from SSO for embeds section
2026-03-25 08:30:39 -04:00
Sébastien
178ecbaeef fixed flaky (#71405) 2026-03-25 13:22:25 +01:00
Nick Fitzpatrick
5ac346612f Small positioning adjustments to the data studio nav bar (#71379)
* Small positioning adjustments to the data studio nav bar

* pr feedback
2026-03-25 17:36:41 +05:30
Chris Truter
2ace0b69c1 Built-in MCP server (#70787)
* Add MCP server with agent API integration

Implements an MCP (Model Context Protocol) server for Metabase that exposes
agent API endpoints as MCP tools. Includes an Oauth server, SSE transport, 
session management, scope-based auth, and tool dispatch via defendpoint metadata.

Co-Authored-By: Edward Paget <edpaget@users.noreply.github.com>
Co-Authored-By: Noah Moss <32746338+noahmoss@users.noreply.github.com>
2026-03-25 05:04:46 -04:00
Nick Fitzpatrick
6b8d7846de Update response on set token with network error (#70959)
* Update response on set token with network error

* report error message from checker when setting token

Currently when anything goes wrong during token checking while we're
setting a token, we *always* report that the token is invalid. This is
incorrect.

The token checker already gives us a descriptive `status` for the token,
which can tell the user whether the token is invalid or if we just
weren't able to check it.

One slight change here is to add `:canonical? true` to responses we
receive from MetaStore directly. This allows us to respond with the
correct HTTP status, since if we receive a response from the MetaStore
that tells us the token is invalid, that's a 400 error, where if we
weren't able to get a canonical response at all, that's a 503.

* add unit test

---------

Co-authored-by: John Swanson <john.swanson@metabase.com>
2026-03-25 14:19:32 +05:30
Chris Truter
d34d1dc9ff Fix search related testing race conditions (#71290)
Closes [BOT-1105: Search API tests fail with missing temp table](https://linear.app/metabase/issue/BOT-1105/search-api-tests-fail-with-missing-temp-table)
2026-03-25 09:10:23 +02:00
appleby
95f85256c2 BOT-1172 Fix markdown link buffering for slack links (#71388)
Fix markdown link buffering for slack links

find-potential-slack-link-start was looking for a full `<metabase://` prefix which doesn't work if the link prefix is
split across streamed text chunks. Updated it to deal with this case.
2026-03-25 00:37:50 +00:00
appleby
33661d4b6a BOT-1044 Convert links back to metabase:// format before sending to llm (#71125)
* BOT-1044 Convert links back to metabase:// format before sending to llm

This is a re-implementation of https://github.com/metabase/ai-service/pull/597

resolve-links converts metabase:// links into real URLs before sending them back to the client. When the LLM sees
these resolved URLs in the conversation history, it can get confused and start generating resolved links
itself (e.g. /question#<base64>), rather than following instructions to always use metabase:// links. This sometimes
results in wrong/hallucinated base64 repr, especially in longer conversations where many resolved links accumulate in
the history.

With this commit, we add a link-registry to the agent state to record every link rewritten by resolve-links, and use
invert-links to restore them to metabase:// URIs in the message history before sending it to the LLM for completion.

* Simplify state management for link-registry-atom

We don't need to track it separately in the agent state in init-agent. Just extract it from the agent memory in
the loop-step and sync it back after the llm call.

* Only invert urls in markdown links

* Replace handcrafted legacy MBQL in markdown_link_buffer_test.clj

* Mark deftests ^:parallel in markdown_link_buffer_test.clj

* Mark deftests ^:parallel in links_test.clj and breakup into smaller deftests

* Replace handcrafted legacy MBQL in links_test.clj

* Move resolve-slack-links into metabot.agent.links ns

To match existing resolve-links handling for generic metabase links

* Use with-temporary-setting-values to override site-url in markdown_link_buffer_test.clj

To match existing conventions in the codebase.

* Consolidate with-temporary-setting-values and breakup deftests in markdown_link_buffer_test.clj

* Record slack links into the registry map and invert them before sending history back to the llm

See f90619f5e2

* Fix agent memory initialize-test

* review feedback: extract part->invert-links-key helper
2026-03-24 23:41:03 +00:00
github-automation-metabase
d39d8c5800 Auto-update documentation for master (#71389)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-24 18:06:41 -04:00
Ryan Laurie
60da7517d1 Allow toggling auto releases via variable (#71313)
allow toggling auto releases via variable
2026-03-24 15:58:32 -06:00
appleby
ea43a75160 Fix metabot snowplow test flakes (#71337)
Fix snowplow test flakes

In call-llm-snowplow-test and call-llm-structured-snowplow-test filter the snowplow events to remove potential
new_instance_created events.
2026-03-24 17:23:04 -04:00
Noah Moss
77f28324a0 Add saved question display type to metabot context (#71372)
add saved question display type to metabot context
2026-03-24 20:14:19 +00:00
metamben
778dcf54a1 Introduce config option to specify known hosts for SSH tunnels (#71194)
* Introduce config option to specify known hosts for SSH tunnels

Closes QUE2-424
2026-03-24 22:44:56 +03:00
Alex Yarosh
161ebe81d5 docs: tenant example (#71365)
* Refine tenant attributes and permissions documentation

* Update docs/embedding/tenants.md

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* Update docs/embedding/tenants.md

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-03-24 15:38:54 -04:00
Jeff Bruemmer
1dbfa65885 docs - agent API oss (#71380)
* oss and session

* link

* update
2026-03-24 14:51:59 -04:00
Cam Saul
8cb8e93394 Update GH teams config + modules config for Querying Platform (#71182) 2026-03-24 18:25:45 +00:00
Cam Saul
afc8eb428e Fix Snowflake Netty code scanning alert (#71309)
* Fix Snowflake Netty code scanning alert

* Dummy commit to trigger CI
2026-03-24 18:18:05 +00:00
Anderson
1fbe726c08 Fix ARIA grid role hierarchy in DataGrid for accessibility compliance (#70978)
* Fix aria-expanded on dashboard filter trigger button (#70543)

Closes https://github.com/metabase/metabase/issues/70543

### Description

Move aria-expanded from a wrapper div to the actual trigger button element
for dashboard filter popovers. Mantine's Popover.Target injects
aria-expanded via cloneElement onto its direct child. Previously, a Box
wrapper div was the direct child, so aria-expanded landed on a
non-interactive div instead of the button. This restructures the
component hierarchy so ParameterValueWidgetTrigger (which renders as a
button) is the direct child of Popover.Target, and moves the Sortable
wrapper outside the Popover.

### How to verify

1. Open a dashboard with filter parameters
2. Inspect the filter trigger button element in the DOM
3. Verify aria-expanded is on the button element (not a wrapper div)
4. Click the filter to open the popover and confirm aria-expanded
   toggles to true on the button

### Checklist

- [x] Tests have been added/updated to cover changes in this PR

* Fix ARIA grid role hierarchy in DataGrid for accessibility compliance (#70547)

Closes https://github.com/metabase/metabase/issues/70547

### Description

The DataGrid component had an invalid ARIA role hierarchy: `role="button"`
elements (from dnd-kit's useSortable) were nested directly inside a
`role="grid"` element without proper intermediate roles. Per the WAI-ARIA
grid pattern, the hierarchy must be: grid > rowgroup? > row > gridcell/columnheader.

This commit:
- Adds `role="rowgroup"` to header and body container divs
- Adds `role="row"` to the header row div
- Adds `role="columnheader"` to header cell wrapper divs in DataGrid
- Adds `role="gridcell"` to body cell wrapper divs in DataGrid
- Strips the invalid `role="button"` that dnd-kit injects into SortableHeader
- Removes now-redundant role attributes from HeaderCellWrapper and BodyCell
  (roles are now correctly placed on their parent wrapper elements)

### How to verify

1. Open any dashboard with a table/grid visualization
2. Inspect the DOM of the table-scroll-container element
3. Verify the ARIA role hierarchy follows: grid > rowgroup > row > columnheader/gridcell
4. Run an accessibility audit tool and confirm no `role="button"` violations inside the grid

* Keep button role in sortable header (it's ok since it's not wrapping the columnheader)

* Clean up

* Add unit test

* Fix a few tests

* Fix more e2e tests broken by ARIA role changes

* Fix more e2e tests

* Fix embedding-native.cy

* Another e2e test fix batch

* Fix e2e tests (again)

* fix reproductions-3.cy.spec.js
2026-03-24 15:05:33 -03:00
Jeff Bruemmer
2aec3aa887 Update agent API reference documentation (#71360)
* Update agent API reference documentation

Removed requirement for Metabase Pro/Enterprise from documentation.

* path
2026-03-24 17:36:43 +00:00
Nicolò Pretto
446de63944 feat(embedding-hub): add "copy dashboard to shared tenant collection" step (#70726)
* wip

* wip3

* test ci

* update tests

* remove code from tests

* sort xray dashboards

* cleanup

* clean up tests

* small refactor

* more cleaning up

* keep step disabled if  tenants is not active
2026-03-24 16:18:41 +01:00
Oleksandr Solovyov
08717ff883 closing-connection-native-agent-test: lower amount of chunks we check for (#71354) 2026-03-24 16:38:06 +02:00
Chris Truter
ccef0576f9 Calculate init time reliably (#71356) 2026-03-24 14:16:25 +00:00
github-automation-metabase
1b12502ec3 Revive and merge PR #67694: disable sync_schema side-effect in db-operations-test (#71201)
Fix H2 lock timeout in db-operations-test by mocking sync task

Split the monolithic db-operations-test into three focused tests:
- non-admin-sync-schema-test: mocks quick-task/submit-task! to prevent
  the actual sync job from running, eliminating the race condition with
  DATA_PERMISSIONS cleanup in mt/with-all-users-data-perms-graph!
- non-admin-discard-field-values-test: isolated test for discard_values
- non-admin-rescan-field-values-test: isolated test for rescan_values

Also renames update-database-test, delete-database-test, and fetch-db-test
to non-admin-* for consistency, and adds explicit permission-denial (403)
test cases to each test.

Revives the fix from closed PR #67694.
2026-03-24 14:00:45 +00:00
Braden Shepherdson
b42c2f6605 [QP] Override LIMIT for pivot queries to ensure consistent results (#71033)
Closes #70963
Fixes QUE2-431.

Previously, pivot queries got the same default row limits added as any
aggregated query. But since pivot queries synthesize the results from
several inner queries with different subsets of breakouts, they
have different row counts.

The main ("row details") query the user wrote has all the breakouts,
so it has the most rows. If that query gets truncated, only a subset
of the data is returned, leaving gaps in the pivot table. Worse, the
sub-queries with other subsets of the breakouts, intended to collect
group, column and row subtotals, and the grand total, are inconsistent
with the details in the cells, and with each other, because they can
all end up `SUM`ing (etc.) over different sets of the raw rows.

With this change, a new and much larger row limit is permitted for the
pivot sub-queries. The pivot table viz contains one cell for each
aggregation in the query, multiplied by the rows of the main query.
We budget for **200k cells**, divide by the number of aggregations,
and that becomes the row limit.

If the row details query reaches that limit, the query is rejected:
- Other sub-queries are skipped
- A `pivot-rows-truncated` flag in the results signal the FE
- Which shows an error suggesting the user add a filter or remove
  breakouts to lower the row count.

Some reasonable pivot queries might still be excluded, but now we never
run a pivot query with incorrect data in it. If we execute the query and
render the pivot results, then all the returned data should be correct.
2026-03-24 13:45:34 +00:00
Nick Fitzpatrick
cfa96f3fe0 Catch drag events on Dashcard placeholders (#71274)
* Catch drag events on Dashcard placeholders

* scope drag events, add test
2026-03-24 18:46:31 +05:30
github-automation-metabase
3f55d1b9f4 Use env var indirection in Loki stress-test workflow (#71349)
Fix script injection via env var indirection in loki-stress-test-flake-fix.yml
2026-03-24 13:48:02 +01:00
Nemanja Glumac
acbf9bcc88 Delete Claude bug fix workflow (#70993) 2026-03-24 12:52:30 +01:00
Uladzimir Havenchyk
442cf4abb9 fix: cross branch migration workflow (#71153) 2026-03-24 14:31:51 +03:00
Kamil Mielnik
aee64c6047 GDGT-1869 Remove "as VisualizationSettingsDefinitions" casts (#71217) 2026-03-24 17:30:47 +07:00
Nicolò Pretto
41ca038f67 feat(embedding hub): add ability to hide sidebar for some pages (#71278) 2026-03-24 10:09:27 +01:00
Chris Truter
a0896d1faf Fix clickhouse test flake (#71306) 2026-03-24 10:26:47 +02:00
Romeo Van Snick
c8cb9c2f04 Remove sync buttons for managed cloud databases (#71203)
* Remove sync buttons for managed cloud databases

* Remove sync settings button for table in attached data warehouse in admin > table metadata and data studio
2026-03-24 14:17:29 +11:00
Fraser
b8c90cdfbe Push shared files down from feature modules (#71137) 2026-03-24 15:24:34 +13:00
Romeo Van Snick
0ab5a0f603 Fix parameter tags reordering in the presence of snippet tags (#71209)
* Disregard snippet tags when reordering parameters

* Add repro for metabase#69160

* Wording

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>

---------

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
2026-03-24 11:59:52 +11:00
Riley Thompson
8f9282dca1 Don't generate join alias that matches source table name (#70876)
* include source name in taken names

* add test

* Update src/metabase/lib/join.cljc

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>

* fix tests

* fix test

* Fix test metadata for self-join test

* e2e test fix attempt

* second attempt

---------

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-03-24 00:39:09 +00:00
bryan
c8560d8251 Models to transforms (#70735)
Co-authored-by: Alexander Polyankin <alexander.polyankin@metabase.com>
2026-03-23 23:59:16 +00:00
appleby
74a9d5baeb BOT-1157 Update metabot prometheus and snowplow metrics to include both provider and model (#71259)
We now report the full provider and model rather than just the model, e.g.

    anthropic/claude-haiku-4-5 -> openrouter/anthropic/claude-haiku-4-5
    claude-haiku-4-5           -> anthropic/claude-haiku-4-5

In addition to being useful info to capture, this matches the behavior of the pre-existing ai-service metrics.
2026-03-23 16:42:40 -07:00
bryan
fb428f307e Extend ci-report to accept branch names and commit SHAs (#70742) 2026-03-23 15:50:21 -06:00
Jeff Bruemmer
e50748827a docs - sdk playground (#71314)
* sdk playground link

* edit

* image

* Optimized 1 images, saved 188.7 KB (35.5% smaller)

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-03-23 21:48:31 +00:00
Ben Grabow
9ec99afb69 Ghy-3258/table tag dependencies missing in incremental transforms (#71316)
Include table tags as dependencies of native queries
2026-03-23 16:40:06 -05:00
Oleksandr Solovyov
c2ad36cccb move ee/llm into oss (#71068)
* move ee/llm into oss

* lots of file movement

* move slack connect to opensource

* ability to have enterprise-only tools

* move tests

* Fix stale metabot-v3 aliases, dangling module refs, disabled linting, broken test paths, and typo

* start using deprecated-name, more ns path fixes

* move llm transforms code to oss

* fix namespaces

* whitespace fix

* checkpoint: switch tools to use mu/defn or defenterprise directly

* compilation fix

* lint fixes

* move around some code to break drivers dependency

* fix tests

* fix tool calls

* sort routes

* fix openapi

* fix module config

* lint

* fix clj-kondo

* fix test

* fix token check tests

* cljfmt

* fix slack test

* maybe fix dashboard tests?

* cljfmt

* is this a fix for dashboard tests

* move metabot models to oss

* untangle module config after rebase

* fix module order

* cleanup

* add back ai service-related settings that are still used

* move some more code to more appropriate places

* more code movement

* better schema names

* clean up mu/defn function name formatting

* update module config

* remove unneeded ai service-related test

* fix module config again

* fix auth in tests by removing more ai service stuff

* enable native agent by default

* test transforms over normal function calls

* requires

* pass through metabot-id to tool calls instead of hardcoding embedded-metabot-id, to fix answer sources test

* fix wrap-tools-with-state calls

* try to fix failing field/dashboard tests by matching the code on master (might have been from a bad rebase)

* Revert "try to fix failing field/dashboard tests by matching the code on master (might have been from a bad rebase)"

This reverts commit cd5ec8b487.

* hard revert parameters.dashboard-test to the version on master after the latest rebase

* mark snippet tests as sequential to try to fix perms test issues, remove a couple stray metabot feature checks

* move entity analysis + sql fixing code to oss

* wrap dashboard tests with with-full-data-perms-for-all-users to isolate the failure

* move ai-entity-analysis and ai-sql-fixer under the metabot module

* fix module test

* remove now-unneeded ai feature falgs

* disable perms cache for these failing dashboard tests

* simplify test that was still using agent-api feature flag

* memoize spec-index-query by app db to fix failing search tests caused by new test execution order

* LLM OSS FE (#71197)

* bulk move files

* replace usage of /ee/metabot-v3/ to /metabot

* remove PLUGIN_METABOT

* fix broken metabot routes, removes metabot_v3 token feature usage

* remove PLUGIN_AI_*

* bring back enterprise vs oss behavoir, with the code still being OSS

* fix lint + unit tests

* fix embedding sdk stories

* add back missing FixSqlQueryButton.unit.spec.tsx test

* fix last references of `/ee/ai-*` endpoints

* pr feedback

* test fix

---------

Co-authored-by: Noah Moss <noahbmoss@gmail.com>
Co-authored-by: Sloan Sparger <sloansparger@users.noreply.github.com>
2026-03-23 17:30:50 -04:00
Chris Truter
76b01b772b Define additional metrics for release monitoring (#71237) 2026-03-23 17:20:34 -04:00
Noah Moss
733176c925 Add cancelled check for sdk-tests (#71307) 2026-03-24 10:03:27 +13:00
Ryan Laurie
88a87db2b8 Sanitize database details during serialization import (#71311)
[59] Sanitize database details during serialization import (#335)

* Sanitize database details during serialization import

Strip low-level JDBC keys (classname, subprotocol, connection-uri, subname,
init) from database details during serialization import and reject h2 engine.

Also add subname and init to the connection-details dispatch blocklist and
sanitize details in h2 connection-details->spec as defense in depth.

* Use custom runners

* fix error printing and test

* rebind assert-not-h2! during tests

* kondo ignore

---------



# Conflicts:
#	.github/workflows/backend.yml
#	.github/workflows/cross-branch-migrations.yml
#	.github/workflows/tusk-sanity-check.yml

Co-authored-by: Nicola Mometto <nicola.mometto@metabase.com>
Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
Co-authored-by: Nemanja <31325167+nemanjaglumac@users.noreply.github.com>
2026-03-23 14:40:06 -06:00
Ryan Laurie
45380db283 Send release jars to cloud bucket (#71093)
* update var

* do it in the tag step
2026-03-23 14:29:58 -06:00
metamben
84b79b9126 Reorganize QP module (#70808)
* Introduce qp.core and qp.test namespaces
2026-03-23 20:14:12 +00:00
Nicola Mometto
d6d3bbb89a Fix bug with temporal values in bigquery incremental transforms (#71291)
* fix: coerce temporal values used in incremental transforms filters

* fallthrough on LocalDateTime

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>

---------

Co-authored-by: metamben <103100869+metamben@users.noreply.github.com>
2026-03-23 18:41:40 +00:00
Alex Yarosh
1bb4b60d60 docs: workflow schedule (#71305)
docs workflow schedule
2026-03-23 14:33:38 -04:00
Sébastien
1827c0c37d Fixed dancing MB (#71211) 2026-03-23 16:48:56 +01:00
github-automation-metabase
5dd6f68c46 Fix title escaping for JSON (#71289) 2026-03-23 16:08:50 +01:00
Nemanja Glumac
c85d47365d Remove hard-coded Trivy version (#71287) 2026-03-23 15:03:46 +01:00
github-automation-metabase
17bca77f06 Auto-update documentation for master (#71268)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-23 09:19:13 -04:00
Jakub Chodorowicz
4c4c049ac8 update copy and styling of table variable select (#71101)
* update copy and styling of table variable select

* extract common LabelWithInfo component

standardize styling
2026-03-23 13:13:42 +01:00
Kevin Kelleher
38fa86db3f Fix scatter plot flicker on hover (#71036) 2026-03-23 07:38:27 -04:00
Nemanja Glumac
ac3a38209f Patch vulnerable Trivy action (#71282)
Resolves DEV-1712

Patches Trivy GitHub action to a version that is confirmed not to be affected by the supply chain attack. We're using SHA-1 instead of a tag for an improved security.

See: https://app.mendral.com/insights/01KM6NFNZPV1JS72KAPCA2K9XH
2026-03-23 12:22:55 +01:00
Chris Truter
c568186ebc Only run Eastwood (test) on master (#71232) 2026-03-23 12:30:13 +02:00
Nicolò Pretto
a49e97237f add ability to hide Our analytics in MiniPicker and hide it in the table picker in the embedding setup (#70920)
* add ability to hide Our analytics in MiniPicker

* remove changes not needed

* update getOurAnalytics with the values from EntityPicker
2026-03-23 11:12:55 +01:00
Fraser
f1c36719aa Move shared query_builder logic to querying (#71129) 2026-03-23 09:22:12 +00:00
Sébastien
292a667717 Fixes a loki flaky (#71225)
* loki flaky

* .

* .

* fonts
2026-03-23 10:02:28 +01:00
Kamil Mielnik
361549c5af GDGT-2024 Consolidate ChartSettingsWidget's noPadding, marginBottom, and borderBottom (#71154) 2026-03-23 14:25:07 +07:00
Romeo Van Snick
c009e25907 Fix timeline form icon input (#71199)
* Add icon to timeline icons

* Use Select from mantine/ui for timeline form

* Add test for icon in timeline creation
2026-03-23 16:21:20 +11:00
Romeo Van Snick
e13fd47144 Convert dataref to ts (#71062)
* Convert dataref to ts

* Fix unit test

* Fix actions

* Fix field reference

* Fix native data reference

* Fix field pane
2026-03-23 13:01:43 +11:00
metabase-bot[bot]
2812c4496d [chore] Update E2E test timings with recent data (#71265)
Update E2E test timings with averaged data

Collected timing data from 7 days of test runs.
All specs with timing data were included (simple averages).

🤖 Generated automatically by update-e2e-timings workflow

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Actions <actions@github.com>
2026-03-23 10:11:46 +13:00
Nathan Voxland
28093ce803 Support Group Sync in OIDC (#71176) 2026-03-21 00:24:40 -05:00
Nathan Voxland
a649b08171 Field filter does not show dropdown values (#71114) 2026-03-21 00:23:58 -05:00
Egor Grushin
7a7585be68 Gdgt 1864 Add pinned columns and rows to the DataGrid (#70384) 2026-03-21 02:10:09 +00:00
Anderson
6b241a18fd Fix dashboard tab list items having role="button" instead of role="tab" (#70546) (#70976)
* Fix aria-expanded on dashboard filter trigger button (#70543)

Closes https://github.com/metabase/metabase/issues/70543

### Description

Move aria-expanded from a wrapper div to the actual trigger button element
for dashboard filter popovers. Mantine's Popover.Target injects
aria-expanded via cloneElement onto its direct child. Previously, a Box
wrapper div was the direct child, so aria-expanded landed on a
non-interactive div instead of the button. This restructures the
component hierarchy so ParameterValueWidgetTrigger (which renders as a
button) is the direct child of Popover.Target, and moves the Sortable
wrapper outside the Popover.

### How to verify

1. Open a dashboard with filter parameters
2. Inspect the filter trigger button element in the DOM
3. Verify aria-expanded is on the button element (not a wrapper div)
4. Click the filter to open the popover and confirm aria-expanded
   toggles to true on the button

### Checklist

- [x] Tests have been added/updated to cover changes in this PR

* Fix dashboard tab list items having role="button" instead of role="tab"

Closes https://github.com/metabase/metabase/issues/70546

### Description

The Sortable wrapper around each dashboard tab item was applying a default
`role="button"` to its container div, which overrode the inner TabButton
`role="tab"` from the perspective of screen readers and accessibility tools.
Changed the Sortable wrapper to use `role="presentation"` so it is transparent
to the accessibility tree, allowing the inner TabButton role="tab" to be
properly recognized within the role="tablist" container.

### How to verify

1. Open a dashboard with multiple tabs
2. Inspect the tab items in the browser DevTools
3. Verify that tab items have `role="tab"` (not `role="button"`)
4. Verify the tab list container has `role="tablist"`

### Checklist

- [x] Tests have been added/updated to cover changes in this PR

* Add issue number to test titles (#70546)

* Improve test

* Clean up
2026-03-20 20:19:30 -03:00
Ryan Laurie
b9573fc138 Use Local DigiCert for docker container builds (#71095)
use local cert

# Conflicts:
#	Dockerfile
#	bin/docker/Dockerfile
#	bin/docker/Dockerfile_ubuntu
2026-03-20 21:55:40 +00:00
Ben Grabow
8d41c7d068 Disallow adding new classpath resources as geojson paths (#70745)
* Disallow adding new classpath resources as geojson paths
2026-03-20 21:16:01 +00:00
Ben Grabow
e0365e3297 Revert "Temporarily disable app db version checks" (#71191)
Revert "Temporarily disable app db version checks (#70884)"

This reverts commit c5dcdc3238.
2026-03-20 15:36:19 -05:00
Braden Shepherdson
6b6ea19ab7 [MBQL lib] Add user docs for breakouts functions (#71034)
Part of QUE2-366.
2026-03-20 19:56:00 +00:00
Uladzimir Havenchyk
5a142f6eb1 add a workflow to publish custom viz npm package (#71221)
* add a workflow to publish custom viz npm package

* trigger CI
2026-03-20 12:32:56 -06:00
ignacio
cfacacd556 Update account settings docs (#71240)
This is currently doable in version 59.3.



See this issue: https://github.com/metabase/metabase/issues/63581
2026-03-20 15:22:12 -03:00
Uladzimir Havenchyk
a9b9ad9d85 chore: convert isa.js to typescript (#71014)
* chore: convert isa.js to typescript

* address comments

* address review

* revert back broken checks

* fix review
2026-03-20 20:35:23 +03:00
Chris Truter
68ec84ae4d Make all model imports and exports explicit (#71224) 2026-03-20 09:58:44 -07:00
Jakub Chodorowicz
6724b20a7b enable transform inspector only with advanced (python) transforms (#71067)
hide inspect tab when it's not supported by current features
2026-03-20 17:40:50 +01:00
Edward Paget
dd34d56120 fix: cancel serialization when client disconnects (#69864)
* fix: cancel serialization when client disconnects

Co-Authored-By: John Swanson <john.swanson@metabase.com>

* fix: complete serialization virtual thread streaming response

- Fix channel signal direction: use >!! to write to done-chan instead of <!! (read)
- Fix streaming response: use io/copy instead of .write for File objects
- Set explicit 200 status on success, set-status!/set-content-type! on error path
- Add Content-Disposition header on success for proper download behavior
- Clean up temp files on cancel and use finally for deterministic cleanup
- Remove commented-out old synchronous code
- Use canonical clojure.core.async alias (a instead of async)
- Add server module to enterprise/serialization allowed dependencies
- Relax full_stacktrace test threshold (200 -> 50 lines) to avoid JVM-dependent flakiness

---------

Co-authored-by: John Swanson <john.swanson@metabase.com>
2026-03-20 11:28:41 -05:00
Braden Shepherdson
3559ec98b0 [QP] Do coercions (eg. Unix timestamps) with native sandboxing (RCLS) (#70885)
Fixes QUE2-376.

For MBQL queries, type coercions (like integers to Unix timestamps) only
get applied on the earliest stage where that field is introduced. When
the table the field comes from is sandboxed by a native query, an
extra stage is introduced but it doesn't do the coercion.

Decorates the metadata of any native sandbox column whose table counterpart had coercion defined.

It's unfortunately complicated inside lib since the metadata needs to be propagated through exactly one
stage boundary. If it propagates all the way then we get double-coercion and SQL errors.
2026-03-20 16:24:21 +00:00
Jeff Bruemmer
bc80f935a2 docs - transform inspector (#71188)
* draft

* edits

* edits

* toc

* Optimized 3 images, saved 326.0 KB (47.1% smaller)

* Update docs/data-studio/transforms/transform-inspector.md

Co-authored-by: Alex Yarosh <alexandra@metabase.com>

* edits

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
Co-authored-by: Alex Yarosh <alexandra@metabase.com>
2026-03-20 12:19:19 -04:00
github-automation-metabase
d0ac061fee Fix trivy action tag prefix (#71213)
fix: use v prefix on aquasecurity/trivy-action@v0.29.0 tag reference
2026-03-20 09:17:26 -06:00
Jeff Bruemmer
0cdcd2232c docs - agent resources for embedding (#71108) 2026-03-20 10:40:15 -04:00
Sébastien
8de615a69d Fixed empty csv for adhoc pivot tables in SDK (#71141) 2026-03-20 14:28:59 +01:00
Uladzimir Havenchyk
24968aa972 build: use a proper link for cert download (#71064) 2026-03-20 16:22:23 +03:00
Ngoc Khuat
a3ae3ba1f4 Transform inspector reorganization (#71136) 2026-03-20 10:34:24 +00:00
Nicola Mometto
22b6962656 default transforms impls with informative errors (#71145)
feat: provide default transforms impls with informative errors
2026-03-20 10:14:14 +00:00
Nicolò Pretto
94be4bfae3 Emb 1450 simplify sdk e2e test commands and docs (#71168)
* remove the embedding dev command and update the docs

* fix typo
2026-03-20 09:42:10 +01:00
Ngoc Khuat
39bc014f14 Server-side rendering for comment emails (#70938) 2026-03-20 07:55:16 +00:00
Fraser
4864f0ae24 Revert "Further pdf export improvements" (#71208)
Revert "Further pdf export improvements (#71031)"

This reverts commit 536d6bf22f.
2026-03-20 19:45:09 +13:00
Aleksandr Lesnenko
cd7eb82bdb remove emotion from browse pages (#71184) 2026-03-20 06:43:45 +05:30
Nick Fitzpatrick
536d6bf22f Further pdf export improvements (#71031)
* use cssText

* include font props on <text>, <tspan>, and <textPath> nodes
2026-03-20 05:59:35 +05:30
Anderson
ac092618ac Hide 'New metric' and 'Publish a table' actions on Data Studio Library empty state when in read-only mode (#71112)
* Fix: Hide metric creation and table publishing actions in read-only mode

In read-only instances (remote sync read-only mode), the Library still showed:
- 'New metric' action link in the Metrics empty state
- 'Publish a table' action button in the Data empty state
- 'New snippet' action link in the SQL snippets empty state

While the +New button was already hidden in read-only mode, the empty state
action links were not checking for this permission, allowing users to attempt
actions that should be disabled.

Changes:
- Add read-only mode check to EmptyStateAction component in LibrarySectionLayout
- Empty state actions now return null when in remote sync read-only mode
- Add e2e test to verify actions are hidden in read-only mode

* Refactor: use hideAction param and call-site guard instead of returning null from EmptyStateAction

Leverage the existing third param of createEmptyStateItem to suppress
actionUrl for metrics/snippets in read-only mode (consistent with how
the snippet tree handles it). Guard the 'Publish a table' button at
the EmptyStateAction call site in LibrarySectionContent, outside the
component itself. Reorganise the e2e test into the empty state describe
block.
2026-03-19 20:34:20 -03:00
Tyler Plude
03a09e1832 BOT-1127 Metabot Conditional Tool Schema Params (#71050)
* conditional tool schema input params

* simplify feature-available?

* unit tests

* applying to other search tools
2026-03-19 16:01:59 -07:00
Chris Truter
cc74f66a1b Restrict which models are accessible by each module (#71010) 2026-03-19 21:08:20 +00:00
John Swanson
bfa572ec6b Metric viewer can use metrics based on cards (#71107)
[UXW-3260: Metrics from models/table questions are not viewable in the Metrics viewer](https://linear.app/metabase/issue/UXW-3260/metrics-from-modelstable-questions-are-not-viewable-in-the-metrics)
2026-03-19 12:22:17 -08:00
Romeo Van Snick
fb38572a8c Fix flaky test in sidebar content (#71135)
Fix flaky test
2026-03-20 07:11:37 +11:00
lbrdnk
a02acabac1 Add database_engine to enhanced tables (#70968) 2026-03-19 20:59:15 +01:00
Rodrigo López Dato
198cd9a9a3 Remove X-Metabase-Session from autogenerated API docs in favor of X-API-Key (#71155)
* X-API-Key capitalization in docs

* Use X-API-Key instead of X-Metabase-Session in Scalar API docs
2026-03-19 19:32:45 +00:00
Eric Normand
c9c7f230fb Fix string field IDs leaking into bulk-metadata queries (#71167)
Add pos-int? guard to all-implicitly-joined-field-ids pattern match,
matching the guard already used by all-field-ids. String field names
(e.g. from native/model sources) were being collected and passed to
bulk-metadata, causing Postgres type mismatch errors.
2026-03-19 13:58:07 -05:00
Aleksandr Lesnenko
e1608e708d Add skill to migrate emotion to mantine and css modules (#71173)
add skill to migrate emotion to mantine and css modules
2026-03-19 14:21:59 -04:00
Chris Truter
213b1b5905 Add mechanism for more reliably renaming settings (#71088) 2026-03-19 14:12:31 -04:00
Eric Normand
42fdf326b6 Fix StackOverflowError in deduplicated column name resolution (#71096)
Add a dynamic guard `*in-deduplicated-column-resolution?*` and convert the
mutual recursion between `resolve-nonexistent-deduplicated-column-name` and
`resolve-from-previous-stage-or-source` into a loop. Previously, resolving
a field like `name_5000` would create O(N) deep mutual recursion, overflowing
the JVM stack at ~3000-4000 frames.
2026-03-19 12:24:47 -05:00
Anderson
3969de09bb Enable keyboard navigation in dashcard download data popover (#70541) (#70965)
* Enable keyboard navigation in dashcard download data popover

* Use mantine's Menu.Sub for submenus in DashCardMenu
2026-03-19 13:22:04 -03:00
bryan
99548507aa Add ci:run-<driver> and ci:run-all-drivers labels to mage driver decisions (#70894)
* Add ci:run-<driver> and ci:run-all-drivers labels to mage driver decisions

Allow force-running specific drivers or all drivers via PR labels,
overriding quarantine. Inserted at priority 3 (after H2/Postgres
always-run, before quarantine check). Added Labels of Interest and
All Drivers sections to help output.

* Add hint about ci:run labels in CI driver decisions output

Shows a helpful message in CI logs pointing users to the new labels
and the local help command for more info.

---------

Co-authored-by: Ben Grabow <ben.grabow@gmail.com>
2026-03-19 16:07:07 +00:00
Edward Paget
e0a2e2a80a fix: remove logging from session api (#70444) 2026-03-19 11:03:02 -05:00
Edward Paget
b8805a8d92 feat: admin password reset link for users (#70464)
* feat: admin password reset link for users

* fixup! add missing dep to modules.edn

* fixup! response schema position

* fixup! reset e2e tests

* fix: store temporary password in Redux after no-SMTP password reset

The handleResetConfirm function called updatePassword via RTK Query but
never dispatched storeTemporaryPassword, so the temporary password modal
never appeared.

* fix: replace smart quotes with straight apostrophes in password reset modal

The modal title and body strings used Unicode RIGHT SINGLE QUOTATION MARK
(U+2019) instead of ASCII apostrophe, causing E2E tests to fail on text
matching.

* fix: correct reset URL assertion in e2e test

The test expected "password-reset" but the actual URL path is
"reset_password".
2026-03-19 10:30:32 -05:00
Braden Shepherdson
90042b1f02 [MBQL lib] User docs for aggregation functions (#70515)
Part of QUE2-366.

Adds user-facing documentation for aggregations functions in `lib.core`.
2026-03-19 14:26:22 +00:00
Kamil Mielnik
55144d12be GDGT-2001 Get rid of VisualizationSettingDefinition["default"] (#71015) 2026-03-19 14:08:16 +00:00
Stas Gavrylov
91ca661215 GDGT-2037: Fix CssVarsDeclarationPlugin crashing dev server on missing files (#71139)
* Fix CssVarsDeclarationPlugin to not break on build if file is missing

* Fix path to color-keys file

* Add CssVarsDeclarationPlugin warning comments to referenced source files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:03:29 +00:00
github-automation-metabase
b42f3332c0 Auto-update documentation for master (#71128)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-19 09:32:36 -04:00
Nicola Mometto
4937134c3e Batch metabase_table update migration (#70996)
* fix: batched metabase_table update on data_layer

* don't replace inline

* preserve

* no need for rollback in liquidbase

* run outside of a transaction

* runInTransaction:false
2026-03-19 12:05:42 +00:00
Jakub Chodorowicz
ebbc0adc70 display sql transforms errors in editor (#71092)
* update setupCardDataset to use named params

* display sql transform errors in editor

* improve mock data in test

* use isObject helper
2026-03-19 11:10:33 +00:00
Nicolò Pretto
45cd62fe3e remove claude from mise as it's not a required tool (#71138) 2026-03-19 10:22:47 +01:00
Jakub Chodorowicz
2d250a7a71 change incremental transform copy (#71103) 2026-03-19 09:59:14 +01:00
Chris Truter
7ecba8f6d4 Create table entries for transform targets on save (#70552) 2026-03-19 08:09:03 +00:00
Aleksandr Lesnenko
e53b32e245 stacked series (#70682) 2026-03-19 00:49:25 +00:00
Edward Paget
48d1a7c36e fix: handle recycled jetty http response objects (#70674)
* fix: handle recycled jetty http response objects

In the streaming response handler. Make sure that the response object is
both committed and that its AsyncContext has not been timedout.
Attempting the write after the timeout results in an NPE.

* fixup! missing reify method

* make async-context-completed? private
2026-03-18 16:37:35 -05:00
Anderson
d2406bf64b Fix aria-expanded on dashboard filter trigger button (#70543) (#70973)
* Fix aria-expanded on dashboard filter trigger button (#70543)

Closes https://github.com/metabase/metabase/issues/70543

Move aria-expanded from a wrapper div to the actual trigger button element
for dashboard filter popovers. Mantine's Popover.Target injects
aria-expanded via cloneElement onto its direct child. Previously, a Box
wrapper div was the direct child, so aria-expanded landed on a
non-interactive div instead of the button. This restructures the
component hierarchy so ParameterValueWidgetTrigger (which renders as a
button) is the direct child of Popover.Target, and moves the Sortable
wrapper outside the Popover.

1. Open a dashboard with filter parameters
2. Inspect the filter trigger button element in the DOM
3. Verify aria-expanded is on the button element (not a wrapper div)
4. Click the filter to open the popover and confirm aria-expanded
   toggles to true on the button

- [x] Tests have been added/updated to cover changes in this PR

* Wrap dashboard filter items in a list container for a11y
2026-03-18 17:30:40 -03:00
Kevin Kelleher
c62b32e78f Fix pivot text overlapping when next header column is collapsed (#71104) 2026-03-18 15:49:56 -04:00
Noah Moss
241e1fb189 Better error logging for slackbot (#71098) 2026-03-18 14:23:36 -05:00
Rodrigo López Dato
db25f8fa80 Docs: improve embedding appearance theming section (#70967)
* Docs: improve embedding appearance theming section

Rewrite the basic theming section for clarity: rename to "Dark mode and
light mode", simplify the explanation, and preserve the old
#basic-theming anchor for existing links.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update docs/embedding/appearance.md

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-03-18 18:35:09 +00:00
Ben Grabow
86d1b95df0 Bgrabow/ci test repl mage task (#71025)
* ci-test-repl mage task

* Use ci port map to set port. Include junit name in job string

* Move to bgrabow

* Add dry-run mode

* task! wrapper to support helptext
2026-03-18 18:18:33 +00:00
Jeff Bruemmer
ee6203aa6e docs - agent demo link (#71074)
* agent demo link

* Optimized 1 images, saved 125.8 KB (45.1% smaller)

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-03-18 18:03:17 +00:00
Anderson
4b7056a383 Make it clearer why transforms can't be created in read-only mode (#71051)
test: update tests for read-only mode transform creation UI changes

Update E2E tests to verify:
- Create transform button is disabled with explanatory tooltip in read-only mode
- New transform pages show not found message when accessed directly in read-only mode
2026-03-18 13:58:32 -03:00
Nathan Voxland
25100c1310 Allow linear to trigger repro-bot (#71049) 2026-03-18 09:03:46 -07:00
Alex Yarosh
2f7a83ddc5 docs: new incremental transforms (#71080)
* incremental transforms

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-03-18 11:21:06 -04:00
Ben Grabow
0abb871d3f Fix flaky metabase-enterprise.dependencies.api-test (#71024)
* Drain the single-threaded async executor to ensure all pending dependency work completes before model cleanup deletes cards, avoiding lock timeouts.

* Use fresh-dependency-async-executor-fixture on every test
2026-03-18 10:01:34 -05:00
Eric Normand
693550cf45 Correctly hide/show card filters based on permissions (#70980)
Backend (query_processor/dashboard.clj): Added an explicit view-data permission check using most-permissive-database-permission-for-user. This returns 403 when a user has no view-data access, taking precedence over parameter validation errors (400).

Frontend (DashCardVisualization.tsx): Decoupled the three concerns:

* Inline parameters render when inlineParameters.length > 0 AND the user has view access (no 403 on the card result)
* DashCardMenu (download button) renders when DashCardMenu.shouldRender() is true (requires canEditQuestion or canDownloadResults)
* 403 from backend hides both inline params and menu — no point showing UI for data the user can't access
2026-03-18 09:35:17 -05:00
Ryan Laurie
8255a879de Parameterize runners (#71077)
* Use custom runners

# Conflicts:
#	.github/workflows/cross-branch-migrations.yml
#	.github/workflows/pre-release.yml
#	.github/workflows/tusk-sanity-check.yml

* more updates

---------

Co-authored-by: Nemanja <31325167+nemanjaglumac@users.noreply.github.com>
2026-03-18 08:24:12 -06:00
Sébastien
ea2e642c82 Fixed flaky e2e test (#71019) 2026-03-18 15:00:06 +01:00
github-automation-metabase
3bfd0aa9bf Auto-update documentation for master (#71058)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-18 08:41:53 -04:00
Uladzimir Havenchyk
f45dbe78a1 chore: update formatting (#71072) 2026-03-18 13:04:05 +01:00
Kevin Kelleher
43ab4dbb8d Allow grid columns in Detail visualization to grow and shrink (#70964) 2026-03-18 07:49:40 -04:00
Jakub Chodorowicz
80d08cce82 use array/list for sources table in transforms (#70100) 2026-03-18 11:11:24 +01:00
Fraser
7e5435f6f1 Dev 1662 move libcolors into UI (#70754) 2026-03-18 22:52:16 +13:00
Kamil Mielnik
3c229eb38d GDGT-1998 Get rid of VisualizationSettingDefinition["marginBottom"] (#71004) 2026-03-18 15:59:11 +07:00
Kamil Mielnik
e271bc5e52 GDGT-1995 Get rid of VisualizationSettingDefinition["disabled"] (#71000) 2026-03-18 14:00:46 +07:00
Cam Saul
f5eeabb4be Rework Pivot QP to add pivot-grouping in post-processing (#70986)
* Rework Pivot QP to add `pivot-grouping` in post-processing

* PR feedback

* Fix MongoDB test failures

* Fix test failures

* cljfmt

* Fix calculating pivot group for queries with remaps

* cljfmt

* Test fix 🔧

* Test fix 🔧

* Delete unused code
2026-03-18 01:29:20 +00:00
Anderson
f2164af918 Add snowplow events when creating new job (#70279)
* Add snoplow events when creating new job

* Fix e2e
2026-03-17 21:05:09 -04:00
Anderson
832e71aa46 Add aria-level attribute to dashboard title heading (#70985)
* Add aria-level attribute to dashboard title heading

Closes https://github.com/metabase/metabase/issues/70544

### Description

Dashboard title elements with role="heading" now include the aria-level
attribute set to 1, properly indicating they are primary headings. This
improves accessibility for screen reader users by establishing the correct
heading hierarchy.

The fix adds aria-level={1} to the Box component in DashboardHeaderView
that already has role="heading". This ensures screen readers can properly
navigate the heading structure of the dashboard page.

### How to verify

1. Open any dashboard in Metabase
2. Inspect the dashboard title element in browser DevTools
3. Verify the heading has both role="heading" and aria-level="1"
4. Test with a screen reader (e.g., NVDA, JAWS) to verify it's announced
   as a level 1 heading

Alternatively, run the new unit test:
```bash
bun jest --testPathPatterns="DashboardHeader/tests/common.unit.spec"
```

### Checklist

- [x] Tests have been added/updated to cover changes in this PR

* Clean up
2026-03-17 20:45:09 -03:00
Sloan Sparger
6826140380 [Metabot] Slackbot (#68473)
* wip

* clean up and move secrets to settings

* validate incoming slack requests and better sending of messages / processing responses

* get ai response working, make slack app an agent, fetch threads

* add loading / thinking state

* image uploading and clean up

* add test coverage for validation of slack requests

* add test coverage for endpoints only being available with metabot token feature

* add test coverage for user messages

* fix image uploads not working

* add test coverage for messages with visualizations

* fix convo history

* allow for rendering adhoc questions and surrounding the static viz with padding

* Re-enable +auth middleware for endpoints in metabase-enterprise.metabot-v3.api

* Ensure user has linked slack and metabase accounts before sending messages

And send them an ephemeral message with a (currently invalid) link to authorize

* Add some more repl testing comments

* dev comments: fix assert when updating manifest and encourage env vars for dev secrets

* Use request/with-current-user rather than binding api/*current-user* directly

This is the preferred way to set the user and handles binding things like api/*is-superuser?* and
api/*current-user-permissions-set* as well.

* smol refactor to clean up process-user-message

* Fix kondo lint warning about undeclared channel module dep

* Fix reflection warning for metabase.server.streaming_response/StreamingResponse

* Fix reflection warning in user-message-with-visualizations-test

* Use :on-complete handler to capture lines in make-ai-request

* Make all defs except endpoints and routes private

* Rename client :bot-token to :token

Currently only used for endpoints that expect a bot token, but in principal these could be used for other endpoints
that might expect, e.g. an app config token to update the app manifest.

* fix builds

* throw down a basic settings ui page

* validate slack bot token

* fix failing verification step

* refine sso flow, update temp admin form to collect info we need

* ensure slackbot is only enabled if setup is complete

* add instructions to add bot image

* Re-enable user-not-linked-sends-auth-message-test

This deftest got accidentally slurped into the preceding one in 07eb42c76f4. Extract it back out into the toplevel.

* Fix kondo whitespace lint

* temp staging deploy fix?

* BOT-853 Add malli schemas to slackbot defendpoints

* Ensure site-url is configured and fix failing test

* Add reminder to set MB_SLACK_CONNECT_ENABLED

* make image rendering larger

* Ensure :test-users are initialized in slackbot tests

* Fix busted tests due to missing site-url

Site-url needs to be added to with-slackbot-setup and do-with-setup-override since I added site-url to
metabase-enterprise.metabot-v3.api.slackbot/setup-complete?

These tests passed when run from the REPL because I had site-url configured locally, but failed when run from the cli.

* make setup instructions clearer

* clean up dev comment form

* BOT-852 Add malli schemas for slack event types

* improve auth flow

* oops

* Rename SlackHandledMessageEvent to SlackKnownMessageEvent

And fix the order of types in SlackKnownMessageEvent

* improve settings page

* WIP csv uploads

* tweaks

* tweaks

* fixes & cleanups

* Ad-hoc querying for slackbot (#69315)

* basic ad-hoc query support

* fix capabilities

* cleanup + another test

* more test fixes

* fix some kondo

* move middleware auth tests to a new EE namespace

* minor fixes

* fix tests

* remove unneeded transforms capability

* remove some debug code

* [Metabot] Slackbot channel support (#69391)

* channel support

* ignore edited messages

* add tests

* lint fix

* lint fix

* pr feedback

* test fix

* skip channel messages

* default enable slack sso

* Fix failing metabase.core.modules-test/module-uses-should-be-sorted-test

* Require user to be active in slack-id->user-id

* metabot/slackbot - Adjust CSV upload support (#69832)

* init

* remove debug def

* BOT-984 Add slack channel support to create-dashboard-subscription tool (#69371)

* Add slack channel support to create-dashboard-subscription tool

* Fix render-html-to-png

We recently added a new 3-arg arity, but were only passing along the :content of the ::body/RenderedPartCard rather
than the full map, resulting in a malli schema error due to dropping the required :attachments key for any callers of
the 2-arg arity, e.g. when trying to send a slack notification.

    clojure.lang.ExceptionInfo: Invalid input: {:attachments ["missing required key, got: nil"]}
	at metabase.channel.render.png$fn__107479$fn__107505.invoke(png.clj:124)
	at metabase.channel.render.png$fn__107479$render_html_to_png107478__107482.invoke(png.clj:128)
	at metabase.channel.render.png$fn__107479$fn__107505.invoke(png.clj:124)
	at metabase.channel.render.card$fn__108322$png_from_render_info108321__108323.invoke(card.clj:302)
	at metabase.channel.render.card$fn__108322$fn__108325.invoke(card.clj:298)
	at metabase.channel.impl.slack$part__GT_sections_BANG_.invokeStatic(slack.clj:117)
	at metabase.channel.impl.slack$part__GT_sections_BANG_.invoke(slack.clj:87)

* Add find-cached-slack-channel-or-username

* Validate slack-channel in create-dashboard-subscription

Serves the dual purpose of making sure the channel exists and also normalizing the name to the display-name with
leading # which is how channels selected in the UI are stored.

* Drop support for email subscriptions

Since this is only going to be used from the slackbot profile, we'll only support sending subscriptions to slack
channels for now.

* handle-agent-error in create-dashboard-subscription

* Add create-pulse-with-perm-checks! and call that from create-dashboard-subscription tool

To ensure the same permission checks and other logic is used as when creating a pulse from the UI or API.

* Do no export pulse.models.create-pulse! from pulse.core

The only consumer was metabot's create-dashboard-subscription which now calls create-pulse-with-perm-checks! instead.

* BOT-985 Add create-alert metabot tool for slackbot (#69533)

* Add create-alert metabot tool

To allow the agent to create alerts for a given card on a given schedule, similar to the existing
create-dashboard-subscription tool.

* Add notification.api/create-notification! and call it from metabot_v3.tools/create-alert

This ensures that create-alert does the same perms checks and send the same verification email and notifications as
the main api endpoint.

* Improve ::create-alert-arguments malli schema

Use :multi with :dispatch on :channel_type to make it clear that email is required when :channel_type is "email" and
likewise :slack_channel is required when :channel_type is "slack"

* Destructure email in create-alert args

* handle-agent-error in create-alert

* Drop support for email subscriptions

Since this is only going to be used from the slackbot profile, we'll only support sending subscriptions to slack
channels for now.

* Add api test for create-alert metabot tool endpoint

* Fix bad conflict resolution from rebase

* BOT-1026 Pass slack-channel-id in the ai-service request context (#69828)

* BOT-1026 Pass slack-channel-id in the ai-service request context

For ai-service requests originating from slackbot, send along the slack channel id. The ai-service tools will also be
updated to use this channel id when creating dashboard subscriptions or alerts.

* Add make-ai-request-args-test

* Slackbot table output format (#69511)

* WIP - table ouptut for Slack metabot

* lint

* WIP support for pre-fetched data

* WIP

* lint

* add slack-connect-enabled to do-with-other-sso-types-disabled

* fix another test

* change slack-connect-enabled default to false

* simplify approach

* Misc fixups

* more cleanup

* fix another test

* small readability improvements

* Move some of the table data preparation stuff to a new namespace to share with  existing slack/email rendering code

* fix modules lint

* extract table-block components and include in thread history

* revert query results changes (for now) and just drive slack table formatting via viz type selection

* truncation message

* truncation message test case

* rollback more execute-query stuff

* fix build

* temporarily comment out extract-table-blocks stuff

* fix test

* Merge slackbots and settings pages (#69782)

* wip

* more progress

* conslidate manifest endpoints

* remove slack-token

* handle missing scopes case

* respect slackbot feature toggle in event handler

* other-sso-enabled? now takes into account sso mode

* new slack app-info endpoint for getting helpful metadata for the connected slack app, update links on FE to more direct location in slack

* replaced "metabot-slack-bot-enabled" with "slack-connect-enabled"

* misc. small fixes

* move slack scopes endpoint to app-info endpoint, update copy, misc ui improvements

* fix slack events api endpoint to still ack events if metabot later becomes disabled

* first step now configured to show obscured token, remove old modal component that was unused

* improve app icon section

* small design pass and linkage improvement to step 3

* new settings endpoint for metabot slackbot settings, updated FE component and form validation

* improve slack bug channel input, also update api endpoint so that slack setting values can be set seperately

* obfuscate client secret and signing secret

* improve the webhooks page a bit

* inline save / delete btuton for slack oauth token input

* allow removing metabot related slack settings in the ui

* self-review

* fix kondo

* fix some be tests

* test fixes, link fixes, page reorg

* poll for updates

* fix some e2e tests

* show info button w/ bot token info on hover

* updated profile image for slackbot

* fix be tests

* fix unit tests

* fix be test

* add unit test converage for base setup

* remove SlackSettingsPage which had duplicate tests

* add test coverage for settings endpoint

* new settings ui tests

* remove todo comment

* set default icon / name for slackbot

* make slack-get match slack-post response format

* show api errors better in slack metabot form

* impl set-many feedback

* fix requiring resolve code smell

* log error based on feedback

* handle error for app-info

* Slackbot streaming (#69947)

* [wip] Slackbot streaming

* simplify, lint

* Fix tests

* Use a volatile

* fix tests

* refactors, cleanup

* more cleanup

* fix tool name

* Better stream-related error handling + tests

* add another test

* properly destructure api calls for streaming / auth

* fix streaming not having icon_url set and make sure that channel level @mentions has the bot reply with auth request at channel level too

* embarrassing

* Rework + simplify streaming using an agent to try to reduce burstiness

* prevent tests from clearing out setting values

* Add a thinking... placeholder before stream starts +  do initial Slack requests in futures

* rip out overrides, streaming api reverts the defaults so it flashes images if they differ from the one saved for the slack app

* [Slackbot] Render unsupported static viz as tables (#70077)

render unsupported static viz as tables

* Slackbot test refactor + fix overwriting encrypted settings (#70113)

* Fix encryption overwriting in tests; small cleanup

* more refactor

* [Slackbot] Modularize (#70130)

modularize

* [Slackbot] Adds check to prevent replay attacks (#70081)

* prevent relay attack

* fix syntax issue

* pr feedback

* lint fix

* [Slackbot] Prevent channel response (#70136)

* add more tests, refactor

* bit more clean up

* ignore channel messages without @mention

* simplify, fix tests

* add file mock to tests

* move bot id fetch

* Migrate slack-connect SSO to dedicated /auth/sso/slack-connect URL pattern

Move slack-connect from the old unified /auth/sso multimethod dispatch to
dedicated GET /auth/sso/slack-connect and GET /auth/sso/slack-connect/callback
endpoints, aligning with the pattern used by custom OIDC providers.

* fix oauth flow requiring user page refresh

* kick ci

* fix threading for auth message in dms

* Report QP errors to Slack, and run queries off the main thread (#70124)

* WIP - surface QP errors, don't block queries on text stream

* simplifications

* fix up a couple other tests that were corrupting values

* try to fix test

* cleanup

* more cleanup

* more cleanup

* Fix test

* small simplification

* more fixes

* timeouts

* simplify comment

* call shutdown-agents at the end of the core shutdown routine

* [Slackbot] Stable conversation ids (#70281)

* make convo ids stable for slack conversations

* fix lint

* fix missing paren

* Update enterprise/backend/test/metabase_enterprise/metabot_v3/api/slackbot_test.clj

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>

---------

Co-authored-by: appleby <86076+appleby@users.noreply.github.com>

* fixup! set a proper response code in safari

* Slack SSO info logs in initiate & callback endpoints

* make slack user look up deterministic

* log actual oidc cookie

* [Slackbot] Better static viz sizing (#70288)

* make slack user look up deterministic

* calc size of static viz based on display type

* fix doc string

* fix tests, add sankey support

* fix lint

* make metabot self-aware... of @mentions

* add some truncation logic for slack blocks + long cell values

* Improved titles for slackbot visualizations; misc other fixes (#70396)

* support context text for results

* viz captions with links; hold viz results until stream finishes

* tests; remove separate caption & description fields

* grab bag of fixes

* strip out user mention stuff from this branch

* caption->title

* fix build

* fix test

* polish

* some escaping logic for markdown links

* fix filename

* Slackbot feedback buttons (#70280)

* WIP slack feedback buttons

* wire up feedback buttons

* tests

* feedback modal

* revert back to a single hm call

* fix parens

* revert an unexpected change

* small fixes

* fix tests

* tweaks

* fix rebase

* Update enterprise/backend/src/metabase_enterprise/metabot_v3/api/slackbot/streaming.clj

* fix rebase

* [Slackbot] Enrich chat history (#70408)

* poc

* move code around

* fix tests, clean up logs

* clean up

* fix

* automated review feedback

* do not store messages on error

* test coverage

* lint fix

* Ensure we respect slack api rate limits (#70409)

* try to respect slack streaming rate limits

* tests

* simplify

* lint

* try to fix tests, some tweaks

* bump ci

* [Slackbot] Better auth messages (#70465)

* improve auth message responses

* better auth messages

* pr feedback

* Split slackbot DM and channel response paths, embed viz in message (#70531)

* Viz delivery refactor for slackbot

* simplify

* more refactor

* more test porting

* Update enterprise/backend/src/metabase_enterprise/metabot_v3/api/slackbot/channel.clj

* condense some code

* fix tests

* fix stale session bug

* Slackbot message deletion (#70533)

* Slackbot message deletion

* Tests

* Fix tests

* minor cleanup

* add user_id to metabot_message to drive deletion permissions

* balence parens

* fix parens

* feedback

* fix failing migration tests

---------

Co-authored-by: Sloan Sparger <sloansparger@gmail.com>

* Fix slackbot feedback acknowledgement (#70642)

* fix slackbot feedback acknowledgement

* simplify docstring

* remove feedback acknowledgement entirely

* Add slackbot prometheus metrics (#70654)

* add slackbot prometheus metrics

* simplify

* lint

* remove todo comment

* [Slackbot] Cleanup (#70692)

* move migrations to v60 file

* clean up auth debug logging

* move slackbot into its own module and split up the core test file

* fix whitespace lint

* [Slackbot] Fancy (#70729)

* mostly working w/o proper saving of messages

* backfill slack_msg_id

* Minor tweaks: swap volatiles for atoms, switch away from deprecated StringBufferInputStream

* address libor's comments

* fix tests - wrap async functions with bound-fn*

* oops, fix last test

* fix reflection issue

* whitespace chagne to kick new jar build

* update subscription tool + tests

* fix .clj-kondo/config/modules/config.edn resolution and related failing tests

* [Slackbot] Port ai service (#71026)

* main port

* port https://github.com/metabase/ai-service/pull/626/changes

* clean up

* kondo fix

---------

Co-authored-by: Mike Appleby <86076+appleby@users.noreply.github.com>
Co-authored-by: Tyler Plude <tsplude@gmail.com>
Co-authored-by: Noah Moss <32746338+noahmoss@users.noreply.github.com>
Co-authored-by: Noah Moss <noahbmoss@gmail.com>
Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
Co-authored-by: Edward Paget <ed.paget@gmail.com>
2026-03-17 23:23:40 +00:00
Riley Thompson
47ff6b70a6 Pin google auth dependency to avoid driver mismatches (#70958)
* pin dep

* trigger ci tests

* update test
2026-03-17 20:59:53 +00:00
Nathan Voxland
7ab0784977 ReproBot Improvements (#70990) 2026-03-17 15:57:47 -05:00
Jeff Bruemmer
e8bc1db487 docs - remote sync providers (#70969) 2026-03-17 16:16:15 -04:00
Uladzimir Havenchyk
527a4d67e9 chore: upgrade underscore (#70778) 2026-03-17 22:58:29 +03:00
Nick Fitzpatrick
b0deebf2b6 Clear ChartSettingSelect display value on remove (#70957) 2026-03-17 15:57:55 -04:00
appleby
09d9a4a446 BOT-1131 Rename :snake_case keys to :kebab-case in metabot stats (#71027) 2026-03-17 19:55:33 +00:00
Fraser
08b84f4b16 Lift data_grid.js up to shared (#70755)
* Lift data_grid.js up to shared

     data_grid.js is in lib/lib but imports makeCellBackgroundGetter from table_format in shared/visualizations, causing a lib/lib → shared/visualizations boundary violation. data_grid.js is not used by
     anything in lib — all consumers are in shared/visualizations, feature/dashboard, and visualizer. table_format already lives in shared/visualizations and only depends on basic/ui colors.
2026-03-18 08:33:39 +13:00
Fraser
829b34e44a Remove metadata re export from reference (#70561) 2026-03-18 08:32:01 +13:00
Nathan Voxland
5d6c208865 Improve search endpoint performance with many disabled tables (#70982) 2026-03-17 13:09:19 -05:00
Jeff Bruemmer
6d37120fb9 docs - Fix typo in JWT group mapping documentation (#71017)
Fix typo in JWT group mapping documentation
2026-03-17 11:52:02 -04:00
Sébastien
2390c28119 Fixed metrics not being translated in guest embeds (#70484) 2026-03-17 16:44:59 +01:00
Cam Saul
5217327fb9 Fix running app DB with MariaDB 10.6 official image in CI (#70824)
* Fix running app DB with MariaDB 10.6 official image

* Update all MariaDB configs

* Use custom Docker image 🔧

* Loosen dependence on quoting format of mysql/mariadb error messages

* Use tz aliases supported in mariadb 12

* Run tests in series. Use fresh H2 DB.

---------

Co-authored-by: Ben Grabow <ben.grabow@metabase.com>
2026-03-17 14:52:37 +00:00
Chris Truter
b215b0ff3d Fix mage mise-diagnosis (#71012) 2026-03-17 14:34:13 +00:00
Mihael Konjević
9796110af3 Ai service port (#68463)
* Experiment with communicating with LLMs in Clojure

* Libor comments

* switch to new openai /v1/responses api, check for structured output support

* support for claude to show how different adapters are

* linter fixes

* Implement native Clojure agent for MetaBot v3

This commit implements a production-ready native Clojure agent loop that
replaces the external Python AI Service with an in-process implementation,
leveraging the existing llm-inside streaming infrastructure.

Key Components:
- agent/core.clj: Main agent loop with iteration control and streaming
- agent/memory.clj: Conversation state and step tracking
- agent/messages.clj: Message formatting and LLM communication
- agent/profiles.clj: Profile configurations (embedding, internal, transforms)
- agent/tools.clj: Tool registry with 13 wrapped tools

Integration:
- api.clj: Feature-flagged routing via use-native-agent setting
- settings.clj: New use-native-agent boolean setting (default: false)

Benefits:
- Direct Toucan2 database access (no HTTP round-trips)
- Single JVM process deployment
- Lower latency for tool execution
- Unified logging and error handling
- Reuses 80%+ of existing streaming infrastructure

Test Coverage:
- 5 test files with comprehensive coverage
- All tests passing (133 total assertions)
- Unit tests for memory, messages, profiles, tools
- Integration tests for agent loop with mocked LLM

The implementation is production-ready and can be enabled via feature flag
for gradual rollout and validation against the Python service.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Integrate Phase 2A: Prompts & Context System into agent loop

This commit completes Phase 2A of the native Clojure agent implementation,
integrating the templated prompt system with context enrichment into the
main agent execution loop.

## New Features

### Prompt System (prompts.clj)
- Selmer template loading with caching
- SQL dialect instruction injection (14 dialects supported)
- Tool instruction extraction from docstrings
- System message template rendering with context variables

### Context Enrichment (user_context.clj)
- Timezone-aware time formatting
- SQL dialect extraction from context
- Viewing context formatting for all types:
  - Adhoc queries (notebook editor)
  - Native SQL queries with error context
  - Transforms with source information
  - Code editor with buffer/cursor/selection details
  - Entity details (tables, models, questions, metrics, dashboards)
- Recent views formatting with relevance hints
- Full context enrichment pipeline for template variable injection

### Resource Files
- 3 system prompt templates (embedding, internal, transform-codegen)
- 14 SQL dialect instruction files
- All templates support dynamic context injection

## Integration Changes

### Agent Core (core.clj)
- Updated build-messages-for-llm to accept context, profile, and tools
- Integrated system message building with enriched context
- Fixed pre-existing async blocking issue in run-llm-with-tools
  (changed from blocking a/<!! to returning channel for non-blocking a/<!)
- Agent now builds fresh system messages with context on each iteration

### Messages (messages.clj)
- Updated build-system-message to use templated prompts
- Accepts context, profile, and tools parameters
- Calls prompts/build-system-message-content for rendering

### Profiles (profiles.clj)
- Added :prompt-template mapping to all three profiles
- Maps profile IDs to template files in resources/metabot/prompts/system/

## Tests

All Phase 2A functionality is comprehensively tested:
- prompts-test: 6 tests, 46 assertions ✅
- user-context-test: 5 tests, 81 assertions ✅
- messages-test: 3 tests, 47 assertions ✅
- profiles-test: 1 test, 36 assertions ✅

Total: 15 tests, 210 assertions passing

Note: core-test integration tests have pre-existing failures due to blocking
calls in self/core.clj tool-executor-rff (line 116), which existed before
this change. The async pipeline architecture issue is tracked separately.

## Architecture

The agent now follows this flow on each iteration:
1. Enrich raw context (user_context/enrich-context-for-template)
2. Load and render system prompt template (prompts/build-system-message-content)
3. Build full message array with system message + history
4. Execute LLM call with enriched context
5. Process tool calls and responses
6. Repeat until completion

This matches the Python implementation's prompt and context system while
leveraging Clojure's functional architecture.

* Add Selmer dependency for template rendering

Selmer v1.12.70 provides Jinja-like templating for Clojure,
used by the Phase 2A prompts system for rendering system messages
with dynamic context variables.

* Fix async blocking issue in agent loop by using pipeline-blocking

The tool-executor-rff reducing function blocks in its completion arity
to wait for tool results. Using a/pipeline causes 'Invalid blocking call
in dispatch thread' errors because the regular pipeline uses a fixed
thread pool that doesn't allow blocking.

Solution: Use a/pipeline-blocking instead, which uses a separate thread
pool designed for blocking operations. This allows the tool executor's
completion arity to safely block while waiting for tool results.

Fixes the core-test integration test failures.

* Implement Phase 2B: SQL Query Tools

Add four SQL query manipulation tools to enable the agent to create, edit,
and search SQL queries:

New Tools:
- create_sql_query: Create new SQL queries with validation
- edit_sql_query: Apply structured edits (replace, append, insert, etc.)
- replace_sql_query: Replace SQL content entirely while preserving metadata
- sql_search: Search for SQL queries by content with relevance ranking

Features:
- Direct Toucan2 Card model access (no HTTP round-trips)
- Database and permissions validation
- Structured edit operations (replace, replace-all, append, prepend, insert-after, insert-before)
- Search with ranking by name match, description match, SQL position, and recency
- Integration with existing agent tool registry
- Added to metabot-internal profile and new metabot-sql-only profile

Testing:
- Comprehensive test coverage for all SQL tools
- Integration tests for full workflow (create → edit → search)
- All tests passing (18 assertions, 0 failures)

Architecture:
- Follows existing tool pattern (mu/defn with Malli schemas)
- Returns structured output for agent state tracking
- Error handling with agent-error? flag for LLM-friendly messages

Phase 2B complete. Ready for Phase 2C: Visualization Tools.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix SQL tools to handle lib/query format

Fixed multiple issues with SQL tools not handling the transformed
lib/query format when Card models are read from the database:

## Issues Fixed

1. **extract-sql-content not finding SQL**: Card dataset_query transforms
   from {:native {:query "SQL"}} to {:stages [{:native "SQL"}]} when read.
   Updated extract-sql-content in all SQL tools to handle both formats.

2. **OffsetDateTime vs Date**: The rank-result function in sql_search
   called .getTime on timestamps, but Toucan2 returns OffsetDateTime.
   Added instanceof check to convert appropriately.

3. **Missing visualization_settings**: Test code using t2/insert! needed
   :visualization_settings {} field (database constraint).

4. **Test permissions**: Wrapped all test code with mt/with-current-user
   to provide proper permission context.

5. **Fixed typo in sql_search**: Changed accessible-cards variable reference
   from undefined to all-cards.

## Files Changed

- edit_sql_query.clj: Handle both lib/query and legacy formats
- replace_sql_query.clj: Handle both lib/query and legacy formats
- sql_search.clj: Handle both formats + OffsetDateTime conversion
- sql_tools_test.clj: Add visualization_settings, fix permissions,
  add clojure.string alias, fix indentation

## Test Results

All 19 assertions passing (4 test functions):
- edit-sql-query-test: 4 edit types tested
- replace-sql-query-test: Replace with metadata preservation
- sql-search-test: Search, filter by database, limit parameter
- integration-create-edit-search-test: Full workflow

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Add read_resource tool for fetching Metabase resources via URIs

Implement the foundational read_resource tool that provides unified
interface for fetching detailed information about Metabase resources
using URI patterns. This tool implements the "Context Engineering with
Links" pattern where URIs serve as lightweight, token-efficient
references that can be fetched on-demand at the appropriate detail level.

Supported URI patterns:
- metabase://table/{id} - Basic table info
- metabase://table/{id}/fields - Table structure with fields
- metabase://table/{id}/fields/{field_id} - Specific field details
- metabase://model/{id}/fields - Model structure with fields
- metabase://metric/{id}/dimensions - Metric dimensions
- metabase://transform/{id} - Transform details (not yet implemented)
- metabase://dashboard/{id} - Dashboard details (not yet implemented)

Key features:
- Parses metabase:// URIs and routes to appropriate handlers
- Supports fetching multiple resources in parallel (max 5)
- Handles field IDs with slashes (e.g., c75/17)
- Returns formatted XML-like output for LLM consumption
- Provides detailed error messages for invalid URIs

This tool is critical for:
- Enabling agents to get more context about resources on demand
- Supporting workflows where search returns URIs that agents fetch later
- Reducing token usage by deferring detail fetching until needed

Changes:
- Created read_resource.clj with URI parsing and resource handlers
- Added read-resource-tool wrapper in tools.clj with Malli schema
- Updated all 4 profiles to include read_resource tool
- Added comprehensive tests for URI parsing and resource fetching

Tests: 24 assertions, 0 failures

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Implement Phase 2C chart tools, 2F profiles, and fix async blocking

Phase 2C - Chart Tools:
- Add create_chart tool to create visualizations from queries
- Add edit_chart tool to modify existing chart types
- Register chart tools in agent tools registry

Phase 2F - Profiles & Templates:
- Port sql-querying-only.selmer from Jinja2 (fixes broken :metabot-sql-only)
- Port metabot-next.selmer template
- Port natural-language-querying-only.selmer template
- Add :metabot-next, :metabot-nlq-only, :metabot-beta-customer profiles

Async Blocking Fix:
- Use a/thread instead of a/pipeline-blocking in run-llm-with-tools
- tool-executor-rff uses a/<!! in completion arity which cannot run in go-blocks
- Synchronous transducer application in thread context is now aligned with self code
- Fix stream-parts-to-output! and finalize-stream! to use a/>! in go blocks
- Fix api.clj syntax error with metadata on string literal
- Update tests to generate proper Claude raw SSE format

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add AI SDK v4 line protocol output format for native agent

The Python ai-service outputs a specific line protocol format that the
frontend expects. This adds the conversion layer to output the same format
from the native Clojure agent.

Format examples:
- 0:"text content"  - Text (JSON-encoded string)
- 2:{"type":...}    - Data parts
- 3:"error"         - Errors
- 9:{"toolCallId":..., "toolName":..., "args":...}  - Tool calls
- a:{"toolCallId":..., "result":...}  - Tool results
- d:{"finishReason":"stop", "usage":{...}}  - Finish message
- f:{"messageId":...}  - Start message

Changes:
- Add format-* functions for each part type in self/core.clj
- Add aisdk-line-xf transducer for batch conversion
- Export new functions via self.clj
- Update api.clj to stream parts in line protocol format
- Add comprehensive tests for all format functions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix native agent loop for multi-turn conversations with tools

Key fixes:
- Fix premature loop exit: Continue when tool calls present, regardless of text
- Fix Claude API integration: Extract system message separately, pass via :system param
- Fix tool name mismatch: Pass tools as [name, var] pairs so Claude sees registry keys
- Fix message format for tool_use: Use content array format for assistant messages
- Handle START_STEP/FINISH_STEP types in aisdk->messages
- Rename profiles to simpler keys (:embedding, :internal, etc.)
- Add /native-agent-streaming endpoint for testing (bypasses feature flag)
- Add debug logging for message building

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add state management for query/chart IDs in native agent tools

State-dependent tools (show_results_to_user, create_chart, edit_chart) now
look up queries and charts from agent memory by ID instead of expecting raw
query objects. This mirrors the Python AI service pattern.

Key changes:
- memory.clj: Add query/chart storage functions (remember-query, find-query,
  store-chart, find-chart, load-queries-from-state, load-charts-from-state)
- core.clj: Extract queries/charts from tool outputs and store in memory
- tools.clj: Wrap state-dependent tools with memory atom access
- claude.clj: Update schema to accept wrapped tool maps with :fn/:doc/:schema
- self/core.clj: Handle wrapped tool maps in tool-executor-rff
- show_results_to_user.clj: Accept query-id and look up from queries-state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add metabase:// link resolution for native agent streaming

The native agent now resolves metabase:// URIs in text parts before
streaming to clients. This mirrors the Python AI service's
MarkdownLinkBuffer behavior.

Link types supported:
- metabase://query/{uuid} - Resolves to /question# using stored query
- metabase://chart/{uuid} - Resolves to /question# via chart's query
- metabase://model/{id} - Resolves to /model/{id}
- metabase://metric/{id} - Resolves to /metric/{id}
- metabase://dashboard/{id} - Resolves to /dashboard/{id}
- metabase://question/{id} - Resolves to /question/{id}
- metabase://transform/{id} - Resolves to /admin/transforms/{id}

Changes:
- links.clj: New module for link resolution with markdown parsing
- core.clj: Integrate link processing in stream-parts-to-output!
- Tests for link resolution and streaming integration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix tool result formatting for LLM and link resolution

Key changes:
- Format tool results as XML for LLM consumption (messages.clj)
  - Query results now show query ID clearly: <query id="..." type="query">
  - Chart results include chart ID and reference link
  - This matches Python ai-service format so LLM can extract IDs properly

- Add fallback for chart link resolution (links.clj)
  - When LLM uses metabase://chart/ with a query ID, fall back to
    treating it as a query link
  - Handles common LLM mistake of using wrong link type

- Fix chart extraction logic (core.clj)
  - Charts identified by :chart-id + :query-id (without :query)
  - Distinguishes charts from query results correctly

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add InstructionResultSchema pattern for native agent tool outputs

Align Clojure native agent tool outputs with Python AI Service patterns
by wrapping results in <result>/<instructions> XML format for consistent
LLM guidance.

- Add llm_representations.clj with entity-to-XML formatting utilities
- Add instructions.clj with LLM guidance constants for each tool type
- Update messages.clj to use format-with-instructions wrapper
- Add type-specific formatters for search, entity, query, chart results
- Include comprehensive tests for LLM representation functions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Align Clojure LLM representations exactly with Python AI Service

Updates tool output formatting to match Python AI Service patterns exactly:

- field->xml: Add quotes around name, database_type attr, ## Description header
- metric->xml: Add collection, default_time_dimension_field, metabase:// URIs
- table->xml: Add database_engine, fully_qualified_name, related_tables section
- model->xml: Use <metabase-model> tag, slug-based fqn, related_tables
- query->xml: Include database_id, query-content, query_results with rows
- Add question->xml, dashboard->xml, collection->xml, user->xml formats
- Add visualization->xml for full chart representation
- Add field-metadata->xml, get-metadata-result->xml for metadata results
- Update search result tags: model→metabase-model, card→metabase_question
- Update instructions.clj with complete Python-matching guidance text
- Update messages.clj to use <metabase-models> tag
- Expand tests to verify Python format compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add auto-navigation and fix tool output handling for native agent

- Add redirect reactions to SQL query tools (create, edit, replace) for
  auto-navigation after query operations
- Add streaming module to convert reactions to AI SDK data parts
- Fix structured output extraction to handle both :structured-output and
  :structured_output key formats (Clojure vs JSON style)
- Add role normalization in message formatting to handle both keyword
  and string roles from frontend
- Include :query key in SQL tool returns for memory storage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Implement AI Service parity for native Clojure agent

This commit adds feature parity between the Clojure native agent and
Python AI service, implementing all phases from the parity plan.

New Tools:
- todo_write / todo_read: Task management with todo_list data parts
- navigate_user: Page and entity navigation with redirect reactions
- ask_for_sql_clarification: SQL clarification with final-response flag
- write_transform_sql / write_transform_python: Transform code editing
- create_autogenerated_dashboard: X-ray dashboard generation
- list_snippets / get_snippet_details: SQL snippet access

Core Changes:
- State data part format: {type: data, data-type: "state", version: 1}
- Finish message format: {type: finish, finish-reason: "stop"|"error"}
- New data part types: todo_list, code_edit, transform_suggestion
- Data part streaming from tool results via :data-parts key
- Final response pattern: tools return :final-response? true to stop loop
- Capability-based tool filtering with tool-capabilities map

Profile Updates:
- internal: Added navigate_user, create_autogenerated_dashboard
- transforms-codegen: Added todo tools, transform write tools, snippets
- sql-only: Added ask_for_sql_clarification, :required-tool-call? flag
- nlq-only: Added navigate_user, create_chart, edit_chart

Tests:
- Comprehensive tests for all new tools (29 new tests, 200+ assertions)
- Updated existing tests for new formats and capabilities

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add streaming link buffer and improve AI service compatibility

Key changes:
- Add markdown_link_buffer.clj for handling links split across streaming
  text chunks (matches Python AI Service MarkdownLinkBuffer behavior)
- Seed agent state with queries from user_is_viewing context
- Support both snake_case and kebab-case keys in context for prompts
- Normalize entity types (keyword/string) in message formatting
- Decode tool arguments from JSON strings when needed
- Add table link resolution (metabase://table/{id})
- Preserve system-instructions when wrapping state-dependent tools
- Fix iteration boundary check (use inc iteration for comparison)
- Gracefully handle unresolvable links by falling back to link text

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor SQL tools to use in-memory state and data parts

Key changes:
- SQL tools (edit_sql_query, replace_sql_query) now operate on in-memory
  query state instead of persisting to database
- Convert tools from reactions to data-parts pattern (navigate_to)
- Add semantic_queries and keyword_queries to search tool schema
- Make edit_sql_query and replace_sql_query state-dependent tools
- Support string query IDs (in addition to int) for memory-based queries
- Extract SQL dialect only from native query context type
- Use streaming.navigate-to-part instead of direct reactions
- Add instructions pattern to SQL tools for better LLM guidance

This aligns the SQL tools with the AI Service's transient query model
where queries exist in conversation state until explicitly saved.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Cleanup

* AI Service cleanup

* Refactor metabot tool registration

* Render LLM representations with Selmer

* Refactor tool result formatting

* define caps by metadata

* Refactor metabot v3 tools namespace

* Route agent tools to core implementations

* Simplify metabot tool wrappers

* factor out core.async as much as possible

* Refactor metabot v3 to use IReduceInit reducibles instead of core.async

- Replace channels with IReduceInit (reducible) pattern throughout
- Standard Clojure transducers work directly: (into [] xf (llm-call {...}))
- Remove core.async dependency from self namespace
- Rename chan? -> reducible?, sse-chan -> sse-reducible
- Rename tool-executor-rff -> tool-executor-xf
- Add malli schemas for agent state, messages, parts
- Simplify agent loop using reducible composition
- Update tests to use reducible patterns

* clarify

* ehhh sideeffect

* fix capabilities/nicer profiles

* tools tests

* api tests

* tools tests

* llm-repr test fixes

* fix profiles.clj

* clj-kondo woes

* is this the last test

* damn

* cljfmt

* fix api test

* update path for e2e tests

* little things

* output text fluidly to a user

* update modules config

* cleaning up links code duplication

* cleanup user context templates

* integration test

* cljfmt

* test

* fix read resource output format

* make clj-kondo happier

* simpler tool executor function

* better error messages from metabot backend

* potentially: ability to reload repl state when js is compiled differently

* metabot error messages and tests

* cleanup agent.messages code/tests a bit

* configurable api urls for llms

* use simple templates more

* format tools to string at their definition

* collecting usage and schema on tool

* system prompts

* datadog observability

* fix modules config

* make clj-kondo happy

* fix read_resource

* fix fe formatting

* more prompt changes

* improve search results tool response

* little cleanups and field-metadata with instructions

* sql dialect

* proper context instructions for sql tools

* report tool args well

* tool result should always have 'result' key

* coerce tool args to json

* good filters validation/errors to llm

* fix construct rendering in system prompt

* fix 'SQL Fixing'

* put queries in the context

* proper rendering for query data

* no :system-instructions

* proper capability check

* handle inline llm errors

* ability to debug llm queries

* fixes to improve llm behavior

* unify query formatting

* accept both variations of db id

* debug for python ai-service

* do not escape instructions

* fix claude usage tracking

* python has no header for tool instructions

* instruct llms to use fq table names

* slight prompt rewording to bring it closer to what benchmarks measure

* fancy field select - reduces amount of tool calls needed

* more clear naming

* simpler date filters handling

* retry logic

* great namespace movement

* no more stacktraces on agent errors

* fix token undercounting

* make sure request cancellation works through with native agent streaming

* Replace raw/endraw tags with safe/endsafe

* Fix issues with the sql profile

* openrouter as main provider

* review comments

* fix test for closing connections

* better schema for incoming messages

* fix agent tests

* cumulative usage from agent loop to reduce need for arithmetics

* clj-kondo/cljfmt

* transforms in read_resource tool

* get-dashboard-details

* test fixes

* fix temporal checks

* fix field_stats test

* fix remove-unreadable-transforms

* oh yeah very important type

* fix usage in native streaming

* fix api test formatting

* Get tools back (#69901)

* drop separate url for native agent, only have single url with a toggle

* fix error message in custom-elements e2e

* prompt generation (#69970)

* feat: documents content generation for clojure metabot (#69807)

* llm model select (#70061)

* BOT-866 Add prometheus metrics to metabot clojure agent part II (#70094)

* BOT-866 Add prometheus metrics to metabot clojure agent

* BOT-866 Also count inline aisdk errors

* BOT-866 push prometheus token counting down into call-llm

This is more in keeping with how current ai-service reports the metric, i.e. per llm call rather than a single batched
token usage per agent request.

This also has the advantage of keeping most prometheus metrics localized to the llm layer.

* Ensure that :metabase-metabot/llm-requests counts retries

This is more consistent with the existing ai-service llm_invocations_total metric

* Make :metabase-metabot/llm-duration-ms per llm invocation rather than cumulative

This more closely matches the existing ai-service llm_duration_ms metric

* Add :metabase-metabot/agent-duration-ms prometheus metric

Records the full duration of the outer agent loop, now that :metabase-metabot/llm-duration-ms records individual llm calls.

* Add comment on :metabase-metabot/llm-duration-ms buckets

And extend the low end of the range down to 100ms

* fixup! Add :metabase-metabot/agent-duration-ms prometheus metric

* Fix whitespace

* Add prometheus metrics tracking to call-llm-structured

* Allow llm callers to specificy the source label for prometheus metrics

And pass "example-question-generation" for question generation.

* Fix call-llm-prometheus-test

After rebasing on #70061 these tests need to include the "openrouter/" provider prefix along with the model.

* BOT-1051 Fix provider in generate-example-questions (#70121)

After #70061 call-llm-structured now expects the model to include a
provider prefix, so "anthropic/claude-haiku-4-5" was getting parsed as
:provider "anthropic" :model "claude-haiku-4-5".

Update metabase-enterprise.metabot-v3.example-question-generator/call-llm to
use the provider + model configured via llm/ee-ai-metabot-provider

* BOT-1039 Add snowplow token_usage event tracking to the clojure agent (#70049)

* BOT-1039 Include the profile :name in the profile map in register-profile!

Sometimes it's useful to be able to exract the profile name when you only
have a reference to the profile.

* Add token_usage snowplow event tracking to the clojure agent

- refactor register-profile! to single-arg form with :name now stored in the profile map and returned by get-profile
- report-token-usage-xf now emits a :snowplow/token_usage event per LLM call alongside existing prometheus metrics
- pass tracking-opts from loop-step through call-llm into the token-usage transducer
- likewise for callers of call-llm-structured
- init-agent generates a per-run request-id UUID
- run-agent-loop accepts an optional conversation-id that turns into the snowplow token_usage session_id

* Add :model to tracking-opts and drop separate source arg

The prometheus source label should always be the same as the snowplow :tag (that's how it works in ai-service), so
drop the separate `source` arg from various functions and just take the source label from the tracking-opts :tag.

* Destructure token-opts in report-token-usage-xf

* Allow passing tracking-opts to run-agent-loop

And use this to override the :source in metabot-v3.api.document/native-generate-content

* Improve docstring for tracking-opts

* Provide model and source defaults in report-aisdk-errors-xf

* Fallback to analytics-uuid if premium-embedding-token is unset

* Fix typo: snowplot -> snowplow

* Compute hashed-token-or-uuid once rather than for each usage part in report-token-usage-xf

* BOT-1039 Move token-usage funcs into analytics module (#70213)

* BOT-1052 Move token-usage funcs into analytics module

So they can be shared between oss sql gen and metabot.

* Add some snowplot tests for self/call-llm and call-llm-structured

* Add prometheus tests for call-llm-structured

* Add basic snowplow and prometheus tests for example question generation

* Modify agent loop snowplot test to use with-fake-snowplow-collector

* Modify native-generate-content-snowplow-test to use with-fake-snowplow-collector

* Fix whitespace

* fix: context not included in sql profile

* drop unused tool find-outliers (#70424)

* fix: add error field to transforms

* fix: common library not used by agent

* BOT-1053 Add snowplow events for agent tool calls (#70338)

* BOT-1039 Create metabase.analytics.util

And move some func there from metabase.analytics.llm-token-usage that will also be needed for agent_used_tool events.

* Add ai_service_event snowplow schema

In preparation for tracking the following events:

- agent_tool_used
- user_intent
- agent_action_summary

* Track snowplow agent_used_tool events in metabase-enterprise.metabot-v3.self/call-llm

- add agent loop :iteration to tracking-opts
- add :duration-ms to :tool-output parts
- add report-tool-usage-xf to transducer chain in metabase-enterprise.metabot-v3.self/call-llm to track
  :snowplow/ai_service_event with :event "agent_used_tool"

* BOT-1054 Add snowplow events for user intent classification (#70351)

* BOT-1054 Add snowplow events for user intent classification

* Move run-agent-loop :conversation-id into :tracking-opts :session-id

It does make some sense as a top-level argument, but it's currently only used for snowplow tracking, so push it down
into the tracking-opts.

* Move run-agent-loop :track-user-intent? into :tracking-opts

* Add ee-ai-metabot-provider-lite defsetting and use it in classify-user-intent!

This allows using a lighter weight model for simple tasks like user intent and/or agent response
classification. Defaults to openrouter/openai/gpt-oss-20b which is the same model used by ai-service for these tasks.

* Add run-agent-loop! test helper to run the agent for side effects

* whitespace: fix alignment

* Extract :tracking-opts schema into an mr/def similar to other agent opts

* Rename :profile-name -> :profile-id in tracking-opts

* use embeddings proxy directly rather than via ai-service (#70419)

* fix modules config

* fix: metabot has no context on the currently open transform (#70506)

* sql profile requires tool call to be forced (#70497)

* fix snowplow test

* revert back renames for ai-service embedding provider to evade migration (#70649)

* Add ai-service-port to branches list in uberjar.yml

* BOT-1085 Allow user-intent classification to be disabled (#70521)

Add a new ee-ai-metabot-internal-tasks-enabled? setting to allow disabling metabot internal "bookkeeping" tasks that might make additional llm calls, like user intent classification.

* Fix incorrect docstrings and copy-paste errors in settings and tools (#70793)

* Ai service port  validate sql (#70259)

* metabot-v3.tools.util/validate-sql stub

* Add query to dialect mapping

* Hook validation into tools code

* Add validation error instruction

* query-by-id helper

* Move sql validation to agent/tools/sql.clj

* WIP convert edit-sql-query-tool

* Revert edit-sql-query tool

* Drop validation code from tools/util.clj

* Add tools/sql_validation.clj

* Use sql-validation in tools/create_sql_query.clj

* Use sql-validation in agent.tools.sql/create-sql-query-tool

* Add transpile_sql to sql_tools.py

* Add transpile-sql to sql-parsing

* Macaw implementation stub

* Add transpile-sql interface multimethod

* Update transpile_sql in sql_tools.py

* sql-tools/transpile-sql stub

* Add template tag sentinel to sql_tools.py

* sql-tools/transpile-sql schema

* Sqlglot transpile-sql wrapper

* sql-parsing/transpile-sql result normalization

* Hook sql-tools/transpile-sql into metabot-v3.tools.sql-validation/validate-sql

* Adjust create-sql-query-tool to validate-sql changes

* Adjust reason in sql_tools.py

* Add create-sql-query-validation-failure-test

* Addjust validate-sql

* Revert edit-sql-query tool

* Add validation to metabot_v3/tools/edit_sql_query.clj

* Add schema and update returns for create-sql-query

* Add metabot-v3.tools.sql.common

* Update edit_sql_query

* Update edit_sql_query tool

* Add validation failure test for edit-sql-query-tool

* Adjust edit-sql-query-code-edit-tool

* Adjust create-sql-query-code-edit-tool

* Update replace-sql-query and related

* Adjust replace-sql-query-tool

* Update replace-sql-query-code-edit-tool

* Adjsutment for test

* metabot-v3.tools.sql.validation ns

* validate-sql sentinels

* Hoist template tags sentinel into tools.sql.validation

* Update schema

* Validation tests

* Add transpilation tests and adjust sql_tools.py

* Add dialect mapping

* Adjust validation and tests

* Adjust test

* linter

* Cleanup

* Add tests

* Tests and validation

* More tests and cleanup

* Drop redundant tests

* Use original dialect

* Adjust tests

* whitespace

* Remove direct query manipulation from tests

* Remove direct mbql manipulation from create-sql-query

* Remove more of direct query manipulation

* More remarks

* surface openrouter error messages

* API keys are sensitive (#70847)

* Generate tool manifest from endpoint metadata (#70780)

* Generate tool manifest from endpoint metadta

* Delete reference file

* tweak-and-tidy

* More testing, more robustness

* Fix kondo and cljfmt

* Scrap name inference

* Clean up tool assertions

* Remove silly separators

* Switch away from tool registry approach

* rename + slightly simplify prefer-tool-descriptions

* Replace definitions binding with an explicitly threaded atom

* Minor cleanups/refactor

* Pull over some annotations from my prior PR; tweak test comments

* address comments

---------

Co-authored-by: Noah Moss <noahbmoss@gmail.com>

* properly fetch data for user-is-viewing context (#70857)

* serialize error to string if it is not - backend expects a string anyways

* chore: trigger CI

* [BOT-1056] Metabot Analyze Chart in CLJ Port (#70177)

* init analyze chart tool

* fix compilation error; add interpretation instructions; reduce memory leak

* Add license overrides for techascent/tech.config and net.openhft/zero-allocation-hashing

* Remove hard-coded :debug true in /agent-streaming endpoint

* Fix conflict resolution due to tools.analytics after rebase

tools.analytics and find-outliers were dropped in 974b595784

* BOT-1058 analyze chart add support for non timeseries charts (#70616)

* Add stats for categorical charts to analyze-chart tool

* Add stats for scatter plots to analyze-chart tool

* Add stats for histograms to analyze-chart tool

* Add time series tests for metabot chart analytics

* Add outliers tests for metabot chart analytics

* Small tweaks to reduce the diff between clojure port an ai-service stats

stats/scatter.clj

- Added sample-points helper (random sample up to 30 pairs)
- compute-series-stats: adds :sampled_points (30 random [x y] pairs),
  :outliers (MAD-based, x-values passed as "dates" so each outlier carries its x-coordinate in :date)
- compute-scatter-stats: extracts :x_name / :y_name from series metadata

stats/categorical.clj

- compute-categorical-stats: extracts :x_name / :y_name from series metadata

stats/histogram.clj

- compute-series-stats: now takes optional x-values (single-arity falls back to sequential indices);
  adds :bin_data (all [x y] pairs)
- compute-histogram-stats: extracts :x_name / :y_name from series metadata

stats/repr.clj

- generate-temporal-context: added "incomplete period" sentence
- New compute-data-characteristics: derives small_counts/high_variance/sparse_data flags from existing stats fields (no raw values needed)
- New render-data-characteristics-note: emits **Note**: ... matching Python's exact warning text
- All generate-*-representation functions: use display-type from context for the **Type**: line (fixes "Scatter" → "bar" for binned bar charts)
- Scatter: added **X-axis**, **Y-axis**, note, **Sample Data**, **Outliers** (x=..., y=...) sections
- Categorical: added **X-axis**, **Y-axis**, note
- Histogram: added **X-axis**, **Y-axis**, note, **Bin Data** section (in addition to existing percentile/shape stats)

* Add Data Points to categorical repr to match python implementation

* Do not mention cross-referencing the chart visual

Unlike the Python implementation, the clojure port does not include the chart image in the context.

* Fix detect-chart-type to correctly detect histograms

histograms have :display_type "bar" not "histogram"

* Add some limits on amount of data to analyze in analyze_charts

Downsample series data to 10k datapoints and cap series at 10 when calculating cross-series correlations.

* Add metabot-v3.stats.util ns and move some shared code there

* Use TMD funcs to compute percentiles, quartiles, skew and kurtosis in histogram.clj

* Cleanup comments and formatting in stats/core.clj

* Cleanup comments and formatting in stats/categorical.clj

* Cleanup comments and formatting in stats/histogram.clj

* Cleanup comments and formatting in stats/repr.clj

* Cleanup comments and formatting in stats/scatter.clj

* Fix make-series and drop compute-summary-median-test in util_test.clj

* Cleanup stats tests

* Use =?/approx in test assertions where appropriate

* Move all repr tests into repr_test.clj

* Add some more repr tests

* Add ^:parallel to metabot stats tests

* Make defs private where possible

* Add malli schemas for public funcs in metabot stats

* Replace some? with more specific test assertions where appropriate

* Remove unused find-outlier-indices

* Remove unused malli schemas ::analyze-chart-input ::analyze-chart-output ::chart-type

* Move nan->nil into stats.util and use it in compute-series-stats

* Rename `dates` arg to `labels` in find-outliers and find-outliers-cumulative

These funcs were originally added for timeseries data where the dependent variable was a date/time value, but are now
used for other chart types were the dimension might be any value.

* Fix trailing whitespace

* Convert series keys to strings in chart configs

* Change ::column-type malli enum from keywords to strings

This matches what we get from the frontend

* Add X-axis range to histogram stats and rename **Bin Data** to **Sample Data**

To match the scatter plot output

* Reduce code dup and extract shared helpers in repr.clj

* Fix kondo warning: reduce -> apply

* Add missing keys to malli schemas

* Ensure top/bottom categories are also deduped and aggregated

* Only calculate categorical :percentage if all values are non-negative

* Handle case where correlation coefficient is exactly zero when setting :direction

* Record :data_points in addtion to :categor_count for categorical stats

* Switch to computing weighted estimates plus structural metrics for binned histogram data

* Fix lint error due to reflection warning

* Fix waterfall chart type detection and improve handling for :unknown charts

Co-authored-by: Oleksandr Solovyov <o@solovyov.net>
Co-authored-by: Maksym Yakubych <undrfined@gmail.com>
Co-authored-by: Mike Appleby <86076+appleby@users.noreply.github.com>
Co-authored-by: Maksym Yakubych <maksym@metabase.com>
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
Co-authored-by: lbrdnk <lbrdnk@users.noreply.github.com>
Co-authored-by: Noah Moss <noahbmoss@gmail.com>
Co-authored-by: Tyler Plude <tsplude@gmail.com>
2026-03-17 16:22:57 +02:00
Nemanja Glumac
65309ff9cb Revert "Update translations 2026-03-11 (#70712)" (#71003)
This reverts commit a989194135.
2026-03-17 14:26:08 +01:00
Nemanja Glumac
36be55e774 Increase the polling timeout (#70999) 2026-03-17 14:00:30 +01:00
Riley Thompson
0c0f3450dd Show expected type in custom expression error message (#70400)
* initial attempt at better error message

* clean up

* Clean up

* improve doc string

* fix cljc test

* add test

* fix test

* exclude perf functions

* have schema produce user facing expected type

* clean up

* Add test

* fix kondo

* defer i18n

* try test fix

* address comments

* update tests
2026-03-17 08:54:48 -04:00
github-automation-metabase
5ac9afd0f5 Auto-update documentation for master (#70988)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-17 08:52:37 -04:00
Jakub Chodorowicz
21ebfb0a1b Gdgt 2002 transform inspector shows 12 matched columns but renders 2 (#70951)
* create failing test case for comparison layout and groups

* fix cards not loading when they part of group is visible
2026-03-17 13:48:32 +01:00
Uladzimir Havenchyk
52d5e78767 fix: perf improvements for documents (#70802)
* fix: perf improvements for documents

* fix unit test

* address comments
2026-03-17 13:40:47 +03:00
Kamil Mielnik
492008d7fd GDGT-1939 Get rid of VisualizationSettingDefinition["props"] (#70867) 2026-03-17 14:12:56 +07:00
Rodrigo López Dato
08c8928cf3 Update Athena parameter suggestions to JDBC v3 driver (#62689) 2026-03-16 17:16:14 -06:00
github-automation-metabase
a989194135 Update translations 2026-03-11 (#70712)
* Update translation sources 2026-03-11

* Update translations

* Fix host/sample apps e2e assertions

* Improve assertion

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Nemanja <31325167+nemanjaglumac@users.noreply.github.com>
2026-03-16 17:15:56 -06:00
Braden Shepherdson
3c152db0a1 [MBQL lib] Start on user docs: top-level and expressions (#70375)
Part of QUE2-366.
2026-03-16 17:15:33 -06:00
Nathan Voxland
19d6d374c8 Correctly detect funnel chart config in static-viz (#68158) 2026-03-16 15:43:29 -05:00
Kevin Kelleher
7e3b3f1818 Fix trend visualizations that rely on aggregated datetimes (#70941) 2026-03-16 14:37:45 -04:00
Kevin Kelleher
4cb40dd358 Fix sunburst chart labels not appearing (#70889) 2026-03-16 14:37:25 -04:00
Nicola Mometto
525b09b87b incremental transforms with source filter (#70573)
* WIP: incremental transforms with source filter

* fix: route through substitution

* chore: remove useless check, we're already in python specific code

* fix: validate before parsing

* style

* fix test

* chore: format

* chore: turn off legacy incremental settings

* chore: remove dead code

* chore: remove dead code

* fix tests

* fix warnings

* fix

* update modules

* feat: store actual type

* feat: derive type, store lo/hi in transform runs

* feat: store checkpoint early so it's available on afailure

* feat: surface checkpoint values in UI

* refetch after run

* surface checkpoint value in UI

* fixes for the linter gods

* chore: format

* remove dead e2e test

* fix rebase

* feat: reset checkpoint if incremental field changes

* chore: do it in before-update instead

* chore: test

* chore: add preconds

* fix: remove rebase field that got in

* feat: thread source-range-params through

* fix test

* remove secondary index

* chore: proper schema

* fix: extract proper source columns from UI components

* fix e2e test

* add option to reset checkpoint

* UI: confirmation on field change

* style

* don't break on old import

* fail when running an incremental transform w/o a field set

* chore: polish UI

* appease linter

* add source-filters to table template tags in the BE

* chore: test

* chore: restrict ops

* extend schema

* chore: extract ->sql util

* chore

* some BE cleanup

* make checkpointFilterFieldId mandatory

* preselect first checkpointFilterFieldId when selecting incremental in standalone variant

* improve forced type assertion

* autoselect first value for checkpoint field also in embedded mode

* extract useAutoSelectFirstOption hook

* refactor nested ternary

* disable incremental toggle when no fields checkpoint available

* add better warning for native query lack of tag

* allow turning off incremental for transforms with issues

* consolidate temporal value to ISO

* Adjust style of reset checkpoint button

* Adjust modal confirmation copy

* convert date to iso also in  save-watermark

* fix date time field type detection

* remove superflous space

* feat: better mbql migration

* fix: whitespaces

* extend FormInlineUpdated to accept break condition onBeforeUpdate

* adjust styling

* extract ResetCheckpointSection

* remove superfluous comment

* rename hooks file

* refactor hasCheckpointOptions

* add comment about stage

* chore: validation

* CTA warning on malformed transforms

fix

* fix: refer to variable instead of template tag

* chore: i18n

* refactor transform warning

* schemas for source-range-params

* deal with review comments

* feat: emit via honeysql instead of string interpolation

* fix: use lib/template-tags

* cleanup

* fix IncrementalTransformSettings tests

* refactor, extract TransformType type

* fix: indent

* fix check

* fix tests

* fix lint

* simplify useHasCheckpointOptions

* cleanup utils unit tests

* chore: also add to lib/schema

* fix missing query metadata in useHasCheckpointOptions

* reset the checkpointFilterFieldId if incremental switch is toggled of

* fix transforms e2e tests

* feat: migration test

* chore: reorder

* adjust transform create tracking

* fix e2e GDGT-1774 reproduction

* fix missing space

* simplify useAutoSelectFirstOption hook

* add incremental sql transform e2e test

* add native sql incremental transform test

* add python incremental transform e2e test

* skip sql test for now

* auto include aliases

* simplify hover info

* fix and unskip incremental schema test

---------

Co-authored-by: Dan Stone <wotbrew@gmail.com>
Co-authored-by: Jakub Chodorowicz <jakub.chodorowicz@metabase.com>
2026-03-16 17:20:46 +00:00
Riley Thompson
0ac98129d9 Inline parameter in clickhouse honeysql method (#70905)
* suggested fix and test

* Clean up test

* clean up test
2026-03-16 15:49:00 +00:00
Nicolò Pretto
64712e1f3f feat(embed-onboarding): Block all schemas that does not contain selected (#70717)
* feat(embed-onboarding): Block all schemas that does not contain selected
tables when setting up RLS

* v2

* fix e2e

* clean up tests
2026-03-16 16:16:45 +01:00
Eric Normand
f3bff3f028 Fix 500 error in check-card endpoint for cards with broken dependents (#70828)
Fix unhandled exception in POST /api/ee/dependencies/check-card that causes a 500 when a transitive dependent card fails to compile

 When check-query-soundness iterates over all dependents, if any card's query throws during compilation (e.g. alias resolution errors), the exception propagated uncaught and crashed the entire endpoint
 
Added try/catch around check-entity calls in check-query-soundness, returning a validation-exception-error instead of crashing — consistent with the pattern already used in `/dependencies/findings.clj`

On the stats instance, card 4523 has 2,557 transitive card dependents. One of those dependents (a native query referencing card 34523 via a template tag) fails during query compilation with `Missing ::desired-alias->escaped for "email_domain"`. This compilation error is unrelated to the proposed edit but was bringing down the whole endpoint.

Test plan

  - Verify POST /api/ee/dependencies/check-card returns 200 with {success: false, bad_cards: [...]} instead of 500 when a dependent card has a query compilation error
  - Verify that cards with compilation errors appear in the response as validation-exception-error findings
  - Existing dependency check tests still pass
2026-03-16 14:59:17 +00:00
Ngoc Khuat
8454245aaa Use safe identifier quoting in workspace grant statements (#70869) 2026-03-16 14:58:24 +00:00
Nicola Mometto
3cc709ee43 Aliases for table template tags (#70893)
* auto include aliases

* simplify hover info

* add unit test for emit tables toggle

---------

Co-authored-by: Jakub Chodorowicz <jakub.chodorowicz@metabase.com>
2026-03-16 14:56:21 +00:00
Nicola Mometto
88729c52f1 Table tags filters (#70699)
* add source-filters to table template tags in the BE

* chore: test

* chore: restrict ops

* extend schema

* chore: extract ->sql util

* deal with review comments

* feat: emit via honeysql instead of string interpolation

* fix check

* chore: also add to lib/schema
2026-03-16 13:56:47 +00:00
9392 changed files with 1024317 additions and 708738 deletions

View File

@@ -1,4 +1,4 @@
--b-
---
name: add-tracing
description: Add OpenTelemetry tracing spans to Clojure code following Metabase tracing conventions. Use when instrumenting backend code with trace coverage.
---

View File

@@ -15,7 +15,7 @@ Analytics events in Metabase use Snowplow with typed event schemas. All events m
**Key Files:**
- `frontend/src/metabase-types/analytics/event.ts` - Event type definitions
- `frontend/src/metabase-types/analytics/schema.ts` - Schema registry
- `frontend/src/metabase/lib/analytics.ts` - Core tracking functions
- `frontend/src/metabase/analytics/` - Core tracking functions (import from `metabase/analytics`)
- Feature-specific `analytics.ts` files - Tracking function wrappers
## Quick Checklist
@@ -91,7 +91,7 @@ export type DataStudioEvent =
In your feature's `analytics.ts` file (e.g., `enterprise/frontend/src/metabase-enterprise/data-studio/analytics.ts`):
```typescript
import { trackSimpleEvent } from "metabase/lib/analytics";
import { trackSimpleEvent } from "metabase/analytics";
export const trackDataStudioTablePickerFiltersApplied = () => {
trackSimpleEvent({
@@ -471,7 +471,7 @@ Event type definitions (all in one place):
frontend/src/metabase-types/analytics/event.ts
Core tracking utilities:
frontend/src/metabase/lib/analytics.ts
frontend/src/metabase/analytics/ (import from `metabase/analytics`)
```
## Real-World Examples

View File

@@ -0,0 +1,173 @@
---
name: clojure-eval
description: Evaluate Clojure code via nREPL using clj-nrepl-eval. Use this when you need to test code, check if edited files compile, verify function behavior, or interact with a running REPL session.
---
# Clojure REPL Evaluation
## When to Use This Skill
Use this skill when you need to:
- **Verify that edited Clojure files compile and load correctly**
- Test function behavior interactively
- Check the current state of the REPL
- Debug code by evaluating expressions
- Require or load namespaces for testing
- Validate that code changes work before committing
## How It Works
The `clj-nrepl-eval` command evaluates Clojure code against an nREPL server. **Session state persists between evaluations**, so you can require a namespace in one evaluation and use it in subsequent calls. Each host:port combination maintains its own session file.
## Instructions
### 0. Discover and select nREPL server
First, discover what nREPL servers are running in the current directory:
```bash
clj-nrepl-eval --discover-ports
```
This will show all nREPL servers (Clojure, Babashka, shadow-cljs, etc.) running in the current project directory.
**Then use the AskUserQuestion tool:**
- **If ports are discovered:** Prompt user to select which nREPL port to use:
- **question:** "Which nREPL port would you like to use?"
- **header:** "nREPL Port"
- **options:** Present each discovered port as an option with:
- **label:** The port number
- **description:** The server type and status (e.g., "Clojure nREPL server in current directory")
- Include up to 4 discovered ports as options
- The user can select "Other" to enter a custom port number
- **If no ports are discovered:** Prompt user how to start an nREPL server:
- **question:** "No nREPL servers found. How would you like to start one?"
- **header:** "Start nREPL"
- **options:**
- **label:** "deps.edn alias", **description:** "Find and use an nREPL alias in deps.edn"
- **label:** "Leiningen", **description:** "Start nREPL using 'lein repl'"
- The user can select "Other" for alternative methods or if they already have a server running on a specific port
IMPORTANT: IF you start a REPL do not supply a port let the nREPL start and return the port that it was started on.
### 1. Evaluate Clojure Code
> Evaluation automatically connects to the given port
Use the `-p` flag to specify the port and pass your Clojure code.
**Recommended: Pass code as a command-line argument:**
```bash
clj-nrepl-eval -p <PORT> "(+ 1 2 3)"
```
**For multiple expressions (single line):**
```bash
clj-nrepl-eval -p <PORT> "(def x 10) (+ x 20)"
```
**Alternative: Using heredoc (may require permission approval for multiline commands):**
```bash
clj-nrepl-eval -p <PORT> <<'EOF'
(def x 10)
(+ x 20)
EOF
```
**Alternative: Via stdin pipe:**
```bash
echo "(+ 1 2 3)" | clj-nrepl-eval -p <PORT>
```
### 2. Display nREPL Sessions
**Discover all nREPL servers in current directory:**
```bash
clj-nrepl-eval --discover-ports
```
Shows all running nREPL servers in the current project directory, including their type (clj/bb/basilisp) and whether they match the current working directory.
**Check previously connected sessions:**
```bash
clj-nrepl-eval --connected-ports
```
Shows only connections you have made before (appears after first evaluation on a port).
### 3. Common Patterns
**Require a namespace (always use :reload to pick up changes):**
```bash
clj-nrepl-eval -p <PORT> "(require '[my.namespace :as ns] :reload)"
```
**Test a function after requiring:**
```bash
clj-nrepl-eval -p <PORT> "(ns/my-function arg1 arg2)"
```
**Check if a file compiles:**
```bash
clj-nrepl-eval -p <PORT> "(require 'my.namespace :reload)"
```
**Multiple expressions:**
```bash
clj-nrepl-eval -p <PORT> "(def x 10) (* x 2) (+ x 5)"
```
**Complex multiline code (using heredoc):**
```bash
clj-nrepl-eval -p <PORT> <<'EOF'
(def x 10)
(* x 2)
(+ x 5)
EOF
```
*Note: Heredoc syntax may require permission approval.*
**With custom timeout (in milliseconds):**
```bash
clj-nrepl-eval -p <PORT> --timeout 5000 "(long-running-fn)"
```
**Reset the session (clears all state):**
```bash
clj-nrepl-eval -p <PORT> --reset-session
clj-nrepl-eval -p <PORT> --reset-session "(def x 1)"
```
## Available Options
- `-p, --port PORT` - nREPL port (required)
- `-H, --host HOST` - nREPL host (default: 127.0.0.1)
- `-t, --timeout MILLISECONDS` - Timeout (default: 120000 = 2 minutes)
- `-r, --reset-session` - Reset the persistent nREPL session
- `-c, --connected-ports` - List previously connected nREPL sessions
- `-d, --discover-ports` - Discover nREPL servers in current directory
- `-h, --help` - Show help message
## Important Notes
- **Prefer command-line arguments:** Pass code as quoted strings: `clj-nrepl-eval -p <PORT> "(+ 1 2 3)"` - works with existing permissions
- **Heredoc for complex code:** Use heredoc (`<<'EOF' ... EOF`) for truly multiline code, but note it may require permission approval
- **Sessions persist:** State (vars, namespaces, loaded libraries) persists across invocations until the nREPL server restarts or `--reset-session` is used
- **Automatic delimiter repair:** The tool automatically repairs missing or mismatched parentheses
- **Always use :reload:** When requiring namespaces, use `:reload` to pick up recent changes
- **Default timeout:** 2 minutes (120000ms) - increase for long-running operations
- **Input precedence:** Command-line arguments take precedence over stdin
## Typical Workflow
1. Discover nREPL servers: `clj-nrepl-eval --discover-ports`
2. Use **AskUserQuestion** tool to prompt user to select a port
3. Require namespace:
```bash
clj-nrepl-eval -p <PORT> "(require '[my.ns :as ns] :reload)"
```
4. Test function:
```bash
clj-nrepl-eval -p <PORT> "(ns/my-fn ...)"
```
5. Iterate: Make changes, re-require with `:reload`, test again

View File

@@ -67,3 +67,7 @@ Only fall back to `./bin/mage` commands when clojure-mcp is not available.
- End all files with a newline
- When editing tabular code, where the columns line up, try to keep them aligned
- Spaces on a line with nothing after it is not allowed
- After changing module boundaries (adding/removing/renaming a `src` namespace, a cross-module `require`
or `:model/X` reference, or a new module), run `./bin/mage fix-modules-config` to regenerate
`.clj-kondo/config/modules/config.edn` and keep `metabase.core.modules-test` green. No-op when nothing
drifted; see "Module Boundaries" in the project `CLAUDE.md`.

View File

@@ -0,0 +1,834 @@
---
name: emotion-migrate
description: Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports from components.
---
# Emotion → Mantine + CSS Modules Migration Skill
Migrate Emotion styled-components (`@emotion/styled`, `@emotion/react`) to Mantine layout components with style props and CSS modules. The goal is zero Emotion imports, zero inline styles, and maximum use of design system tokens.
## Priority Order (Strict)
1. **Mantine components + style props** — `Box`, `Flex`, `Stack`, `Group`, `Text`, `Title`, `Card`. This is the DEFAULT. Every CSS property must be checked against style props FIRST.
2. **CSS modules** (`.module.css`) — ONLY for properties that Mantine style props genuinely cannot express: pseudo-selectors (`:hover`, `:focus`, `::before`), `box-shadow`, `border` shorthand, `animation`/`@keyframes`, complex selectors, `cursor`, `pointer-events`, `overflow`, `text-overflow`, `white-space`, `transition`.
3. **Inline styles ONLY for dynamic values** — `style={{ }}` is allowed only for truly dynamic runtime values (e.g., computed widths, positions, data-driven colors). All static styles must use Mantine props or CSS modules.
### Mantine-First Decision Gate (CRITICAL)
For EACH styled component, go through every CSS property and ask: "Can this be a Mantine style prop?" If yes → style prop. If no → CSS module. Do NOT dump an entire component into a CSS module just because one property needs it — split them.
**Properties that ARE style props** (use these, not CSS modules):
- `display` → `display` prop
- `color` → `c` prop (`c="brand"`, `c="text-primary"`)
- `background-color` → `bg` prop (`bg="background-primary"`)
- `font-size` → `fz` prop (`fz="md"`)
- `font-weight` → `fw` prop (`fw="bold"`)
- `line-height` → `lh` prop (`lh="md"`)
- `text-align` → `ta` prop (`ta="center"`)
- `padding` (all variants) → `p`, `px`, `py`, `pt`, `pb`, `pl`, `pr`
- `margin` (all variants) → `m`, `mx`, `my`, `mt`, `mb`, `ml`, `mr`
- `width` → `w`, `min-width` → `miw`, `max-width` → `maw`
- `height` → `h`, `min-height` → `mih`, `max-height` → `mah`
- `flex` → `flex` prop (`flex="0 0 auto"`, `flex={1}`)
- `gap` → `gap` prop (on Flex/Stack/Group)
- `align-items` → `align` prop (on Flex/Stack/Group)
- `justify-content` → `justify` prop (on Flex/Stack/Group)
- `flex-direction` → `direction` prop (on Flex)
- `flex-wrap` → `wrap` prop (on Flex)
- `position` → `pos` prop
- `top/right/bottom/left` → `top`, `right`, `bottom`, `left` props
- `opacity` → `opacity` prop
**Properties that NEED CSS modules** (no style prop equivalent):
- `:hover`, `:focus`, `:active`, `::before`, `::after` (pseudo-selectors)
- `box-shadow`, `border` (shorthand with color), `outline`
- `cursor`, `pointer-events`
- `overflow`, `text-overflow`, `white-space`
- `animation`, `transition`, `transform`
- `@media` queries (UNLESS it's simple responsive spacing/sizing — then use responsive syntax: `p={{ base: "md", lg: "xl" }}`)
**Hybrid approach** — when a component needs BOTH, put style props on the Mantine component AND add a CSS module class for the rest:
```tsx
<Flex
className={S.root} /* for :hover, box-shadow, border */
align="center" /* style prop */
gap="sm" /* style prop */
p="md" /* style prop */
bg="background-primary" /* style prop */
>
```
## CSS Module Conventions (Strict)
### Class Naming: camelCase
All CSS module class names MUST use **camelCase**. This is the dominant convention across the codebase (~830 camelCase vs ~620 PascalCase classes), used consistently in Mantine UI components, and matches standard CSS module conventions.
```css
/* CORRECT */
.root {
}
.settingsSection {
}
.dragHandle {
}
.closeIcon {
}
/* WRONG — do not use PascalCase or kebab-case */
.ItemRoot {
}
.settings-section {
}
```
Modifier/state classes also use camelCase:
```css
.selected {
}
.disabled {
}
.interactive {
}
.draggable {
}
```
### No Cascading — Direct Class Assignment
Cascading selectors are **discouraged**. Instead of styling through parent-child relationships, assign a class directly to the element that needs styling.
```css
/* WRONG — cascading/descendant selectors */
.root > input {
}
.root .label {
}
.container > div > span {
}
/* CORRECT — direct class on the target element */
.input {
}
.label {
}
.title {
}
```
The only acceptable nesting patterns are:
- **Pseudo-selectors on the same element**: `.item { &:hover { } }`
- **Modifier composition**: `.item { &.selected { } }`
- **Hover-reveal patterns** where a parent hover affects a child: `.root:hover .showOnHover { opacity: 1; }` — but only when structurally necessary (the child has no way to know about the parent's hover state)
### Import Alias
Always import the CSS module as `S`:
```tsx
import S from "./ComponentName.module.css";
```
## Step-by-Step Migration Process
### Step 1: Read and Understand
Read the `.styled.tsx` file AND every component that imports from it. Understand:
- Which styled components are used and where
- Which props drive dynamic styles
- Which styles are static vs conditional
- Which styles can map directly to Mantine style props
### Step 2: Classify Each Styled Component
For each styled component, apply the Mantine-First Decision Gate above. Then determine the migration target:
| Emotion Pattern | Migration Target |
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `styled.div` with only layout/spacing/color | `Box`, `Flex`, `Stack`, or `Group` with style props. **NO CSS module needed.** |
| `styled.div` with flexbox column | `Stack` component with style props |
| `styled.div` with flexbox row | `Flex` or `Group` component with style props |
| `styled.span` / `styled.p` with color/weight/size | **`Text component="span"` with style props** (`c`, `fw`, `fz`). NO CSS module. |
| `styled.div` with hover/focus/pseudo-selectors | **Hybrid**: Mantine component with style props for expressible properties + CSS module class for pseudo-selectors only |
| `styled.div` with media queries (simple spacing/sizing) | **Responsive style props**: `p={{ base: "md", lg: "xl" }}`. NO CSS module. |
| `styled.div` with media queries (complex/non-spacing) | CSS module for the media query parts, style props for the rest |
| `styled.div` with animations/keyframes | CSS module for animation, style props for layout |
| `styled(SomeComponent)` with only color/spacing/flex | **Wrap in Mantine component** with style props: `<Box c="brand" flex="0 0 auto"><Icon /></Box>`, or pass style props if the component accepts them |
| `styled(SomeComponent)` with pseudo-selectors/complex styles | CSS module `className` on the component |
| Dynamic props `styled.div<{ isActive: boolean }>` | Mantine style props for simple toggles (`c={active ? "brand" : "text-primary"}`), `cx()` with CSS module classes for complex state combinations involving pseudo-selectors |
### Step 3: Create CSS Module (if needed)
Create `ComponentName.module.css` alongside the component file:
```css
/* Use design system tokens — NEVER raw color/spacing values */
.root {
border: 1px solid var(--mb-color-border);
border-radius: var(--mantine-radius-md);
background-color: var(--mb-color-background-primary);
}
/* Conditional states as separate classes, combined with cx() */
.active {
background-color: var(--mb-color-brand);
color: var(--mb-color-text-primary-inverse);
}
.disabled {
color: var(--mb-color-text-tertiary);
pointer-events: none;
}
/* Hover/focus/pseudo-selectors — nest with & */
.interactive {
cursor: pointer;
&:hover {
color: var(--mb-color-brand);
background-color: var(--mb-color-background-hover);
}
}
/* Hover-reveal: acceptable parent→child nesting */
.root:hover .showOnHover {
opacity: 1;
}
/* Responsive styles */
@media (--breakpoint-min-md) {
.root {
padding: var(--mantine-spacing-lg);
}
}
```
### Step 4: Update the Component TSX
```tsx
import cx from "classnames";
import CS from "metabase/css/core/index.css";
import { Box, Flex, Group, Stack, Text } from "metabase/ui";
import S from "./ComponentName.module.css";
// Dynamic props → cx() with conditional classes
<Flex
className={cx(S.root, {
[S.active]: isActive,
[S.disabled]: disabled,
})}
align="center"
gap="sm"
p="md"
w="100%"
>
```
### Step 5: Delete the `.styled.tsx` File
Remove the old styled file entirely. Remove all imports of it from other files.
### Step 6: Verify
- Confirm zero `@emotion/styled` or `@emotion/react` imports remain in the migrated files
- Confirm no static inline styles (dynamic runtime values are fine)
- Confirm all colors use tokens, not raw hex/rgb values
## Design System Token Reference
### Mantine Style Props (use directly on components)
**Spacing** (`p`, `px`, `py`, `pt`, `pb`, `pl`, `pr`, `m`, `mx`, `my`, `mt`, `mb`, `ml`, `mr`):
- `"xs"` = 4px, `"sm"` = 8px, `"md"` = 16px, `"lg"` = 24px, `"xl"` = 32px
- Custom: `rem(48)` for non-standard values (import `rem` from `metabase/ui`)
**Dimensions** (`w`, `h`, `maw`, `mah`, `miw`, `mih`):
- `"100%"`, `"100vh"`, `rem(400)`, etc.
**Colors** (`c`, `bg`):
- Text: `"text-primary"`, `"text-secondary"`, `"text-tertiary"`
- Background: `"background-primary"`, `"background-secondary"`, `"background-hover"`
- Brand: `"brand"`, `"error"`, `"success"`, `"warning"`
**Typography** (`fz`, `fw`, `lh`, `ta`, `ff`):
- Font size: `"xs"` = 11px, `"sm"` = 12px, `"md"` = 14px, `"lg"` = 17px, `"xl"` = 21px
- Font weight: `"bold"`, `"normal"`, or numeric `700`
- Line height: `"xs"` = 100%, `"sm"` = 115%, `"md"` = 122%, `"lg"` = 138%, `"xl"` = 150%
- Text align: `"center"`, `"left"`, `"right"`
**Flexbox** (`align`, `justify`, `gap`, `direction`, `wrap`, `flex`):
- `align="center"`, `justify="space-between"`, `gap="sm"`, `direction="column"`, `wrap="nowrap"`
- `flex={1}`, `flex="0 0 auto"`
**Other**: `pos` (position), `display`
**Responsive syntax**: `px={{ base: "md", md: "lg", lg: rem(48) }}`
### CSS Variable Tokens (use in `.module.css` files)
**Colors** — `var(--mb-color-<name>)`:
- `--mb-color-text-primary`, `--mb-color-text-secondary`, `--mb-color-text-tertiary`
- `--mb-color-background-primary`, `--mb-color-background-secondary`, `--mb-color-background-hover`
- `--mb-color-border`, `--mb-color-border-strong`, `--mb-color-border-subtle`
- `--mb-color-brand`, `--mb-color-brand-hover`
- `--mb-color-error`, `--mb-color-success`, `--mb-color-warning`
- `--mb-color-shadow`, `--mb-color-focus`
- `--mb-color-accent0` through `--mb-color-accent7`
**Mantine spacing** — `var(--mantine-spacing-<size>)`:
- `--mantine-spacing-xs` (4px), `--mantine-spacing-sm` (8px), `--mantine-spacing-md` (16px), `--mantine-spacing-lg` (24px), `--mantine-spacing-xl` (32px)
**Mantine radius** — `var(--mantine-radius-<size>)`:
- `--mantine-radius-xs` (4px), `--mantine-radius-sm` (6px), `--mantine-radius-md` (8px), `--mantine-radius-xl` (40px)
**Mantine font sizes** — `var(--mantine-font-size-<size>)`:
- `--mantine-font-size-xs` (11px), `--mantine-font-size-sm` (12px), `--mantine-font-size-md` (14px), `--mantine-font-size-lg` (17px), `--mantine-font-size-xl` (21px)
**Breakpoints** (in `.module.css` files):
- `@media (--breakpoint-min-sm)` (40em / 640px)
- `@media (--breakpoint-min-md)` (60em / 960px)
- `@media (--breakpoint-min-lg)` (80em / 1280px)
- `@media (--breakpoint-min-xl)` (120em / 1920px)
- Also available: `--breakpoint-max-*` variants
### Snap Hardcoded Literals to Nearest Design Token (CRITICAL)
When migrating, **never carry over hardcoded `rem`/`px` values** from the original Emotion code. Instead, snap them to the nearest design system token. The original values were often arbitrary — the migration is an opportunity to align with the design system.
**Spacing** — snap to the nearest Mantine spacing token:
| Hardcoded value | Nearest token | Style prop | CSS variable |
| --------------------------------- | ------------- | ------------ | ------------------------------ |
| `0.125rem` (2px) | `2px` | `2` (number) | `2px` (keep literal, no token) |
| `0.2rem` (3.2px), `0.25rem` (4px) | **xs** (4px) | `"xs"` | `var(--mantine-spacing-xs)` |
| `0.5rem` (8px) | **sm** (8px) | `"sm"` | `var(--mantine-spacing-sm)` |
| `0.75rem` (12px) | between sm/md | `rem(12)` | `0.75rem` (no exact token) |
| `1rem` (16px) | **md** (16px) | `"md"` | `var(--mantine-spacing-md)` |
| `1.5rem` (24px) | **lg** (24px) | `"lg"` | `var(--mantine-spacing-lg)` |
| `2rem` (32px) | **xl** (32px) | `"xl"` | `var(--mantine-spacing-xl)` |
**Border radius** — snap to the nearest Mantine radius token:
| Hardcoded value | Nearest token | CSS variable |
| ---------------- | ----------------------------- | -------------------------- |
| `0.25rem` (4px) | **xs** (4px) | `var(--mantine-radius-xs)` |
| `0.375rem` (6px) | **sm** (6px) | `var(--mantine-radius-sm)` |
| `0.5rem` (8px) | **md** (8px) | `var(--mantine-radius-md)` |
| `1rem`+ (16px+) | **xl** (40px) or keep literal | `var(--mantine-radius-xl)` |
**Font sizes** — snap to the nearest Mantine font size token:
| Hardcoded value | Nearest token | Style prop | CSS variable |
| -------------------------------- | ------------- | ---------- | ----------------------------- |
| `0.6875rem` (11px) | **xs** (11px) | `fz="xs"` | `var(--mantine-font-size-xs)` |
| `0.75rem` (12px) | **sm** (12px) | `fz="sm"` | `var(--mantine-font-size-sm)` |
| `0.875rem` (14px), `1rem` (16px) | **md** (14px) | `fz="md"` | `var(--mantine-font-size-md)` |
| `1.063rem` (17px) | **lg** (17px) | `fz="lg"` | `var(--mantine-font-size-lg)` |
| `1.3rem` (21px) | **xl** (21px) | `fz="xl"` | `var(--mantine-font-size-xl)` |
**Rules:**
- If the hardcoded value is within ~2px of a token, use the token
- If it falls exactly between two tokens, prefer the smaller one (tighter is safer)
- If no token is close (e.g., `48px` spacing), use `rem(48)` for style props or the literal value in CSS modules
- This applies everywhere: style props, CSS module values, Icon `size` props, etc.
## Common Migration Patterns
### Pattern 1: Static Layout Container → Mantine Component
**Before:**
```tsx
// Component.styled.tsx
export const Container = styled.div`
display: flex;
align-items: center;
gap: 0.5rem;
padding: 1rem;
width: 100%;
`;
```
**After:**
```tsx
<Flex align="center" gap="sm" p="md" w="100%">
```
### Pattern 2: Vertical Stack → Stack Component
**Before:**
```tsx
export const Wrapper = styled.div`
display: flex;
flex-direction: column;
gap: 1.5rem;
padding: 2rem;
`;
```
**After:**
```tsx
<Stack gap="lg" p="xl">
```
### Pattern 3: Dynamic Props → cx() with CSS Module Classes
**Before:**
```tsx
export const Item = styled.div<{ isSelected: boolean; disabled: boolean }>`
color: ${(props) =>
props.disabled ? color("text-tertiary") : color("text-primary")};
background-color: ${(props) =>
props.isSelected ? color("brand") : "transparent"};
cursor: ${(props) => (props.disabled ? "default" : "pointer")};
&:hover {
color: ${(props) => !props.disabled && color("brand")};
}
`;
```
**After (CSS module):**
```css
/* Item.module.css */
.itemRoot {
color: var(--mb-color-text-primary);
cursor: pointer;
&:hover {
color: var(--mb-color-brand);
}
}
.selected {
background-color: var(--mb-color-brand);
}
.disabled {
color: var(--mb-color-text-tertiary);
cursor: default;
&:hover {
color: var(--mb-color-text-tertiary);
}
}
```
**After (TSX):**
```tsx
<Box
className={cx(S.itemRoot, {
[S.selected]: isSelected,
[S.disabled]: disabled,
})}
>
```
### Pattern 4: Extending a Component → Style Props First, CSS Module Only If Needed
When a styled component wraps another component, first check if the styles can be expressed as style props on a Mantine wrapper or on the component itself. Only use a CSS module when properties genuinely need it.
**Before:**
```tsx
export const CardIcon = styled(Icon)`
display: block;
flex: 0 0 auto;
color: var(--mb-color-brand);
`;
export const CardTitle = styled(Ellipsified)`
color: var(--mb-color-text-primary);
font-size: 1rem;
font-weight: bold;
margin-left: 1rem;
`;
```
**After — style props on Mantine wrapper (preferred when ALL properties are expressible):**
```tsx
<Box display="block" flex="0 0 auto" c="brand">
<Icon {...icon} />
</Box>
<Box c="text-primary" fz="md" fw="bold" ml="md">
<Ellipsified>{title}</Ellipsified>
</Box>
```
**After — CSS module (use only when component has pseudo-selectors or non-expressible styles):**
```css
.icon {
color: var(--mb-color-brand);
width: 1.5rem;
height: 1.5rem;
cursor: pointer;
&:hover {
color: var(--mb-color-brand-hover);
}
}
```
```tsx
<Icon className={S.icon} name="warning" />
```
### Pattern 4b: `styled.span` / `styled.p` with Text Styles → `Text` Component
**Before:**
```tsx
export const Label = styled.span`
color: var(--mb-color-text-secondary);
font-weight: bold;
`;
export const Title = styled.span`
color: var(--mb-color-text-primary);
font-size: 1rem;
font-weight: bold;
margin-left: 0.5rem;
`;
```
**After — `Text` component with style props (NO CSS module needed):**
```tsx
<Text component="span" c="text-secondary" fw="bold">
{label}
</Text>
<Text component="span" c="text-primary" fz="md" fw="bold" ml="sm">
{title}
</Text>
```
`Text` renders as `<p>` by default. Use `component="span"` to render inline. All color, typography, and spacing props work directly — no CSS module needed for pure text styling.
### Pattern 5: Keyframes Animation → CSS Module
**Before:**
```tsx
const spin = keyframes`
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
`;
export const Spinner = styled.div`
animation: ${spin} 1s infinite linear;
`;
```
**After (CSS module):**
```css
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.spinner {
animation: spin 1s infinite linear;
}
```
**After (TSX):**
```tsx
<Box className={S.spinner} />
```
### Pattern 6: color() Function → CSS Variables
| Emotion (JS) | CSS Module | Mantine Style Prop |
| ----------------------- | ------------------------------------------------------------ | --------------------------- |
| `color("brand")` | `var(--mb-color-brand)` | `c="brand"` or `bg="brand"` |
| `color("text-primary")` | `var(--mb-color-text-primary)` | `c="text-primary"` |
| `color("border")` | `var(--mb-color-border)` | N/A (use CSS module) |
| `alpha("brand", 0.2)` | `color-mix(in srgb, var(--mb-color-brand), transparent 80%)` | N/A (use CSS module) |
### Pattern 7: Responsive Spacing/Sizing → Responsive Style Props (NOT CSS Media Queries)
When a styled component only changes spacing, sizing, or other style-prop-expressible values at breakpoints, use Mantine's responsive syntax instead of CSS module media queries.
**Before:**
```tsx
export const CaptionRoot = styled.div`
margin-bottom: 1.5rem;
${breakpointMinExtraLarge} {
margin-bottom: 2rem;
}
`;
```
**After — responsive style prop (NO CSS module needed):**
```tsx
<Flex mb={{ base: "lg", xl: "xl" }}>
```
Breakpoint keys: `base` (default), `xs` (40em), `sm` (48em), `md` (60em), `lg` (80em), `xl` (120em).
Only use CSS module `@media` queries when the responsive change involves non-style-prop properties (e.g., `box-shadow`, `border`, `cursor` changes at breakpoints).
### Pattern 8: Dynamic Computed Styles → Inline Styles
Inline styles are allowed **only** for truly dynamic values computed at runtime (e.g., widths, positions, colors from data). Everything else must use Mantine style props or CSS modules.
**Before:**
```tsx
export const Bar = styled.div<{ width: number }>`
width: ${(props) => props.width}%;
`;
```
**After:**
```tsx
<Box style={{ width: `${width}%` }} />
```
### Pattern 9: Core CSS Utility Classes (Discouraged)
**Do NOT introduce new `CS` utility class usage.** Core CSS utilities (`CS` from `metabase/css/core/index.css`) are legacy and discouraged for new code. Prefer Mantine style props or CSS modules instead.
If existing code already uses `CS` classes and you're not migrating that specific code, leave them alone. But when migrating Emotion → Mantine, replace with the proper alternative:
| Instead of `CS.*` | Use |
| ------------------- | ------------------------------------------------------- |
| `CS.overflowHidden` | CSS module: `overflow: hidden` |
| `CS.cursorPointer` | CSS module: `cursor: pointer` |
| `CS.flex1` | Style prop: `flex={1}` |
| `CS.flexNoShrink` | Style prop: `flex="0 0 auto"` |
| `CS.textBrandHover` | CSS module: `&:hover { color: var(--mb-color-brand); }` |
### Pattern 10: Shared Emotion Styles Across Files → Shared CSS Module + cx()
When Emotion exports a shared `css` block (like `animationStyles`) imported by many files, create ONE CSS module with the shared keyframe and a reusable class, then compose via `cx()`.
**Before (Emotion — shared animation used by 13+ skeleton files):**
```tsx
// ChartSkeleton.styled.tsx — defines shared animation
const fadingKeyframes = keyframes`
0% { opacity: 0.0625; }
50% { opacity: 0.125; }
100% { opacity: 0.0625; }
`;
export const animationStyles = css`
opacity: 0.1;
animation: ${fadingKeyframes} 1.5s infinite;
`;
// AreaSkeleton.styled.tsx — consumes it
import { animationStyles } from ".../ChartSkeleton.styled";
export const SkeletonImage = styled.svg`
${animationStyles};
flex: 1 1 0;
margin-top: 1rem;
border-bottom: 1px solid currentColor;
`;
// BarSkeleton.styled.tsx — also consumes it
import { animationStyles } from ".../ChartSkeleton.styled";
export const SkeletonImage = styled.svg`
${animationStyles};
flex: 1 1 0;
margin-top: 1rem;
`;
```
**After — shared CSS module:**
```css
/* ChartSkeleton.module.css */
@keyframes fading {
0% {
opacity: 0.0625;
}
50% {
opacity: 0.125;
}
100% {
opacity: 0.0625;
}
}
.animated {
opacity: 0.1;
animation: fading 1.5s infinite;
}
```
**After — per-component CSS modules with own styles only:**
```css
/* AreaSkeleton.module.css */
.skeletonImage {
flex: 1 1 0;
margin-top: 1rem;
border-bottom: 1px solid currentColor;
}
```
```css
/* BarSkeleton.module.css */
.skeletonImage {
flex: 1 1 0;
margin-top: 1rem;
}
```
**After — component TSX uses cx() to compose:**
```tsx
// AreaSkeleton.tsx
import cx from "classnames";
import ChartSkeletonS from "../ChartSkeleton/ChartSkeleton.module.css";
import S from "./AreaSkeleton.module.css";
const AreaSkeleton = (): JSX.Element => {
return (
<svg
className={cx(ChartSkeletonS.animated, S.skeletonImage)}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 371 113"
preserveAspectRatio="none"
>
<path d="..." fill="currentColor" />
</svg>
);
};
```
The shared module owns the `@keyframes` and the `.animated` class. Each consumer composes it with its own module class via `cx()`. No duplication of the keyframe definition.
## Import Template
```tsx
import cx from "classnames"; // only if needed for conditional classes
import { Box, Flex, Group, Stack, Text, rem } from "metabase/ui";
import S from "./ComponentName.module.css"; // only if CSS module is needed
```
## Checklist Before Finishing
- [ ] **MANTINE-FIRST CHECK**: Every CSS property that has a style prop equivalent (`c`, `bg`, `p`, `m`, `fw`, `fz`, `flex`, `gap`, `align`, `w`, `h`, etc.) is expressed as a style prop, NOT in a CSS module
- [ ] `styled.span`/`styled.p` with only color/weight/size are replaced with `Text component="span"` + style props, NOT CSS module classes
- [ ] `styled.div` with only layout props are replaced with `Flex`/`Stack`/`Group`/`Box` + style props, NOT CSS module classes
- [ ] Responsive spacing/sizing uses responsive style props (`mb={{ base: "lg", xl: "xl" }}`), NOT CSS module `@media` queries
- [ ] CSS modules are used ONLY for: pseudo-selectors, box-shadow, border, cursor, overflow, animation, transition, transform, or complex selectors
- [ ] All `@emotion/styled` and `@emotion/react` imports removed from migrated files
- [ ] The `.styled.tsx` file is deleted
- [ ] All imports of the deleted styled file are updated
- [ ] No static inline styles — `style={{ }}` used only for truly dynamic runtime values
- [ ] All colors use design tokens (`c="brand"`, `var(--mb-color-brand)`), not raw hex/rgb
- [ ] All spacing uses design tokens (`p="md"`, `var(--mantine-spacing-md)`), not arbitrary px values — hardcoded `rem`/`px` literals snapped to the nearest token (see mapping table above)
- [ ] All border-radius values use radius tokens (`var(--mantine-radius-md)`), not hardcoded `0.5rem`
- [ ] All font sizes use font-size tokens (`fz="md"`, `var(--mantine-font-size-md)`), not hardcoded `1rem`
- [ ] Layout uses Mantine components (`Flex`, `Stack`, `Group`, `Box`, `Text`) not raw divs/spans with CSS
- [ ] All CSS module class names use camelCase
- [ ] No cascading/descendant selectors targeting raw elements — use direct class assignment instead
- [ ] Component renders identically to the original (visually verify if possible)
## Visual Verification with Screenshots (Optional — Only When Explicitly Asked)
When the user explicitly asks to create before/after screenshots, use the Playwright MCP tools to capture them. This is NOT done by default — only when requested.
Screenshots must be taken in **both light and dark mode** to verify token usage is correct in both themes.
### Switching Color Scheme via API
Toggle between light and dark mode using the Metabase settings API. Use Playwright's `browser_evaluate` or `browser_navigate` with fetch:
```js
// Switch to dark mode
await fetch("/api/setting/color-scheme", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ value: "dark" }),
});
// Switch to light mode
await fetch("/api/setting/color-scheme", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ value: "light" }),
});
```
After switching, reload the page for the theme to take effect.
### Process
For each state (**after** = migrated code, **before** = stashed original), capture screenshots in both themes at both viewport widths. This produces **8 screenshots total**.
1. **Take "after" screenshots first** (migrated code is already in place):
- Navigate to the page that renders the migrated components (`browser_navigate`)
- Wait for content to load (`browser_wait_for` with key text, or `textGone: "Loading..."`)
- Close the sidebar if it overlaps content on narrow viewports (`browser_click` the toggle button)
- **Light mode** (set `color-scheme` to `"light"` via API, reload):
- Desktop (1280x900): `after-light-desktop.png`
- Narrow (640x900), close sidebar: `after-light-narrow.png`
- **Dark mode** (set `color-scheme` to `"dark"` via API, reload):
- Desktop (1280x900): `after-dark-desktop.png`
- Narrow (640x900), close sidebar: `after-dark-narrow.png`
2. **Stash changes to capture "before" screenshots**:
- Run `git stash --include-untracked` to temporarily revert to the Emotion version
- Repeat the same 4 screenshots with `before-` prefix:
- `before-light-desktop.png`, `before-light-narrow.png`
- `before-dark-desktop.png`, `before-dark-narrow.png`
- Run `git stash pop` to restore the migrated code
3. **Restore the user's original color scheme** (set back to `"auto"` or whatever it was before).
4. **Display all 8 screenshots** using the `Read` tool so the user can visually compare before/after in both themes at both viewport sizes.
### Key Details
- Save screenshots in the repo root (they will be untracked — user can delete after review)
- The dev server must be running (typically `http://localhost:3000`)
- Use `browser_snapshot` to find element refs when you need to click buttons (e.g., sidebar toggle)
- The greeting text on the home page changes randomly on each load — this is expected, not a regression
- If the page being tested is not the home page, navigate to the correct URL that exercises the migrated components
- Dark mode screenshots are critical for verifying that CSS variable tokens (`var(--mb-color-*)`) are used correctly — hardcoded colors will look wrong in dark mode

View File

@@ -0,0 +1,76 @@
---
name: serdes-workflow
description: Export content from a running Metabase instance, validate with checkers, edit YAML, and import back. Use when the user wants to export, import, or run the full serdes round-trip workflow.
---
# Serdes Workflow Skill
End-to-end workflow for exporting Metabase content as YAML, validating it, editing it, and importing it back.
## Prerequisites
All commands require the `:ee` alias since export, import, and checkers live in enterprise code.
You need `MB_DB_CONNECTION_URI` set to point at the target Metabase Postgres database when importing. The running dev instance's connection string is in the worktree environment (check the nREPL or startup output).
## Export
```bash
clojure -M:run:ee export /path/to/export-dir
```
Flags:
- `-c 123,456` - export only specific collection IDs
- `-f` - include field values
- `-e` - continue on error
This writes YAML files in the serdes directory layout:
```
databases/<db-name>/<db-name>.yaml
databases/<db-name>/schemas/<schema>/tables/<table>/<table>.yaml
databases/<db-name>/schemas/<schema>/tables/<table>/fields/<field>.yaml
collections/<eid>_<slug>/cards/<eid>_<slug>.yaml
```
## Validate
Always run both checkers after any modification. They skip full Metabase startup (~8s each).
### Structural checker (fast, schema-based)
```bash
clojure -M:run:ee --mode checker --checker structural --export /path/to/export-dir
```
Validates YAML shapes against Malli schemas. Catches typos, missing required fields, wrong types.
### Cards checker (query validation)
```bash
clojure -M:run:ee --mode checker --checker cards --export /path/to/export-dir
```
Validates that card queries resolve correctly against exported metadata (tables, fields, FKs). Uses MLv2 under the hood.
Additional cards checker flags:
- `--lenient` - fabricate metadata on demand (when no database schema files exist)
- `--manifest /path/to/manifest.yaml` - write manifest of all referenced entities (lenient mode only)
- `--output /path/to/results.edn` - write detailed results to file
## Edit YAML
See the **serdes-yaml-edit** skill for guidance on editing YAML files. The critical rule: **run both checkers after every edit before proceeding**.
## Import
```bash
MB_DB_CONNECTION_URI="postgres://user:pass@localhost:5432/metabase_dbname" \
clojure -M:run:ee import /path/to/export-dir -e
```
The `-e` flag continues on error. The `MB_DB_CONNECTION_URI` must point at the same Postgres database the running Metabase instance uses - without it, import defaults to H2 which is wrong.
## Full Demo Sequence
1. Export: `clojure -M:run:ee export /tmp/metabase-export`
2. Validate baseline: run both checkers
3. Edit YAML files (see serdes-yaml-edit skill)
4. Re-validate: run both checkers again
5. Import: `MB_DB_CONNECTION_URI=... clojure -M:run:ee import /tmp/metabase-export -e`
6. Verify in browser

View File

@@ -0,0 +1,243 @@
---
name: serdes-yaml-edit
description: Edit Metabase serdes YAML files (cards, dashboards, databases) with correct portable references and structural conventions. Use when modifying exported YAML content.
---
# Serdes YAML Edit Skill
## Golden Rule
**Run both checkers after every edit.** No exceptions. Do not batch multiple edits before validating.
```bash
clojure -M:run:ee --mode checker --checker structural --export /path/to/export-dir
clojure -M:run:ee --mode checker --checker cards --export /path/to/export-dir
```
If either checker fails, fix the issue before making further edits.
## Portable References
Serdes YAML uses portable references instead of integer IDs. This is the most important concept for editing.
### Database references
String name: `"Sample Database"`
### Table references
Array of `[database, schema, table]`:
```yaml
table_id:
- Sample Database
- PUBLIC
- ACCOUNTS
```
### Field references
Array of `[database, schema, table, field]`:
```yaml
id:
- Sample Database
- PUBLIC
- ACCOUNTS
- EMAIL
```
### Field refs in queries
```yaml
field_ref:
- field
- - Sample Database
- PUBLIC
- ACCOUNTS
- EMAIL
- null
```
The outer array is `[field, <field-path>, <options>]`. The options are usually `null`.
### Card references
Entity ID string (21 characters): `"Qk5TgsNx4ubXIUtsQmT8G"`
### Source table in queries
```yaml
dataset_query:
database: Sample Database
query:
source-table:
- Sample Database
- PUBLIC
- ACCOUNTS
type: query
```
## Safe Edits (low risk)
These rarely break validation:
- `name` - card/dashboard display name
- `description` - card/dashboard description
- `display` - visualization type (table, bar, line, pie, etc.)
- `visualization_settings` - chart configuration
- `archived` - true/false
- `collection_id` - move to a different collection (use entity_id of target collection, or null for root)
## Structural Edits (must match schema)
These must use valid portable refs and will be caught by the cards checker if wrong:
- `dataset_query` - the query definition
- `result_metadata` - column metadata (must match the query's output columns)
- `table_id` - must reference a table that exists in the export
- `database_id` - must reference a database that exists in the export
## Editing result_metadata
Each entry in `result_metadata` describes an output column. When changing a query's source table or fields, you must update result_metadata to match. Each field entry needs at minimum:
- `name` - column name (e.g., `EMAIL`)
- `base_type` - Metabase type (e.g., `type/Text`, `type/Integer`, `type/DateTime`)
- `display_name` - human-readable name
- `field_ref` - portable field reference
- `id` - portable field path
- `table_id` - portable table path
- `source` - usually `fields`
## Common Operations
### Rename a card
Change `name:` at the top level. Safe, no ref changes needed.
### Change a card's source table
Update all of these consistently:
1. `table_id` - top-level
2. `dataset_query.query.source-table` - in the query
3. `result_metadata` - every field entry's `id`, `field_ref`, and `table_id`
### Add a filter to a structured query
```yaml
dataset_query:
database: Sample Database
query:
source-table:
- Sample Database
- PUBLIC
- ORDERS
filter:
- ">"
- - field
- - Sample Database
- PUBLIC
- ORDERS
- TOTAL
- null
- 100
type: query
```
### Change visualization type
```yaml
display: bar # was: table
```
Valid types: `table`, `bar`, `line`, `pie`, `scalar`, `row`, `area`, `combo`, `scatter`, `funnel`, `map`, `pivot`, `progress`, `gauge`, `waterfall`
## Looking Up Valid References
The export directory IS the reference catalog. When you need to find the correct name for a database, table, or field, look it up directly from the export.
### List valid databases
```bash
ls databases/
```
Each entry is a database name (directory for serdes format, .yaml for concise format).
### List valid tables for a database
```bash
ls databases/<db-name>/schemas/<schema>/tables/
```
Example: `ls databases/Sample\ Database/schemas/PUBLIC/tables/` shows `ACCOUNTS`, `ORDERS`, `PRODUCTS`, etc.
### List valid fields for a table
```bash
ls databases/<db-name>/schemas/<schema>/tables/<table>/fields/
```
Example: `ls databases/Sample\ Database/schemas/PUBLIC/tables/PRODUCTS/fields/` shows `CATEGORY.yaml`, `TITLE.yaml`, `PRICE.yaml`, etc. The filename (minus `.yaml`) is the field name.
### Find card entity IDs
Card filenames encode the entity ID: `<entity-id>_<slug>.yaml`. The entity ID is the part before the first underscore (21 characters).
## Self-Healing Loop
When a checker reports errors, follow this loop:
1. **Read the error message** - it tells you what's wrong and often suggests the fix
2. **Look up the correct value** from the export directory (see "Looking Up Valid References" above)
3. **Fix the YAML**
4. **Re-run both checkers**
5. **Repeat until clean**
Do not guess at fixes. Always look up the correct value from the export.
## Understanding Checker Errors
### Structural checker errors
The structural checker validates YAML shape against Malli schemas. Common errors:
**Missing required key with typo suggestion:**
```
Missing required key 'name' - found 'nameee' which may be a typo
```
Fix: rename the typo'd key back to the correct name. The checker tells you what it expected and what it found.
**Wrong type:**
```
'archived' should be a boolean, got: "yes"
```
Fix: use `true`/`false`, not strings.
**Unknown key:**
```
Unknown key 'foobar' in card
```
Fix: remove the key, or check if it's a typo of a known key.
### Cards checker errors
The cards checker validates that queries resolve against exported metadata. Error types:
**UNRESOLVED REFERENCES:**
```
UNRESOLVED REFERENCES:
- field: Sample Database.PUBLIC.PRODUCTS.CATEGORYYY
```
The dotted path tells you exactly which reference failed. Look up the correct value:
- Last segment is the field name - check `databases/.../tables/<table>/fields/`
- Third segment is the table name - check `databases/.../schemas/<schema>/tables/`
- First segment is the database name - check `databases/`
**ERROR (query construction failed):**
```
ERROR: Error creating query from legacy query: Invalid output: ...
```
Usually means the query structure is malformed. This often follows unresolved references - fix the refs first, then re-check.
**ERROR (nil name):**
```
ERROR: Invalid output: {:name ["should be a string, got: nil"]}
```
The card is missing its `name` field (likely a structural issue that also affects the cards checker).
### Exit codes
- Exit 0: all checks passed
- Exit 1: one or more failures
## File Naming Convention
Card files: `collections/<collection-path>/cards/<entity-id>_<slug>.yaml`
The filename slug should match the card name (lowercase, underscored). If you rename a card, consider renaming the file to match, though import works on entity_id not filename.
## What NOT to Edit
- `entity_id` - this is the identity of the object, changing it creates a new object on import
- `serdes/meta` - internal serdes metadata, leave it alone
- `created_at` - timestamp, no reason to change
- `creator_id` - email reference, leave it
- `metabase_version` - informational, leave it

View File

@@ -39,7 +39,7 @@
;;
;; TODO (cam): most of these either have a ton of things that need to be fixed or cause issues
;; with [[metabase.lib.util.match/match]] and friends -- we need to write a custom hook for those macros so Kondo
;; with [[metabase.util.match/match]] and friends -- we need to write a custom hook for those macros so Kondo
;; can understand them.
#_:docstring-leading-trailing-whitespace
@@ -52,6 +52,7 @@
:redundant-ignore {:level :off} ; lots of false positives, see https://github.com/clj-kondo/clj-kondo/issues/2414
:unexpected-recur {:level :off} ; TODO (cam): I think we just need to tell it how to handle MBQL match and we can enable this?
:redundant-primitive-coercion {:level :off} ; there are many defensive boolean coercions, kondo looks too much into it
;;
;; globally-enabled custom linters
@@ -68,6 +69,7 @@
:metabase/modules {:level :warning} ; more config in `.clj-kondo/config/modules/config.edn`
:metabase/namespace-name {:level :warning}
:metabase/no-jsqlparser-imports {:level :warning}
:metabase/toucan-model-ns {:level :warning}
:metabase/require-shape-checker {:level :warning}
:metabase/tests-must-live-in-known-modules {:level :warning}
:metabase/validate-defendpoint-route-uses-kebab-case {:level :warning}
@@ -175,14 +177,18 @@
clojure.core.async/take!
clojure.core.async/to-chan!
clojure.core.async/to-chan!!
metabase.api.macros.defendpoint.tools-manifest/assert-optional-fields-nullable!
metabase.channel.core/send!
metabase.warehouses.models.database/assert-not-h2!
metabase.driver.sql-jdbc.execute/execute-prepared-statement!
metabase.notification.models/create-notification!
metabase.oauth-server.core/reset-provider!
metabase.pulse.send/send-pulse!
metabase-enterprise.transforms-python.python-runner-test/execute!
;; Workspace isolation functions are safe in parallel tests because they operate on
;; isolated, test-specific workspaces and databases that don't affect other tests
metabase-enterprise.workspaces.isolation/destroy-workspace-isolation!
metabase.lib-metric.ast.plan/validate-arithmetic-ast!
metabase-enterprise.workspaces.isolation/ensure-database-isolation!
metabase-enterprise.workspaces.test-util/create-ready-ws!
;; Safe in tests, where we typically use a private, temporary index per thread.
@@ -226,10 +232,6 @@
;; misc config for built-in linters
;;
:invalid-arity {:skip-args [metabase.lib.util.match/match
metabase.lib.util.match/match-lite
metabase.lib.util.match/match-many
metabase.lib.util.match/replace-lite]} ; TODO (cam): can we fix these?
:refer-all {:level :warning, :exclude [clojure.test]}
:unused-referred-var {:exclude {compojure.core [GET DELETE POST PUT]}}
@@ -245,25 +247,17 @@
instaparse.core/transform
(metabase.server.streaming-response/streaming-response)
(metabase.driver.druid.query-processor-test/druid-query-returning-rows)
(metabase.lib.util.match/match)
(metabase.lib.util.match/match-one)
(metabase.lib.util.match/match-lite)
(metabase.lib.util.match/match-many)
(metabase.lib.util.match/replace)
(metabase.lib.util.match/replace-in)
(metabase.lib.util.match/replace-lite)
(metabase.driver-api.core/match)
(metabase.util.match/match-one)
(metabase.util.match/match-many)
(metabase.util.match/replace-in)
(metabase.util.match/replace)
(metabase.driver-api.core/match-one)
(metabase.driver-api.core/match-lite)
(metabase.driver-api.core/match-many)
(metabase.driver-api.core/replace)
(metabase.driver-api.core/replace-in)
(metabase.driver-api.core/replace-lite)
(metabase.query-processor.middleware.cache-backend.interface/with-cached-results)
(metabase.util.regex/rx [opt])
(metabase.util/prog1 [<>])
(taoensso.nippy/extend-freeze)
(taoensso.nippy/extend-thaw)]}
(metabase.util/prog1 [<>])]}
;; TODO (Cam 8/22/25) -- what's the point of marking stuff deprecated if we don't have Kondo enforce it? We should
;; remove all of these and add individual ignores as needed.
@@ -310,9 +304,6 @@
metabase.lib.convert/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
metabase.lib.core/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
metabase.lib.equality/find-column-indexes-for-refs {:message "Use lib.equality/closest-matches-in-metadata or lib.equality/find-closest-matches-for-refs instead of lib.equality/find-column-indexes-for-refs"}
metabase.lib.util.match/match {:message "Use lib.util.match/match-many instead of lib.util.match/match."}
metabase.lib.util.match/match-one {:message "Use lib.util.match/match-lite instead of lib.util.match/match-one."}
metabase.lib.util.match/replace {:message "Use lib.util.match/replace-lite instead of lib.util.match/replace."}
metabase-enterprise.transforms.interface/execute! {:message "Use metabase-enterprise.transforms.execute/execute! instead of metabase-enterprise.transforms.interface/execute!"}
next.jdbc/active-tx? {:message "Use metabase.app-db.core/in-transaction? to see if we are in a transaction"}
toucan2.core/debug {:message "Don't commit code using t2/debug"}
@@ -346,7 +337,6 @@
clj-time.coerce time.coerce
clj-time.format time.format
clojure.core.async a
clojure.core.match clojure.core.match
clojure.data data
clojure.math.combinatorics math.combo
clojure.pprint pprint
@@ -469,14 +459,15 @@
metabase.premium-features.settings/define-premium-feature clojure.core/def
metabase.pulse.dashboard-subscription-test/with-dashboard-sub-for-card clojure.core/let
metabase.pulse.send-test/with-pulse-for-card clojure.core/let
metabase.query-processor.pipeline-queries-test/pmbql-query clojure.core/->
metabase.query-processor.pipeline-queries-test/run-pmbql-query clojure.core/->
metabase.query-processor.pipeline-queries-test/mbql5-query clojure.core/->
metabase.query-processor.pipeline-queries-test/run-mbql5-query clojure.core/->
metabase.query-processor.error-type/deferror clojure.core/def
metabase.query-processor.middleware.cache.impl/with-reducible-deserialized-results clojure.core/let
metabase.query-processor.middleware.process-userland-query-test/with-query-execution! clojure.core/let
metabase.query-processor.setup/with-qp-setup clojure.core/let
metabase.search.api-test/do-test-users clojure.core/let
metabase.settings.core/define-multi-setting clojure.core/def
metabase.api-scope.core/defscope clj-kondo.lint-as/def-catch-all
metabase.settings.core/defsetting clj-kondo.lint-as/def-catch-all
metabase.settings.models.setting.multi-setting/define-multi-setting clojure.core/def
metabase.settings.models.setting/defsetting clj-kondo.lint-as/def-catch-all
@@ -487,6 +478,7 @@
metabase.task.impl/defjob clojure.core/defn
metabase.test.data.interface/defdataset clojure.core/def
metabase.test.data.interface/defdataset-edn clojure.core/def
metabase.test.embedded-postgres.core/with-system clojure.core/let
metabase.test.gentest/defgentest clojure.test/deftest
metabase.test/defdataset clojure.core/def
metabase.test/let-url clojure.core/let
@@ -552,6 +544,7 @@
metabase-enterprise.cache.config-test/with-temp-persist-models hooks.common/with-seven-bindings
metabase-enterprise.dependencies.api-test/with-dependents-test! hooks.common/with-two-bindings
metabase-enterprise.metabot-v3.tools.dependencies-test/with-dependent-transforms! hooks.common/with-two-bindings
metabase-enterprise.metabot.tools.dependencies-test/with-dependent-transforms! hooks.common/with-two-bindings
metabase-enterprise.semantic-search.db.connection/with-migrate-tx hooks.common/let-one-with-optional-value
metabase-enterprise.serialization.api-test/with-serialization-test-data! hooks.common/with-three-bindings
metabase-enterprise.serialization.test-util/with-temp-dpc hooks.toucan2.tools.with-temp/with-temp
@@ -559,7 +552,7 @@
metabase.api.common.internal-test/with-jetty-server hooks.common/with-one-binding
metabase.api.macros/defendpoint hooks.metabase.api.macros/defendpoint
metabase.app-db.schema-migrations-test.impl/test-migrations hooks.metabase.app-db.schema-migrations-test.impl/test-migrations
metabase.channel.render.js.svg/with-static-viz-context hooks.common/with-one-top-level-binding
metabase.channel.render.js.pool/with-static-viz-context hooks.common/with-one-top-level-binding
metabase.channel.template.handlebars-helper/defhelper hooks.metabase.channel.template.handlebars-helper/defhelper
metabase.channel.template.handlebars-test/with-temp-template! hooks.common/with-vec-first-binding
metabase.classloader.core/require hooks.clojure.core.require/lint-require
@@ -586,6 +579,8 @@
metabase.lib.schema.mbql-clause/define-catn-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
metabase.lib.schema.mbql-clause/define-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
metabase.lib.schema.mbql-clause/define-tuple-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
methodical.core/defmethod hooks.metabase.toucan.table-name/lint-defmethod
methodical.macros/defmethod hooks.metabase.toucan.table-name/lint-defmethod
metabase.lib.test-util.macros/$ids hooks.metabase.test.data/$ids
metabase.lib.test-util.macros/mbql-query hooks.metabase.test.data/mbql-query
metabase.lib.test-util.macros/mbql-5-query hooks.metabase.test.data/mbql-query
@@ -683,9 +678,15 @@
metabase.util.log/warn hooks.metabase.util.log/info
metabase.util.log/warnf hooks.metabase.util.log/infof
metabase.util.malli.registry/def hooks.metabase.util.malli.registry/def
metabase.util.match/match-one hooks.metabase.util.match/match-one
metabase.util.match/match-many hooks.metabase.util.match/match-many
metabase.util.match/replace hooks.metabase.util.match/replace
metabase.util.match/replace-in hooks.metabase.util.match/replace-in
metabase.util/case-enum hooks.metabase.util/case-enum
metabase.util/format-color hooks.metabase.util/format-color
metabase.xrays.api.automagic-dashboards-test/with-indexed-model! hooks.metabase.xrays.api.automagic-dashboards-test/with-indexed-model!}
metabase.xrays.api.automagic-dashboards-test/with-indexed-model! hooks.metabase.xrays.api.automagic-dashboards-test/with-indexed-model!
taoensso.nippy/extend-freeze hooks.taoensso.nippy/extend-freeze
taoensso.nippy/extend-thaw hooks.taoensso.nippy/extend-thaw}
:macroexpand
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
@@ -793,7 +794,7 @@
:private-call {:level :off}
:metabase/defsetting-must-specify-export {:level :off}
:metabase/defsetting-namespace {:level :off}
:metabase/test-helpers-use-non-thread-safe-functions {:level :warning} ; more config in `.clj-kondo/config/test-helpers/config.edn
:metabase/test-helpers-use-non-thread-safe-functions {:level :warning} ; more config in `.clj-kondo/config/test-helpers/config.edn`
:discouraged-var
{metabase.driver/database-supports? {:level :off}

File diff suppressed because it is too large Load Diff

View File

@@ -110,6 +110,7 @@
metabase.settings.models.setting-test/test-user-local-allowed-setting!
metabase.settings.models.setting-test/test-user-local-only-setting!
metabase.settings.models.setting.cache/restore-cache!
metabase.settings.models.setting/log-deprecated-env-var-usage!
metabase.settings.models.setting/set!
metabase.settings.models.setting/validate-settings-formatting!
metabase.setup.core/create-token!

View File

@@ -500,8 +500,8 @@
(and resolved (:ns resolved))
(symbol (name (:ns resolved)) (name (:name resolved)))
;; if it wasn't resolved but is still qualified it's probably using the full namespace name rather than an
;; alias.
;; if it wasn't resolved but is still qualified it's probably using the full namespace name rather than an
;; alias.
(qualified-symbol? sexpr)
sexpr)))))
;; some symbols like `*count/Integer` aren't resolvable.

View File

@@ -28,8 +28,8 @@
(mapcat (fn [[setting-name v]]
(concat
[(with-meta (hooks/token-node '_) (meta setting-name)) v]
;; if the setting name is namespace-qualified add a `_`
;; entry for it too.
;; if the setting name is namespace-qualified add a `_`
;; entry for it too.
(when (namespaced-symbol-node? setting-name)
[(with-meta (hooks/token-node '_) (meta setting-name)) setting-name]))))
(partition 2 (:children bindings))))

View File

@@ -0,0 +1,43 @@
(ns hooks.metabase.toucan.table-name
"Lint that toucan2 model namespaces (those defining `t2/table-name`) live
under `metabase[-enterprise]/<module>/models[/…]`.
This wraps the methodical `defmethod` hook: we delegate to
`hooks.methodical.macros/defmethod` for the actual analysis (so arg bindings
etc. still work), and additionally check the file location when the
multimethod being defined is `t2/table-name`."
(:require
[clj-kondo.hooks-api :as hooks]))
(defn- models-file? [filename]
(boolean
(and filename
(re-find #"/(?:metabase|metabase_enterprise)/[^/]+/models(?:/|\.clj[cs]?$)"
filename))))
(defn- table-name-dispatch? [sym]
(and (symbol? sym)
(= (name sym) "table-name")
(#{"t2" "toucan2.core"} (namespace sym))))
(defn lint-defmethod
"Hook registered at the project level on `methodical.core/defmethod` (and
`methodical.macros/defmethod`). Performs the table-name check, then
delegates to the vendored methodical hook so kondo's normal analysis of the
defmethod body still runs."
[{:keys [filename node] :as input}]
(let [[_defmethod multimethod & _args] (:children node)
dispatch (when multimethod
(try (hooks/sexpr multimethod) (catch Exception _ nil)))]
(when (and (table-name-dispatch? dispatch)
(not (models-file? filename)))
(hooks/reg-finding!
(assoc (meta multimethod)
:message (format "Toucan2 `table-name` defmethods must live under `<module>/models[/…]` (got %s)"
filename)
:type :metabase/toucan-model-ns))))
;; Delegate to the vendored methodical defmethod hook so kondo's normal
;; analysis of the defmethod body still runs. Resolved lazily because
;; `hooks.methodical.macros` isn't on the classpath during `.clj-kondo/test`
;; unit-test runs.
((requiring-resolve 'hooks.methodical.macros/defmethod) input))

View File

@@ -13,23 +13,23 @@
(let [c (first chars)]
(if (= c \')
(cond
;; Doubled quotes: skip both
;; Doubled quotes: skip both
(and (next chars) (= (second chars) \'))
(recur (nthrest chars 2))
;; Placeholder escape. We allow for intentional escaping, like either '{0}` or '{{`, but not 'arbitrary
;; strings' So basically, just require the *next* character to be a `{`, find the next single-quote, and
;; continue on from there.
;;
;; Technically a string like "foo bar's happy but i'm not" is totally fine, but we'll say it's invalid
;; because... that inner string literal is probably not what we actually want. So since we've never done
;; this intentionally as far as I can tell, let's just remove the possibility to avoid ambiguity.
;; Placeholder escape. We allow for intentional escaping, like either '{0}` or '{{`, but not 'arbitrary
;; strings' So basically, just require the *next* character to be a `{`, find the next single-quote, and
;; continue on from there.
;;
;; Technically a string like "foo bar's happy but i'm not" is totally fine, but we'll say it's invalid
;; because... that inner string literal is probably not what we actually want. So since we've never done
;; this intentionally as far as I can tell, let's just remove the possibility to avoid ambiguity.
(= (second chars) \{)
(let [at-closing (drop-while #(not= \' %) (next chars))]
(if (seq at-closing)
(recur (next at-closing))
false))
;; Unpaired single quote: invalid
;; Unpaired single quote: invalid
:else
false)
(recur (rest chars)))))))

View File

@@ -0,0 +1,132 @@
(ns hooks.metabase.util.match
(:require [clj-kondo.hooks-api :as api]))
(defn- token-node? [node token-name]
(and (api/token-node? node)
(= (str (:value node)) token-name)))
(defn- keyword-node? [node k]
(and (api/keyword-node? node)
(= (:k node) k)))
(defn- binding-symbol?
"True when the token is a plain symbol that should be treated as a binding
(not a keyword, not a boolean literal, not nil, not a number, not `_`)."
[node]
(and (api/token-node? node)
(let [v (:value node)]
(and (symbol? v)
(not (contains? '#{_ nil true false} v))))))
;; To establish fake let bindings, we need to bind them to something. This "something" has to be vague enough to trick
;; kondo into not looking too much into it. E.g., if we bind everything to `nil`, Kondo linters will get fired up.
(def opaque-node (api/list-node (list (api/token-node 'rand-nth) (api/list-node []))))
(defn collect-bindings
"Return a list of token nodes (symbols) introduced as bindings by `pattern`."
[pattern]
(cond
(binding-symbol? pattern)
[[pattern opaque-node]]
;; Lists: (:or ...), (:and ...), (sym :guard pred ...), or a quoted symbol.
(api/list-node? pattern)
(let [[head & rst] (:children pattern)]
(cond (or (keyword-node? head :or)
(keyword-node? head :and))
(mapcat collect-bindings rst)
(api/token-node? head)
(cond-> []
(binding-symbol? head) (conj [head opaque-node])
(keyword-node? (first rst) :guard) (conj [(api/token-node '_) (second rst)]))
;; anything else (e.g. a function call used as predicate) — no bindings
:else []))
(api/vector-node? pattern)
(mapcat (fn [node]
(when-not (token-node? node "&")
(collect-bindings node)))
(:children pattern))
;; Maps: keys are literal selectors, values are sub-patterns.
;; Special value-symbols &truthy and _ are NOT user bindings.
(api/map-node? pattern)
(let [children (:children pattern)
pairs (partition 2 children)]
(mapcat (fn [[_k v]]
(when-not (or (token-node? v "&truthy")
(token-node? v "_"))
(collect-bindings v)))
pairs))
;; For sets, make sure to use them because they may reference some of the outer local bindings, so if we don't
;; include them, Kondo will complain about those bindings being unused.
(api/set-node? pattern)
[[(api/token-node '_) pattern]]
;; Anything else — no bindings.
:else []))
(defn- dedupe-bindings
"Prevent Kondo complaining that we have multiple fake bindings."
[pairs]
(loop [[[sym v] & r] pairs, seen #{}, result []]
(cond (nil? sym) result
(and (seen sym) (not (token-node? sym "_"))) (recur r seen result)
:else (recur r (conj seen sym) (conj result sym v)))))
(defn match-one
"clj-kondo :analyze-call hook for match-one.
Rewrites
(match-one value
pat1 ret1
pat2 ret2
...)
into a `let` form that declares all binding symbols introduced by the patterns, so clj-kondo knows they are
legitimate and won't warn about unresolved/unused symbols:
(let [<all-bindings> (rand-nth []) ...]
(or ret1 ret2 ...))"
[{:keys [node]}]
(let [[_ value & clauses] (:children node)]
(when (odd? (count clauses))
(api/reg-finding!
{:message "match-one/match-many/replace requires an even number of pattern/result pairs"
:type :metabase/match-odd-clauses
:row (:row node)
:col (:col node)
:filename (-> node meta :filename)}))
(let [pairs (partition 2 clauses)
all-bindings (->> pairs
(mapcat (fn [[pat _ret]] (collect-bindings pat)))
dedupe-bindings)
special-bindings [(api/token-node '_) value
(api/token-node '&match) opaque-node
(api/token-node '&parents) opaque-node
(api/token-node '&recur) opaque-node
;; Pretend to use &match, &parents, and &recur so that Kondo doesn't complain about them
;; if they are not used by the user code.
(api/token-node '_) (api/token-node '&match)
(api/token-node '_) (api/token-node '&parents)
(api/token-node '_) (api/token-node '&recur)]
binding-nodes (into special-bindings all-bindings)
result-exprs (map second pairs)
new-node (api/list-node
(list
(api/token-node 'let)
(api/vector-node binding-nodes)
(api/list-node
(concat [(api/token-node 'clojure.core/or)] result-exprs [opaque-node]))))]
{:node new-node})))
(def match-many match-one)
(def replace match-one)
(defn replace-in [context]
;; Everything is the same as in match-one, just drop the middle argument.
(match-one (update-in context [:node :children] (fn [[sym value path & clauses]] (list* sym value clauses)))))

View File

@@ -0,0 +1,22 @@
(ns hooks.taoensso.nippy
(:require [clj-kondo.hooks-api :as api]))
(defn extend-thaw
[{:keys [node]}]
(let [[_ _ arglist & body] (:children node)]
{:node (with-meta (api/list-node
(list*
(api/token-node 'fn)
arglist
body))
(meta node))}))
(defn extend-freeze
[{:keys [node]}]
(let [[_ _ _ arglist & body] (:children node)]
{:node (with-meta (api/list-node
(list*
(api/token-node 'fn)
arglist
body))
(meta node))}))

View File

@@ -0,0 +1,46 @@
(ns hooks.metabase.toucan.table-name-test
(:require
[clj-kondo.hooks-api :as hooks]
[clj-kondo.impl.utils]
[clojure.test :refer :all]
[hooks.metabase.toucan.table-name :as toucan.table-name]))
;; `hooks.methodical.macros` isn't on the classpath under `.clj-kondo/test`,
;; so stub it out — `lint-defmethod` delegates to it via `requiring-resolve`.
;; If it ever becomes available, warn so we remember to delete this stub.
(if (find-ns 'hooks.methodical.macros)
(println (str "WARNING: hooks.methodical.macros is now on the test classpath; "
"the stub in " *ns* " is no longer needed and can be removed."))
(do (create-ns 'hooks.methodical.macros)
(intern 'hooks.methodical.macros 'defmethod (fn [input] input))))
(defn- lint-defmethod [form filename]
(binding [clj-kondo.impl.utils/*ctx* {:config {:linters {:metabase/toucan-model-ns {:level :warning}}}
:ignores (atom nil)
:findings (atom [])
:namespaces (atom {})}]
(toucan.table-name/lint-defmethod {:node (hooks/parse-string (pr-str form))
:filename filename})
@(:findings clj-kondo.impl.utils/*ctx*)))
(deftest ^:parallel table-name-must-live-in-models-namespace-test
(testing "defmethod t2/table-name outside a models namespace is flagged"
(is (=? [{:type :metabase/toucan-model-ns}]
(lint-defmethod '(defmethod t2/table-name :model/Foo [_] :foo)
"/repo/src/metabase/foo/core.clj"))))
(testing "defmethod t2/table-name inside <module>/models.clj is allowed"
(is (empty? (lint-defmethod '(defmethod t2/table-name :model/Foo [_] :foo)
"/repo/src/metabase/foo/models.clj"))))
(testing "defmethod t2/table-name inside <module>/models/<model>.clj is allowed"
(is (empty? (lint-defmethod '(defmethod t2/table-name :model/Foo [_] :foo)
"/repo/src/metabase/foo/models/bar.clj"))))
(testing "enterprise modules are also allowed"
(is (empty? (lint-defmethod '(defmethod t2/table-name :model/Foo [_] :foo)
"/repo/enterprise/backend/src/metabase_enterprise/foo/models.clj"))))
(testing "toucan2.core/table-name is also recognized"
(is (=? [{:type :metabase/toucan-model-ns}]
(lint-defmethod '(defmethod toucan2.core/table-name :model/Foo [_] :foo)
"/repo/src/metabase/foo/core.clj"))))
(testing "unrelated defmethods are ignored"
(is (empty? (lint-defmethod '(defmethod t2/primary-keys :model/Foo [_] [:id])
"/repo/src/metabase/foo/core.clj")))))

View File

@@ -0,0 +1,59 @@
(ns hooks.metabase.util.match-test
(:require
[clj-kondo.hooks-api :as hooks]
[clj-kondo.impl.utils]
[clojure.test :refer :all]
[hooks.metabase.util.match :as sut]))
(defn- expand [src]
(-> (sut/match-one {:node (hooks/parse-string (pr-str src))})
:node
hooks/sexpr))
(defn- lint [src]
(binding [clj-kondo.impl.utils/*ctx* {:config {:linters {:metabase/match-odd-clauses {:level :warning}}}
:ignores (atom nil)
:findings (atom [])
:namespaces (atom {})}]
(expand src)
@(:findings clj-kondo.impl.utils/*ctx*)))
(deftest odd-clauses-triggers-warning-test
(is (=? [{:type :metabase/match-odd-clauses}]
(lint '(metabase.util.match/match-one [] [a b c])))))
(deftest expansion-test
(testing "simple expansion example"
(is (= '(let [_ []
&match (rand-nth ())
&parents (rand-nth ())
&recur (rand-nth ())
_ &match
_ &parents
_ &recur
a (rand-nth ())
b (rand-nth ())
c (rand-nth ())]
(clojure.core/or (+ a b) (rand-nth ())))
(expand '(match-one [] [a b c] (+ a b))))))
(testing "complicated expansion example that uses most match-one macro features, and also binding deduplication by the rewriter"
(is (= '(let [_ []
&match (rand-nth ())
&parents (rand-nth ())
&recur (rand-nth ())
_ &match
_ &parents
_ &recur
val1 (rand-nth ())
a (rand-nth ())
_ #{maybe important}
rst (rand-nth ())
x (rand-nth ())
_ (= x 3)
b (rand-nth ())]
(clojure.core/or (+ a val1) (rand-nth ())))
(expand '(match-one []
(:or [{:key1 val1, :key2 &truthy, :key3 _} _ a #{maybe important} & rst]
(:and (x :guard (= x 3))
[a b]))
(+ a val1)))))))

27
.clj-surgeon.edn Normal file
View File

@@ -0,0 +1,27 @@
{:aliases
{;; defendpoint shape: (api.macros/defendpoint METHOD URL DOCSTRING? [args] body)
;; No name slot — synthesize :route [METHOD "path"] from first 2 args.
"defendpoint"
{:fields {:method (fn [z] (some-> z z/down z/right z/sexpr name str/upper-case))
:path (fn [z] (-> z z/down z/right z/right z/sexpr))}}
;; defenterprise shape:
;; (defenterprise NAME DOCSTRING? EE-NAMESPACE [args] body)
"defenterprise"
{:fields {:name ->defn-name
:ee-namespace (fn [z]
;; Skip name + optional docstring; the next
;; symbol is the EE namespace.
(let [name-z (-> z z/down z/right)
after-doc (z/right name-z)
candidate (if (string? (try (z/sexpr after-doc)
(catch Exception _ nil)))
(z/right after-doc)
after-doc)]
(try (let [v (z/sexpr candidate)]
(when (symbol? v) v))
(catch Exception _ nil))))
:arglist ->defn-arg-list}}
"defsetting"
{:fields {:name ->defn-name}}}}

View File

@@ -1,12 +1,10 @@
;; Cljfmt config. See https://github.com/weavejester/cljfmt#formatting-options
{:sort-ns-references?
true
:function-arguments-indentation
:community
:parallel?
true
{:function-arguments-indentation :community
:indent-line-comments? true
:normalize-newlines-at-file-end? true
:parallel? true
:remove-blank-lines-in-forms? true
:sort-ns-references? true
:paths
["src"
@@ -15,11 +13,63 @@
"bin/build"
"bin/lint-migrations-file"
"bin/release-list"
#_"mage/src"
#_"mage/test"
"modules/drivers"
".clj-kondo/src"
".clj-kondo/test"
#_"dev"]
;; a map of symbols that tell cljfmt which forms are allowed to have blank lines inside of them. The value may be
;; either `:all`, which means blank lines are allowed between all elements in the form, e.g. `{cond :all}` to allow
;; blank lines between any of the elements inside cond; or it may be a set of element indexes that are allowed to
;; have blank lines, e.g. `{let #{0}}`, to allow blank lines in the binding of a `let` form.
:extra-blank-line-forms
{are :all
case :all
cond-> :all
cond->> :all
condp :all
clojure.spec.alpha/or :all
clojure.test/are :all
metabase.driver-api.core/match-many :all
metabase.driver-api.core/match-one :all
metabase.driver-api.core/replace :all
metabase.driver-api.core/replace-in :all
metabase.lib.drill-thru.test-util/test-drill-variants-with-merged-args :all
metabase.lib.drill-thru.test-util/test-returns-drill :all
metabase.test/with-temp #{0}
metabase.util.match/match-many :all
metabase.util.match/match-one :all
metabase.util.match/replace :all
metabase.util.match/replace-in :all
;; TODO (Cam 2026-05-21) I'm not really sure about `defprotocol`, `deftype`, `defrecord`, `proxy`, `reify`, etc., I
;; think idiomatically these should probably not have blank lines but we can worry about that later
defprotocol :all
defrecord :all
deftype :all
extend :all
extend-protocol :all
potemkin.types/def-map-type :all
potemkin.types/defprotocol+ :all
potemkin.types/defrecord+ :all
potemkin.types/deftype+ :all
potemkin/def-map-type :all
potemkin/defprotocol+ :all
potemkin/defrecord+ :all
potemkin/deftype+ :all
proxy :all
reify :all
;; TODO (Cam 2026-05-21) -- save these for a follow-on PR
defmacro :all
defmethod :all
defn :all
defn- :all
fn :all
metabase.util.malli/defmethod :all
metabase.util.malli/defn :all
metabase.util.malli/defn- :all}
;; See https://github.com/weavejester/cljfmt/blob/master/cljfmt/resources/cljfmt/indents/clojure.clj for the default
;; definitions
;;
@@ -37,13 +87,12 @@
;; `:inner` indentation is like `:defn` indentation. `[[:inner 0]]` (default for `defn`) means all arguments at depth
;; zero get indented 2 spaces if not on the first line regardless of number of elements on the first line.
:extra-indents
{;; clojure.core stuff
{ ;; clojure.core stuff
fn* [[:inner 0]]
let* [[:block 1]]
with-meta [[:default]]
;; library stuff. Libraries that specify their own `:style/indent` specs are included in the section after this
;; since it is generated automatically.
clojure.core.match/match [[:block 1]]
clojure.test.check.properties/for-all [[:block 1]]
clojure.test.check.properties/for-all* [[:block 1]]
clojure.test.check/quick-check [[:block 1]]
@@ -118,12 +167,9 @@
metabase.collections.models.collection-test/with-personal-and-impersonal-collections [[:block 1]]
metabase.config/build-type-case [[:block 0]]
metabase.dashboards.models.dashboard-test/with-dash-in-collection [[:inner 0]]
metabase.driver-api.core/match [[:inner 0]]
metabase.driver-api.core/match-one [[:inner 0]]
metabase.driver-api.core/match-lite [[:inner 0]]
metabase.driver-api.core/match-many [[:inner 0]]
metabase.driver-api.core/replace [[:inner 0]]
metabase.driver-api.core/replace-lite [[:inner 0]]
metabase.driver-api.core/replace-in [[:inner 0]]
metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]]
metabase.driver.druid.client/DELETE [[:default]]
@@ -156,13 +202,10 @@
metabase.lib.test-util.macros/$ids [[:inner 0]]
metabase.lib.test-util.macros/mbql-5-query [[:inner 0]]
metabase.lib.test-util.macros/mbql-query [[:inner 0]]
metabase.lib.util.match/match [[:inner 0]]
metabase.lib.util.match/match-one [[:inner 0]]
metabase.lib.util.match/match-lite [[:inner 0]]
metabase.lib.util.match/match-many [[:inner 0]]
metabase.lib.util.match/replace [[:inner 0]]
metabase.lib.util.match/replace-lite [[:inner 0]]
metabase.lib.util.match/replace-in [[:inner 0]]
metabase.util.match/match-one [[:inner 0]]
metabase.util.match/match-many [[:inner 0]]
metabase.util.match/replace [[:inner 0]]
metabase.util.match/replace-in [[:inner 0]]
metabase.models.interface/define-batched-hydration-method [[:inner 0]]
metabase.models.interface/define-simple-hydration-method [[:inner 0]]
metabase.parameters.chain-filter-test/with-clean-field-values-for-field [[:block 1]]
@@ -188,8 +231,8 @@
metabase.query-processor.error-type/deferror [[:block 1]]
metabase.query-processor.middleware.cache-backend.interface/with-cached-results [[:block 4]]
metabase.query-processor.middleware.process-userland-query-test/with-query-execution [[:block 1]]
metabase.query-processor.pipeline-queries-test/pmbql-query [[:block 1]]
metabase.query-processor.pipeline-queries-test/run-pmbql-query [[:block 1]]
metabase.query-processor.pipeline-queries-test/mbql5-query [[:block 1]]
metabase.query-processor.pipeline-queries-test/run-mbql5-query [[:block 1]]
metabase.query-processor.store/cached [[:block 1]]
metabase.query-processor.store/with-metadata-provider [[:inner 0]]
metabase.query-processor.streaming/streaming-response [[:block 1]]

28
.coderabbit.yaml Normal file
View File

@@ -0,0 +1,28 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
reviews:
high_level_summary: false
review_status: false
auto_review:
enabled: false
drafts: true
labels:
- "coderabbit"
path_filters:
- "!**/*.snap"
- "!**/*.po"
- "!**/bun.lock"
- "!**/package-lock.json"
tools:
eslint:
enabled: false
knowledge_base:
code_guidelines:
filePatterns:
- ".claude/skills/clojure-review/SKILL.md"
- ".claude/skills/typescript-review/SKILL.md"
- ".claude/skills/e2e-test-review/SKILL.md"
- ".claude/skills/docs-review/SKILL.md"

10
.config/wt.toml Normal file
View File

@@ -0,0 +1,10 @@
# worktrunk project config — https://worktrunk.dev
# Hooks run when switching, creating, merging, or removing worktrees.
# Setup tasks for newly-created worktrees. Run once, blocking, in the
# new worktree's cwd before post-start background tasks kick off.
[pre-start]
# Trust mise config so it loads the project's tool versions without prompting.
trust-mise = "mise trust"
# Install JS deps so husky git hooks get wired up in the new worktree.
bun-install = "bun install"

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/vscode/devcontainers/java:21
FROM mcr.microsoft.com/vscode/devcontainers/java:25
# Set up nodesource, install node, bun, fontconfig for static viz, rlwrap for dev ergonomics

View File

@@ -41,11 +41,6 @@
(eval . (put-clojure-indent 'c/step 1))
(eval . (put-clojure-indent 'impl/test-migrations 2))
(eval . (put-clojure-indent 'let-404 0))
(eval . (put-clojure-indent 'lib.schema.match/match '(:defn)))
(eval . (put-clojure-indent 'lib.schema.match/match-one '(:defn)))
(eval . (put-clojure-indent 'lib.schema.match/replace '(:defn)))
(eval . (put-clojure-indent 'lib.schema.match/replace-in '(:defn)))
(eval . (put-clojure-indent 'lib.util.match/replace '(:defn)))
(eval . (put-clojure-indent 'macros/case 0))
(eval . (put-clojure-indent 'match 1))
(eval . (put-clojure-indent 'mt/dataset 1))
@@ -87,6 +82,8 @@
(cider-default-cljs-repl . shadow-select)
(cider-shadow-default-options . "app"))
(makefile-mode (indent-tabs-mode . t))
("bin"
(clojure-mode
(cider-clojure-cli-aliases . "dev:drivers:build:build-dev"))))

View File

@@ -1,5 +1,6 @@
.git
.babel_cache
target/*
target
.circleci
.cpcache
.devcontainer
@@ -15,6 +16,7 @@ test_config/*
test_modules/*
test_resources/*
node_modules
**/node_modules
**metabase.jar
*.db

12
.github/CODEOWNERS vendored
View File

@@ -12,12 +12,12 @@ enterprise/backend/src/metabase_enterprise/dependencies @metabase/graphy
enterprise/frontend/src/metabase-enterprise/dependencies @metabase/graphy
# @metabase/embedding
enterprise/frontend/src/embedding-sdk-package @metabase/embedding
enterprise/frontend/src/embedding-sdk-bundle @metabase/embedding
enterprise/frontend/src/embedding-sdk-shared @metabase/embedding
frontend/src/metabase/embedding-sdk @metabase/embedding
frontend/src/metabase/public/containers/PublicOrEmbeddedQuestion @metabase/embedding
frontend/src/metabase/public/containers/PublicOrEmbeddedDashboard @metabase/embedding
# enterprise/frontend/src/embedding-sdk-package @metabase/embedding
# enterprise/frontend/src/embedding-sdk-bundle @metabase/embedding
# enterprise/frontend/src/embedding-sdk-shared @metabase/embedding
# frontend/src/metabase/embedding-sdk @metabase/embedding
# frontend/src/metabase/public/containers/PublicOrEmbeddedQuestion @metabase/embedding
# frontend/src/metabase/public/containers/PublicOrEmbeddedDashboard @metabase/embedding
# @metabase/tech-writers
docs @metabase/tech-writers

View File

@@ -4,7 +4,7 @@ description: |
inputs:
java-version:
required: true
default: "21"
default: "25"
clojure-version:
required: true
default: "1.12.0.1488"

View File

@@ -16,5 +16,5 @@ runs:
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.7"
bun-version-file: 'package.json'
token: ${{ github.token }}

View File

@@ -6,7 +6,7 @@ runs:
steps:
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.7"
bun-version-file: 'package.json'
token: ${{ github.token }}
- name: Build release scripts

View File

@@ -31,24 +31,25 @@ runs:
- name: Prepare frontend
uses: ./.github/actions/prepare-frontend
- name: Download current uberjar
uses: actions/download-artifact@v4
uses: ./.github/actions/fetch-artifact
with:
name: metabase-${{ inputs.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
path: temp/current-uberjar
commit: ${{ github.event.pull_request.head.sha || github.sha }}
edition: ${{ inputs.edition }}
extract-path: temp/current-uberjar
- name: Extract embedding-sdk bundles from current uberjar
shell: bash
run: |
# Try new layout first (app/embedding-sdk/legacy/), fall back to old layout (app/embedding-sdk.js)
unzip -j temp/current-uberjar/target/uberjar/metabase.jar \
unzip -j temp/current-uberjar/metabase.jar \
"frontend_client/app/embedding-sdk/legacy/embedding-sdk.js" \
-d temp/current-bundle 2>/dev/null || \
unzip -j temp/current-uberjar/target/uberjar/metabase.jar \
unzip -j temp/current-uberjar/metabase.jar \
"frontend_client/app/embedding-sdk.js" \
-d temp/current-bundle
# Extract chunks directory if it exists (new layout only)
mkdir -p temp/current-chunks
unzip -j temp/current-uberjar/target/uberjar/metabase.jar \
unzip -j temp/current-uberjar/metabase.jar \
"frontend_client/app/embedding-sdk/chunks/*" \
-d temp/current-chunks 2>/dev/null || true
- name: Checkout base ref

View File

@@ -7,9 +7,7 @@ inputs:
output-name:
required: true
default: test
aws-access-key-id:
required: true
aws-secret-access-key:
role-to-assume:
required: true
aws-region:
required: true
@@ -30,8 +28,7 @@ runs:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ inputs.aws-access-key-id }}
aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
role-to-assume: ${{ inputs.role-to-assume }}
aws-region: ${{ inputs.aws-region }}
- name: zip test results
env:
@@ -50,13 +47,18 @@ runs:
aws s3 cp ${OUTPUT_FILE}.zip s3://$BUCKET/$DATE/$GITHUB_RUN_ID/$GITHUB_RUN_ATTEMPT/
- name: Upload results to Trunk
if: always() # Important! This step must always run because it controls job's exit code.
# Important!
# 1. This step must always run because it controls job's exit code.
# 2. There's no point in running this outside the organization
if: always() && github.repository_owner == 'metabase'
uses: trunk-io/analytics-uploader@main
env:
TRUNK_REPO_URL: git@github.com:metabase/metabase.git
with:
junit-paths: ${{ inputs.input-path }}
variant: ${{ inputs.variant || inputs.output-name }}
org-slug: metabase
dry-run: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == true }} # quarantine, but do not upload results
dry-run: ${{ (github.event_name == 'pull_request' && github.event.pull_request.draft == true) || github.repository != 'metabase/metabase' }} # quarantine, but do not upload results
token: ${{ inputs.trunk-api-token }}
use-cache: true
previous-step-outcome: ${{ inputs.previous-step-outcome }}

View File

@@ -200,6 +200,10 @@ cljs:
- ".github/workflows/cljs.yml"
- "shadow-cljs.edn"
serialization_baseline:
- ".github/workflows/serialization.yml"
- "test_resources/serialization_baseline/**"
openapi:
- ".github/workflows/openapi.yml"
- "src/metabase/api/**"

View File

@@ -8,7 +8,7 @@ const specialTestConfigs = [
{
name: "oss-subset",
edition: "oss",
tags: "@OSS @smoke+-@EE",
tags: "@OSS @prerelease+-@EE",
specs: DEFAULT_SPEC_PATTERN,
},
{ name: "mongo", tags: "@mongo", specs: DEFAULT_SPEC_PATTERN },
@@ -52,17 +52,21 @@ function buildMatrix(options, inputSpecs, inputChunks) {
);
}
const regularTests = new Array(regularChunks).fill(1).map((files, index) => ({
name: `e2e-group-${index + 1}`,
// works when specs less than 5, otherwise seems all chunks will contain
// same specs
...(!isDefaultSpecPattern && {
specs: inputSpecs
.split(",")
.slice(SPECS_PER_CHUNK * index, SPECS_PER_CHUNK * (index + 1))
.join(","),
}),
}));
const regularTests = Array.from({ length: regularChunks }, (_, index) => {
const paddedIndex = String(index + 1).padStart(2, "0");
return {
name: `e2e-group-${paddedIndex}`,
// works when specs less than 5, otherwise seems all chunks will contain
// same specs
...(!isDefaultSpecPattern && {
specs: inputSpecs
.split(",")
.slice(SPECS_PER_CHUNK * index, SPECS_PER_CHUNK * (index + 1))
.join(","),
}),
};
});
const testSets = isDefaultSpecPattern
? regularTests.concat(specialTestConfigs)

View File

@@ -3,7 +3,7 @@ const { buildMatrix } = require("./build-e2e-matrix");
describe("buildMatrix", () => {
const options = {
java: "11",
defaultRunner: "ubuntu-22.04",
defaultRunner: "${{ vars.DEFAULT_RUNNER_KEY }}",
};
const testBuildMatrix = (entry, chunks) =>
buildMatrix(options, entry, chunks);
@@ -34,7 +34,9 @@ describe("buildMatrix", () => {
);
regularTests.forEach((test, index) => {
expect(test.name).toBe(`e2e-group-${index + 1}`);
expect(test.name).toBe(
`e2e-group-${String(index + 1).padStart(2, "0")}`,
);
});
});
@@ -92,6 +94,6 @@ describe("buildMatrix", () => {
const result = testBuildMatrix("test1.cy.spec.js,test2.cy.spec.js", 50);
expect(result.config.length).toBe(1);
expect(result.config[0].name).toBe("e2e-group-1");
expect(result.config[0].name).toBe("e2e-group-01");
});
});

22
.github/team.json vendored
View File

@@ -1,13 +1,14 @@
{
"teams": [
{
"name": "Querying",
"label": ".Team/Querying",
"name": "Querying Platform",
"label": ".Team/QueryingPlatform",
"members": [
"bshepherdson",
"metamben",
"rileythomp",
"camsual"
"camsaul",
"technomancy"
]
},
{
@@ -15,12 +16,12 @@
"label": ".Team/Graphy",
"members": [
"ranquild",
"romeovs",
"ericnormand",
"galdre",
"dpsutton",
"escherize",
"bgrabow"
"bgrabow",
"alxnddr"
]
},
{
@@ -46,7 +47,8 @@
"label": ".Team/DevEx",
"members": [
"fraserdrops",
"nemanjaglumac"
"nemanjaglumac",
"romeovs"
]
},
{
@@ -89,9 +91,9 @@
"nvoxland",
"edpaget",
"deniskaber",
"alxnddr",
"andsalves",
"kevinkelleher12"
"kevinkelleher12",
"AndreyChernykh"
]
},
{
@@ -106,7 +108,9 @@
"kamilmielnik",
"stasgavrylov",
"uladzimirdev",
"egorgrushin"
"egorgrushin",
"metatyb",
"olehk-mb"
]
},
{

21
.github/workflows/ai-benchmark.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: AI Benchmark
# Runs the ai-benchmark E2E test suite against a Metabase instance.
#
# NOTE: This is a stub. The real workflow — which builds the benchmark Docker
# image, boots Metabase + Postgres with a preloaded data dump, runs the
# benchmark suite, and uploads results as artifacts — is being developed on a
# feature branch. This stub exists on master so that the "Run workflow" button
# is available in the GitHub Actions UI, which lets us dispatch the real
# workflow against feature branches during development.
on:
workflow_dispatch:
jobs:
placeholder:
name: Placeholder
runs-on: ubuntu-latest
steps:
- name: Stub
run: echo "ai-benchmark workflow stub — real implementation pending"

View File

@@ -14,7 +14,7 @@ concurrency:
jobs:
be-tests-mariadb:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -22,12 +22,12 @@ jobs:
version:
- name: MariaDB 10.6
junit-name: be-tests-mariadb-10-6-ee
image: circleci/mariadb:10.6
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
env:
enable-ssl-tests: 'false'
- name: MariaDB Latest
junit-name: be-tests-mariadb-latest-ee
image: circleci/mariadb:latest
image: mariadb:latest
env:
enable-ssl-tests: 'false'
job:
@@ -57,8 +57,8 @@ jobs:
ports:
- "3306:3306"
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: circle_test
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: true
MARIADB_DATABASE: mb_test
env:
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
# with the tag `mb/old-migrations-test`. `__ADDITIONAL_EXCLUDED_TAG__` is not used anywhere outside of splicing
@@ -81,7 +81,7 @@ jobs:
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
MB_MYSQL_TEST_USER: root
#
@@ -120,23 +120,22 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
be-tests-mysql:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
version:
- name: MySQL 8.4
junit-name: be-tests-mysql-8-4-ee
image: cimg/mysql:8.4
- name: MySQL 8.0
junit-name: be-tests-mysql-8-0-ee
image: mysql:8.0
env:
enable-ssl-tests: 'false'
enable-aws-iam-tests: 'false'
@@ -174,7 +173,7 @@ jobs:
- "3306:3306"
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: circle_test
MYSQL_DATABASE: mb_test
env:
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
# with the tag `mb/old-migrations-test`. `__ADDITIONAL_EXCLUDED_TAG__` is not used anywhere outside of splicing
@@ -197,7 +196,7 @@ jobs:
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
MB_MYSQL_TEST_USER: root
#
@@ -244,15 +243,14 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
be-tests-postgres:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -355,8 +353,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -366,7 +363,7 @@ jobs:
- be-tests-mariadb
- be-tests-mysql
- be-tests-postgres
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
name: app-db-tests-result
if: always() && !cancelled()

View File

@@ -7,32 +7,49 @@ on:
description: 'Athena JDBC driver version to publish'
required: true
type: string
force:
description: 'Re-deploy even if version already exists'
required: false
default: false
type: boolean
permissions:
id-token: write
contents: read
jobs:
deploy:
name: Publish Athena JDBC Driver
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
- name: Check out the code
uses: actions/checkout@v4
- name: Set up Babashka
uses: turtlequeue/setup-babashka@v1.7.0
uses: turtlequeue/setup-babashka@2d4df498c7a578b4f3e906283f9af913590bdec7 # v1.7.0
with:
babashka-version: 1.12.197
- name: Set up AWS CLI
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_MAVEN_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_MAVEN_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_MAVEN_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- name: Deploy Athena JDBC driver
run: ./bin/athena.bb ${{ github.event.inputs.version }} --deploy
run: ./bin/athena.bb "${VERSION}" --deploy ${{ github.event.inputs.force == 'true' && '--force' || '' }}
env:
VERSION: ${{ github.event.inputs.version }}
- name: Verify deployment
env:
VERSION: ${{ github.event.inputs.version }}
run: |
aws s3 ls s3://metabase-maven-downloads/com/metabase/athena-jdbc/${{ github.event.inputs.version }}/
echo "Athena JDBC driver version ${{ github.event.inputs.version }} has been deployed"
aws s3 ls s3://metabase-maven-downloads/com/metabase/athena-jdbc/"${VERSION}"/
aws s3 ls s3://metabase-maven-downloads/com/metabase/athena-jdbc-thin/"${VERSION}"/
echo "Verifying thin jar pom exists..."
aws s3 ls s3://metabase-maven-downloads/com/metabase/athena-jdbc-thin/"${VERSION}"/athena-jdbc-thin-"${VERSION}".pom
echo "Verifying athena-streaming..."
aws s3 ls s3://metabase-maven-downloads/com/metabase/athena-streaming/2.0/
echo "Athena JDBC driver version ${VERSION} has been deployed (uber-jar, thin jar, and athena-streaming)"

View File

@@ -7,10 +7,10 @@ on:
jobs:
update_pr:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- uses: tibdex/github-app-token@v2.1.0
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0
id: generate-token
with:
app_id: ${{ secrets.METABASE_BOT_APP_ID }}

View File

@@ -20,7 +20,7 @@ jobs:
github.event.label.name == 'triple-backport'
)
)
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- uses: actions/checkout@v4
with:

View File

@@ -17,7 +17,7 @@ on:
jobs:
setup-oss:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
outputs:
modules: ${{ steps.get-modules.outputs.modules }}
steps:
@@ -31,7 +31,7 @@ jobs:
needs: setup-oss
# don't run this workflow on a cron for forks
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
strategy:
fail-fast: false
@@ -57,14 +57,14 @@ jobs:
- name: Upload coverage to codecov.io
if: always()
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./target/coverage/codecov.json
flags: back-end
setup-ee:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
outputs:
modules: ${{ steps.get-modules.outputs.modules }}
steps:
@@ -78,7 +78,7 @@ jobs:
needs: setup-ee
# don't run this workflow on a cron for forks
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
strategy:
fail-fast: false
@@ -104,7 +104,7 @@ jobs:
- name: Upload coverage to codecov.io
if: always()
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./target/coverage/codecov.json

View File

@@ -14,7 +14,7 @@ concurrency:
jobs:
be-linter-clj-kondo:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
name: Clj-Kondo
steps:
@@ -26,7 +26,7 @@ jobs:
be-linter-eastwood:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 20
name: Eastwood
steps:
@@ -37,25 +37,84 @@ jobs:
name: Run Eastwood linter
be-linter-eastwood-test:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
timeout-minutes: 30
name: Eastwood (test)
# Master / release safety net: lint every backend test namespace.
if: ${{ !inputs.skip && github.event_name == 'push' && (github.ref_name == 'master' || startsWith(github.ref_name, 'release-')) }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
name: Eastwood (tests only)
steps:
- uses: actions/checkout@v4
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood/test
name: Run Eastwood linter on tests
name: Run Eastwood linter on our backend tests
be-linter-eastwood-test-changed:
# PR / merge queue: lint only the test namespaces touched by the diff.
if: ${{ !inputs.skip && (github.event_name == 'pull_request' || github.event_name == 'merge_group') }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
name: Eastwood (changed tests only)
steps:
- uses: actions/checkout@v4
with:
# Enough history for `git merge-base` on a typical PR.
fetch-depth: 200
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Compute changed test namespaces
id: changed
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }}
run: |
set -euo pipefail
# Diff from the merge-base so changes that landed on the base branch after the PR was
# last synced don't widen our scope. Falls back to BASE_SHA if older than fetched depth.
git fetch --no-tags --depth=200 origin "$BASE_SHA"
DIFF_BASE=$(git merge-base "$BASE_SHA" "$HEAD_SHA" 2>/dev/null || echo "$BASE_SHA")
# Test roots we lint: core, EE, and each driver module's test tree.
# :eastwood/test derives matching :source-paths from the classpath, so any
# namespace we name here is reachable. Include renames (R) so a test that
# was just renamed still gets linted under its new name.
FILES=$(git diff --name-only --diff-filter=AMR "$DIFF_BASE" "$HEAD_SHA" \
-- 'test/**/*.clj' 'test/**/*.cljc' \
'enterprise/backend/test/**/*.clj' 'enterprise/backend/test/**/*.cljc' \
'modules/drivers/**/test/**/*.clj' 'modules/drivers/**/test/**/*.cljc')
if [ -z "$FILES" ]; then
echo "No backend test files changed; skipping lint."
echo "namespaces=" >> "$GITHUB_OUTPUT"
exit 0
fi
# Defence-in-depth: paths flow into a subsequent shell command via $GITHUB_OUTPUT.
if grep -Eqv '^[A-Za-z0-9_./-]+$' <<<"$FILES"; then
echo "Refusing to lint: unexpected characters in changed file paths:" >&2
echo "$FILES" >&2
exit 1
fi
NS=$(sed -E 's#^(test/|enterprise/backend/test/|modules/drivers/[^/]+/test/)##; s#\.cljc?$##; s#/#.#g; s#_#-#g' <<<"$FILES" \
| sort -u \
| paste -sd' ' -)
echo "Changed test namespaces: $NS"
echo "namespaces=$NS" >> "$GITHUB_OUTPUT"
- name: Run Eastwood on changed test namespaces
if: steps.changed.outputs.namespaces != ''
env:
NAMESPACES: ${{ steps.changed.outputs.namespaces }}
run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood/test :namespaces "[$NAMESPACES]"
be-tests:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
java-version: [21]
java-version: [25]
job:
- name: Enterprise Tests
edition: ee
@@ -138,14 +197,13 @@ jobs:
input-path: ./target/junit/
output-name: be-tests-java-${{ matrix.java-version }}-${{ matrix.job.edition }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.run-java-tests.outcome }}
- name: Publish Test Report (JUnit)
uses: dorny/test-reporter@v1
uses: dorny/test-reporter@3eeb9fc888e82e8be2fb356bbeec2750231672bc # v1
if: failure()
with:
path: "target/junit/**/*_test.xml"
@@ -167,11 +225,11 @@ jobs:
# as `:dev` dependencies). See #27009 for more context.
be-check:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
strategy:
matrix:
java-version: [21]
java-version: [25]
name: "Check Java ${{ matrix.java-version }}"
steps:
- uses: actions/checkout@v4
@@ -183,7 +241,7 @@ jobs:
be-cljfmt:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
name: Cljfmt
steps:
@@ -197,12 +255,13 @@ jobs:
needs:
- be-linter-eastwood
- be-linter-eastwood-test
- be-linter-eastwood-test-changed
- be-linter-clj-kondo
- be-cljfmt
- be-check
- be-tests
if: always() && !cancelled()
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
env:
needs: ${{ toJson(needs) }}

View File

@@ -10,10 +10,10 @@ on:
jobs:
backport-reminder:
name: Decide whether to backport or not
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: jesusvasquez333/verify-pr-label-action@v1.4.0
- uses: jesusvasquez333/verify-pr-label-action@657d111bbbe13e22bbd55870f1813c699bde1401 # v1.4.0
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
valid-labels: 'no-backport, backport, double-backport, triple-backport'

View File

@@ -5,7 +5,7 @@ on:
jobs:
check-source-branch:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- name: Block master branch PRs
if: github.head_ref == 'master'

View File

@@ -14,7 +14,7 @@ on:
jobs:
start-message:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
@@ -45,7 +45,7 @@ jobs:
}).catch(console.error);
trigger-milestone-check:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
needs: start-message
steps:
@@ -66,7 +66,7 @@ jobs:
check-version:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
outputs:
ee: ${{ fromJson(steps.canonical_version.outputs.result).ee }}
@@ -118,7 +118,7 @@ jobs:
return versions;
check-commit:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
- name: Check out the code to verify the release branch
@@ -150,7 +150,7 @@ jobs:
git checkout -
get-build-requirements:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
outputs:
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
@@ -185,7 +185,7 @@ jobs:
build-uberjar-for-release:
needs: [check-version, check-commit, get-build-requirements]
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 50
strategy:
matrix:

View File

@@ -10,7 +10,7 @@ on:
jobs:
test-build-scripts:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- uses: actions/checkout@v4
- name: Prepare back-end environment

View File

@@ -39,7 +39,8 @@ concurrency:
jobs:
get-cache-keys:
runs-on: ubuntu-22.04
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- uses: actions/checkout@v4
- name: Get cache keys
@@ -52,7 +53,7 @@ jobs:
cypress-cache-key: ${{ steps.get-cache-keys.outputs.cypress-cache-key }}
generate-caches:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
needs: get-cache-keys
env:
@@ -107,11 +108,6 @@ jobs:
bun run cypress verify
shell: bash
# Run Prettier to populate its cache (stored in node_modules/.cache/prettier)
# so it gets saved as part of the node_modules cache below
- name: Run Prettier to populate cache
run: bun run lint-prettier-pure
- name: Update node_modules cache
uses: ./.github/actions/update-cache
with:

View File

@@ -7,7 +7,7 @@ on:
jobs:
check-backport-script:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- name: Checkout code
uses: actions/checkout@v4

View File

@@ -15,7 +15,7 @@ on:
jobs:
set-milestone:
name: Automatically set milestone
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
env:
isNewReleaseBranch: ${{ github.event_name == 'workflow_dispatch' }}

View File

@@ -14,14 +14,14 @@ concurrency:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
frontend_all: ${{ steps.changes.outputs.frontend_all }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -30,7 +30,7 @@ jobs:
fe-chromatic:
needs: files-changed
if: contains(github.event.pull_request.labels.*.name, 'chromatic') || (github.ref_name == 'master' && needs.files-changed.outputs.frontend_all == 'true')
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -43,7 +43,7 @@ jobs:
- name: Compile CLJS
run: NODE_ENV=development bun run build-pure:cljs
- name: Publish to Chromatic
uses: chromaui/action@v10.2.0
uses: chromaui/action@c03d144dc72e609acbb960489fdd1e1a4a23b34f # v10.2.0
env:
PUBLISH_CHROMATIC: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
STORYBOOK_BUILD_TIMEOUT: 900000

View File

@@ -1,360 +0,0 @@
name: "Claude Code Bug Fix"
on:
issues:
types:
- labeled
pull_request_review:
types:
- submitted
workflow_dispatch:
inputs:
issue_number:
description: "Issue number to fix"
required: true
type: string
override_employee_check:
description: "Override employee check for issue creator"
required: false
default: false
type: boolean
jobs:
auto-fix-bug:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
issues: write
pull-requests: write
id-token: write
if: |
(github.event_name == 'issues' && github.event.label.name == 'Claude Bug Fix' && github.event.issue.state == 'open') ||
(github.event_name == 'workflow_dispatch') ||
(github.event_name == 'pull_request_review' && github.event.review.state == 'changes_requested' && startsWith(github.event.pull_request.head.ref, 'claude-fix/'))
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get issue data and check employee status
id: get_issue
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { getIssueData } = require('${{ github.workspace }}/.github/scripts/get-issue-data.js');
try {
const result = await getIssueData({
context,
github,
manualIssueNumber: '${{ github.event.inputs.issue_number }}'
});
const { issueNumber, issueData, employeeComments, prReviews, triggerType, isIssueAuthorEmployee } = result;
// Check employee status and fail if not authorized
if (!result.isIssueAuthorEmployee && '${{ github.event.inputs.override_employee_check }}' !== 'true') {
core.setFailed(`User ${result.issueData.user.login} is not a Metabase employee. Skipping bug fix automation.`);
}
core.setOutput('issue_number', issueNumber);
core.setOutput('issue_data', JSON.stringify(issueData));
core.setOutput('issue_comments', JSON.stringify(employeeComments));
core.setOutput('pr_reviews', JSON.stringify(prReviews));
core.setOutput('trigger_type', triggerType);
} catch (error) {
core.setFailed(error.message);
}
- name: Find linked Linear issue
id: find_linear
continue-on-error: true
uses: actions/github-script@v7
env:
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
ISSUE_NUMBER: ${{ steps.get_issue.outputs.issue_number }}
with:
script: |
const { findLinearIssue } = require('${{ github.workspace }}/.github/scripts/find-linear-issue.js');
const githubIssueUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/issues/${process.env.ISSUE_NUMBER}`;
const result = await findLinearIssue({
githubIssueUrl,
linearApiKey: process.env.LINEAR_API_KEY
});
if (result.success) {
console.log(`Found Linear issue: ${result.issue}`);
core.setOutput('linear_issue', result.issue);
} else {
console.log(`Could not find Linear issue: ${result.message}`);
}
- name: Set up git configuration
run: |
git config --global user.name "Claude Code[bot]"
git config --global user.email "noreply@anthropic.com"
- name: Prepare issue context and bug fix prompt
id: prepare_prompt
uses: actions/github-script@v7
env:
ISSUE_DATA: ${{ steps.get_issue.outputs.issue_data }}
ISSUE_COMMENTS: ${{ steps.get_issue.outputs.issue_comments }}
PR_REVIEWS: ${{ steps.get_issue.outputs.pr_reviews }}
TRIGGER_TYPE: ${{ steps.get_issue.outputs.trigger_type }}
with:
script: |
const issueData = JSON.parse(process.env.ISSUE_DATA);
const issueComments = JSON.parse(process.env.ISSUE_COMMENTS);
const prReviews = JSON.parse(process.env.PR_REVIEWS);
const triggerType = process.env.TRIGGER_TYPE;
const issueNumber = '${{ steps.get_issue.outputs.issue_number }}';
// Convert issue title to kebab case and create branch name
const issueTitle = issueData.title || 'untitled';
const kebabTitle = issueTitle
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.replace(/-+/g, '-')
.substring(0, 50);
const branchName = `claude-fix/issue-${issueNumber}-${kebabTitle}`;
// Format data for bash environment
const formattedComments = issueComments.length > 0
? issueComments.map(comment =>
`**Comment by @${comment.user.login} on ${comment.created_at}:**\n${comment.body}`
).join('\n\n---\n\n')
: '';
const formattedReviews = prReviews.length > 0
? prReviews.map(review =>
`**Review by @${review.user.login} on ${review.submitted_at} (Changes Requested):**\n${review.body || 'No review comment provided'}`
).join('\n\n---\n\n')
: '';
core.exportVariable('BRANCH_NAME', branchName);
core.exportVariable('ISSUE_NUMBER', issueNumber);
core.exportVariable('ISSUE_TITLE', issueData.title);
core.exportVariable('ISSUE_AUTHOR', issueData.user.login);
core.exportVariable('ISSUE_CREATED', issueData.created_at);
core.exportVariable('ISSUE_BODY', issueData.body || 'No description provided');
core.exportVariable('ISSUE_COMMENTS_FORMATTED', formattedComments);
core.exportVariable('PR_REVIEWS_FORMATTED', formattedReviews);
core.exportVariable('TRIGGER_TYPE', triggerType);
- name: Generate bug fix prompt
run: |
cat << 'EOF' >> $GITHUB_ENV
BUG_FIX_PROMPT<<PROMPT_END
You are helping to fix a bug reported in GitHub issue #${{ env.ISSUE_NUMBER }}.
## Bug Report Details
**Title:** ${{ env.ISSUE_TITLE }}
**Reporter:** ${{ env.ISSUE_AUTHOR }}
**Created:** ${{ env.ISSUE_CREATED }}
**Description:**
${{ env.ISSUE_BODY }}
**Comments:**
${{ env.ISSUE_COMMENTS_FORMATTED }}
EOF
# Add review feedback section only if this is a review trigger
if [ "${{ env.TRIGGER_TYPE }}" = "pull_request_review" ]; then
cat << 'EOF' >> $GITHUB_ENV
## Previous Fix Attempt - Review Feedback (MUST ADDRESS)
Your previous automated fix attempt received the following feedback that needs to be addressed:
${{ env.PR_REVIEWS_FORMATTED }}
EOF
fi
# Add the rest of the prompt (always the same)
cat << 'EOF' >> $GITHUB_ENV
## Instructions
1. **Analyze the issue description and comments** - Understand what the issue is describing
a. Pay particular attention to reproduction steps and if there's an agreed upon fix.
2. **Search the codebase** - Use Grep and Glob tools to find relevant files
3. **Implement a fix** - Use the appropriate write skill based on the file types:
- Use clojure-write for Clojure/ClojureScript files
- Use typescript-write for TypeScript/JavaScript files
- Follow the coding standards and patterns in each skill
4. **Test the fix** - Run appropriate tests as specified in the skills
5. **Verify the fix** - Ensure tests pass and code follows conventions
## Important Guidelines
- Use the appropriate write skill for the language you're working in
- Write failing tests first, then fix them
- Work in small, testable increments
- Don't commit changes - leave that for the workflow
- IMPORTANT: Focus only on fixing the reported bug, don't make unrelated changes
## Scratchpad Directory
A `.claude-scratchpad/` directory has been created for your temporary files:
- Use this directory for ALL temporary verification files. However, prefer using production test files rather than adhoc ones.
- Only modify actual production code in the main repository directories
- The scratchpad will be cleaned up automatically - nothing from it will be committed
Start by understanding the bug and then systematically work toward a solution.
PROMPT_END
EOF
- name: Determine branch strategy
run: |
BRANCH_NAME="${{ env.BRANCH_NAME }}"
if git branch -r --list "origin/${BRANCH_NAME}" | grep -q .; then
echo "Branch already exists: $BRANCH_NAME"
echo "Will push to existing branch and skip PR creation"
echo "BRANCH_EXISTS=true" >> $GITHUB_ENV
else
echo "Creating new branch: $BRANCH_NAME"
echo "Will create new PR"
echo "BRANCH_EXISTS=false" >> $GITHUB_ENV
fi
# Export the branch name
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
- name: Create scratchpad directory for Claude
run: |
# Create a scratchpad directory for Claude to use for temporary files
mkdir -p .claude-scratchpad
echo "Scratchpad directory created for Claude's temporary verification files"
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Generate GitHub App Token for Claude
uses: actions/create-github-app-token@v1
id: claude-app-token
with:
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
- name: Generate Bug Fix with Claude Code
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.claude-app-token.outputs.token }}
prompt: ${{ env.BUG_FIX_PROMPT }}
claude_args: |
--allowedTools "Bash(bun run test-unit*),Bash(bun run lint-eslint-pure),Bash(./bin/mage*),Bash(clojure*),Bash(find*),Bash(ls*),Bash(grep*),Write,Read,Glob,Grep,Edit,MultiEdit"
--disallowedTools "Bash(git commit*),Bash(git push*),Bash(gh*)"
- name: Clean up scratchpad directory
run: |
# Remove the scratchpad directory so its contents don't get committed
rm -rf .claude-scratchpad/
# Remove output file generated by Claude Code action
rm -f output.txt
echo "Scratchpad directory and action output files cleaned up"
- name: Check for changes and analysis
id: check_changes
run: |
# Now check for actual code changes (excluding analysis file)
if [ -n "$(git status --porcelain)" ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "Changes detected:"
git status --short
else
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "No changes made by Claude."
fi
- name: Get issue team labels and prepare reviewers
if: steps.check_changes.outputs.has_changes == 'true'
id: prepare_reviewers
uses: actions/github-script@v7
env:
ISSUE_DATA: ${{ steps.get_issue.outputs.issue_data }}
with:
script: |
const issueData = JSON.parse(process.env.ISSUE_DATA);
const issueLabels = issueData.labels.map(label => label.name);
const teamLabels = issueLabels.filter(label => label.startsWith('.Team/'));
const issueCreator = issueData.user.login;
core.setOutput('team_labels', teamLabels.join(','));
core.setOutput('reviewers', issueCreator);
- name: Create Pull Request
id: create_pr
if: steps.check_changes.outputs.has_changes == 'true'
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ env.BRANCH_NAME }}
base: master
commit-message: |
Fix: ${{ env.ISSUE_TITLE }}
Fixes #${{ env.ISSUE_NUMBER }}
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
committer: "Claude Code[bot] <noreply@anthropic.com>"
author: "Claude Code[bot] <noreply@anthropic.com>"
title: "Fix: ${{ env.ISSUE_TITLE }}"
body: |
This PR automatically fixes the bug reported in #${{ env.ISSUE_NUMBER }}.
${{ steps.find_linear.outputs.linear_issue && format('Closes {0}', steps.find_linear.outputs.linear_issue) || '' }}
## Bug Report
**Original Issue:** #${{ env.ISSUE_NUMBER }}
**Reporter:** @${{ env.ISSUE_AUTHOR }}
**Issue Title:** ${{ env.ISSUE_TITLE }}
## Automated Fix
🤖 This fix was generated automatically by Claude Code based on the issue description.
**Please review carefully:**
- [ ] The fix addresses the reported issue
- [ ] Tests pass and code follows conventions
- [ ] No unrelated changes were introduced
- [ ] The solution is appropriate and maintainable
## Next Steps
- Review the changes and test thoroughly
- If the fix looks good, approve and merge
- If issues are found, either fix them or close this PR
---
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
labels: |
${{ steps.prepare_reviewers.outputs.team_labels }}
automated-fix
ci skip
reviewers: ${{ steps.prepare_reviewers.outputs.reviewers }}
assignees: ${{ github.actor }}
- name: Log existing branch update
if: steps.check_changes.outputs.has_changes == 'true' && env.BRANCH_EXISTS == 'true'
uses: actions/github-script@v7
with:
script: |
console.log('🔄 Updated existing branch: ${{ env.BRANCH_NAME }}');
console.log('The changes have been pushed to the existing PR for this issue.');
- name: Log analysis for no changes
if: steps.check_changes.outputs.has_changes == 'false'
uses: actions/github-script@v7
with:
script: |
console.log('🤖 No code changes were made by Claude.');

View File

@@ -14,14 +14,14 @@ concurrency:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
cljs: ${{ steps.changes.outputs.cljs }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -30,7 +30,7 @@ jobs:
shared-tests-cljs:
needs: files-changed
if: needs.files-changed.outputs.cljs == 'true'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
steps:
- uses: actions/checkout@v4

View File

@@ -10,14 +10,14 @@ on:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
codeql: ${{ steps.changes.outputs.codeql }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -25,7 +25,7 @@ jobs:
analyze:
needs: files-changed
if: needs.files-changed.outputs.codeql == 'true'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

View File

@@ -8,7 +8,8 @@ on:
jobs:
codespell:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
permissions:
contents: write
@@ -67,7 +68,7 @@ jobs:
- name: Fix spelling errors with Claude Code
if: steps.codespell_check.outcome == 'failure'
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@3ac52d0da9f8ec9ca7b4dc23bb477e36ef9c77a9 # v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.claude-app-token.outputs.token }}
@@ -82,7 +83,7 @@ jobs:
- name: Create Pull Request
if: steps.codespell_check.outcome == 'failure'
uses: peter-evans/create-pull-request@v6
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: claude-fix/codespell-${{ steps.date.outputs.date }}

View File

@@ -48,16 +48,18 @@ on:
jobs:
containerize:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
outputs:
platforms: ${{ steps.platforms.outputs.result }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-depth: 1
# we want the dockerfile from the commit we're building
- run: git checkout ${{ inputs.commit }} -- bin/docker
- run: |
git fetch --depth=1 origin ${{ inputs.commit }}
git checkout ${{ inputs.commit }} -- bin/docker
- name: Retrieve uberjar artifact
uses: ./.github/actions/fetch-artifact
with:
@@ -84,19 +86,19 @@ jobs:
- name: Move the Uberjar to the context dir
run: mv ./metabase.jar bin/docker/.
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU
if: contains(steps.platforms.outputs.result, 'arm')
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
- name: Set up Docker Buildx
if: contains(steps.platforms.outputs.result, 'arm')
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Build multi-arch container
if: contains(steps.platforms.outputs.result, 'arm')
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: bin/docker/.
platforms: ${{ steps.platforms.outputs.result }}
@@ -117,12 +119,12 @@ jobs:
bin/docker/.
verify-docker-pull:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
needs: containerize
timeout-minutes: 15
steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}
@@ -142,7 +144,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}

View File

@@ -6,7 +6,7 @@ on:
jobs:
get-container-info:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
name: Get container info
outputs:
@@ -68,7 +68,7 @@ jobs:
strategy:
matrix:
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
name: Run Trivy vulnerability scanner
steps:
- name: Check out the code
@@ -76,7 +76,7 @@ jobs:
with:
ref: ${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.29.0
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1
env:
TRIVY_OFFLINE_SCAN: true
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
@@ -85,7 +85,6 @@ jobs:
image-ref: docker.io/${{ env.repo }}:${{ env.tag }}
format: sarif
output: trivy-results.sarif
version: "v0.57.1"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
@@ -94,7 +93,7 @@ jobs:
post-comment:
needs: [get-container-info, containerize]
if: github.event_name == 'pull_request'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
name: Post PR comment
steps:
- name: Check out the code
@@ -107,7 +106,7 @@ jobs:
edition: ee
- name: Post PR comment
uses: marocchino/sticky-pull-request-comment@v2
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
with:
header: docker-build-status
message: |

View File

@@ -8,21 +8,21 @@ on:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
frontend_embedding_sources: ${{ steps.changes.outputs.frontend_embedding_sources }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
filters: .github/file-paths.yaml
pr-title-reminder:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
permissions:
pull-requests: write

View File

@@ -7,7 +7,8 @@ on:
jobs:
fe-test-coverage:
runs-on: ubuntu-22.04
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
@@ -22,7 +23,7 @@ jobs:
--coverage \
--testTimeout=60000
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
files: ./coverage/lcov.info
flags: front-end

View File

@@ -25,7 +25,7 @@ concurrency:
jobs:
migrations-h2-ee:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
env:
CI: 'true'
@@ -39,23 +39,23 @@ jobs:
head-ref: ${{ env.HEAD_REF }}
migrations-postgres-14-ee:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
env:
CI: 'true'
MB_DB_TYPE: postgres
MB_DB_PORT: 5432
MB_DB_HOST: localhost
MB_DB_DBNAME: circle_test
MB_DB_USER: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: mb_test
services:
postgres:
image: postgres:14-alpine
ports:
- "5432:5432"
env:
POSTGRES_USER: circle_test
POSTGRES_DB: circle_test
POSTGRES_USER: mb_test
POSTGRES_DB: mb_test
POSTGRES_HOST_AUTH_METHOD: trust
steps:
- uses: actions/checkout@v4
@@ -65,23 +65,23 @@ jobs:
head-ref: ${{ env.HEAD_REF }}
migrations-postgres-latest-ee:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
env:
CI: 'true'
MB_DB_TYPE: postgres
MB_DB_PORT: 5432
MB_DB_HOST: localhost
MB_DB_DBNAME: circle_test
MB_DB_USER: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: mb_test
services:
postgres:
image: circleci/postgres:latest
image: postgres:latest
ports:
- "5432:5432"
env:
POSTGRES_USER: circle_test
POSTGRES_DB: circle_test
POSTGRES_USER: mb_test
POSTGRES_DB: mb_test
POSTGRES_HOST_AUTH_METHOD: trust
steps:
- uses: actions/checkout@v4
@@ -90,21 +90,24 @@ jobs:
base-ref: ${{ env.BASE_REF }}
head-ref: ${{ env.HEAD_REF }}
migrations-mysql-8-4-ee:
runs-on: ubuntu-22.04
migrations-mysql-8-0-ee:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
env:
CI: 'true'
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
services:
mysql:
image: cimg/mysql:8.4
image: mysql:8.0
ports:
- "3306:3306"
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: mb_test
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/cross-branch-migration-test
@@ -113,14 +116,14 @@ jobs:
head-ref: ${{ env.HEAD_REF }}
migrations-mysql-latest-ee:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
env:
CI: 'true'
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
MB_MYSQL_TEST_USER: root
services:
@@ -128,7 +131,7 @@ jobs:
image: mysql:latest
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: circle_test
MYSQL_DATABASE: mb_test
ports:
- "3306:3306"
steps:
@@ -139,20 +142,23 @@ jobs:
head-ref: ${{ env.HEAD_REF }}
migrations-mariadb-10-6-ee:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
env:
CI: 'true'
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
services:
mariadb:
image: circleci/mariadb:10.6
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
ports:
- "3306:3306"
env:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: true
MARIADB_DATABASE: mb_test
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/cross-branch-migration-test
@@ -161,20 +167,23 @@ jobs:
head-ref: ${{ env.HEAD_REF }}
migrations-mariadb-latest-ee:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
env:
CI: 'true'
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
services:
mariadb:
image: circleci/mariadb:latest
image: mariadb:latest
ports:
- "3306:3306"
env:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: true
MARIADB_DATABASE: mb_test
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/cross-branch-migration-test

View File

@@ -25,8 +25,8 @@ on:
jobs:
# Generate matrix for scheduled/PR runs
generate-matrix:
if: github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
if: github.event_name != 'workflow_dispatch' && github.repository == 'metabase/metabase'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
@@ -47,9 +47,9 @@ jobs:
# Matrix-based tests for scheduled/PR runs
test-matrix:
if: github.event_name != 'workflow_dispatch'
if: github.event_name != 'workflow_dispatch' && github.repository == 'metabase/metabase'
needs: generate-matrix
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
strategy:
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
@@ -61,13 +61,38 @@ jobs:
- name: Prepare release scripts
uses: ./.github/actions/prepare-release-scripts
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare Cypress environment
uses: ./.github/actions/prepare-cypress
- name: Run cross-version test
env:
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
RESULT_FILE: ${{ runner.temp }}/cross-version-result-${{ matrix.source }}-${{ matrix.target }}.json
run: |
cd cross-version
./test.sh --source "${{ matrix.source }}" --target "${{ matrix.target }}"
- name: Upload failure result
uses: actions/upload-artifact@v4
if: failure()
with:
name: failure-result-${{ matrix.source }}-${{ matrix.target }}
path: ${{ runner.temp }}/cross-version-result-${{ matrix.source }}-${{ matrix.target }}.json
if-no-files-found: ignore
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-recording-${{ matrix.source }}-${{ matrix.target }}
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: Show logs on failure
if: failure()
run: |
@@ -75,10 +100,92 @@ jobs:
docker compose logs metabase || true
docker compose logs postgres || true
# Notify Slack on nightly failures
notify-on-failure:
if: >-
always()
&& github.event_name == 'schedule'
&& github.repository == 'metabase/metabase'
&& needs.test-matrix.result == 'failure'
needs: [test-matrix]
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
release
.github
- name: Prepare release scripts
uses: ./.github/actions/prepare-release-scripts
- name: Download failure results
uses: actions/download-artifact@v4
continue-on-error: true
with:
pattern: failure-result-*
path: failure-results
merge-multiple: true
- name: Notify Slack
continue-on-error: true
uses: actions/github-script@v7
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
with:
script: |
const fs = require('fs');
const path = require('path');
const { buildFailureResults, sendCrossVersionSlackNotification } = require('${{ github.workspace }}/release/dist/index.cjs');
// Get all failed jobs from the GitHub API
const { owner, repo } = context.repo;
const jobInfo = await github.rest.actions.listJobsForWorkflowRun({
owner,
repo,
run_id: context.runId,
per_page: 100,
});
const failedJobs = jobInfo.data.jobs
.filter(job => job.status === 'completed' && job.conclusion === 'failure' && job.name.startsWith('test-matrix'))
.map(job => ({ name: job.name, url: job.html_url }));
if (failedJobs.length === 0) {
console.log('No failed test-matrix jobs found');
return;
}
// Load artifact data for enrichment (phase)
const resultsDir = 'failure-results';
const artifactData = new Map();
if (fs.existsSync(resultsDir)) {
for (const file of fs.readdirSync(resultsDir)) {
if (!file.endsWith('.json')) continue;
try {
const result = JSON.parse(fs.readFileSync(path.join(resultsDir, file), 'utf8'));
artifactData.set(`${result.source}-${result.target}`, result);
} catch (e) {
console.log(`Skipping malformed result: ${file}`);
}
}
}
const failures = buildFailureResults(failedJobs, artifactData);
const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.runId}`;
const slackBotToken = process.env.SLACK_BOT_TOKEN;
if (!slackBotToken) {
console.log('SLACK_BOT_TOKEN is not set; skipping Slack notification.');
return;
}
await sendCrossVersionSlackNotification(failures, runUrl, slackBotToken);
console.log('Slack notification sent');
# Manual test for workflow_dispatch
test-manual:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
steps:
@@ -87,6 +194,12 @@ jobs:
- name: Prepare release scripts
uses: ./.github/actions/prepare-release-scripts
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare Cypress environment
uses: ./.github/actions/prepare-cypress
- name: Run cross-version test
env:
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
@@ -94,6 +207,16 @@ jobs:
cd cross-version
./test.sh --source "${{ inputs.source_version }}" --target "${{ inputs.target_version }}"
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-recording-${{ inputs.source_version }}-${{ inputs.target_version }}
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: Show logs on failure
if: failure()
run: |

113
.github/workflows/cut-release-branch.yml vendored Normal file
View File

@@ -0,0 +1,113 @@
name: Cut Next Release Branch
run-name: Cut next release branch (current is v${{ vars.CURRENT_VERSION }})
on:
workflow_dispatch:
jobs:
cut:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
ref: master
sparse-checkout: |
release
.github
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
- name: Prepare release scripts
uses: ./.github/actions/prepare-release-scripts
- name: Preflight
id: preflight
uses: actions/github-script@v7
with:
script: | # js
const { versionRequirements } = require('${{ github.workspace }}/release/dist/index.cjs');
const currentRaw = '${{ vars.CURRENT_VERSION }}';
const current = Number(currentRaw);
if (!Number.isInteger(current) || current <= 0) {
throw new Error(`vars.CURRENT_VERSION is not a valid positive integer: "${currentRaw}"`);
}
const next = current + 1;
const branch = `release-x.${next}.x`;
const needsRequirementsUpdate = !(next in versionRequirements);
try {
await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${branch}`,
});
throw new Error(`Branch ${branch} already exists — nothing to cut.`);
} catch (err) {
if (err.status !== 404) {
throw err;
}
}
core.setOutput('branch', branch);
core.setOutput('next', String(next));
core.setOutput('needsRequirementsUpdate', String(needsRequirementsUpdate));
- name: Cut the branch
id: cut
run: |
BRANCH="${{ steps.preflight.outputs.branch }}"
git config user.name github-actions
git config user.email github-actions@github.com
git checkout -b "$BRANCH"
git commit --allow-empty --no-verify -m "Cut $BRANCH branch"
git push -u origin "$BRANCH"
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Bump vars.CURRENT_VERSION
id: bump
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
run: |
gh variable set CURRENT_VERSION \
--body "${{ steps.preflight.outputs.next }}" \
--repo "${{ github.repository }}"
- name: Notify Slack
uses: actions/github-script@v7
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
with:
script: | # js
const { sendSlackMessage, mentionSlackTeam } = require('${{ github.workspace }}/release/dist/index.cjs');
const branch = '${{ steps.preflight.outputs.branch }}';
const next = '${{ steps.preflight.outputs.next }}';
const sha = '${{ steps.cut.outputs.sha }}';
const bumpFailed = '${{ steps.bump.outcome }}' === 'failure';
const needsReqs = '${{ steps.preflight.outputs.needsRequirementsUpdate }}' === 'true';
const owner = context.repo.owner;
const repo = context.repo.repo;
const shaLink = `<https://github.com/${owner}/${repo}/commit/${sha}|${sha.slice(0, 7)}>`;
const team = mentionSlackTeam('releasemanagers');
const notes = [
bumpFailed
? `:warning: Auto-bump of \`CURRENT_VERSION\` to *${next}* failed — please update it manually: <https://github.com/${owner}/${repo}/settings/variables/actions|Actions variables>`
: `\`CURRENT_VERSION\` bumped to *${next}*.`,
needsReqs
? `:warning: Please add \`versionRequirements[${next}]\` to \`release/src/version-helpers.ts\` (java + node + platforms).`
: null,
].filter(Boolean).join('\n');
const message = `:tree_evergreen: Cut new release branch *${branch}* at ${shaLink} ${team}\n${notes}`;
await sendSlackMessage({
channelName: '${{ vars.SLACK_RELEASE_CHANNEL }}',
message,
}).catch(console.error);

View File

@@ -7,7 +7,8 @@ on:
jobs:
notify-disabled-drivers:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/setup-node@v4

View File

@@ -1,4 +1,3 @@
# .github/workflows/reusable-doc-update.yml
name: Reusable - Update Documentation
# This workflow is designed to be called by other workflows.
@@ -21,7 +20,7 @@ permissions:
jobs:
update-docs:
name: Update documentation for ${{ inputs.target_branch }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
steps:
- name: Checkout code
@@ -34,6 +33,11 @@ jobs:
- name: Prepare backend environment
uses: ./.github/actions/prepare-backend
- name: Set up Babashka
uses: turtlequeue/setup-babashka@2d4df498c7a578b4f3e906283f9af913590bdec7 # v1.7.0
with:
babashka-version: 1.12.197
- name: Generate environment variables documentation
id: generate_env_docs
run: |
@@ -68,6 +72,9 @@ jobs:
clojure -M:ee:doc command-documentation
continue-on-error: false
- name: Generate usage analytics documentation
run: ./bin/generate-usage-analytics-docs.bb
- name: Check for changes
id: check_changes
run: |
@@ -137,6 +144,8 @@ jobs:
- \`clojure -M:ee:doc environment-variables-documentation\`
- \`clojure -M:ee:doc config-template\`
- \`clojure -M:ee:doc api-documentation\`
- \`clojure -M:ee:doc command-documentation\`
- \`./bin/generate-usage-analytics-docs.bb\`
Please review the changes and merge if they look correct." \
--label "Type:Documentation" \

View File

@@ -7,7 +7,7 @@ on:
jobs:
link-checker:
name: Check documentation links
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
permissions:
contents: read
@@ -27,7 +27,7 @@ jobs:
- name: Run link checker
id: link-check
uses: lycheeverse/lychee-action@v2.3.0
uses: lycheeverse/lychee-action@f613c4a64e50d792e0b31ec34bbcbba12263c6a6 # v2.3.0
with:
fail: true
args: docs --config ./.lychee/config.toml --offline

View File

@@ -2,11 +2,12 @@ name: Auto-update documentation (master)
on:
schedule:
- cron: "0 1 * * 1-5" # Every Monday through Friday at 1 AM UTC
- cron: "0 21 * * 1-5" # Every Monday through Friday at 9 PM UTC
workflow_dispatch:
jobs:
update-master-docs:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
name: Call documentation update for master
uses: ./.github/workflows/docs-generate-base.yml
with:

View File

@@ -2,20 +2,21 @@ name: Auto-update documentation (release)
on:
schedule:
- cron: "0 2 * * 1-5" # Every Monday through Friday at 2 AM UTC
- cron: "0 22 * * 1-5" # Every Monday through Friday at 10 PM UTC
workflow_dispatch:
inputs:
target_branch:
description: "Target branch for documentation update"
required: false
default: "release-x.59.x"
default: "release-x.60.x"
type: string
jobs:
update-release-docs:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
name: Call documentation update for release branch
uses: ./.github/workflows/docs-generate-base.yml
with:
target_branch: ${{ inputs.target_branch || 'release-x.59.x' }}
target_branch: ${{ inputs.target_branch || 'release-x.60.x' }}
secrets:
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}

View File

@@ -49,7 +49,7 @@ jobs:
be-tests-athena-ee:
if: inputs.driver == 'Athena'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -68,7 +68,7 @@ jobs:
be-tests-bigquery-cloud-sdk-ee:
if: inputs.driver == 'BigQuery'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -89,7 +89,7 @@ jobs:
be-tests-druid-ee:
if: inputs.driver == 'Druid'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -111,7 +111,7 @@ jobs:
be-tests-mariadb-10-6-ee:
if: inputs.driver == 'Maria DB'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -119,14 +119,17 @@ jobs:
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
MB_MYSQL_TEST_USER: root
services:
mariadb:
image: circleci/mariadb:10.6
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
ports:
- "3306:3306"
env:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: true
MARIADB_DATABASE: mb_test
steps:
- uses: actions/checkout@v4
- name: Test MariaDB driver (10.6)
@@ -137,7 +140,7 @@ jobs:
be-tests-mariadb-latest-ee:
if: inputs.driver == 'Maria DB (latest)'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -145,14 +148,17 @@ jobs:
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
MB_MYSQL_TEST_USER: root
services:
mariadb:
image: circleci/mariadb:latest
image: mariadb:latest
ports:
- "3306:3306"
env:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: true
MARIADB_DATABASE: mb_test
steps:
- uses: actions/checkout@v4
- name: Test MariaDB driver (latest)
@@ -163,7 +169,7 @@ jobs:
be-tests-mongo-4-4-ee:
if: inputs.driver == 'Mongo 4'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -185,7 +191,7 @@ jobs:
be-tests-mongo-4-4-ssl-ee:
if: inputs.driver == 'Mongo 4 (SSL)'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -219,7 +225,7 @@ jobs:
be-tests-mongo-5-0-ee:
if: inputs.driver == 'Mongo 5'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -241,7 +247,7 @@ jobs:
be-tests-mongo-5-0-ssl-ee:
if: inputs.driver == 'Mongo 5 (SSL)'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -275,7 +281,7 @@ jobs:
be-tests-mongo-latest-ee:
if: inputs.driver == 'Mongo (latest)'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -284,7 +290,7 @@ jobs:
MB_MONGO_TEST_PASSWORD: metasample123
services:
mongodb:
image: circleci/mongo:latest
image: mongo:latest
ports:
- "27017:27017"
env:
@@ -298,9 +304,9 @@ jobs:
junit-name: 'be-tests-mongo-latest-ee'
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
be-tests-mysql-8-4-ee:
be-tests-mysql-8-0-ee:
if: inputs.driver == 'MySQL'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -308,25 +314,28 @@ jobs:
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
MB_MYSQL_TEST_USER: root
services:
mysql:
image: cimg/mysql:8.4
image: mysql:8.0
ports:
- "3306:3306"
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: mb_test
steps:
- uses: actions/checkout@v4
- name: Test MySQL driver (8.4)
- name: Test MySQL driver (8.0)
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-mysql-8-4-ee'
junit-name: 'be-tests-mysql-8-0-ee'
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
be-tests-mysql-latest-ee:
if: inputs.driver == 'MySQL (latest)'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -334,7 +343,7 @@ jobs:
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
MB_MYSQL_TEST_USER: root
# set up env vars for something named "MYSQL_SSL" to run MySQL SSL tests verifying connectivity with PEM cert
@@ -364,7 +373,7 @@ jobs:
be-tests-oracle-18-4-ee:
if: inputs.driver == 'Oracle 18'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -390,7 +399,7 @@ jobs:
be-tests-oracle-21-3-ee:
if: inputs.driver == 'Oracle 21'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -425,7 +434,7 @@ jobs:
be-tests-postgres-ee:
if: inputs.driver == 'Postgres'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -455,7 +464,7 @@ jobs:
be-tests-postgres-latest-ee:
if: inputs.driver == 'Postgres (latest)'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -463,20 +472,20 @@ jobs:
MB_DB_TYPE: postgres
MB_DB_PORT: 5432
MB_DB_HOST: localhost
MB_DB_DBNAME: circle_test
MB_DB_USER: circle_test
MB_POSTGRESQL_TEST_USER: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: mb_test
MB_POSTGRESQL_TEST_USER: mb_test
MB_POSTGRES_SSL_TEST_SSL: true
MB_POSTGRES_SSL_TEST_SSL_MODE: verify-full
MB_POSTGRES_SSL_TEST_SSL_ROOT_CERT_PATH: 'test-resources/certificates/us-east-2-bundle.pem'
services:
postgres:
image: circleci/postgres:latest
image: postgres:latest
ports:
- "5432:5432"
env:
POSTGRES_USER: circle_test
POSTGRES_DB: circle_test
POSTGRES_USER: mb_test
POSTGRES_DB: mb_test
POSTGRES_HOST_AUTH_METHOD: trust
steps:
- uses: actions/checkout@v4
@@ -488,7 +497,7 @@ jobs:
be-tests-presto-jdbc-ee:
if: inputs.driver == 'Presto JDBC'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -537,7 +546,7 @@ jobs:
be-tests-redshift-ee:
if: inputs.driver == 'Redshift'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -556,7 +565,7 @@ jobs:
be-tests-snowflake-ee:
if: inputs.driver == 'Snowflake'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -577,7 +586,7 @@ jobs:
be-tests-sparksql-ee:
if: inputs.driver == 'SparkSQL'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -597,7 +606,7 @@ jobs:
be-tests-sqlite-ee:
if: inputs.driver == 'SQLite'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -612,7 +621,7 @@ jobs:
be-tests-sqlserver-2017-ee:
if: inputs.driver == 'SQL Server 2017'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -639,7 +648,7 @@ jobs:
be-tests-sqlserver-2022-ee:
if: inputs.driver == 'SQL Server 2022'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -666,7 +675,7 @@ jobs:
be-tests-starburst-ee:
if: inputs.driver == 'Starburst'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
env:
CI: 'true'
@@ -691,14 +700,14 @@ jobs:
login-to-ecr:
if: inputs.driver == 'Vertica'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.CI_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
@@ -720,7 +729,7 @@ jobs:
be-tests-vertica-ee:
if: inputs.driver == 'Vertica'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
needs: login-to-ecr
timeout-minutes: 90
env:

View File

@@ -15,7 +15,7 @@ jobs:
# This job determines which driver tests should run based on PR context.
# All decision logic is consolidated in mage driver-decisions.
determine-driver-skips:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
outputs:
# All driver run decisions (from mage driver-decisions)
h2-should-run: ${{ steps.driver-decisions.outputs.h2-should-run }}
@@ -72,8 +72,8 @@ jobs:
# All decision logic is in mage - it analyzes module dependencies,
# branch context, labels, and file changes to decide what runs.
MAGE_ARGS=(
--git-ref="${{ github.event.pull_request.base.ref || github.ref_name }}"
--is-master-or-release="${{ github.ref_name == 'master' || startsWith(github.ref_name, 'release-') }}"
--git-ref="${BASE_REF}"
--is-master-or-release="${IS_MASTER_OR_RELEASE}"
--pr-labels="${{ join(github.event.pull_request.labels.*.name, ',') }}"
--skip="${{ inputs.skip }}"
)
@@ -82,14 +82,20 @@ jobs:
echo ""
# First call: show human-readable output for debugging
./bin/mage -driver-decisions "${MAGE_ARGS[@]}"
echo ""
echo "Want to force-run a specific driver? Add a ci:run-<driver> or ci:run-all-drivers label to your PR."
echo "Run './bin/mage -driver-decisions -h' locally for the full list of drivers and labels."
# Second call: output only key=value lines for GITHUB_OUTPUT
./bin/mage -driver-decisions "${MAGE_ARGS[@]}" --github-output-only >> $GITHUB_OUTPUT
env:
BASE_REF: ${{ github.event.pull_request.base.ref || github.ref_name }}
IS_MASTER_OR_RELEASE: ${{ github.ref_name == 'master' || startsWith(github.ref_name, 'release-') }}
shell: bash
be-tests-athena-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.athena-should-run == 'true' || needs.determine-driver-skips.outputs.athena-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -131,8 +137,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -140,7 +145,7 @@ jobs:
be-tests-bigquery-cloud-sdk-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.bigquery-should-run == 'true' || needs.determine-driver-skips.outputs.bigquery-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 60
strategy:
fail-fast: false
@@ -197,8 +202,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -212,7 +216,7 @@ jobs:
be-tests-clickhouse-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.clickhouse-should-run == 'true' || needs.determine-driver-skips.outputs.clickhouse-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -255,7 +259,7 @@ jobs:
run: |
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
- name: Start ClickHouse in Docker
uses: hoverkraft-tech/compose-action@v2.0.0
uses: hoverkraft-tech/compose-action@802a148945af6399a338c7906c267331b39a71af # v2.0.0
with:
compose-file: "modules/drivers/clickhouse/docker-compose.yml"
down-flags: "--volumes"
@@ -280,8 +284,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -295,7 +298,7 @@ jobs:
be-tests-druid-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.druid-should-run == 'true' || needs.determine-driver-skips.outputs.druid-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -335,8 +338,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -344,7 +346,7 @@ jobs:
be-tests-druid-jdbc-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.druid-jdbc-should-run == 'true' || needs.determine-driver-skips.outputs.druid-jdbc-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -384,8 +386,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -393,8 +394,33 @@ jobs:
be-tests-databricks-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.databricks-should-run == 'true' || needs.determine-driver-skips.outputs.databricks-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
job:
- name: Driver Tests Part 1
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
:fail-fast? true
:partition/total 3
:partition/index 0
- name: Driver Tests Part 2
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
:fail-fast? true
:partition/total 3
:partition/index 1
- name: Driver Tests Part 3
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
:fail-fast? true
:partition/total 3
:partition/index 2
env:
CI: 'true'
DRIVERS: databricks
@@ -423,9 +449,7 @@ jobs:
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-databricks-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -433,8 +457,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -442,8 +465,33 @@ jobs:
be-tests-databricks-multi-catalog-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.databricks-should-run == 'true' || needs.determine-driver-skips.outputs.databricks-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
job:
- name: Driver Tests Part 1
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
:fail-fast? true
:partition/total 3
:partition/index 0
- name: Driver Tests Part 2
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
:fail-fast? true
:partition/total 3
:partition/index 1
- name: Driver Tests Part 3
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
:fail-fast? true
:partition/total 3
:partition/index 2
env:
CI: 'true'
DRIVERS: databricks
@@ -473,9 +521,7 @@ jobs:
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-databricks-multi-catalog-ee'
test-args: >-
:only-tags [:mb/driver-tests]
:exclude-tags [:mb/transforms-python-test]
test-args: ${{ matrix.job.test-args }}
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
if: always()
@@ -483,8 +529,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -492,7 +537,7 @@ jobs:
be-tests-h2:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.h2-should-run == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -514,8 +559,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -523,7 +567,7 @@ jobs:
be-tests-h2-oss:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.h2-should-run == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -546,8 +590,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -555,7 +598,7 @@ jobs:
be-tests-mysql-mariadb:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.mysql-mariadb-should-run == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -563,19 +606,19 @@ jobs:
version:
- name: MariaDB 10.6
junit-name: be-tests-mariadb-10-6-ee
image: circleci/mariadb:10.6
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
env:
enable-ssl-tests: 'false'
enable-aws-iam-tests: 'false'
- name: MariaDB Latest
junit-name: be-tests-mariadb-latest-ee
image: circleci/mariadb:latest
image: mariadb:latest
env:
enable-ssl-tests: 'false'
enable-aws-iam-tests: 'false'
- name: MySQL 8.4
junit-name: be-tests-mysql-8-4-ee
image: mysql:8.4
- name: MySQL 8.0
junit-name: be-tests-mysql-8-0-ee
image: mysql:8.0
env:
enable-ssl-tests: 'false'
enable-aws-iam-tests: 'false'
@@ -602,7 +645,7 @@ jobs:
- "3306:3306"
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: circle_test
MYSQL_DATABASE: mb_test
env:
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
# with the tag `mb/old-migrations-test`. `__ADDITIONAL_EXCLUDED_TAG__` is not used anywhere outside of splicing
@@ -625,7 +668,7 @@ jobs:
MB_DB_TYPE: mysql
MB_DB_HOST: localhost
MB_DB_PORT: 3306
MB_DB_DBNAME: circle_test
MB_DB_DBNAME: mb_test
MB_DB_USER: root
MB_MYSQL_TEST_USER: root
#
@@ -677,8 +720,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -692,7 +734,7 @@ jobs:
be-tests-mongo:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.mongo-should-run == 'true' || needs.determine-driver-skips.outputs.mongo-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -758,8 +800,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -773,7 +814,7 @@ jobs:
be-tests-mongo-ssl:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.mongo-ssl-should-run == 'true' || needs.determine-driver-skips.outputs.mongo-ssl-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -841,8 +882,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -850,7 +890,7 @@ jobs:
be-tests-mongo-sharded-cluster-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.mongo-sharded-cluster-should-run == 'true' || needs.determine-driver-skips.outputs.mongo-sharded-cluster-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -887,8 +927,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -896,7 +935,7 @@ jobs:
be-tests-oracle:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.oracle-should-run == 'true' || needs.determine-driver-skips.outputs.oracle-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -973,8 +1012,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -982,7 +1020,7 @@ jobs:
be-tests-postgres:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.postgres-should-run == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -1078,8 +1116,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1093,7 +1130,7 @@ jobs:
be-tests-presto-jdbc-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.presto-jdbc-should-run == 'true' || needs.determine-driver-skips.outputs.presto-jdbc-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -1166,8 +1203,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1175,7 +1211,7 @@ jobs:
be-tests-redshift-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.redshift-should-run == 'true' || needs.determine-driver-skips.outputs.redshift-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
strategy:
fail-fast: false
@@ -1215,7 +1251,7 @@ jobs:
MB_REDSHIFT_TEST_USER: metabase_ci
MB_REDSHIFT_TEST_DB: testdb
MB_REDSHIFT_TEST_DB_ROUTING: dev
MB_REDSHIFT_TEST_HOST: ${{ secrets.MB_REDSHIFT_TEST_HOST }}
MB_REDSHIFT_TEST_HOSTS: ${{ secrets.MB_REDSHIFT_TEST_HOSTS }}
MB_REDSHIFT_TEST_PASSWORD: ${{ secrets.MB_REDSHIFT_TEST_PASSWORD }}
name: ${{ matrix.job.name }}
steps:
@@ -1251,8 +1287,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1266,7 +1301,7 @@ jobs:
be-tests-snowflake-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.snowflake-should-run == 'true' || needs.determine-driver-skips.outputs.snowflake-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 60
strategy:
fail-fast: false
@@ -1332,8 +1367,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1347,7 +1381,7 @@ jobs:
be-tests-sparksql-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.sparksql-should-run == 'true' || needs.determine-driver-skips.outputs.sparksql-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -1385,8 +1419,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1394,7 +1427,7 @@ jobs:
be-tests-sqlite-ee:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.sqlite-should-run == 'true' || needs.determine-driver-skips.outputs.sqlite-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -1427,8 +1460,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1436,7 +1468,7 @@ jobs:
be-tests-sqlserver:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.sqlserver-should-run == 'true' || needs.determine-driver-skips.outputs.sqlserver-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
strategy:
fail-fast: false
@@ -1504,8 +1536,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1520,7 +1551,7 @@ jobs:
# be-tests-starburst-ee:
# needs: determine-driver-skips
# if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
# runs-on: ubuntu-latest
# runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
# timeout-minutes: 40
# env:
# CI: 'true'
@@ -1550,22 +1581,21 @@ jobs:
# input-path: ./target/junit/
# output-name: ${{ github.job }}
# bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
# aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
# aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
# role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
# aws-region: ${{ vars.AWS_REGION }}
# trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
login-to-ecr:
needs: determine-driver-skips
if: ${{ needs.determine-driver-skips.outputs.vertica-should-run == 'true' || needs.determine-driver-skips.outputs.vertica-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.CI_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
@@ -1588,7 +1618,7 @@ jobs:
be-tests-vertica-ee:
needs: [determine-driver-skips, login-to-ecr]
if: ${{ needs.determine-driver-skips.outputs.vertica-should-run == 'true' || needs.determine-driver-skips.outputs.vertica-quarantine-conflict == 'true' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: 'true'
@@ -1631,8 +1661,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1642,7 +1671,9 @@ jobs:
# if you add a driver job, you must add it to this list for it to be a required check
- be-tests-h2
- be-tests-athena-ee
- be-tests-bigquery-cloud-sdk-ee
# these are not reliable, but we want to run them anyway to gather data about failures
# - be-tests-snowflake-ee
# - be-tests-bigquery-cloud-sdk-ee
- be-tests-druid-ee
- be-tests-databricks-ee
- be-tests-druid-jdbc-ee
@@ -1654,7 +1685,6 @@ jobs:
- be-tests-postgres
- be-tests-presto-jdbc-ee
- be-tests-redshift-ee
- be-tests-snowflake-ee
- be-tests-sparksql-ee
- be-tests-sqlite-ee
- be-tests-sqlserver
@@ -1662,7 +1692,7 @@ jobs:
# TODO(rileythomp, 2025-08-20): Re-enable vertica tests once the docker image is updated
# - be-tests-vertica-ee
- be-tests-clickhouse-ee
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
name: drivers-tests-result
if: always() && !cancelled()
@@ -1682,7 +1712,7 @@ jobs:
}));
// are all jobs skipped or successful?
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success' || job.name == 'be-tests-starburst-ee'))) {
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
console.log("");
console.log(" _------. ");
console.log(" / , \_ ");

View File

@@ -21,7 +21,7 @@ on:
jobs:
build-matrix:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
outputs:
matrix: ${{ steps.matrix.outputs.config }}
@@ -47,7 +47,7 @@ jobs:
const inputSpecs = "${{ inputs.specs }}";
const inputChunks = Number(process.env.chunks);
const java = 21;
const defaultRunner = "ubuntu-22.04";
const defaultRunner = "${{ vars.DEFAULT_RUNNER_KEY }}";
const options = {
java,
defaultRunner,

View File

@@ -31,11 +31,16 @@ on:
type: boolean
required: false
default: true
fail_fast:
description: "Stop running remaining tests after the first failure"
type: boolean
required: false
default: false
jobs:
determine-test-type:
name: Determine test type
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
outputs:
type: ${{ steps.detect.outputs.type }}
@@ -53,7 +58,7 @@ jobs:
workflow-summary:
name: Stress test inputs
needs: [determine-test-type]
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- name: Generate workflow summary
@@ -67,12 +72,13 @@ jobs:
echo '- `grep`: "${{ inputs.grep }}"' >> $GITHUB_STEP_SUMMARY
echo '- `qa_db`: "${{ inputs.qa_db }}"' >> $GITHUB_STEP_SUMMARY
echo '- `enable_network_throttling`: "${{ inputs.enable_network_throttling }}"' >> $GITHUB_STEP_SUMMARY
echo '- `fail_fast`: "${{ inputs.fail_fast }}"' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo 'triggered by: @${{ github.event.sender.login }}' >> $GITHUB_STEP_SUMMARY
stress-test-flake-fix:
needs: [determine-test-type]
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 60
name: Stress test E2E flake fix
env:
@@ -144,10 +150,13 @@ jobs:
env:
GREP: ${{ github.event.inputs.grep }}
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
FAIL_FAST: ${{ inputs.fail_fast }}
SPEC: ${{ github.event.inputs.spec }}
BURN_IN: ${{ github.event.inputs.burn_in }}
run: |
node e2e/runner/run_cypress_ci.js ${{ needs.determine-test-type.outputs.type }} \
--spec '${{ github.event.inputs.spec }}' \
--env burn=${{ github.event.inputs.burn_in }} \
--spec "${SPEC}" \
--expose burn="${BURN_IN}" \
--config-file e2e/support/cypress-stress-test.config.js
- name: Upload Cypress Artifacts upon failure

View File

@@ -21,7 +21,7 @@ on:
runner:
required: false
type: string
default: ubuntu-22.04
default: ${{ vars.DEFAULT_RUNNER_KEY }}
total_chunks: # number of chunks
required: false
type: number
@@ -134,7 +134,7 @@ jobs:
run: |
echo "Running tests with specs: $SPEC_PATH"
node e2e/runner/run_cypress_ci.js e2e \
--env grepTags="-@mongo+-@python+-@OSS+-@skip" \
--expose grepTags="-@mongo+-@python+-@OSS+-@skip" \
--spec "$SPEC_PATH"
# Trunk Quarantine controls the final state of the run in the CI!
# Even if this step fails we let it pass, and then `trunk-io/analytics-uploader` determines
@@ -151,7 +151,7 @@ jobs:
run: |
echo "Running tests with specs: $SPEC_PATH"
node e2e/runner/run_cypress_ci.js e2e \
--env grepTags="${{inputs.tags && format('{0}+-@skip', inputs.tags) || '-@skip'}}" \
--expose grepTags="${{inputs.tags && format('{0}+-@skip', inputs.tags) || '-@skip'}}" \
--spec "$SPEC_PATH"
# Trunk Quarantine controls the final state of the run in the CI!
# Even if this step fails we let it pass, and then `trunk-io/analytics-uploader` determines
@@ -166,8 +166,7 @@ jobs:
# Related to our custom upload to S3
output-name: e2e-${{ inputs.name }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
# Related to Trunk uploader
variant: e2e-tests
@@ -235,7 +234,7 @@ jobs:
uses: actions/upload-artifact@v4
if: steps.extract-timings.outcome == 'success' && steps.extract-timings.outputs.has-changes == 'true'
with:
name: timing-data-${{ inputs.name }}-${{ inputs.split_index }}
name: timing-data-${{ inputs.name }}
path: chunkTimings.json
retention-days: 1 # gets merged immediately by parent workflow

View File

@@ -24,7 +24,7 @@ jobs:
reset-e2e-test-comment:
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- name: Remove test results comment
@@ -75,7 +75,7 @@ jobs:
collect-timings:
name: Collect E2E Timings
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
needs: e2e-tests
if: always() && !cancelled() && github.ref == 'refs/heads/master'
@@ -138,7 +138,7 @@ jobs:
e2e-tests-result:
needs:
- e2e-tests
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
name: e2e-tests-result
if: always() && !cancelled()

View File

@@ -12,7 +12,7 @@ env:
jobs:
sdk-component-tests:
name: "SDK component tests (React ${{ matrix.react-version }})"
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
strategy:
matrix:
@@ -94,8 +94,7 @@ jobs:
input-path: ./target/junit
output-name: e2e-embedding-sdk-react-${{ matrix.react-version }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.run-tests.outcome }}
@@ -128,7 +127,7 @@ jobs:
await core.summary.addRaw(summary).write();
decide-target-ref:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
outputs:
target-ref: ${{ steps.get-target-ref.outputs.value }}
steps:
@@ -151,7 +150,7 @@ jobs:
sdk-component-tests-backward-compatibility:
name: "SDK component tests (Backward compatibility)"
needs: [decide-target-ref]
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
env:
MB_RUN_MODE: "e2e"
@@ -239,7 +238,7 @@ jobs:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: |
node e2e/runner/run_cypress_ci.js component \
--env grepTags="-@skip-backward-compatibility"
--expose grepTags="-@skip-backward-compatibility"
- name: Upload Test Results
uses: ./.github/actions/upload-test-results
@@ -248,8 +247,7 @@ jobs:
input-path: ./target/junit
output-name: e2e-embedding-sdk
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.run-tests.outcome }}

View File

@@ -12,7 +12,7 @@ env:
MB_EDITION: ee
jobs:
get-sample-apps-data:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
env:
# we must support the last two stable releases
@@ -94,7 +94,7 @@ jobs:
environment: ["production", "development"]
if: |
!cancelled() && needs.get-sample-apps-data.result == 'success'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
name: e2e-sample-app-${{ matrix.test_suite }}-tests-${{ matrix.environment }}-bundle
env:
@@ -106,6 +106,8 @@ jobs:
SAMPLE_APP_ENVIRONMENT: ${{ matrix.environment }}
permissions:
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@v4
@@ -114,7 +116,7 @@ jobs:
# We have to use the latest version that does not have this issue https://github.com/docker/compose/pull/12752
- name: Set up Docker Compose
uses: docker/setup-compose-action@v1
uses: docker/setup-compose-action@2fe291b7677a45ee1269ec56a42604c143505e7e # v1
with:
version: latest
@@ -201,7 +203,7 @@ jobs:
if: |
!cancelled() && needs.get-sample-apps-data.result == 'success' &&
(github.ref == 'refs/heads/master' || github.base_ref == 'master')
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
name: e2e-sample-app-shoppy-e2e-tests-${{ matrix.environment }}-bundle
env:
@@ -213,6 +215,8 @@ jobs:
SAMPLE_APP_ENVIRONMENT: ${{ matrix.environment }}
permissions:
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@v4
@@ -264,7 +268,7 @@ jobs:
!cancelled() && needs.get-sample-apps-data.result == 'success' &&
github.event_name == 'push' &&
github.ref == 'refs/heads/master'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
name: e2e-shoppy-synthetic-monitoring-tests
env:
@@ -277,6 +281,7 @@ jobs:
permissions:
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@v4
@@ -354,7 +359,7 @@ jobs:
"angular-20-host-app-e2e",
]
environment: ["production", "development"]
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
name: e2e-host-app-${{ matrix.test_suite }}-tests-${{ matrix.environment }}-bundle
env:
@@ -362,6 +367,8 @@ jobs:
HOST_APP_ENVIRONMENT: ${{ matrix.environment }}
permissions:
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@v4

View File

@@ -10,14 +10,14 @@ on:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
frontend_embedding_sdk_package_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_package_sources }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -29,11 +29,11 @@ jobs:
!cancelled() &&
(needs.files-changed.outputs.frontend_embedding_sdk_package_sources == 'true')
name: Embedding SDK package release-notes/no-release-notes label reminder
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: jesusvasquez333/verify-pr-label-action@v1.4.0
- uses: jesusvasquez333/verify-pr-label-action@657d111bbbe13e22bbd55870f1813c699bde1401 # v1.4.0
id: verify-release-notes-labels
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'

View File

@@ -10,7 +10,7 @@ concurrency:
jobs:
build-sdk-package:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 25
steps:
- uses: actions/checkout@v4

View File

@@ -39,7 +39,7 @@ concurrency:
jobs:
embedding-sdk-cli-snippets-type-check:
if: inputs.cli-snippets-type-check && !inputs.skip
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
@@ -55,7 +55,7 @@ jobs:
embedding-sdk-docs-snippets-type-check:
if: inputs.docs-snippets-type-check && !inputs.skip
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
@@ -71,7 +71,7 @@ jobs:
embedding-sdk-api-documentation-generation-validation:
if: inputs.documentation-validation && !inputs.skip
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
@@ -131,7 +131,7 @@ jobs:
embedding-sdk-bundle-size-check:
if: inputs.bundle-size-check
continue-on-error: true
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 20
env:
BUNDLE_SIZE_THRESHOLD_PERCENT: 2
@@ -186,7 +186,7 @@ jobs:
- embedding-sdk-api-documentation-generation-validation
- embedding-sdk-component-tests
if: always() && !cancelled()
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
env:
needs: ${{ toJson(needs) }}

View File

@@ -0,0 +1,78 @@
# Weekly count of files importing @emotion/*, uploaded to stats.metabase.com.
# Feeds the "Emotion CSS Elimination Progress" chart tracking the migration
# off Emotion onto Mantine (+ CSS modules as a fallback).
name: Emotion Stats Update
on:
workflow_dispatch:
schedule:
- cron: 40 1 * * 2 # every Tuesday at 1:40
jobs:
emotion-stats:
if: github.repository == 'metabase/metabase'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: Get Emotion file count
id: get-emotion-file-count
run: |
# Match both `import ... from '@emotion/...'` and bare side-effect imports
# (`import '@emotion/...'`, used in e.g. frontend/src/types/emotion.d.ts).
# `|| true` prevents failing on the day the count reaches 0 — git grep
# exits 1 on no matches, which cascades through bash's default pipefail.
oss=$(
git grep -l -E "(from|import) ['\"]@emotion/" -- \
'frontend/**/*.ts' \
'frontend/**/*.tsx' \
'frontend/**/*.js' \
'frontend/**/*.jsx' \
| wc -l || true
)
ee=$(
git grep -l -E "(from|import) ['\"]@emotion/" -- \
'enterprise/frontend/**/*.ts' \
'enterprise/frontend/**/*.tsx' \
'enterprise/frontend/**/*.js' \
'enterprise/frontend/**/*.jsx' \
| wc -l || true
)
echo "$(($oss+$ee)) files importing @emotion/*"
echo "EMOTION_FILES=$(($oss+$ee))" >> $GITHUB_OUTPUT
- name: Upload count to Stats
uses: actions/github-script@v7
env:
EMOTION_FILES: ${{ steps.get-emotion-file-count.outputs.EMOTION_FILES }}
API_KEY: ${{ secrets.STATS_API_KEY }}
with:
script: | # js
const { uploadCsvToMb } = require('${{ github.workspace }}/.github/scripts/csv-to-mb.js');
console.log("EMOTION_FILES", Number(process.env.EMOTION_FILES));
// Keys become CSV headers and must match the target table's columns
// (Metabase slugifies both sides, so casing/spacing is forgiving).
const data = [
{
'Date': new Date().toISOString().slice(0, 10), // YYYY-MM-DD
'Emotion Files': Number(process.env.EMOTION_FILES),
}
];
// tableId 76283 = "Emotion Files" table on stats.metabase.com.
// Auth via x-api-key, sourced from STATS_API_KEY (see env block above).
uploadCsvToMb({
baseUrl: 'https://stats.metabase.com',
tableId: 76283,
jsonData: data,
mode: 'append',
}).then(() => {
console.log('Data uploaded successfully');
}).catch((error) => {
console.error('Error uploading data:', error);
process.exit(1);
});

View File

@@ -7,7 +7,7 @@ on:
jobs:
on-escalation-label:
if: github.event.label.name == '.Escalation'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
- name: Format issue title (escape quotes)
@@ -18,7 +18,7 @@ jobs:
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
- name: Send escalated issue ${{ github.event.issue.number }} to Slack
id: slack
uses: slackapi/slack-github-action@v1.23.0
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844 # v1.23.0
with:
payload: |
{

View File

@@ -19,7 +19,7 @@ on:
jobs:
workflow-summary:
name: Stress test inputs
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- name: Generate workflow summary
@@ -34,7 +34,7 @@ jobs:
echo 'triggered by: @${{ github.event.sender.login }}' >> $GITHUB_STEP_SUMMARY
frontend-unit-test-stress-test-flake-fix:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 60
name: Stress test frontend unit test flake fix
steps:
@@ -47,11 +47,16 @@ jobs:
run: bun run build:cljs
- name: Stress-test ${{ github.event.inputs.spec }} ${{ github.event.inputs.burn_in }} times
run: |
for i in {1..${{ github.event.inputs.burn_in }}}; do
for ((i=1; i<=BURN_IN; i++)); do
bun run jest --runInBand --ci --silent --no-cache \
--testPathPatterns '${{ github.event.inputs.spec }}' \
--testNamePattern '${{ github.event.inputs.grep }}'
--testPathPatterns "${SPEC}" \
--testNamePattern "${GREP}"
if [[ "$?" != 0 ]]; then
echo "Failed after $i attempts" && break;
fi
done
env:
BURN_IN: ${{ github.event.inputs.burn_in }}
SPEC: ${{ github.event.inputs.spec }}
GREP: ${{ github.event.inputs.grep }}

View File

@@ -16,7 +16,7 @@ on:
type: boolean
default: false
skip-lint:
description: "Skip linting jobs (prettier, eslint, css variables check)"
description: "Skip linting jobs (oxfmt, eslint, css variables check)"
type: boolean
default: false
@@ -27,7 +27,7 @@ concurrency:
jobs:
fe-lint:
if: ${{ !inputs.skip-lint }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
@@ -49,8 +49,8 @@ jobs:
run: bun run build-pure:cljs
- name: Find CSS variables that are used but never defined
run: bun run find-never-defined-css-variables
- name: Run Prettier formatting linter
run: bun run lint-prettier-pure
- name: Run oxfmt formatting linter
run: bun run lint-format-pure
- name: Run ESLint linter
run: bun run lint-eslint-pure
- name: Run CSS linter
@@ -58,7 +58,7 @@ jobs:
fe-type-check:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
@@ -73,7 +73,7 @@ jobs:
fe-tests-unit:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
strategy:
fail-fast: false
@@ -103,22 +103,19 @@ jobs:
input-path: ./target/junit
output-name: frontend-unit
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.fe-tests.outcome }}
fe-tests-timezones:
if: ${{ !inputs.skip }}
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Run frontend timezones tests
run: bun run test-timezones
@@ -129,7 +126,7 @@ jobs:
- fe-tests-unit
- fe-tests-timezones
if: always() && !cancelled()
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
env:
needs: ${{ toJson(needs) }}

View File

@@ -24,7 +24,7 @@ on:
# TODO: Consider renaming the job in next milestones
jobs:
generative-query-test:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 90
name: Generative Query Test
env:
@@ -47,7 +47,7 @@ jobs:
run: clojure -X:dev:ci:test:ee:ee-dev :only '${{ inputs.test }}'
- name: Publish Test Report (JUnit)
uses: dorny/test-reporter@v1
uses: dorny/test-reporter@3eeb9fc888e82e8be2fb356bbeec2750231672bc # v1
if: failure()
with:
path: "target/junit/**/*_test.xml"

View File

@@ -14,14 +14,14 @@ concurrency:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
i18n: ${{ steps.changes.outputs.i18n }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -30,22 +30,22 @@ jobs:
verify-i18n-files:
needs: files-changed
if: needs.files-changed.outputs.i18n == 'true'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Install xgettext if necessary
run: |
if ! command -v xgettext > /dev/null; then
sudo apt install gettext
fi
- name: Check xgettext version
run: xgettext --version
- name: Check i18n tags/make sure template can be built
run: ./bin/i18n/update-translation-template
- name: Verify i18n translations (.po files)
run: ./bin/i18n/build-translation-resources
- uses: actions/checkout@v4
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Install xgettext if necessary
run: |
if ! command -v xgettext > /dev/null; then
sudo apt install gettext
fi
- name: Check xgettext version
run: xgettext --version
- name: Check i18n tags/make sure template can be built
run: ./bin/i18n/update-translation-template
- name: Verify i18n translations (.po files)
run: ./bin/i18n/build-translation-resources

View File

@@ -7,7 +7,8 @@ on:
jobs:
js-stats:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4

View File

@@ -6,7 +6,7 @@ on:
jobs:
add-issue-references:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- name: Checkout code
uses: actions/checkout@v4

View File

@@ -21,7 +21,7 @@ on:
jobs:
detect-changed-stories:
name: Detect changed stories
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
outputs:
story_files: ${{ steps.detect.outputs.story_files }}
@@ -84,32 +84,40 @@ jobs:
name: Summary
needs: detect-changed-stories
if: needs.detect-changed-stories.outputs.should_run == 'true'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- name: Generate workflow summary
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
STORY_FILES: ${{ needs.detect-changed-stories.outputs.story_files }}
BURN_IN: ${{ inputs.burn_in || 50 }}
EVENT_NAME: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_ACTOR: ${{ github.event.pull_request.user.login }}
ACTOR: ${{ github.actor }}
run: |
echo '## Loki Stress Test' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '**Branch:** `${{ github.head_ref || github.ref_name }}`' >> $GITHUB_STEP_SUMMARY
echo "**Branch:** \`${BRANCH_NAME}\`" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '**Story files:**' >> $GITHUB_STEP_SUMMARY
echo '${{ needs.detect-changed-stories.outputs.story_files }}' | tr ',' '\n' | while read -r file; do
echo "${STORY_FILES}" | tr ',' '\n' | while read -r file; do
echo "- \`$file\`" >> $GITHUB_STEP_SUMMARY
done
echo '' >> $GITHUB_STEP_SUMMARY
echo '**Burn-in:** ${{ inputs.burn_in || 50 }} runs' >> $GITHUB_STEP_SUMMARY
echo "**Burn-in:** ${BURN_IN} runs" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo 'Triggered by PR #${{ github.event.pull_request.number }} from @${{ github.event.pull_request.user.login }}' >> $GITHUB_STEP_SUMMARY
if [[ "${EVENT_NAME}" == "pull_request" ]]; then
echo "Triggered by PR #${PR_NUMBER} from @${PR_ACTOR}" >> $GITHUB_STEP_SUMMARY
else
echo 'Triggered manually by @${{ github.actor }}' >> $GITHUB_STEP_SUMMARY
echo "Triggered manually by @${ACTOR}" >> $GITHUB_STEP_SUMMARY
fi
stress-test-loki:
needs: detect-changed-stories
if: needs.detect-changed-stories.outputs.should_run == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 60
name: Stress test Loki visual tests
services:

View File

@@ -7,7 +7,7 @@ on:
jobs:
check-label-and-update-snapshot:
if: contains(github.event.pull_request.labels.*.name, 'loki-update')
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4

View File

@@ -7,7 +7,7 @@ on:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
frontend_ci: ${{ steps.changes.outputs.frontend_ci }}
@@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -25,7 +25,7 @@ jobs:
visual-test:
needs: files-changed
if: needs.files-changed.outputs.frontend_ci == 'true' || needs.files-changed.outputs.frontend_sources == 'true' || needs.files-changed.outputs.frontend_loki_ci == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
services:
docker:
image: docker:19.03.12

View File

@@ -6,7 +6,7 @@ on:
jobs:
# run mage tests even if the rest of be isnt needed
mage-tests:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
name: Mage
steps:
- uses: actions/checkout@v4

View File

@@ -6,7 +6,7 @@ on:
- labeled
jobs:
on-issue-labeled:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
if: |
contains(github.event.issue.labels.*.name, 'Type:New Feature')
@@ -19,7 +19,7 @@ jobs:
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
- name: Send the issue ${{ github.event.issue.number }} to Slack
id: slack
uses: slackapi/slack-github-action@v1.26.0
uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e # v1.26.0
with:
payload: |
{

View File

@@ -8,10 +8,10 @@ on:
- unlabeled
- synchronize
paths:
- src/metabase/lib/js.cljs
- src/metabase/utils/js.cljs
jobs:
mbql-library-changelog-check:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
if: ${{ !contains( github.event.pull_request.labels.*.name, '.metabase-lib/no-change' ) }}
steps:
# - uses: jesusvasquez333/verify-pr-label-action@v1.4.0

View File

@@ -6,11 +6,13 @@ on:
- 'master'
- 'release-**'
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
lint-migrations:
runs-on: ubuntu-22.04
if: github.event.action != 'labeled' && github.event.action != 'unlabeled'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
@@ -18,3 +20,19 @@ jobs:
uses: ./.github/actions/prepare-backend
- name: Verify Liquibase Migrations
run: ./bin/mage lint-migrations
check-migration-versions:
if: github.event_name == 'pull_request'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
filter: blob:none
fetch-depth: 0
- name: Check migration versions
env:
BASE_REF: ${{ github.base_ref }}
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
run: bin/check-migration-versions

View File

@@ -8,10 +8,16 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
permissions:
id-token: write
contents: read
checks: write
actions: read
jobs:
# Only run if the PR has the minimal-tests label
check-label:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 1
outputs:
should-run: ${{ steps.check.outputs.result }}
@@ -29,7 +35,7 @@ jobs:
postgres-app-db-test:
needs: check-label
if: needs.check-label.outputs.should-run == 'true'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: "true"
@@ -70,8 +76,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -80,7 +85,7 @@ jobs:
postgres-driver-test:
needs: check-label
if: needs.check-label.outputs.should-run == 'true'
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 40
env:
CI: "true"
@@ -118,8 +123,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -128,7 +132,7 @@ jobs:
frontend-unit-tests:
needs: check-label
if: needs.check-label.outputs.should-run == 'true'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
env:
JEST_JUNIT_OUTPUT_DIR: ./target/junit
@@ -151,8 +155,7 @@ jobs:
input-path: ./target/junit
output-name: frontend-unit
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.run-tests.outcome }}
@@ -163,7 +166,7 @@ jobs:
- postgres-app-db-test
- postgres-driver-test
- frontend-unit-tests
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
name: minimal-tests-result
if: always() && !cancelled()

View File

@@ -7,11 +7,14 @@ on:
jobs:
module-boundaries-stats:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version-file: 'package.json'
- name: Install dependencies
run: bun install --frozen-lockfile
- name: get module boundary stats
@@ -20,6 +23,13 @@ jobs:
NAMED_MODULES=$(node --input-type=module -e "
import { elements } from './frontend/lint/module-boundaries.mjs';
const named = elements.filter(e => !['shared/other', 'app/misc', 'other'].includes(e.type));
console.log(named.length);
")
ENFORCED_MODULES=$(node --input-type=module -e "
import { elements } from './frontend/lint/module-boundaries.mjs';
const enforced = new Set(elements.filter(e => e.enforceOutgoing).map(e => e.type));
console.log(enforced.size);
")
NAMED_DIRS=$(node --input-type=module -e "
@@ -29,6 +39,7 @@ jobs:
const m = el.pattern.match(/^frontend\/src\/metabase\/(\w[\w-]*)\//)
if (m) dirs.add(m[1]);
}
for (const d of dirs) console.log(d);
")
TOTAL_DIRS=$(ls -d frontend/src/metabase/*/ | xargs -I{} basename {} | wc -l | tr -d ' ')
NAMED_COUNT=$(echo "$NAMED_DIRS" | grep -c . || true)
@@ -36,15 +47,17 @@ jobs:
VIOLATIONS=$(bun run module-boundaries 2>&1 | grep -o '✖ [0-9]* problems' | grep -o '[0-9]*' || echo "0")
echo "$NAMED_MODULES modules, $UNMODULED unmoduled folders, $VIOLATIONS violations"
echo "$NAMED_MODULES modules, $ENFORCED_MODULES enforced, $UNMODULED unmoduled folders, $VIOLATIONS violations"
echo "NAMED_MODULES=$NAMED_MODULES" >> $GITHUB_OUTPUT
echo "ENFORCED_MODULES=$ENFORCED_MODULES" >> $GITHUB_OUTPUT
echo "UNMODULED=$UNMODULED" >> $GITHUB_OUTPUT
echo "VIOLATIONS=$VIOLATIONS" >> $GITHUB_OUTPUT
- name: Upload stats
uses: actions/github-script@v7
env:
NAMED_MODULES: ${{ steps.get-stats.outputs.NAMED_MODULES }}
ENFORCED_MODULES: ${{ steps.get-stats.outputs.ENFORCED_MODULES }}
UNMODULED: ${{ steps.get-stats.outputs.UNMODULED }}
VIOLATIONS: ${{ steps.get-stats.outputs.VIOLATIONS }}
API_KEY: ${{ secrets.STATS_API_KEY }}
@@ -53,6 +66,7 @@ jobs:
const { uploadCsvToMb } = require('${{ github.workspace }}/.github/scripts/csv-to-mb.js');
console.log("NAMED_MODULES", Number(process.env.NAMED_MODULES));
console.log("ENFORCED_MODULES", Number(process.env.ENFORCED_MODULES));
console.log("UNMODULED", Number(process.env.UNMODULED));
console.log("VIOLATIONS", Number(process.env.VIOLATIONS));
@@ -60,6 +74,7 @@ jobs:
{
'Date': new Date().toISOString().slice(0, 10), // YYYY-MM-DD
'Modules': Number(process.env.NAMED_MODULES),
'Enforced modules': Number(process.env.ENFORCED_MODULES),
'Unmoduled folders': Number(process.env.UNMODULED),
'Violations': Number(process.env.VIOLATIONS),
}

View File

@@ -10,7 +10,7 @@ concurrency:
jobs:
check-openapi-specs:
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
if: contains(github.event.pull_request.labels.*.name, 'openapi-self-healing')
steps:

View File

@@ -14,14 +14,14 @@ concurrency:
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
openapi: ${{ steps.changes.outputs.openapi }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -30,7 +30,7 @@ jobs:
validate-openapi:
needs: files-changed
if: needs.files-changed.outputs.openapi == 'true'
runs-on: ubuntu-22.04
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

View File

@@ -7,7 +7,7 @@ on:
jobs:
optimize-images:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
if: ${{ contains(github.event.pull_request.labels.*.name,'Type:Documentation') }}
steps:
- name: Checkout repository

View File

@@ -5,7 +5,7 @@ on:
workflow_call:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
if: github.event.pull_request.head.repo.full_name == github.repository
name: Build Metabase Docker image
timeout-minutes: 60
@@ -17,13 +17,13 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.PR_ENV_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
aws-region: us-east-1
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2
with:
registries: "${{ secrets.PR_ENV_AWS_ACCOUNT_ID }}"
- name: Retrieve uberjar artifact for ee
@@ -38,7 +38,7 @@ jobs:
jar xf target/uberjar/metabase.jar
mv target/uberjar/metabase.jar bin/docker/metabase.jar
- name: Build container
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: bin/docker/
platforms: linux/amd64
@@ -47,7 +47,7 @@ jobs:
push: true
launch_perf_test:
needs: [ build ]
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
name: Launch Perf Test
permissions:
id-token: write
@@ -56,7 +56,7 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- name: Tailscale
uses: tailscale/github-action@v2
uses: tailscale/github-action@4e4c49acaa9818630ce0bd7a564372c17e33fb4d # v2
with:
oauth-client-id: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_SECRET }}
@@ -67,12 +67,14 @@ jobs:
id: create-oidc-token
shell: bash
run: |
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${{ secrets.PR_ENV_K8S_AUDIENCE }}"
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${PR_ENV_K8S_AUDIENCE}"
IDTOKEN=$(curl -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" -H "Accept: application/json; api-version=2.0" "$OIDC_URL_WITH_AUDIENCE" | jq -r .value)
echo "::add-mask::${IDTOKEN}"
echo "idToken=${IDTOKEN}" >>$GITHUB_OUTPUT
env:
PR_ENV_K8S_AUDIENCE: ${{ secrets.PR_ENV_K8S_AUDIENCE }}
- name: Setup Kube Context
uses: azure/k8s-set-context@v2
uses: azure/k8s-set-context@0b53301dcc7ddd3f6d35f97fb67228ac4e5ab2fd # v2
with:
method: kubeconfig
kubeconfig: |
@@ -95,7 +97,7 @@ jobs:
namespace: default
user: oidc-token
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.PR_ENV_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
@@ -106,9 +108,9 @@ jobs:
env:
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
id: split
run: echo "::set-output name=fragment::${SHA:5}"
run: echo "fragment=${SHA:5}" >> $GITHUB_OUTPUT
- name: Render Deployment YAML
uses: nowactions/envsubst@v1
uses: nowactions/envsubst@c4b8e3977354d294659a7ec164279ce8fba5c2e1 # v1
with:
input: ./perf-test-pr.yml.tmpl
output: ./perf-test-pr.yml

View File

@@ -14,7 +14,7 @@ on:
jobs:
approve:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
environment: pr-env-admin
steps:
- name: Approved

View File

@@ -19,7 +19,7 @@ env:
jobs:
destroy_pr:
runs-on: ubuntu-latest
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
name: PR Review ENV Destroy
permissions:
id-token: write
@@ -28,7 +28,7 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- name: Tailscale
uses: tailscale/github-action@v2
uses: tailscale/github-action@4e4c49acaa9818630ce0bd7a564372c17e33fb4d # v2
with:
oauth-client-id: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_SECRET }}
@@ -39,12 +39,14 @@ jobs:
id: create-oidc-token
shell: bash
run: |
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${{ secrets.PR_ENV_K8S_AUDIENCE }}"
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${PR_ENV_K8S_AUDIENCE}"
IDTOKEN=$(curl -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" -H "Accept: application/json; api-version=2.0" "$OIDC_URL_WITH_AUDIENCE" | jq -r .value)
echo "::add-mask::${IDTOKEN}"
echo "idToken=${IDTOKEN}" >>$GITHUB_OUTPUT
env:
PR_ENV_K8S_AUDIENCE: ${{ secrets.PR_ENV_K8S_AUDIENCE }}
- name: Setup Kube Context
uses: azure/k8s-set-context@v2
uses: azure/k8s-set-context@0b53301dcc7ddd3f6d35f97fb67228ac4e5ab2fd # v2
with:
method: kubeconfig
kubeconfig: |
@@ -73,7 +75,7 @@ jobs:
kubectl delete ns hosting-pr${{ env.PR_NUMBER }}
- name: Setup Helm
if: ${{ inputs.self_hosting }}
uses: azure/setup-helm@v4
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
with:
version: 'latest'
- name: Destroy PR Review ENV (self-hosting)
@@ -87,9 +89,15 @@ jobs:
sudo apt-get install -y postgresql-client
- name: Drop app database if exists
run: |
PGPASSWORD='${{ secrets.PR_ENV_DB_PASSWORD }}' \
PGPASSWORD="${PR_ENV_DB_PASSWORD}" \
psql \
-h ${{ secrets.PR_ENV_DB_HOST }} \
-U ${{ secrets.PR_ENV_DB_USER }} \
-d ${{ secrets.PR_ENV_DB_NAME }} \
-h "${PR_ENV_DB_HOST}" \
-U "${PR_ENV_DB_USER}" \
-d "${PR_ENV_DB_NAME}" \
-c "DROP DATABASE IF EXISTS hosting_pr${{ env.PR_NUMBER }};"
env:
PR_ENV_DB_PASSWORD: ${{ secrets.PR_ENV_DB_PASSWORD }}
PR_ENV_DB_HOST: ${{ secrets.PR_ENV_DB_HOST }}
PR_ENV_DB_USER: ${{ secrets.PR_ENV_DB_USER }}
PR_ENV_DB_NAME: ${{ secrets.PR_ENV_DB_NAME }}

Some files were not shown because too many files have changed in this diff Show More