Compare commits
4 Commits
jds/fix-de
...
openapi-sp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42e3a98805 | ||
|
|
245825e10e | ||
|
|
b0b2b597ab | ||
|
|
3eaf107895 |
@@ -12,7 +12,14 @@
|
||||
|
||||
## Testing
|
||||
|
||||
Use `./bin/test-agent` to run backend tests. See the top-level CLAUDE.md for usage.
|
||||
- **Run a test:** `./bin/mage run-tests namespace/test-name`
|
||||
- **Run all tests in a namespace:** `./bin/mage run-tests namespace`
|
||||
- **Run all tests for a module:** `./bin/mage run-tests test/metabase/notification` Because the module lives in that directory.
|
||||
|
||||
Note: the `./bin/mage run-tests` command accepts multiple args, so you can pass
|
||||
`./bin/mage run-tests namespace/test-name namespace/other-test namespace/third-test`
|
||||
to run 3 tests, or
|
||||
`./bin/mage run-tests test/metabase/module1 test/metabase/module2` to run 2 modules.
|
||||
|
||||
## Code Readability
|
||||
|
||||
|
||||
@@ -1,357 +0,0 @@
|
||||
--b-
|
||||
name: add-tracing
|
||||
description: Add OpenTelemetry tracing spans to Clojure code following Metabase tracing conventions. Use when instrumenting backend code with trace coverage.
|
||||
---
|
||||
|
||||
# Add Tracing Spans to Clojure Code
|
||||
|
||||
This skill helps you add OpenTelemetry (OTel) tracing spans to the Metabase backend codebase using the custom `tracing/with-span` macro.
|
||||
|
||||
## Reference Files
|
||||
|
||||
- `src/metabase/tracing/core.clj` - `with-span` macro, group registry, SDK lifecycle, `best-effort-sanitize-sql`, Pyroscope integration
|
||||
- `src/metabase/task/impl.clj` - `defjob` macro that wraps Quartz jobs with root spans
|
||||
- `.clj-kondo/config/modules/config.edn` - Module boundary configuration
|
||||
|
||||
## Module Architecture
|
||||
|
||||
The tracing module has a deliberately minimal API surface. **Only 2 namespaces are public** (listed in `:api` in the module config):
|
||||
|
||||
| Namespace | Role | Status |
|
||||
|---|---|---|
|
||||
| `tracing.core` | Primary API: `with-span`, groups, SDK lifecycle, Pyroscope, MDC, `best-effort-sanitize-sql` | **Public API** |
|
||||
| `tracing.init` | Side-effect loader for `quartz` and `settings` | **Public API** (init convention) |
|
||||
| `tracing.attributes` | `best-effort-sanitize-sql` implementation (re-exported via `tracing.core`) | Internal |
|
||||
| `tracing.settings` | Setting definitions (`MB_TRACING_*` env vars) | Internal |
|
||||
| `tracing.quartz` | Quartz JDBC proxy + JobListener | Internal |
|
||||
|
||||
**Rules:**
|
||||
- Only require `[metabase.tracing.core :as tracing]` from outside the module. `tracing/best-effort-sanitize-sql` and all other public functions are available from this single namespace.
|
||||
- Do not add new API namespaces. Add new public functions to `tracing.core` instead.
|
||||
- Do not require internal namespaces (`tracing.attributes`, `tracing.settings`, `tracing.quartz`) from outside the module.
|
||||
- `:uses :any` on the `core` module does NOT bypass the target module's `:api` check — internal namespaces are still enforced.
|
||||
|
||||
### Cyclic Dependency Avoidance
|
||||
|
||||
`tracing/core.clj` is required by many modules across the codebase. It **must NOT** compile-time require `tracing.settings`, as this creates transitive cyclic load dependencies (e.g., `settings/core -> tracing/settings -> tracing/core -> events/impl -> events/core`).
|
||||
|
||||
Instead, `tracing/core.clj` uses `requiring-resolve` for settings access:
|
||||
|
||||
```clojure
|
||||
;; CORRECT — lazy runtime resolution, no compile-time dependency
|
||||
((requiring-resolve 'metabase.tracing.settings/tracing-enabled))
|
||||
|
||||
;; WRONG — creates cyclic load dependency
|
||||
(require '[metabase.tracing.settings :as settings])
|
||||
(settings/tracing-enabled)
|
||||
```
|
||||
|
||||
External library namespaces (clj-otel API, SDK, exporters) are safe to require normally — they don't participate in Metabase namespace cycles.
|
||||
|
||||
**Important:** `requiring-resolve` must use **literal quoted symbols**. Kondo hooks validate that `required-namespaces` are all simple symbols, so dynamic construction fails:
|
||||
|
||||
```clojure
|
||||
;; CORRECT — literal quoted symbol
|
||||
(requiring-resolve 'metabase.tracing.settings/tracing-endpoint)
|
||||
|
||||
;; WRONG — kondo hook rejects this: "Assert failed: (every? simple-symbol? required-namespaces)"
|
||||
(requiring-resolve (symbol "metabase.tracing.settings" "tracing-endpoint"))
|
||||
```
|
||||
|
||||
## Quick Checklist
|
||||
|
||||
When adding tracing spans:
|
||||
|
||||
- [ ] Module has `tracing` in its `:uses` set in `.clj-kondo/config/modules/config.edn`
|
||||
- [ ] Added `[metabase.tracing.core :as tracing]` to ns requires (alphabetically sorted)
|
||||
- [ ] Span wraps a meaningful I/O boundary (not pure computation)
|
||||
- [ ] Group matches the domain (check `src/metabase/tracing/core.clj` for registered groups; add a new one if none fit)
|
||||
- [ ] Span name follows dot-notation convention (`"domain.subsystem.operation"`)
|
||||
- [ ] Attributes use namespaced keywords (`:search/query-length`, `:db/id`)
|
||||
- [ ] No sensitive data in attributes (use `best-effort-sanitize-sql` for HoneySQL, never raw SQL)
|
||||
- [ ] No new tracing namespaces created (add to `tracing.core` instead)
|
||||
- [ ] No `DO_NOT_ADD_NEW_FILES_HERE.txt` violations in the target directory
|
||||
- [ ] Run `clj-kondo --lint <files>` to verify 0 errors, 0 warnings
|
||||
- [ ] Add or update tests in the corresponding `test/` path (see Testing section below)
|
||||
- [ ] Run tests: `clojure -X:dev:test :only <test-ns>`
|
||||
|
||||
## The `with-span` Macro
|
||||
|
||||
```clojure
|
||||
(tracing/with-span group span-name attrs & body)
|
||||
```
|
||||
|
||||
- **group** - A keyword selecting which trace group this span belongs to (e.g., `:tasks`, `:sync`)
|
||||
- **span-name** - A string identifying the span in traces (e.g., `"search.execute"`)
|
||||
- **attrs** - A map of span attributes (e.g., `{:db/id 42}`)
|
||||
- **body** - The code to execute inside the span
|
||||
|
||||
**When disabled:** zero overhead -- single atom deref + boolean check, body runs directly.
|
||||
**When enabled:** creates OTel span AND injects `trace_id`/`span_id` into Log4j2 MDC for log-to-trace correlation.
|
||||
|
||||
## Trace Groups
|
||||
|
||||
Groups are registered in `src/metabase/tracing/core.clj`. Check that file for the current list. The general rule: **match the group to the domain, not the call site.** If code runs inside a Quartz job but is logically search work, use `:search`, not `:tasks`.
|
||||
|
||||
To add a new group:
|
||||
|
||||
```clojure
|
||||
;; In src/metabase/tracing/core.clj
|
||||
(register-group! :my-domain "Description of what this covers")
|
||||
```
|
||||
|
||||
Users enable groups via `MB_TRACING_GROUPS=tasks,search,sync` (comma-separated, or `"all"`).
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Span Names
|
||||
|
||||
Use dot-separated hierarchical names: `"domain.subsystem.operation"`. The domain prefix should match the group name:
|
||||
|
||||
```
|
||||
search.execute -- `:search` group
|
||||
sync.fingerprint.table -- `:sync` group
|
||||
task.session-cleanup.delete -- `:tasks` group
|
||||
db-app.collection-items -- `:db-app` group
|
||||
```
|
||||
|
||||
### Attributes
|
||||
|
||||
Use namespaced keywords. The namespace groups related attributes:
|
||||
|
||||
```clojure
|
||||
:db/id -- Database ID (integer)
|
||||
:db/engine -- Database engine name (string)
|
||||
:db/statement -- Sanitized SQL (string, via best-effort-sanitize-sql)
|
||||
:search/engine -- Search engine name (string)
|
||||
:search/query-length -- Query string length (integer)
|
||||
:sync/table -- Table name (string)
|
||||
:sync/step -- Sync step name (string)
|
||||
:task/name -- Task name (string)
|
||||
:http/method -- HTTP method (string)
|
||||
:http/url -- Request URL (string)
|
||||
```
|
||||
|
||||
Invent new namespaced attributes as needed (e.g., `:pulse/id`, `:transform/count`). Keep values as primitives (strings, numbers, booleans) -- no maps or collections.
|
||||
|
||||
## Step-by-Step: Adding a Span
|
||||
|
||||
### 1. Check module boundaries
|
||||
|
||||
Look up the module for your namespace in `.clj-kondo/config/modules/config.edn`. If `tracing` is not in the module's `:uses` set, add it (keep alphabetically sorted):
|
||||
|
||||
```edn
|
||||
my-module
|
||||
{:team "MyTeam"
|
||||
:uses #{analytics config tracing util}}
|
||||
```
|
||||
|
||||
### 2. Add the require
|
||||
|
||||
```clojure
|
||||
(ns metabase.my-module.thing
|
||||
(:require
|
||||
[metabase.tracing.core :as tracing]
|
||||
[metabase.util :as u]))
|
||||
```
|
||||
|
||||
`best-effort-sanitize-sql` is available from `tracing.core` — no additional require needed.
|
||||
|
||||
### 3. Identify the I/O boundary
|
||||
|
||||
Only wrap code at **meaningful I/O boundaries**:
|
||||
|
||||
**DO trace:**
|
||||
- External API calls (embedding APIs, metabot, webhooks)
|
||||
- Database queries (both app DB and user DB)
|
||||
- Network requests (HTTP calls to external services)
|
||||
- Heavy batch processing (batch indexing, batch embedding)
|
||||
- Top-level orchestration functions that coordinate multiple sub-operations
|
||||
|
||||
**DO NOT trace:**
|
||||
- Pure computation (sorting, filtering, mapping)
|
||||
- Simple single-row lookups (`t2/select-one :model/Setting :key k`)
|
||||
- Every function in a call chain (only boundaries matter)
|
||||
- Trivial operations (string formatting, hash calculations)
|
||||
|
||||
### 4. Wrap with `with-span`
|
||||
|
||||
```clojure
|
||||
;; Simple span (no attributes needed)
|
||||
(tracing/with-span :search "search.init-index" {}
|
||||
(do-expensive-thing))
|
||||
|
||||
;; Span with static attributes
|
||||
(tracing/with-span :sync "sync.fingerprint.table"
|
||||
{:db/id (:db_id table)
|
||||
:sync/table (:name table)}
|
||||
(fingerprint-fields! table fields))
|
||||
|
||||
;; Span with computed attributes
|
||||
(tracing/with-span :search "search.execute"
|
||||
{:search/engine (name (:search-engine ctx))
|
||||
:search/query-length (count (:search-string ctx))}
|
||||
(search.engine/results ctx))
|
||||
|
||||
;; Span with sanitized SQL (for dynamic HoneySQL queries)
|
||||
(let [hsql {:delete-from [(t2/table-name :model/Session)]
|
||||
:where [:< :created_at oldest-allowed]}]
|
||||
(tracing/with-span :tasks "task.session-cleanup.delete"
|
||||
{:db/statement (tracing/best-effort-sanitize-sql hsql)}
|
||||
(t2/query-one hsql)))
|
||||
|
||||
;; Sub-spans breaking a function into I/O phases
|
||||
(let [embedding (tracing/with-span :search "search.semantic.embedding"
|
||||
{:search.semantic/provider (:provider model)}
|
||||
(get-embedding model search-string))
|
||||
results (tracing/with-span :search "search.semantic.db-query" {}
|
||||
(into [] xform reducible))]
|
||||
(process results))
|
||||
|
||||
;; Per-item iteration spans
|
||||
(doseq [e (search.engine/active-engines)]
|
||||
(tracing/with-span :search "search.ingestion.update" {:search/engine (name e)}
|
||||
(search.engine/update! e batch)))
|
||||
```
|
||||
|
||||
### 5. Add tests
|
||||
|
||||
Create or update tests in the corresponding `test/` path. Follow the patterns in existing tracing tests:
|
||||
|
||||
- **Reference tests:** `test/metabase/tracing/quartz_test.clj`, `test/metabase/server/middleware/trace_test.clj`
|
||||
- Use `tracing/init-enabled-groups!` / `tracing/shutdown-groups!` with `try`/`finally` to manage group lifecycle
|
||||
- Test both enabled and disabled paths (verify zero overhead when group is off)
|
||||
- Use `reify` mocks for Java interfaces (Connection, PreparedStatement, JobListener, etc.)
|
||||
- Add `(set! *warn-on-reflection* true)` and type-hint proxy/reify calls to avoid reflection warnings
|
||||
|
||||
```clojure
|
||||
(deftest my-span-enabled-test
|
||||
(testing "when group is enabled, span is created"
|
||||
(try
|
||||
(tracing/init-enabled-groups! "my-group" "INFO")
|
||||
;; ... test that span behavior occurs ...
|
||||
(finally
|
||||
(tracing/shutdown-groups!)))))
|
||||
|
||||
(deftest my-span-disabled-test
|
||||
(testing "when group is disabled, code runs without tracing"
|
||||
(tracing/shutdown-groups!)
|
||||
;; ... test that code still works, no wrapping applied ...
|
||||
))
|
||||
```
|
||||
|
||||
### 6. Lint and run tests
|
||||
|
||||
```bash
|
||||
# Lint modified source and test files — expect 0 errors, 0 warnings
|
||||
clj-kondo --lint path/to/modified/file.clj path/to/test/file.clj
|
||||
|
||||
# Run tests (requires Java 21+)
|
||||
clojure -X:dev:test :only my-ns.test-ns
|
||||
```
|
||||
|
||||
Expect: all tests pass, 0 failures, 0 errors, no reflection warnings from your files.
|
||||
|
||||
## Sanitizing SQL for Attributes
|
||||
|
||||
When including SQL in span attributes, **always** use `tracing/best-effort-sanitize-sql`. This converts HoneySQL maps to parameterized SQL strings where values become `?` placeholders -- no data leaks.
|
||||
|
||||
```clojure
|
||||
(let [hsql {:delete-from [:core_session]
|
||||
:where [:< :created_at some-timestamp]}]
|
||||
(tracing/with-span :tasks "task.cleanup.delete"
|
||||
{:db/statement (tracing/best-effort-sanitize-sql hsql)}
|
||||
(t2/query-one hsql)))
|
||||
;; Trace attribute: db/statement = "DELETE FROM core_session WHERE created_at < ?"
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Never put raw SQL strings or user-provided values in attributes
|
||||
- Use `best-effort-sanitize-sql` only for app DB (HoneySQL) queries
|
||||
- For external/user DB queries, trace only timing and counts, not SQL content
|
||||
|
||||
## Defjob and Root Spans
|
||||
|
||||
The `defjob` macro in `metabase.task.impl` automatically wraps every Quartz job with a `:tasks` root span:
|
||||
|
||||
```clojure
|
||||
(task/defjob ^{DisallowConcurrentExecution true} SessionCleanup [_]
|
||||
(cleanup-sessions!))
|
||||
;; Automatically creates span: "task.SessionCleanup" {:task/name "SessionCleanup"}
|
||||
```
|
||||
|
||||
You do NOT need a root span inside `defjob` bodies. Add **child spans** for I/O inside the job.
|
||||
|
||||
For code on plain `Thread`s (not Quartz), add the root span manually:
|
||||
|
||||
```clojure
|
||||
(defn init! []
|
||||
(tracing/with-span :search "search.task.init" {}
|
||||
(search/init-index!)))
|
||||
```
|
||||
|
||||
## What NOT to Do
|
||||
|
||||
### Span Usage Mistakes
|
||||
|
||||
```clojure
|
||||
;; WRONG - pure computation, no I/O
|
||||
(tracing/with-span :search "search.format-results" {}
|
||||
(map format-result results))
|
||||
|
||||
;; WRONG - trivial single-row lookup
|
||||
(tracing/with-span :db-app "db-app.get-setting" {}
|
||||
(t2/select-one :model/Setting :key "my-setting"))
|
||||
|
||||
;; WRONG - raw SQL in attributes (data leak)
|
||||
(tracing/with-span :tasks "task.cleanup" {:db/statement raw-sql-string}
|
||||
(execute! raw-sql-string))
|
||||
|
||||
;; WRONG - wrong group (search work should use :search, not :tasks)
|
||||
(tracing/with-span :tasks "search.execute" {} ...)
|
||||
|
||||
;; WRONG - redundant nesting (do-search already has a span)
|
||||
(tracing/with-span :search "search.process" {}
|
||||
(let [results (do-search ctx)]
|
||||
(tracing/with-span :search "search.format" {}
|
||||
(format-results results))))
|
||||
```
|
||||
|
||||
### Architecture Mistakes
|
||||
|
||||
```clojure
|
||||
;; WRONG - creating a new tracing namespace
|
||||
(ns metabase.tracing.my-feature ...)
|
||||
|
||||
;; WRONG - requiring internal tracing namespaces from outside the module
|
||||
(ns metabase.my-module.thing
|
||||
(:require [metabase.tracing.attributes :as trace-attrs] ;; internal!
|
||||
[metabase.tracing.settings :as tracing.settings] ;; internal!
|
||||
[metabase.tracing.quartz :as tracing.quartz])) ;; internal!
|
||||
|
||||
;; WRONG - adding compile-time requires to tracing/core.clj for settings or SDK
|
||||
;; This creates cyclic load dependencies because tracing/core is widely required
|
||||
(ns metabase.tracing.core
|
||||
(:require [metabase.tracing.settings :as settings])) ;; causes cycle!
|
||||
|
||||
;; WRONG - dynamic symbol construction with requiring-resolve (kondo rejects it)
|
||||
(requiring-resolve (symbol "metabase.tracing.settings" "tracing-enabled"))
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
All settings are env-var-only (defined in `src/metabase/tracing/settings.clj`):
|
||||
|
||||
```bash
|
||||
# Core
|
||||
MB_TRACING_ENABLED=true # Enable tracing (default: false)
|
||||
MB_TRACING_ENDPOINT=host:4317 # OTLP collector endpoint (default: http://localhost:4317)
|
||||
MB_TRACING_GROUPS=tasks,search,sync # Comma-separated groups or "all" (default: all)
|
||||
MB_TRACING_SERVICE_NAME=metabase # Service name in traces (default: hostname)
|
||||
MB_TRACING_LOG_LEVEL=DEBUG # Log threshold for traced threads: TRACE/DEBUG/INFO (default: INFO)
|
||||
|
||||
# Batch span processor tuning
|
||||
MB_TRACING_MAX_QUEUE_SIZE=2048 # Max spans queued for export; drops when full (default: 2048)
|
||||
MB_TRACING_EXPORT_TIMEOUT_MS=10000 # Max wait for batch export to complete (default: 10000)
|
||||
MB_TRACING_SCHEDULE_DELAY_MS=5000 # Delay between consecutive batch exports (default: 5000)
|
||||
```
|
||||
@@ -1,173 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,260 +0,0 @@
|
||||
---
|
||||
name: e2e-test-create
|
||||
description: >
|
||||
Analyze React component source code to understand UI structure,
|
||||
then generate idiomatic Cypress E2E tests following Metabase conventions.
|
||||
Falls back to Playwright MCP browser exploration only when code reading
|
||||
and screenshot debugging are insufficient.
|
||||
disable-model-invocation: true
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Grep
|
||||
- Glob
|
||||
- Skill
|
||||
- "mcp__playwright__*"
|
||||
---
|
||||
|
||||
# Code-Reading-First → Generate Cypress Tests (Metabase)
|
||||
|
||||
You are writing Cypress E2E tests for the **Metabase** codebase.
|
||||
Before generating ANY test code, you MUST analyze React component source code
|
||||
to understand DOM structure, selectors, and user flows.
|
||||
|
||||
## Phase 0 — Research
|
||||
|
||||
1. Read existing helpers before writing anything:
|
||||
- `e2e/support/helpers/` — all shared helpers (restore, signInAs, openOrdersTable, etc.)
|
||||
- `e2e/support/cypress_sample_database.ts` — table/field schema constants (ORDERS, PRODUCTS, etc.)
|
||||
- `e2e/support/cypress_sample_instance_data.ts` — instance-specific IDs (ORDERS_DASHBOARD_ID, NORMAL_USER_ID, etc.)
|
||||
2. Glob `e2e/test/scenarios/` to find the closest existing spec to the area under test.
|
||||
Study its patterns — match them exactly.
|
||||
3. Glob `frontend/src/metabase/` to find React components for the feature area.
|
||||
|
||||
## Phase 1 — Code Analysis
|
||||
|
||||
Read React component source to understand DOM structure. No browser needed — source code has everything.
|
||||
|
||||
1. **Find relevant components**: Glob and grep `frontend/src/metabase/` for the feature area.
|
||||
2. **Extract selectors**: Grep for `data-testid` in relevant components.
|
||||
3. **Note visible text**: Read component JSX for button labels, headings, placeholders.
|
||||
4. **Note aria attributes**: Grep for `aria-label` in relevant components.
|
||||
5. **Understand user flows**: Read event handlers (onClick, onSubmit, onChange) to understand interactions.
|
||||
6. **Find API calls**: Grep for `Api.use`, `fetch`, `useQuery`, endpoint definitions to identify API calls to intercept.
|
||||
7. **Cross-reference with existing specs**: Find specs in the same area and reuse their proven selectors and `cy.intercept` patterns.
|
||||
|
||||
## Phase 2 — Start Backend
|
||||
|
||||
Use `MB_EDITION=oss` by default. Only use `MB_EDITION=ee` when the user explicitly asks to write an enterprise test.
|
||||
|
||||
Start the backend using `run_in_background: true` (NOT `&`).
|
||||
`bin/e2e-backend` automatically detects if a backend is already running and reuses it.
|
||||
```bash
|
||||
MB_EDITION=oss bin/e2e-backend
|
||||
```
|
||||
|
||||
Do NOT manually generate snapshots by running unrelated test specs.
|
||||
The `bun test-cypress` runner has `GENERATE_SNAPSHOTS: true` by default and automatically
|
||||
generates snapshots before running any spec. When running tests in Phase 4 via the `/e2e-test` skill,
|
||||
snapshots will be generated on the first run if they don't already exist.
|
||||
|
||||
Restore clean test data:
|
||||
```bash
|
||||
curl -sf -X POST http://localhost:4000/api/testing/restore/default
|
||||
```
|
||||
|
||||
## Phase 3 — Generate Cypress Spec
|
||||
|
||||
### File location & naming
|
||||
|
||||
Place specs in `e2e/test/scenarios/<area>/` mirroring the URL structure.
|
||||
Name: `<feature>.cy.spec.js` (NOT `.cy.spec.ts`).
|
||||
|
||||
### Metabase conventions (mandatory)
|
||||
|
||||
- **Helpers via `cy.H`**: All helpers are accessed via `const { H } = cy;` — NOT via direct imports from `e2e/support/helpers`.
|
||||
|
||||
```js
|
||||
const { H } = cy;
|
||||
|
||||
describe("feature name", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- **Sample Database schema** (table/field definitions): Import from `cypress_sample_database`.
|
||||
|
||||
```js
|
||||
import { ORDERS, ORDERS_ID, PRODUCTS } from "e2e/support/cypress_sample_database";
|
||||
```
|
||||
|
||||
- **Instance data** (dashboard IDs, user IDs, etc.): Import from `cypress_sample_instance_data`.
|
||||
|
||||
```js
|
||||
import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data";
|
||||
```
|
||||
|
||||
- **Selectors** (priority order):
|
||||
1. `cy.findByText()` / `cy.findByLabelText()` / `cy.findByRole()` — from `@testing-library/cypress`
|
||||
2. `cy.findByTestId()` — for `data-testid` attributes
|
||||
3. `cy.get("[data-testid='...']")` — fallback
|
||||
4. NEVER use positional selectors, CSS class names, or XPaths.
|
||||
|
||||
- **Navigation helpers**: Use existing helpers like `H.openOrdersTable()`, `H.openNativeEditor()`,
|
||||
`H.visitDashboard(id)`, `H.visitQuestion(id)` instead of raw `cy.visit()` chains.
|
||||
Grep `e2e/support/helpers/` to discover what's available for your area.
|
||||
|
||||
- **API setup over UI setup**: Use `cy.request()` or existing API helpers to set up state.
|
||||
Only use the UI for the flow you're actually testing.
|
||||
|
||||
- **Assertions**: Assert on visible text, URL, aria state — not DOM structure.
|
||||
|
||||
- **Waits**: Never use `cy.wait(ms)`. Use `cy.intercept()` + `cy.wait("@alias")` for API calls,
|
||||
or `cy.findByText().should("be.visible")` for DOM readiness.
|
||||
|
||||
- **Isolation**: Each `it()` block must be independently runnable.
|
||||
Don't depend on state from a previous `it()`.
|
||||
|
||||
### Spec structure template
|
||||
|
||||
```js
|
||||
const { H } = cy;
|
||||
import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data";
|
||||
import { ORDERS, ORDERS_ID } from "e2e/support/cypress_sample_database";
|
||||
|
||||
describe("area > sub-area > feature (#issue-number)", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
});
|
||||
|
||||
it("should do the primary happy-path thing", () => {
|
||||
// test
|
||||
});
|
||||
|
||||
it("should handle the edge case", () => {
|
||||
// test
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Intercepts
|
||||
|
||||
When you identified API calls during code analysis, stub or wait on them:
|
||||
|
||||
```js
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
// ... trigger action ...
|
||||
cy.wait("@dataset");
|
||||
```
|
||||
|
||||
## Phase 4 — Validate
|
||||
|
||||
After generating specs:
|
||||
1. Check that all imported helpers exist (Grep `e2e/support/helpers/`).
|
||||
2. **You MUST use the `/e2e-test` skill** to run tests — do NOT run `bun test-cypress` directly.
|
||||
The `/e2e-test` skill handles edition selection, snapshot management, and correct env vars.
|
||||
`/e2e-test GREP="should do the thing" --spec e2e/test/scenarios/<path>`
|
||||
If you created multiple `it()` blocks, run each one individually to isolate failures.
|
||||
|
||||
## Phase 5 — Fix Failures (up to 2 attempts)
|
||||
|
||||
When a test fails, **try to fix it from Cypress output first**:
|
||||
1. Read the failure screenshot (path printed under `(Screenshots)`).
|
||||
2. Read the error message and code frame from the console output.
|
||||
3. Fix the test and re-run (back to Phase 4, step 2).
|
||||
|
||||
If you cannot diagnose the issue after 2 attempts, proceed to Phase 6.
|
||||
|
||||
## Phase 6 — Playwright Fallback
|
||||
|
||||
Only reach this phase after 2 failed fix attempts from Phase 5. The backend is already running.
|
||||
|
||||
Restore clean test data:
|
||||
```bash
|
||||
curl -sf -X POST http://localhost:4000/api/testing/restore/default
|
||||
```
|
||||
|
||||
**Bypass CSP** headers before navigating (Metabase serves strict CSP that blocks dev server scripts).
|
||||
Use `browser_run_code` to set this up:
|
||||
|
||||
```js
|
||||
async (page) => {
|
||||
// Strip CSP headers so the page loads (mirrors Cypress chromeWebSecurity: false)
|
||||
await page.context().route('**/*', async (route) => {
|
||||
const response = await route.fetch();
|
||||
const headers = { ...response.headers() };
|
||||
delete headers['content-security-policy'];
|
||||
delete headers['content-security-policy-report-only'];
|
||||
await route.fulfill({ response, headers });
|
||||
});
|
||||
|
||||
// Sign in via API
|
||||
const response = await page.request.post('http://localhost:4000/api/session', {
|
||||
data: { username: 'admin@metabase.test', password: '12341234' }
|
||||
});
|
||||
const session = await response.json();
|
||||
await page.context().addCookies([{
|
||||
name: 'metabase.DEVICE',
|
||||
value: session.id,
|
||||
domain: 'localhost',
|
||||
path: '/'
|
||||
}]);
|
||||
|
||||
await page.goto('http://localhost:4000');
|
||||
await page.waitForLoadState('networkidle');
|
||||
return 'signed in';
|
||||
}
|
||||
```
|
||||
|
||||
**Maintain an observation log incrementally.** After EVERY significant Playwright interaction,
|
||||
IMMEDIATELY append what you observed to the scratch file BEFORE performing the next interaction:
|
||||
|
||||
```bash
|
||||
cat >> /tmp/e2e-observations.md << 'OBSERVATION'
|
||||
## [Page/Flow name]
|
||||
- URL: /question/notebook#...
|
||||
- Clicked: "Box plot" button → visible text "Box plot", role: radio
|
||||
- Selectors: data-testid="viz-type-button", findByText("Box plot")
|
||||
- API call: POST /api/dataset (triggered on viz change)
|
||||
- Key state: after selecting viz type, summary sidebar shows metric picker
|
||||
OBSERVATION
|
||||
```
|
||||
|
||||
For each page/flow:
|
||||
- Take an accessibility snapshot (`browser_snapshot`).
|
||||
- Click through interactive elements, fill forms, trigger modals.
|
||||
- **Append to the observation log immediately after each step**: URLs, visible text, aria labels, `data-testid` attrs, API calls.
|
||||
- Screenshot key states.
|
||||
|
||||
After exploration:
|
||||
1. Read back your observation log: `cat /tmp/e2e-observations.md`
|
||||
2. Fix the test using observed selectors and behavior.
|
||||
3. Re-run the test (back to Phase 4).
|
||||
4. Clean up: `rm -f /tmp/e2e-observations.md`
|
||||
|
||||
## Phase 7 — Cleanup
|
||||
|
||||
After all tests pass (or after giving up on fixing failures), **always kill the backend on port 4000**:
|
||||
```bash
|
||||
lsof -ti:4000 | xargs kill 2>/dev/null || true
|
||||
```
|
||||
|
||||
Do NOT use broad `pkill` patterns — there may be other Metabase instances on different ports.
|
||||
The backend process started in Phase 2 will NOT be killed automatically when the Claude session ends.
|
||||
Leaving it running wastes resources and can interfere with future sessions. Always clean up.
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- Do NOT use Playwright as the first step — always analyze source code first.
|
||||
- Do NOT kill the backend between phases — it stays running throughout.
|
||||
- Do NOT invent selectors you didn't find in source code or observe in the browser.
|
||||
- Do NOT hardcode database IDs — import from `cypress_sample_database` or `cypress_sample_instance_data`.
|
||||
- Do NOT use `cy.wait(1000)` or any numeric wait.
|
||||
- Do NOT create setup flows through the UI when an API helper exists.
|
||||
- Do NOT put tests in `cypress/e2e/` — Metabase uses `e2e/test/scenarios/`.
|
||||
- Do NOT import helpers directly from `e2e/support/helpers` — use `const { H } = cy;`.
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
name: e2e-test
|
||||
description: Run Cypress E2E tests, analyze failures including screenshots, and stress test for flakiness
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
Run Cypress E2E test: $ARGUMENTS
|
||||
|
||||
## Edition selection
|
||||
|
||||
**Default: `MB_EDITION=oss`** — runs without enterprise tokens, simpler and faster.
|
||||
|
||||
Only use `MB_EDITION=ee` when the user explicitly asks to write or run an enterprise test.
|
||||
|
||||
**For `MB_EDITION=ee`:**
|
||||
These tokens must be exported in the shell before starting Claude Code:
|
||||
- `CYPRESS_MB_ALL_FEATURES_TOKEN` — all features (most EE tests need this)
|
||||
- `CYPRESS_MB_PRO_SELF_HOSTED_TOKEN` — pro self-hosted (permissions, SSO, sandboxing)
|
||||
- `CYPRESS_MB_STARTER_CLOUD_TOKEN` — starter plan (rarely needed)
|
||||
- `CYPRESS_MB_PRO_CLOUD_TOKEN` — pro cloud (rarely needed)
|
||||
|
||||
**NEVER echo, print, or log token values. Only check if they are set:**
|
||||
```bash
|
||||
echo "ALL_FEATURES: ${CYPRESS_MB_ALL_FEATURES_TOKEN:+set}" && echo "PRO_SELF_HOSTED: ${CYPRESS_MB_PRO_SELF_HOSTED_TOKEN:+set}" && echo "STARTER: ${CYPRESS_MB_STARTER_CLOUD_TOKEN:+set}" && echo "PRO_CLOUD: ${CYPRESS_MB_PRO_CLOUD_TOKEN:+set}"
|
||||
```
|
||||
|
||||
If tokens are missing, tell the user: "EE tokens are not set. Either export them and restart Claude Code, or add `MB_EDITION=oss` to run OSS-only tests."
|
||||
|
||||
## Running
|
||||
|
||||
First check if snapshots exist:
|
||||
```bash
|
||||
ls e2e/snapshots/default.sql 2>/dev/null && echo "snapshots exist" || echo "snapshots missing"
|
||||
```
|
||||
|
||||
If snapshots exist, skip regeneration for speed:
|
||||
```bash
|
||||
MB_EDITION=oss CYPRESS_VIDEO=false CYPRESS_RETRIES=0 CYPRESS_GUI=false GENERATE_SNAPSHOTS=false bun test-cypress $ARGUMENTS
|
||||
```
|
||||
|
||||
If snapshots are missing (first run), let the runner generate them:
|
||||
```bash
|
||||
MB_EDITION=oss CYPRESS_VIDEO=false CYPRESS_RETRIES=0 CYPRESS_GUI=false bun test-cypress $ARGUMENTS
|
||||
```
|
||||
|
||||
When running enterprise tests, replace `MB_EDITION=oss` with `MB_EDITION=ee` in the commands above.
|
||||
|
||||
If `$ARGUMENTS` is empty, ask the user which spec to run.
|
||||
|
||||
If the user provides a test name or fuzzy name instead of a spec path, find the spec file:
|
||||
```bash
|
||||
find e2e/test/scenarios -name "*FUZZY_NAME*.cy.spec.*" -type f
|
||||
```
|
||||
Then run with `--spec "path/to/matched.cy.spec.js"`.
|
||||
|
||||
To filter by test name, use the `GREP` env var (not `--env grep=`, which breaks on commas):
|
||||
```bash
|
||||
GREP="test name here" MB_EDITION=oss bun test-cypress --spec path/to/spec.js
|
||||
```
|
||||
|
||||
## Stress test (flaky detection)
|
||||
|
||||
When the user asks to verify a test is not flaky:
|
||||
|
||||
```bash
|
||||
MB_EDITION=oss bin/e2e-stress-test $ARGUMENTS
|
||||
```
|
||||
|
||||
Set `E2E_STRESS_RUNS=N` for more iterations (default: 5). Stops on first failure and prints screenshot paths. Read those screenshots to analyze the failure.
|
||||
|
||||
## Analyzing failures
|
||||
|
||||
When tests fail:
|
||||
|
||||
1. Read the **screenshot** for each failure — paths are printed in Cypress output under `(Screenshots)`, typically at `cypress/screenshots/<spec-name>/<test-name> (failed).png`
|
||||
2. Read the error message and code frame from the console output
|
||||
3. Identify the root cause: is it a test bug, a product bug, or a timing issue?
|
||||
4. Suggest a fix with specific code changes
|
||||
@@ -1,834 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -422,8 +422,6 @@
|
||||
ring.util.codec codec
|
||||
ring.util.response response
|
||||
saml20-clj.core saml
|
||||
metabase.transforms-base.util transforms-base.u
|
||||
metabase.transforms.util transforms.u
|
||||
toucan2.core t2}}}
|
||||
|
||||
:lint-as
|
||||
@@ -793,7 +791,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
@@ -110,7 +110,6 @@
|
||||
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!
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
help-link-custom-destination
|
||||
instance-creation
|
||||
is-hosted?
|
||||
is-metabot-enabled
|
||||
jdbc-data-warehouse-max-connection-pool-size
|
||||
jdbc-data-warehouse-unreturned-connection-timeout-seconds
|
||||
jwt-attribute-email
|
||||
|
||||
8
.gitattributes
vendored
8
.gitattributes
vendored
@@ -1,9 +1,7 @@
|
||||
# Custom merge driver for per-release YAML migration files.
|
||||
# Treats each changeset as an atomic unit to avoid conflicts within changesets.
|
||||
# Only applies to per-release *_update_migrations.yaml files — directory-based
|
||||
# migrations (e.g. 060/*.yaml) have one file per migration and don't need this.
|
||||
# Custom merge driver for YAML migration files
|
||||
# Treats each changeset as an atomic unit to avoid conflicts within changesets
|
||||
# Execute this in your shell to enable custom merge driver:
|
||||
# echo '\n[include]\npath=../.gitconfig' >> .git/config
|
||||
#
|
||||
# TODO (Bryan 12/12/25) -- reenable this when we can completely verify correctness
|
||||
# resources/migrations/*_update_migrations.yaml merge=yaml-migrations
|
||||
# resources/migrations/*.yaml merge=yaml-migrations
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# execute this in your shell to enable custom merge driver:
|
||||
# echo '\n[include]\npath=../.gitconfig' >> .git/config
|
||||
[merge "yaml-migrations"]
|
||||
name = Per-release YAML migration changeset-aware merge
|
||||
name = YAML migration changeset-aware merge
|
||||
driver = bin/mage -merge-yaml-migrations %O %A %B %L %P
|
||||
|
||||
268
.github/CODEOWNERS
vendored
268
.github/CODEOWNERS
vendored
@@ -1,30 +1,266 @@
|
||||
# Before changing this file, make sure you have read how we use it:
|
||||
# https://www.notion.so/metabase/Pull-Request-Code-Review-Guide-374f05df889748b69d67af753f9bc9b8?pvs=4#36550c04ceab40b5a8b8bee6264b090c
|
||||
|
||||
# @metabase/core-backend-querying-platform
|
||||
src/metabase/lib @metabase/core-backend-querying-platform
|
||||
src/metabase/lib_be @metabase/core-backend-querying-platform
|
||||
src/metabase/query_processor @metabase/core-backend-querying-platform
|
||||
|
||||
# @metabase/graphy
|
||||
src/metabase/graph @metabase/graphy
|
||||
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
|
||||
frontend/src/metabase/ui @metabase/core-frontend-ux-west
|
||||
# snowplow/* @metabase/data
|
||||
src/metabase/analytics/prometheus.clj @metabase/cloud-ops
|
||||
.github @metabase/devexp
|
||||
.clj-kondo @metabase/devexp
|
||||
deps.edn @metabase/backend-developers
|
||||
|
||||
# @metabase/tech-writers
|
||||
docs @metabase/tech-writers
|
||||
docs/developers-guide # exclude from tech-writer ownership
|
||||
|
||||
# @metabase/devexp
|
||||
.github @metabase/devexp
|
||||
#
|
||||
# Backend modules <=> teams that own them
|
||||
#
|
||||
|
||||
# @metabase/cloud-ops
|
||||
src/metabase/analytics/prometheus.clj @metabase/cloud-ops
|
||||
# modules/drivers @metabase/drivers
|
||||
src/metabase/driver/sql_jdbc/actions.clj @metabase/core-ux-east
|
||||
test/metabase/driver/sql_jdbc/actions_test.clj @metabase/core-ux-east
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/advanced_config @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/advanced_config @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/advanced_permissions @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/advanced_permissions @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/airgap @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/airgap @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/api @metabase/devexp
|
||||
# enterprise/backend/test/metabase_enterprise/api @metabase/devexp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/audit_app @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/audit_app @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/auth_provider @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/auth_provider @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/billing @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/billing @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/cache @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/cache @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/content_verification @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/content_verification @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/core @metabase/devexp
|
||||
# enterprise/backend/test/metabase_enterprise/core @metabase/devexp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/dashboard_subscription_filters @metabase/core-ux-east
|
||||
# enterprise/backend/test/metabase_enterprise/dashboard_subscription_filters @metabase/core-ux-east
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/enhancements @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/enhancements @metabase/core-backend-admin-webapp
|
||||
|
||||
# # enterprise/backend/src/metabase_enterprise/llm
|
||||
# # enterprise/backend/test/metabase_enterprise/llm
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/query_reference_validation @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/query_reference_validation @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/sandbox @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/sandbox @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/scim @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/scim @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/search ...
|
||||
# enterprise/backend/test/metabase_enterprise/search ...
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/serialization @metabase/core-ux-east
|
||||
# enterprise/backend/test/metabase_enterprise/serialization @metabase/core-ux-east
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/snippet_collections @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/snippet_collections @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/sso @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/sso @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/stale @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/stale @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/stats @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/stats @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/task @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/task @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/upload_management @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/upload_management @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/actions @metabase/core-ux-east
|
||||
test/metabase/actions @metabase/core-ux-east
|
||||
|
||||
# src/metabase/activity_feed @metabase/core-backend-admin-webapp
|
||||
# test/metabase/activity_feed @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/analytics @metabase/core-backend-admin-webapp
|
||||
# test/metabase/analytics @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/analyze @metabase/drivers
|
||||
# test/metabase/analyze @metabase/drivers
|
||||
|
||||
# src/metabase/api @metabase/devexp
|
||||
# test/metabase/api @metabase/devexp
|
||||
|
||||
# src/metabase/audit @metabase/core-backend-admin-webapp
|
||||
# test/metabase/audit @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/auth_provider @metabase/core-backend-admin-webapp
|
||||
# test/metabase/auth_provider @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/bookmarks @metabase/core-backend-admin-webapp
|
||||
# test/metabase/bookmarks @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/cmd @metabase/devexp
|
||||
# test/metabase/cmd @metabase/devexp
|
||||
|
||||
# src/metabase/channel @metabase/core-ux-east
|
||||
# test/metabase/channel @metabase/core-ux-east
|
||||
|
||||
# src/metabase/cloud_migration ...
|
||||
# test/metabase/cloud_migration ...
|
||||
|
||||
# src/metabase/cmd @metabase/devexp
|
||||
# test/metabase/cmd @metabase/devexp
|
||||
|
||||
# src/metabase/config @metabase/devexp
|
||||
# test/metabase/config @metabase/devexp
|
||||
|
||||
# src/metabase/core @metabase/devexp
|
||||
# test/metabase/core @metabase/devexp
|
||||
|
||||
# src/metabase/db @metabase/core-backend-admin-webapp
|
||||
# test/metabase/db @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/driver @metabase/drivers
|
||||
# test/metabase/driver @metabase/drivers
|
||||
|
||||
# src/metabase/eid_translation @metabase/core-backend-admin-webapp
|
||||
# test/metabase/eid_translation @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/embed @metabase/embedding
|
||||
# test/metabase/embed @metabase/embedding
|
||||
|
||||
# src/metabase/events @metabase/devexp
|
||||
# test/metabase/events @metabase/devexp
|
||||
|
||||
# src/metabase/formatter @metabase/core-backend-querying
|
||||
# test/metabase/formatter @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/indexed_entities @metabase/core-backend-querying
|
||||
# test/metabase/indexed_entities @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/integrations ...
|
||||
# test/metabase/integrations ...
|
||||
|
||||
# src/metabase/legacy_mbql @metabase/core-backend-querying
|
||||
# test/metabase/legacy_mbql @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/lib @metabase/core-backend-querying
|
||||
# test/metabase/lib @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/logger @metabase/devexp
|
||||
# test/metabase/logger @metabase/devexp
|
||||
|
||||
# src/metabase/login_history @metabase/core-backend-admin-webapp
|
||||
# test/metabase/login_history @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/model_persistence @metabase/core-backend-querying
|
||||
# test/metabase/model_persistence @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/models @metabase/devexp
|
||||
# test/metabase/models @metabase/devexp
|
||||
|
||||
# src/metabase/moderation @metabase/core-backend-admin-webapp
|
||||
# test/metabase/moderation @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/notification @metabase/core-ux-east
|
||||
# test/metabase/notification @metabase/core-ux-east
|
||||
|
||||
# src/metabase/permissions @metabase/core-backend-admin-webapp
|
||||
# test/metabase/permissions @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/plugins @metabase/devexp
|
||||
# test/metabase/plugins @metabase/devexp
|
||||
|
||||
# src/metabase/premium_features @metabase/core-backend-admin-webapp
|
||||
# test/metabase/premium_features @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/public_settings @metabase/core-backend-admin-webapp
|
||||
# test/metabase/public_settings @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/public_sharing @metabase/core-backend-admin-webapp
|
||||
# test/metabase/public_sharing @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/pulse @metabase/core-ux-east
|
||||
# test/metabase/pulse @metabase/core-ux-east
|
||||
|
||||
# src/metabase/query_analysis @metabase/core-backend-querying
|
||||
# test/metabase/query_analysis @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/query_processor @metabase/core-backend-querying
|
||||
# test/metabase/query_processor @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/request @metabase/devexp
|
||||
# test/metabase/request @metabase/devexp
|
||||
|
||||
# src/metabase/revisions @metabase/core-backend-admin-webapp
|
||||
# test/metabase/revisions @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/sample_data @metabase/core-backend-admin-webapp
|
||||
# test/metabase/sample_data @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/search ...
|
||||
# test/metabase/search ...
|
||||
|
||||
# src/metabase/segments @metabase/core-backend-querying
|
||||
# test/metabase/segments @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/server @metabase/devexp
|
||||
# test/metabase/server @metabase/devexp
|
||||
|
||||
# src/metabase/session @metabase/core-backend-admin-webapp
|
||||
# test/metabase/session @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/setup @metabase/core-backend-admin-webapp
|
||||
# test/metabase/setup @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/sso @metabase/core-backend-admin-webapp
|
||||
# test/metabase/sso @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/sync @metabase/drivers
|
||||
# test/metabase/sync @metabase/drivers
|
||||
|
||||
# src/metabase/task @metabase/devexp
|
||||
# test/metabase/task @metabase/devexp
|
||||
|
||||
# src/metabase/tiles @metabase/core-backend-dashviz
|
||||
# test/metabase/tiles @metabase/core-backend-dashviz
|
||||
|
||||
# src/metabase/timeline @metabase/core-backend-dashviz
|
||||
# test/metabase/timeline @metabase/core-backend-dashviz
|
||||
|
||||
# src/metabase/types @metabase/core-backend-querying
|
||||
# test/metabase/types @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/upload @metabase/core-backend-admin-webapp
|
||||
# test/metabase/upload @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/user_key_value @metabase/core-backend-admin-webapp
|
||||
# test/metabase/user_key_value @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/util @metabase/devexp
|
||||
# test/metabase/util @metabase/devexp
|
||||
|
||||
# src/metabase/xrays @metabase/core-backend-querying
|
||||
# test/metabase/xrays @metabase/core-backend-querying
|
||||
|
||||
25
.github/actions/fetch-artifact/action.yml
vendored
25
.github/actions/fetch-artifact/action.yml
vendored
@@ -51,25 +51,12 @@ runs:
|
||||
|
||||
const artifact_id = artifacts.data.artifacts[0].id;
|
||||
|
||||
let download;
|
||||
const maxAttempts = 3;
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: artifact_id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
break;
|
||||
} catch (err) {
|
||||
console.log(`Attempt ${attempt}/${maxAttempts} failed: ${err.message}`);
|
||||
if (attempt === maxAttempts) throw err;
|
||||
const delay = attempt * 15000; // 15s, 30s
|
||||
console.log(`Retrying in ${delay / 1000}s...`);
|
||||
await new Promise(r => setTimeout(r, delay));
|
||||
}
|
||||
}
|
||||
const download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: artifact_id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
|
||||
|
||||
|
||||
8
.github/actions/get-cache-keys/action.yml
vendored
8
.github/actions/get-cache-keys/action.yml
vendored
@@ -7,15 +7,9 @@ outputs:
|
||||
m2-cache-key:
|
||||
description: "The cache key for the Maven cache."
|
||||
value: ${{ steps.keys-factory.outputs.m2-cache-key }}
|
||||
m2-restore-key:
|
||||
description: "The fallback restore key prefix for the Maven cache."
|
||||
value: ${{ steps.keys-factory.outputs.m2-restore-key }}
|
||||
node-cache-key:
|
||||
description: "The cache key for the Node.js cache."
|
||||
value: ${{ steps.keys-factory.outputs.node-cache-key }}
|
||||
node-restore-key:
|
||||
description: "The fallback restore key prefix for the Node.js cache."
|
||||
value: ${{ steps.keys-factory.outputs.node-restore-key }}
|
||||
eslint-cache-key:
|
||||
description: "The cache key for Eslint cache"
|
||||
value: ${{ steps.keys-factory.outputs.eslint-cache-key }}
|
||||
@@ -37,9 +31,7 @@ runs:
|
||||
CACHE_SUFFIX="${{ runner.os }}-$WEEK_OF_YEAR"
|
||||
|
||||
echo "m2-cache-key=$M2_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "m2-restore-key=$M2_CACHE_PREFIX-" >> $GITHUB_OUTPUT
|
||||
echo "node-cache-key=$NODE_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "node-restore-key=$NODE_CACHE_PREFIX-" >> $GITHUB_OUTPUT
|
||||
echo "eslint-cache-key=$ESLINT_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "cypress-cache-key=$CYPRESS_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
16
.github/actions/prepare-backend/action.yml
vendored
16
.github/actions/prepare-backend/action.yml
vendored
@@ -19,18 +19,10 @@ runs:
|
||||
distribution: "temurin"
|
||||
- name: Install Clojure CLI
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
for attempt in 1 2 3; do
|
||||
curl --retry 3 --retry-delay 5 --fail -O https://download.clojure.org/install/linux-install-${{ inputs.clojure-version }}.sh \
|
||||
&& sudo bash ./linux-install-${{ inputs.clojure-version }}.sh \
|
||||
&& break
|
||||
echo "::warning::Clojure install attempt $attempt failed, retrying in 10s..."
|
||||
sleep 10
|
||||
done
|
||||
if ! clojure --version; then
|
||||
echo "::error::Clojure CLI installation failed after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
curl -O https://download.clojure.org/install/linux-install-${{ inputs.clojure-version }}.sh &&
|
||||
sudo bash ./linux-install-${{ inputs.clojure-version }}.sh
|
||||
|
||||
- name: Get M2 cache key
|
||||
uses: ./.github/actions/get-cache-keys
|
||||
@@ -50,5 +42,3 @@ runs:
|
||||
~/.m2
|
||||
~/.gitlibs
|
||||
key: ${{ steps.get-cache-keys.outputs.m2-cache-key }}
|
||||
restore-keys: |
|
||||
${{ steps.get-cache-keys.outputs.m2-restore-key }}
|
||||
|
||||
17
.github/actions/prepare-frontend/action.yml
vendored
17
.github/actions/prepare-frontend/action.yml
vendored
@@ -11,10 +11,17 @@ inputs:
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Prepare Node + Bun environment
|
||||
uses: ./.github/actions/prepare-node-bun
|
||||
- name: Prepare Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
node-version: ${{ inputs.node-version }} # this overrides, if present
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ github.token }}
|
||||
|
||||
- name: Get cache keys
|
||||
uses: ./.github/actions/get-cache-keys
|
||||
@@ -27,8 +34,6 @@ runs:
|
||||
~/.m2
|
||||
~/.gitlibs
|
||||
key: ${{ steps.get-cache-keys.outputs.m2-cache-key }}
|
||||
restore-keys: |
|
||||
${{ steps.get-cache-keys.outputs.m2-restore-key }}
|
||||
|
||||
- name: Get bun cache directory
|
||||
id: bun-cache
|
||||
@@ -46,8 +51,6 @@ runs:
|
||||
${{ steps.bun-cache.outputs.dir }}
|
||||
node_modules
|
||||
key: ${{ steps.get-cache-keys.outputs.node-cache-key }}
|
||||
restore-keys: |
|
||||
${{ steps.get-cache-keys.outputs.node-restore-key }}
|
||||
|
||||
# postinstall lifecycle hook can be dangerous in CI so we only run it locally (see package.json)
|
||||
# If a job wants to patch packages, it does so explicitly in the CI. This setting is opt-out.
|
||||
|
||||
20
.github/actions/prepare-node-bun/action.yml
vendored
20
.github/actions/prepare-node-bun/action.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Prepare Node + Bun environment
|
||||
description: "Set up Node.js and Bun for CI jobs"
|
||||
inputs:
|
||||
node-version:
|
||||
description: "The version of Node.js to use, overriding .nvmrc"
|
||||
required: false
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Prepare Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ github.token }}
|
||||
39
.github/actions/sdk-bundle-size-check/action.yml
vendored
39
.github/actions/sdk-bundle-size-check/action.yml
vendored
@@ -35,22 +35,9 @@ runs:
|
||||
with:
|
||||
name: metabase-${{ inputs.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
path: temp/current-uberjar
|
||||
- name: Extract embedding-sdk bundles from current uberjar
|
||||
- name: Extract embedding-sdk.js 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 \
|
||||
"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 \
|
||||
"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 \
|
||||
"frontend_client/app/embedding-sdk/chunks/*" \
|
||||
-d temp/current-chunks 2>/dev/null || true
|
||||
run: unzip -j temp/current-uberjar/target/uberjar/metabase.jar frontend_client/app/embedding-sdk.js -d temp/current-bundle
|
||||
- name: Checkout base ref
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -66,21 +53,13 @@ runs:
|
||||
id: compare
|
||||
shell: bash
|
||||
run: |
|
||||
# --- Legacy (monolithic) bundle comparison ---
|
||||
current_size=$(wc -c < temp/current-bundle/embedding-sdk.js) || { echo "::error::Failed to read current bundle"; exit 1; }
|
||||
|
||||
# Base ref: try new layout first, fall back to old
|
||||
if [ -f "temp/base-ref-src/resources/frontend_client/app/embedding-sdk/legacy/embedding-sdk.js" ]; then
|
||||
base_ref_size=$(wc -c < "temp/base-ref-src/resources/frontend_client/app/embedding-sdk/legacy/embedding-sdk.js")
|
||||
else
|
||||
base_ref_size=$(wc -c < "temp/base-ref-src/resources/frontend_client/app/embedding-sdk.js")
|
||||
fi
|
||||
base_ref_size=$(wc -c < temp/base-ref-src/resources/frontend_client/app/embedding-sdk.js) || { echo "::error::Failed to read base-ref bundle"; exit 1; }
|
||||
[ "$current_size" -gt 0 ] && [ "$base_ref_size" -gt 0 ] || { echo "::error::Bundle file is empty"; exit 1; }
|
||||
|
||||
diff=$((current_size - base_ref_size))
|
||||
percent=$(( diff * 100 / base_ref_size ))
|
||||
|
||||
echo "=== Legacy (monolithic) bundle ==="
|
||||
echo "Current size: $current_size bytes"
|
||||
echo "Base ref size: $base_ref_size bytes"
|
||||
echo "Difference: $diff bytes ($percent%)"
|
||||
@@ -96,15 +75,3 @@ runs:
|
||||
|
||||
echo "status=$status" >> $GITHUB_OUTPUT
|
||||
echo "size_change_percent=$percent" >> $GITHUB_OUTPUT
|
||||
|
||||
# --- Chunked bundle (informational) ---
|
||||
if ls temp/current-chunks/*.js 1>/dev/null 2>&1; then
|
||||
chunks_size=$(cat temp/current-chunks/*.js | wc -c)
|
||||
echo ""
|
||||
echo "=== Chunked bundle (bootstrap + chunks) ==="
|
||||
echo "Total chunked size: $chunks_size bytes"
|
||||
echo "chunks_size=$chunks_size" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo ""
|
||||
echo "No chunked bundle found (expected on master)"
|
||||
fi
|
||||
|
||||
2
.github/file-paths.yaml
vendored
2
.github/file-paths.yaml
vendored
@@ -30,7 +30,7 @@ frontend_configs: &frontend_configs
|
||||
- "**/tsconfig*.json"
|
||||
- "package.json"
|
||||
- "babel.config.json"
|
||||
- "eslint.config.mjs"
|
||||
- ".eslintrc.js"
|
||||
- "postcss.config.js"
|
||||
- "rspack.config.js"
|
||||
- "rspack.main.config.js"
|
||||
|
||||
52
.github/scripts/build-cross-version-matrix.js
vendored
52
.github/scripts/build-cross-version-matrix.js
vendored
@@ -1,52 +0,0 @@
|
||||
// This script is used in .github/workflows/cross-version.yml
|
||||
// to generate a matrix for cross-version migration testing
|
||||
|
||||
/**
|
||||
* Generate a CI matrix for cross-version migration testing (EE only).
|
||||
*
|
||||
* Creates pairs of [source, target] for testing HEAD against the last N
|
||||
* minor versions in both upgrade and downgrade directions.
|
||||
*
|
||||
* @param {number} currentVersion - The current major version number (e.g., 59)
|
||||
* @param {number} count - Number of previous minor versions to test against (default: 3)
|
||||
* @returns {{ config: Array<{ source: string, target: string }> }}
|
||||
*
|
||||
* @example
|
||||
* buildMatrix(59)
|
||||
* // Returns:
|
||||
* // {
|
||||
* // config: [
|
||||
* // { source: "HEAD", target: "v1.59.x" },
|
||||
* // { source: "v1.59.x", target: "HEAD" },
|
||||
* // { source: "HEAD", target: "v1.58.x" },
|
||||
* // { source: "v1.58.x", target: "HEAD" },
|
||||
* // { source: "HEAD", target: "v1.57.x" },
|
||||
* // { source: "v1.57.x", target: "HEAD" },
|
||||
* // ]
|
||||
* // }
|
||||
*/
|
||||
function buildCrossVersionMatrix(currentVersion, count = 3) {
|
||||
if (!Number.isInteger(currentVersion) || currentVersion < 1) {
|
||||
throw new Error(
|
||||
`Invalid currentVersion: ${currentVersion}. Must be a positive integer.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!Number.isInteger(count) || count < 1) {
|
||||
throw new Error(`Invalid count: ${count}. Must be a positive integer.`);
|
||||
}
|
||||
|
||||
const config = Array.from({ length: count }, (_, i) => currentVersion - i)
|
||||
.filter((majorVersion) => majorVersion >= 1)
|
||||
.flatMap((majorVersion) => {
|
||||
const version = `v1.${majorVersion}.x`;
|
||||
return [
|
||||
{ source: "HEAD", target: version },
|
||||
{ source: version, target: "HEAD" },
|
||||
];
|
||||
});
|
||||
|
||||
return { config };
|
||||
}
|
||||
|
||||
module.exports = { buildCrossVersionMatrix };
|
||||
@@ -1,242 +0,0 @@
|
||||
const { buildCrossVersionMatrix } = require("./build-cross-version-matrix");
|
||||
|
||||
describe("buildCrossVersionMatrix", () => {
|
||||
describe("default behavior", () => {
|
||||
it("should generate matrix with 3 versions by default", () => {
|
||||
const result = buildCrossVersionMatrix(59);
|
||||
expect(result).toEqual({
|
||||
config: [
|
||||
{ source: "HEAD", target: "v1.59.x" },
|
||||
{ source: "v1.59.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.58.x" },
|
||||
{ source: "v1.58.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.57.x" },
|
||||
{ source: "v1.57.x", target: "HEAD" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should use EE prefix (v1)", () => {
|
||||
const result = buildCrossVersionMatrix(50);
|
||||
expect(result.config[0].target).toBe("v1.50.x");
|
||||
expect(result.config[1].source).toBe("v1.50.x");
|
||||
});
|
||||
|
||||
it("should create pairs in both directions (upgrade and downgrade)", () => {
|
||||
const result = buildCrossVersionMatrix(59);
|
||||
const entries = result.config;
|
||||
|
||||
// Check HEAD -> version (upgrade test)
|
||||
expect(entries.filter((e) => e.source === "HEAD")).toHaveLength(3);
|
||||
// Check version -> HEAD (downgrade test)
|
||||
expect(entries.filter((e) => e.target === "HEAD")).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("custom count", () => {
|
||||
it("should generate matrix with custom count", () => {
|
||||
const result = buildCrossVersionMatrix(59, 5);
|
||||
expect(result.config).toHaveLength(10); // 5 versions × 2 directions
|
||||
expect(result.config).toEqual([
|
||||
{ source: "HEAD", target: "v1.59.x" },
|
||||
{ source: "v1.59.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.58.x" },
|
||||
{ source: "v1.58.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.57.x" },
|
||||
{ source: "v1.57.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.56.x" },
|
||||
{ source: "v1.56.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.55.x" },
|
||||
{ source: "v1.55.x", target: "HEAD" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should generate matrix with count=1", () => {
|
||||
const result = buildCrossVersionMatrix(59, 1);
|
||||
expect(result.config).toHaveLength(2);
|
||||
expect(result.config).toEqual([
|
||||
{ source: "HEAD", target: "v1.59.x" },
|
||||
{ source: "v1.59.x", target: "HEAD" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should generate matrix with large count", () => {
|
||||
const result = buildCrossVersionMatrix(59, 10);
|
||||
expect(result.config).toHaveLength(20);
|
||||
|
||||
// Check first and last entries
|
||||
expect(result.config[0]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.59.x",
|
||||
});
|
||||
expect(result.config[18]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.50.x",
|
||||
});
|
||||
expect(result.config[19]).toEqual({
|
||||
source: "v1.50.x",
|
||||
target: "HEAD",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", () => {
|
||||
it("should handle version 1", () => {
|
||||
const result = buildCrossVersionMatrix(1);
|
||||
expect(result).toEqual({
|
||||
config: [
|
||||
{ source: "HEAD", target: "v1.1.x" },
|
||||
{ source: "v1.1.x", target: "HEAD" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should stop when version would go below 1", () => {
|
||||
const result = buildCrossVersionMatrix(2, 5);
|
||||
// Can only generate v1.2.x and v1.1.x (2 versions, not 5)
|
||||
expect(result.config).toHaveLength(4);
|
||||
expect(result).toEqual({
|
||||
config: [
|
||||
{ source: "HEAD", target: "v1.2.x" },
|
||||
{ source: "v1.2.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.1.x" },
|
||||
{ source: "v1.1.x", target: "HEAD" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle high version numbers", () => {
|
||||
const result = buildCrossVersionMatrix(100, 2);
|
||||
expect(result).toEqual({
|
||||
config: [
|
||||
{ source: "HEAD", target: "v1.100.x" },
|
||||
{ source: "v1.100.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.99.x" },
|
||||
{ source: "v1.99.x", target: "HEAD" },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("error handling", () => {
|
||||
it("should throw for undefined currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix()).toThrow(
|
||||
"Invalid currentVersion: undefined. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for non-integer currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix(59.5)).toThrow(
|
||||
"Invalid currentVersion: 59.5. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for zero currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix(0)).toThrow(
|
||||
"Invalid currentVersion: 0. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for negative currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix(-1)).toThrow(
|
||||
"Invalid currentVersion: -1. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for NaN currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix(NaN)).toThrow(
|
||||
"Invalid currentVersion: NaN. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for non-integer count", () => {
|
||||
expect(() => buildCrossVersionMatrix(59, 2.5)).toThrow(
|
||||
"Invalid count: 2.5. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for zero count", () => {
|
||||
expect(() => buildCrossVersionMatrix(59, 0)).toThrow(
|
||||
"Invalid count: 0. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for negative count", () => {
|
||||
expect(() => buildCrossVersionMatrix(59, -1)).toThrow(
|
||||
"Invalid count: -1. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("matrix structure for GitHub Actions", () => {
|
||||
it("should return object with config array", () => {
|
||||
const result = buildCrossVersionMatrix(59);
|
||||
expect(result).toHaveProperty("config");
|
||||
expect(Array.isArray(result.config)).toBe(true);
|
||||
});
|
||||
|
||||
it("should produce valid JSON for GitHub Actions", () => {
|
||||
const result = buildCrossVersionMatrix(59);
|
||||
const json = JSON.stringify(result);
|
||||
expect(() => JSON.parse(json)).not.toThrow();
|
||||
|
||||
const parsed = JSON.parse(json);
|
||||
expect(parsed.config).toHaveLength(6);
|
||||
expect(parsed.config[0]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.59.x",
|
||||
});
|
||||
});
|
||||
|
||||
it("should have consistent entry structure", () => {
|
||||
const result = buildCrossVersionMatrix(59, 5);
|
||||
|
||||
result.config.forEach((entry) => {
|
||||
expect(entry).toHaveProperty("source");
|
||||
expect(entry).toHaveProperty("target");
|
||||
expect(typeof entry.source).toBe("string");
|
||||
expect(typeof entry.target).toBe("string");
|
||||
// Either source or target should be HEAD
|
||||
expect(entry.source === "HEAD" || entry.target === "HEAD").toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("version ordering", () => {
|
||||
it("should generate versions in descending order", () => {
|
||||
const result = buildCrossVersionMatrix(59, 5);
|
||||
const versions = result.config
|
||||
.filter((e) => e.source === "HEAD")
|
||||
.map((e) => e.target);
|
||||
|
||||
expect(versions).toEqual([
|
||||
"v1.59.x",
|
||||
"v1.58.x",
|
||||
"v1.57.x",
|
||||
"v1.56.x",
|
||||
"v1.55.x",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should interleave upgrade and downgrade tests", () => {
|
||||
const result = buildCrossVersionMatrix(59, 2);
|
||||
|
||||
expect(result.config[0]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.59.x",
|
||||
});
|
||||
expect(result.config[1]).toEqual({
|
||||
source: "v1.59.x",
|
||||
target: "HEAD",
|
||||
});
|
||||
expect(result.config[2]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.58.x",
|
||||
});
|
||||
expect(result.config[3]).toEqual({
|
||||
source: "v1.58.x",
|
||||
target: "HEAD",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@ const { buildMatrix } = require("./build-e2e-matrix");
|
||||
describe("buildMatrix", () => {
|
||||
const options = {
|
||||
java: "11",
|
||||
defaultRunner: "${{ vars.DEFAULT_RUNNER_KEY }}",
|
||||
defaultRunner: "ubuntu-22.04",
|
||||
};
|
||||
const testBuildMatrix = (entry, chunks) =>
|
||||
buildMatrix(options, entry, chunks);
|
||||
|
||||
20
.github/team.json
vendored
20
.github/team.json
vendored
@@ -3,24 +3,15 @@
|
||||
{
|
||||
"name": "Querying",
|
||||
"label": ".Team/Querying",
|
||||
"members": [
|
||||
"bshepherdson",
|
||||
"metamben",
|
||||
"rileythomp",
|
||||
"camsual"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Graphy",
|
||||
"label": ".Team/Graphy",
|
||||
"members": [
|
||||
"ranquild",
|
||||
"romeovs",
|
||||
"bshepherdson",
|
||||
"metamben",
|
||||
"wzimrin",
|
||||
"ericnormand",
|
||||
"galdre",
|
||||
"dpsutton",
|
||||
"escherize",
|
||||
"bgrabow"
|
||||
"rileythomp"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -45,6 +36,9 @@
|
||||
"name": "DevEx",
|
||||
"label": ".Team/DevEx",
|
||||
"members": [
|
||||
"camsaul",
|
||||
"dpsutton",
|
||||
"escherize",
|
||||
"fraserdrops",
|
||||
"nemanjaglumac"
|
||||
]
|
||||
|
||||
40
.github/workflows/app-db.yml
vendored
40
.github/workflows/app-db.yml
vendored
@@ -14,20 +14,20 @@ concurrency:
|
||||
jobs:
|
||||
be-tests-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MariaDB 10.6
|
||||
junit-name: be-tests-mariadb-10-6-ee
|
||||
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
|
||||
- name: MariaDB 10.2
|
||||
junit-name: be-tests-mariadb-10-2-ee
|
||||
image: circleci/mariadb:10.2.23
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MariaDB Latest
|
||||
junit-name: be-tests-mariadb-latest-ee
|
||||
image: mariadb:latest
|
||||
image: circleci/mariadb:latest
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
job:
|
||||
@@ -57,8 +57,8 @@ jobs:
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: true
|
||||
MARIADB_DATABASE: mb_test
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_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: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
#
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
MB_MYSQL_SSL_TEST_USER: metabase
|
||||
MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-6-ee (0)"
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -128,15 +128,15 @@ jobs:
|
||||
|
||||
be-tests-mysql:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- 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: cimg/mysql:8.0
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
@@ -174,7 +174,7 @@ jobs:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: mb_test
|
||||
MYSQL_DATABASE: circle_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 +197,7 @@ jobs:
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
#
|
||||
@@ -224,7 +224,7 @@ jobs:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_AURORA_IAM_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_AURORA_IAM_SECRET_ACCESS_KEY }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-6-ee (0)"
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -252,15 +252,15 @@ jobs:
|
||||
|
||||
be-tests-postgres:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Postgres 14.x
|
||||
- name: Postgres 12.x
|
||||
junit-name: postgres-ee
|
||||
docker-image: postgres:14-alpine
|
||||
docker-image: postgres:12-alpine
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
@@ -366,7 +366,7 @@ jobs:
|
||||
- be-tests-mariadb
|
||||
- be-tests-mysql
|
||||
- be-tests-postgres
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: app-db-tests-result
|
||||
if: always() && !cancelled()
|
||||
|
||||
2
.github/workflows/athena.yml
vendored
2
.github/workflows/athena.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
deploy:
|
||||
name: Publish Athena JDBC Driver
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Check out the code
|
||||
|
||||
2
.github/workflows/auto-assign.yml
vendored
2
.github/workflows/auto-assign.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
update_pr:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: tibdex/github-app-token@v2.1.0
|
||||
|
||||
2
.github/workflows/auto-backport.yml
vendored
2
.github/workflows/auto-backport.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
github.event.label.name == 'triple-backport'
|
||||
)
|
||||
)
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
13
.github/workflows/backend-cloverage.yml
vendored
13
.github/workflows/backend-cloverage.yml
vendored
@@ -11,13 +11,12 @@ name: Backend-Cloverage
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# re-enable once we figure out the correctness of the reports in DEV-1104
|
||||
# schedule:
|
||||
# - cron: '0 10 * * 1-5' # every weekday at 5am US eastern time
|
||||
schedule:
|
||||
- cron: '0 10 * * 1-5' # every weekday at 5am US eastern time
|
||||
|
||||
jobs:
|
||||
setup-oss:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
modules: ${{ steps.get-modules.outputs.modules }}
|
||||
steps:
|
||||
@@ -31,7 +30,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -64,7 +63,7 @@ jobs:
|
||||
flags: back-end
|
||||
|
||||
setup-ee:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
modules: ${{ steps.get-modules.outputs.modules }}
|
||||
steps:
|
||||
@@ -78,7 +77,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
25
.github/workflows/backend.yml
vendored
25
.github/workflows/backend.yml
vendored
@@ -14,7 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
be-linter-clj-kondo:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
name: Clj-Kondo
|
||||
steps:
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
be-linter-eastwood:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
name: Eastwood
|
||||
steps:
|
||||
@@ -36,21 +36,9 @@ jobs:
|
||||
- run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood
|
||||
name: Run Eastwood linter
|
||||
|
||||
be-linter-eastwood-test:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 30
|
||||
name: Eastwood (test)
|
||||
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
|
||||
|
||||
be-tests:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -167,7 +155,7 @@ jobs:
|
||||
# as `:dev` dependencies). See #27009 for more context.
|
||||
be-check:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -183,7 +171,7 @@ jobs:
|
||||
|
||||
be-cljfmt:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
name: Cljfmt
|
||||
steps:
|
||||
@@ -196,13 +184,12 @@ jobs:
|
||||
be-tests-result:
|
||||
needs:
|
||||
- be-linter-eastwood
|
||||
- be-linter-eastwood-test
|
||||
- be-linter-clj-kondo
|
||||
- be-cljfmt
|
||||
- be-check
|
||||
- be-tests
|
||||
if: always() && !cancelled()
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
|
||||
2
.github/workflows/backport-reminder.yml
vendored
2
.github/workflows/backport-reminder.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
backport-reminder:
|
||||
name: Decide whether to backport or not
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: jesusvasquez333/verify-pr-label-action@v1.4.0
|
||||
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
|
||||
jobs:
|
||||
check-source-branch:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Block master branch PRs
|
||||
if: github.head_ref == 'master'
|
||||
|
||||
12
.github/workflows/build-for-release.yml
vendored
12
.github/workflows/build-for-release.yml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
|
||||
jobs:
|
||||
start-message:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
}).catch(console.error);
|
||||
|
||||
trigger-milestone-check:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
needs: start-message
|
||||
steps:
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
|
||||
|
||||
check-version:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
ee: ${{ fromJson(steps.canonical_version.outputs.result).ee }}
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
return versions;
|
||||
|
||||
check-commit:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 50
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
2
.github/workflows/build-scripts.yml
vendored
2
.github/workflows/build-scripts.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
|
||||
test-build-scripts:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare back-end environment
|
||||
|
||||
4
.github/workflows/cache-generator.yml
vendored
4
.github/workflows/cache-generator.yml
vendored
@@ -39,7 +39,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
get-cache-keys:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get cache keys
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
cypress-cache-key: ${{ steps.get-cache-keys.outputs.cypress-cache-key }}
|
||||
|
||||
generate-caches:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
needs: get-cache-keys
|
||||
env:
|
||||
|
||||
2
.github/workflows/check-backport-script.yml
vendored
2
.github/workflows/check-backport-script.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
check-backport-script:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/check-milestone.yml
vendored
2
.github/workflows/check-milestone.yml
vendored
@@ -15,7 +15,7 @@ on:
|
||||
jobs:
|
||||
set-milestone:
|
||||
name: Automatically set milestone
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
isNewReleaseBranch: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
4
.github/workflows/chromatic.yml
vendored
4
.github/workflows/chromatic.yml
vendored
@@ -14,7 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
frontend_all: ${{ steps.changes.outputs.frontend_all }}
|
||||
@@ -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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/claude-code-bug-fix.yml
vendored
2
.github/workflows/claude-code-bug-fix.yml
vendored
@@ -21,7 +21,7 @@ on:
|
||||
|
||||
jobs:
|
||||
auto-fix-bug:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
4
.github/workflows/cljs.yml
vendored
4
.github/workflows/cljs.yml
vendored
@@ -14,7 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
cljs: ${{ steps.changes.outputs.cljs }}
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
shared-tests-cljs:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.cljs == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
codeql: ${{ steps.changes.outputs.codeql }}
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
analyze:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.codeql == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
40
.github/workflows/codespell.yml
vendored
40
.github/workflows/codespell.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -43,35 +43,27 @@ jobs:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Build Claude prompt
|
||||
if: steps.codespell_check.outcome == 'failure'
|
||||
run: |
|
||||
cat > /tmp/claude_prompt.txt << 'PROMPT_HEADER'
|
||||
You are tasked with fixing spelling errors detected by codespell in this repository.
|
||||
|
||||
## Codespell Output
|
||||
The following spelling errors were detected:
|
||||
```
|
||||
PROMPT_HEADER
|
||||
cat codespell_output.txt >> /tmp/claude_prompt.txt
|
||||
cat >> /tmp/claude_prompt.txt << 'PROMPT_FOOTER'
|
||||
```
|
||||
|
||||
## Instructions
|
||||
1. **Review the codespell output and identify reported spelling errors** - The usual format codespell uses is `typo ==> correction`
|
||||
2. **Fix every spelling error** - Use the Edit tool to fix each misspelling with the suggested correction
|
||||
- If codespell suggests multiple corrections, choose the most appropriate one based on context
|
||||
|
||||
Human reviewers will verify the changes before merging, so fix all errors reported by codespell.
|
||||
PROMPT_FOOTER
|
||||
|
||||
- name: Fix spelling errors with Claude Code
|
||||
if: steps.codespell_check.outcome == 'failure'
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
prompt_file: /tmp/claude_prompt.txt
|
||||
prompt: |
|
||||
You are tasked with fixing spelling errors detected by codespell in this repository.
|
||||
|
||||
## Codespell Output
|
||||
The following spelling errors were detected:
|
||||
```
|
||||
$(cat codespell_output.txt)
|
||||
```
|
||||
|
||||
## Instructions
|
||||
1. **Review the codespell output and identify reported spelling errors** - The usual format codespell uses is `typo ==> correction`
|
||||
2. **Fix every spelling error** - Use the Edit tool to fix each misspelling with the suggested correction
|
||||
- If codespell suggests multiple corrections, choose the most appropriate one based on context
|
||||
|
||||
Human reviewers will verify the changes before merging, so fix all errors reported by codespell.
|
||||
claude_args: |
|
||||
--allowedTools "Write,Read,Glob,Grep,Edit,MultiEdit,Bash(cat*),Bash(ls*)"
|
||||
--disallowedTools "Bash(git commit*),Bash(git push*),Bash(gh*)"
|
||||
|
||||
4
.github/workflows/containerize-jar.yml
vendored
4
.github/workflows/containerize-jar.yml
vendored
@@ -48,7 +48,7 @@ on:
|
||||
|
||||
jobs:
|
||||
containerize:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
outputs:
|
||||
platforms: ${{ steps.platforms.outputs.result }}
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
bin/docker/.
|
||||
|
||||
verify-docker-pull:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: containerize
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
|
||||
8
.github/workflows/containerize-uberjar.yml
vendored
8
.github/workflows/containerize-uberjar.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
get-container-info:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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@v0.29.0
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
env:
|
||||
TRIVY_OFFLINE_SCAN: true
|
||||
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
post-comment:
|
||||
needs: [get-container-info, containerize]
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
name: Post PR comment
|
||||
steps:
|
||||
- name: Check out the code
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
frontend_embedding_sources: ${{ steps.changes.outputs.frontend_embedding_sources }}
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
pr-title-reminder:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
2
.github/workflows/coverage.yml
vendored
2
.github/workflows/coverage.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
fe-test-coverage:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
66
.github/workflows/cross-branch-migrations.yml
vendored
66
.github/workflows/cross-branch-migrations.yml
vendored
@@ -25,7 +25,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
migrations-h2-ee:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -38,24 +38,24 @@ jobs:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
|
||||
migrations-postgres-14-ee:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
migrations-postgres-12-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_USER: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: circle_test
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14-alpine
|
||||
image: postgres:12-alpine
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_USER: circle_test
|
||||
POSTGRES_DB: circle_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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_USER: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: circle_test
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
image: circleci/postgres:latest
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_USER: circle_test
|
||||
POSTGRES_DB: circle_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -90,24 +90,21 @@ jobs:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
|
||||
migrations-mysql-8-4-ee:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
migrations-mysql-8-0-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.4
|
||||
image: cimg/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
|
||||
@@ -116,14 +113,14 @@ jobs:
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
|
||||
migrations-mysql-latest-ee:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
services:
|
||||
@@ -131,7 +128,7 @@ jobs:
|
||||
image: mysql:latest
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: mb_test
|
||||
MYSQL_DATABASE: circle_test
|
||||
ports:
|
||||
- "3306:3306"
|
||||
steps:
|
||||
@@ -141,25 +138,21 @@ jobs:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
|
||||
migrations-mariadb-10-6-ee:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
migrations-mariadb-10-2-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
services:
|
||||
mariadb:
|
||||
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
|
||||
image: ${{ matrix.version.image }}
|
||||
image: circleci/mariadb:10.2.23
|
||||
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
|
||||
@@ -168,23 +161,20 @@ jobs:
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
|
||||
migrations-mariadb-latest-ee:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:latest
|
||||
image: circleci/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
|
||||
|
||||
102
.github/workflows/cross-version.yml
vendored
102
.github/workflows/cross-version.yml
vendored
@@ -1,102 +0,0 @@
|
||||
name: Cross-Version Migration Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
source_version:
|
||||
description: "Source version (e.g., v1.58.6 or HEAD)"
|
||||
required: true
|
||||
type: string
|
||||
target_version:
|
||||
description: "Target version (e.g., v1.58.7 or HEAD)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- "cross-version/**"
|
||||
- ".github/workflows/cross-version.yml"
|
||||
- ".github/scripts/build-cross-version-matrix.js"
|
||||
|
||||
schedule:
|
||||
# Run nightly at 3 AM UTC
|
||||
- cron: "0 3 * * *"
|
||||
|
||||
jobs:
|
||||
# Generate matrix for scheduled/PR runs
|
||||
generate-matrix:
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github
|
||||
|
||||
- name: Generate version matrix
|
||||
id: set-matrix
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { buildCrossVersionMatrix } = require('${{ github.workspace }}/.github/scripts/build-cross-version-matrix');
|
||||
const { config } = buildCrossVersionMatrix(${{ vars.CURRENT_VERSION }});
|
||||
const matrix = JSON.stringify({ include: config });
|
||||
core.setOutput("matrix", matrix);
|
||||
console.log("matrix:", matrix);
|
||||
|
||||
# Matrix-based tests for scheduled/PR runs
|
||||
test-matrix:
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
needs: generate-matrix
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Run cross-version test
|
||||
env:
|
||||
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
|
||||
run: |
|
||||
cd cross-version
|
||||
./test.sh --source "${{ matrix.source }}" --target "${{ matrix.target }}"
|
||||
|
||||
- name: Show logs on failure
|
||||
if: failure()
|
||||
run: |
|
||||
cd cross-version
|
||||
docker compose logs metabase || true
|
||||
docker compose logs postgres || true
|
||||
|
||||
# Manual test for workflow_dispatch
|
||||
test-manual:
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Run cross-version test
|
||||
env:
|
||||
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
|
||||
run: |
|
||||
cd cross-version
|
||||
./test.sh --source "${{ inputs.source_version }}" --target "${{ inputs.target_version }}"
|
||||
|
||||
- name: Show logs on failure
|
||||
if: failure()
|
||||
run: |
|
||||
cd cross-version
|
||||
docker compose logs metabase || true
|
||||
docker compose logs postgres || true
|
||||
@@ -1,79 +0,0 @@
|
||||
name: Disabled Drivers Slack Notification
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 9 * * 1" # every Monday at 9:00 UTC
|
||||
|
||||
jobs:
|
||||
notify-disabled-drivers:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/Jod
|
||||
|
||||
- name: Install Slack SDK
|
||||
run: npm install @slack/web-api
|
||||
|
||||
- name: Check disabled drivers and notify Slack
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
with:
|
||||
script: |
|
||||
const { WebClient } = require('@slack/web-api');
|
||||
|
||||
// Fetch the config
|
||||
const response = await fetch('https://raw.githubusercontent.com/metabase/ci-test-config/refs/heads/master/ci-test-config.json');
|
||||
const config = await response.json();
|
||||
|
||||
const drivers = config.ignored?.drivers || [];
|
||||
const lastUpdated = config.metadata?.last_updated || 'unknown';
|
||||
const reason = config.metadata?.reason || 'No reason provided';
|
||||
|
||||
if (drivers.length === 0) {
|
||||
console.log('No drivers are currently disabled. Skipping Slack notification.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.SLACK_BOT_TOKEN) {
|
||||
throw new Error('SLACK_BOT_TOKEN is required');
|
||||
}
|
||||
|
||||
const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
|
||||
|
||||
await slack.chat.postMessage({
|
||||
channel: 'engineering-ci',
|
||||
text: 'CI Disabled Drivers Report',
|
||||
blocks: [
|
||||
{
|
||||
type: 'section',
|
||||
text: {
|
||||
type: 'mrkdwn',
|
||||
text: `:no_entry: *CI Disabled Drivers*\n${drivers.map(d => `\`${d}\``).join(' ')}`
|
||||
},
|
||||
accessory: {
|
||||
type: 'button',
|
||||
text: {
|
||||
type: 'plain_text',
|
||||
text: 'View Config',
|
||||
emoji: true
|
||||
},
|
||||
url: 'https://github.com/metabase/ci-test-config/blob/master/ci-test-config.json'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'context',
|
||||
elements: [
|
||||
{
|
||||
type: 'mrkdwn',
|
||||
text: `${reason} · :clock1: *${lastUpdated}*`
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
console.log('Slack notification sent successfully');
|
||||
2
.github/workflows/docs-generate-base.yml
vendored
2
.github/workflows/docs-generate-base.yml
vendored
@@ -21,7 +21,7 @@ permissions:
|
||||
jobs:
|
||||
update-docs:
|
||||
name: Update documentation for ${{ inputs.target_branch }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
2
.github/workflows/docs-links.yml
vendored
2
.github/workflows/docs-links.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
link-checker:
|
||||
name: Check documentation links
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
4
.github/workflows/docs-release-generate.yml
vendored
4
.github/workflows/docs-release-generate.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
target_branch:
|
||||
description: "Target branch for documentation update"
|
||||
required: false
|
||||
default: "release-x.59.x"
|
||||
default: "release-x.58.x"
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
@@ -16,6 +16,6 @@ jobs:
|
||||
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.58.x' }}
|
||||
secrets:
|
||||
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
105
.github/workflows/drivers-stress-test.yml
vendored
105
.github/workflows/drivers-stress-test.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
be-tests-athena-ee:
|
||||
if: inputs.driver == 'Athena'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
|
||||
be-tests-bigquery-cloud-sdk-ee:
|
||||
if: inputs.driver == 'BigQuery'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
|
||||
be-tests-druid-ee:
|
||||
if: inputs.driver == 'Druid'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -109,9 +109,9 @@ jobs:
|
||||
junit-name: 'be-tests-druid-ee'
|
||||
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
|
||||
|
||||
be-tests-mariadb-10-6-ee:
|
||||
be-tests-mariadb-10-2-ee:
|
||||
if: inputs.driver == 'Maria DB'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -119,28 +119,25 @@ jobs:
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
services:
|
||||
mariadb:
|
||||
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
|
||||
image: circleci/mariadb:10.2.23
|
||||
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)
|
||||
- name: Test MariaDB driver (10.2)
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mariadb-10-6-ee'
|
||||
junit-name: 'be-tests-mariadb-10-2-ee'
|
||||
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
|
||||
|
||||
be-tests-mariadb-latest-ee:
|
||||
if: inputs.driver == 'Maria DB (latest)'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -148,17 +145,14 @@ jobs:
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:latest
|
||||
image: circleci/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)
|
||||
@@ -169,7 +163,7 @@ jobs:
|
||||
|
||||
be-tests-mongo-4-4-ee:
|
||||
if: inputs.driver == 'Mongo 4'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -191,7 +185,7 @@ jobs:
|
||||
|
||||
be-tests-mongo-4-4-ssl-ee:
|
||||
if: inputs.driver == 'Mongo 4 (SSL)'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -225,7 +219,7 @@ jobs:
|
||||
|
||||
be-tests-mongo-5-0-ee:
|
||||
if: inputs.driver == 'Mongo 5'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -247,7 +241,7 @@ jobs:
|
||||
|
||||
be-tests-mongo-5-0-ssl-ee:
|
||||
if: inputs.driver == 'Mongo 5 (SSL)'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -281,7 +275,7 @@ jobs:
|
||||
|
||||
be-tests-mongo-latest-ee:
|
||||
if: inputs.driver == 'Mongo (latest)'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -290,7 +284,7 @@ jobs:
|
||||
MB_MONGO_TEST_PASSWORD: metasample123
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:latest
|
||||
image: circleci/mongo:latest
|
||||
ports:
|
||||
- "27017:27017"
|
||||
env:
|
||||
@@ -304,9 +298,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -314,28 +308,25 @@ jobs:
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.4
|
||||
image: cimg/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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -343,7 +334,7 @@ jobs:
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_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
|
||||
@@ -373,7 +364,7 @@ jobs:
|
||||
|
||||
be-tests-oracle-18-4-ee:
|
||||
if: inputs.driver == 'Oracle 18'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -399,7 +390,7 @@ jobs:
|
||||
|
||||
be-tests-oracle-21-3-ee:
|
||||
if: inputs.driver == 'Oracle 21'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -434,7 +425,7 @@ jobs:
|
||||
|
||||
be-tests-postgres-ee:
|
||||
if: inputs.driver == 'Postgres'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -447,7 +438,7 @@ jobs:
|
||||
MB_POSTGRESQL_TEST_USER: mb_test
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14-alpine
|
||||
image: postgres:12-alpine
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
@@ -456,7 +447,7 @@ jobs:
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Postgres driver (14)
|
||||
- name: Test Postgres driver (12)
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-postgres-ee'
|
||||
@@ -464,7 +455,7 @@ jobs:
|
||||
|
||||
be-tests-postgres-latest-ee:
|
||||
if: inputs.driver == 'Postgres (latest)'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -472,20 +463,20 @@ jobs:
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_USER: mb_test
|
||||
MB_POSTGRESQL_TEST_USER: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: circle_test
|
||||
MB_POSTGRESQL_TEST_USER: circle_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: postgres:latest
|
||||
image: circleci/postgres:latest
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_USER: circle_test
|
||||
POSTGRES_DB: circle_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -497,7 +488,7 @@ jobs:
|
||||
|
||||
be-tests-presto-jdbc-ee:
|
||||
if: inputs.driver == 'Presto JDBC'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -546,7 +537,7 @@ jobs:
|
||||
|
||||
be-tests-redshift-ee:
|
||||
if: inputs.driver == 'Redshift'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -565,7 +556,7 @@ jobs:
|
||||
|
||||
be-tests-snowflake-ee:
|
||||
if: inputs.driver == 'Snowflake'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -586,7 +577,7 @@ jobs:
|
||||
|
||||
be-tests-sparksql-ee:
|
||||
if: inputs.driver == 'SparkSQL'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -606,7 +597,7 @@ jobs:
|
||||
|
||||
be-tests-sqlite-ee:
|
||||
if: inputs.driver == 'SQLite'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -621,7 +612,7 @@ jobs:
|
||||
|
||||
be-tests-sqlserver-2017-ee:
|
||||
if: inputs.driver == 'SQL Server 2017'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -648,7 +639,7 @@ jobs:
|
||||
|
||||
be-tests-sqlserver-2022-ee:
|
||||
if: inputs.driver == 'SQL Server 2022'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -675,7 +666,7 @@ jobs:
|
||||
|
||||
be-tests-starburst-ee:
|
||||
if: inputs.driver == 'Starburst'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -700,7 +691,7 @@ jobs:
|
||||
|
||||
login-to-ecr:
|
||||
if: inputs.driver == 'Vertica'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
id-token: write
|
||||
@@ -729,7 +720,7 @@ jobs:
|
||||
|
||||
be-tests-vertica-ee:
|
||||
if: inputs.driver == 'Vertica'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: login-to-ecr
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
|
||||
96
.github/workflows/drivers.yml
vendored
96
.github/workflows/drivers.yml
vendored
@@ -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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
# All driver run decisions (from mage driver-decisions)
|
||||
h2-should-run: ${{ steps.driver-decisions.outputs.h2-should-run }}
|
||||
@@ -82,9 +82,6 @@ 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
|
||||
shell: bash
|
||||
@@ -92,7 +89,7 @@ jobs:
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -143,7 +140,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -154,10 +151,11 @@ jobs:
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
:fail-fast? true
|
||||
- name: Transforms Python Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
# DS 2025-09-26 temporarily disabled while we diagnose flakes
|
||||
# - name: Transforms Python Tests
|
||||
# test-args: >-
|
||||
# :only-tags [:mb/transforms-python-test]
|
||||
# needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: bigquery-cloud-sdk
|
||||
@@ -215,7 +213,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -298,7 +296,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -347,7 +345,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -396,7 +394,7 @@ 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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -445,7 +443,7 @@ 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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -495,7 +493,7 @@ jobs:
|
||||
be-tests-h2:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.h2-should-run == 'true' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -526,7 +524,7 @@ jobs:
|
||||
be-tests-h2-oss:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.h2-should-run == 'true' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -558,27 +556,27 @@ jobs:
|
||||
be-tests-mysql-mariadb:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.mysql-mariadb-should-run == 'true' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MariaDB 10.6
|
||||
junit-name: be-tests-mariadb-10-6-ee
|
||||
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
|
||||
- name: MariaDB 10.2
|
||||
junit-name: be-tests-mariadb-10-2-ee
|
||||
image: circleci/mariadb:10.2.23
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
- name: MariaDB Latest
|
||||
junit-name: be-tests-mariadb-latest-ee
|
||||
image: mariadb:latest
|
||||
image: circleci/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'
|
||||
@@ -605,7 +603,7 @@ jobs:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: mb_test
|
||||
MYSQL_DATABASE: circle_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
|
||||
@@ -628,7 +626,7 @@ jobs:
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
#
|
||||
@@ -695,7 +693,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -776,7 +774,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -853,7 +851,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -899,7 +897,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -985,15 +983,15 @@ jobs:
|
||||
be-tests-postgres:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.postgres-should-run == 'true' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Postgres 14.x
|
||||
- name: Postgres 12.x
|
||||
junit-name: postgres-ee
|
||||
docker-image: postgres:14-alpine
|
||||
docker-image: postgres:12-alpine
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
@@ -1096,7 +1094,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -1178,7 +1176,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -1269,7 +1267,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -1280,10 +1278,12 @@ jobs:
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
:fail-fast? true
|
||||
- name: Transforms Python Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
|
||||
# DS 2025-09-26 temporarily disabled while we diagnose flakes
|
||||
# - name: Transforms Python Tests
|
||||
# test-args: >-
|
||||
# :only-tags [:mb/transforms-python-test]
|
||||
# needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: snowflake
|
||||
@@ -1350,7 +1350,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -1397,7 +1397,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -1439,7 +1439,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -1523,7 +1523,7 @@ jobs:
|
||||
# be-tests-starburst-ee:
|
||||
# needs: determine-driver-skips
|
||||
# if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
# runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
# runs-on: ubuntu-latest
|
||||
# timeout-minutes: 40
|
||||
# env:
|
||||
# CI: 'true'
|
||||
@@ -1561,7 +1561,7 @@ jobs:
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
id-token: write
|
||||
@@ -1591,7 +1591,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -1665,7 +1665,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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: drivers-tests-result
|
||||
if: always() && !cancelled()
|
||||
|
||||
4
.github/workflows/e2e-matrix-builder.yml
vendored
4
.github/workflows/e2e-matrix-builder.yml
vendored
@@ -21,7 +21,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build-matrix:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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 = "${{ vars.DEFAULT_RUNNER_KEY }}";
|
||||
const defaultRunner = "ubuntu-22.04";
|
||||
const options = {
|
||||
java,
|
||||
defaultRunner,
|
||||
|
||||
@@ -35,7 +35,7 @@ on:
|
||||
jobs:
|
||||
determine-test-type:
|
||||
name: Determine test type
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
type: ${{ steps.detect.outputs.type }}
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
workflow-summary:
|
||||
name: Stress test inputs
|
||||
needs: [determine-test-type]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Generate workflow summary
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
|
||||
stress-test-flake-fix:
|
||||
needs: [determine-test-type]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
name: Stress test E2E flake fix
|
||||
env:
|
||||
|
||||
2
.github/workflows/e2e-test.yml
vendored
2
.github/workflows/e2e-test.yml
vendored
@@ -21,7 +21,7 @@ on:
|
||||
runner:
|
||||
required: false
|
||||
type: string
|
||||
default: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
default: ubuntu-22.04
|
||||
total_chunks: # number of chunks
|
||||
required: false
|
||||
type: number
|
||||
|
||||
6
.github/workflows/e2e-tests.yml
vendored
6
.github/workflows/e2e-tests.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
|
||||
reset-e2e-test-comment:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Remove test results comment
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
collect-timings:
|
||||
name: Collect E2E Timings
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: e2e-tests-result
|
||||
if: always() && !cancelled()
|
||||
|
||||
@@ -12,7 +12,7 @@ env:
|
||||
jobs:
|
||||
sdk-component-tests:
|
||||
name: "SDK component tests (React ${{ matrix.react-version }})"
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
await core.summary.addRaw(summary).write();
|
||||
|
||||
decide-target-ref:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
target-ref: ${{ steps.get-target-ref.outputs.value }}
|
||||
steps:
|
||||
@@ -151,7 +151,7 @@ jobs:
|
||||
sdk-component-tests-backward-compatibility:
|
||||
name: "SDK component tests (Backward compatibility)"
|
||||
needs: [decide-target-ref]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
MB_RUN_MODE: "e2e"
|
||||
|
||||
@@ -12,7 +12,7 @@ env:
|
||||
MB_EDITION: ee
|
||||
jobs:
|
||||
get-sample-apps-data:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
# we must support the last two stable releases
|
||||
@@ -88,13 +88,12 @@ jobs:
|
||||
e2e-sample-apps-tests:
|
||||
needs: [get-sample-apps-data]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test_suite: ${{ fromJSON(needs.get-sample-apps-data.outputs.matrix) }}
|
||||
environment: ["production", "development"]
|
||||
if: |
|
||||
!cancelled() && needs.get-sample-apps-data.result == 'success'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-sample-app-${{ matrix.test_suite }}-tests-${{ matrix.environment }}-bundle
|
||||
env:
|
||||
@@ -195,13 +194,12 @@ jobs:
|
||||
e2e-shoppy-tests:
|
||||
needs: [get-sample-apps-data]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
environment: ["production", "development"]
|
||||
if: |
|
||||
!cancelled() && needs.get-sample-apps-data.result == 'success' &&
|
||||
(github.ref == 'refs/heads/master' || github.base_ref == 'master')
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-sample-app-shoppy-e2e-tests-${{ matrix.environment }}-bundle
|
||||
env:
|
||||
@@ -264,7 +262,7 @@ jobs:
|
||||
!cancelled() && needs.get-sample-apps-data.result == 'success' &&
|
||||
github.event_name == 'push' &&
|
||||
github.ref == 'refs/heads/master'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-shoppy-synthetic-monitoring-tests
|
||||
env:
|
||||
@@ -344,7 +342,6 @@ jobs:
|
||||
# For more details see `Embedding SDK integration tests with Host Apps` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
|
||||
e2e-host-apps-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test_suite:
|
||||
[
|
||||
@@ -354,7 +351,7 @@ jobs:
|
||||
"angular-20-host-app-e2e",
|
||||
]
|
||||
environment: ["production", "development"]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-host-app-${{ matrix.test_suite }}-tests-${{ matrix.environment }}-bundle
|
||||
env:
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
frontend_embedding_sdk_package_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_package_sources }}
|
||||
@@ -29,7 +29,7 @@ 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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -10,7 +10,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build-sdk-package:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
23
.github/workflows/embedding-sdk.yml
vendored
23
.github/workflows/embedding-sdk.yml
vendored
@@ -39,7 +39,7 @@ concurrency:
|
||||
jobs:
|
||||
embedding-sdk-cli-snippets-type-check:
|
||||
if: inputs.cli-snippets-type-check && !inputs.skip
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
BUNDLE_SIZE_THRESHOLD_PERCENT: 2
|
||||
@@ -156,18 +156,11 @@ jobs:
|
||||
- name: Send notification to Slack via Zapier
|
||||
if: success() && steps.compare.outputs.status == 'increased'
|
||||
shell: bash
|
||||
env:
|
||||
ZAPIER_WEBHOOK_URL: ${{ secrets.EMBEDDING_ZAPIER_WEBHOOK_URL }}
|
||||
SIZE_CHANGE_PERCENT: ${{ steps.compare.outputs.size_change_percent }}
|
||||
BUNDLE_SIZE_THRESHOLD_PERCENT: ${{ env.BUNDLE_SIZE_THRESHOLD_PERCENT }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
curl -s -X POST "$ZAPIER_WEBHOOK_URL" \
|
||||
-d "⚠️ Embedding SDK bundle size increased by ${SIZE_CHANGE_PERCENT}% (threshold: ${BUNDLE_SIZE_THRESHOLD_PERCENT}%)
|
||||
curl -s -X POST "${{ secrets.EMBEDDING_ZAPIER_WEBHOOK_URL }}" \
|
||||
-d '⚠️ Embedding SDK bundle size increased by ${{ steps.compare.outputs.size_change_percent }}% (threshold: ${{ env.BUNDLE_SIZE_THRESHOLD_PERCENT }}%)
|
||||
|
||||
PR #${PR_NUMBER} ${PR_TITLE} (${PR_URL})"
|
||||
PR #${{ github.event.pull_request.number }} ${{ github.event.pull_request.title }} (${{ github.event.pull_request.html_url }})'
|
||||
|
||||
- name: Comment on PR if bundle size has decreased
|
||||
uses: ./.github/actions/notify-pull-request
|
||||
@@ -186,7 +179,7 @@ jobs:
|
||||
- embedding-sdk-api-documentation-generation-validation
|
||||
- embedding-sdk-component-tests
|
||||
if: always() && !cancelled()
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
on-escalation-label:
|
||||
if: github.event.label.name == '.Escalation'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Format issue title (escape quotes)
|
||||
|
||||
@@ -19,7 +19,7 @@ on:
|
||||
jobs:
|
||||
workflow-summary:
|
||||
name: Stress test inputs
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
name: Stress test frontend unit test flake fix
|
||||
steps:
|
||||
|
||||
10
.github/workflows/frontend.yml
vendored
10
.github/workflows/frontend.yml
vendored
@@ -27,7 +27,7 @@ concurrency:
|
||||
jobs:
|
||||
fe-lint:
|
||||
if: ${{ !inputs.skip-lint }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
fe-type-check:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
|
||||
fe-tests-unit:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
|
||||
fe-tests-timezones:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
- fe-tests-unit
|
||||
- fe-tests-timezones
|
||||
if: always() && !cancelled()
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
|
||||
2
.github/workflows/generative-query-test.yml
vendored
2
.github/workflows/generative-query-test.yml
vendored
@@ -24,7 +24,7 @@ on:
|
||||
# TODO: Consider renaming the job in next milestones
|
||||
jobs:
|
||||
generative-query-test:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
name: Generative Query Test
|
||||
env:
|
||||
|
||||
4
.github/workflows/i18n.yml
vendored
4
.github/workflows/i18n.yml
vendored
@@ -14,7 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
i18n: ${{ steps.changes.outputs.i18n }}
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
verify-i18n-files:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.i18n == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/js-stats-update.yml
vendored
2
.github/workflows/js-stats-update.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
js-stats:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/linear-pr-links.yml
vendored
2
.github/workflows/linear-pr-links.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
|
||||
jobs:
|
||||
add-issue-references:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -21,7 +21,7 @@ on:
|
||||
jobs:
|
||||
detect-changed-stories:
|
||||
name: Detect changed stories
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
story_files: ${{ steps.detect.outputs.story_files }}
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
name: Summary
|
||||
needs: detect-changed-stories
|
||||
if: needs.detect-changed-stories.outputs.should_run == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Generate workflow summary
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
stress-test-loki:
|
||||
needs: detect-changed-stories
|
||||
if: needs.detect-changed-stories.outputs.should_run == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
name: Stress test Loki visual tests
|
||||
services:
|
||||
|
||||
2
.github/workflows/loki-update.yml
vendored
2
.github/workflows/loki-update.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
check-label-and-update-snapshot:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'loki-update')
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
4
.github/workflows/loki.yml
vendored
4
.github/workflows/loki.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
frontend_ci: ${{ steps.changes.outputs.frontend_ci }}
|
||||
@@ -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: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
docker:
|
||||
image: docker:19.03.12
|
||||
|
||||
5
.github/workflows/mage.yml
vendored
5
.github/workflows/mage.yml
vendored
@@ -2,11 +2,14 @@ name: Mage Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "mage/**"
|
||||
- "bb.edn"
|
||||
|
||||
jobs:
|
||||
# run mage tests even if the rest of be isnt needed
|
||||
mage-tests:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
name: Mage
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
- labeled
|
||||
jobs:
|
||||
on-issue-labeled:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
if: |
|
||||
contains(github.event.issue.labels.*.name, 'Type:New Feature')
|
||||
|
||||
2
.github/workflows/mbql-library-changelog.yml
vendored
2
.github/workflows/mbql-library-changelog.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
- src/metabase/lib/js.cljs
|
||||
jobs:
|
||||
mbql-library-changelog-check:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !contains( github.event.pull_request.labels.*.name, '.metabase-lib/no-change' ) }}
|
||||
steps:
|
||||
# - uses: jesusvasquez333/verify-pr-label-action@v1.4.0
|
||||
|
||||
2
.github/workflows/migrations.yml
vendored
2
.github/workflows/migrations.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
|
||||
lint-migrations:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
10
.github/workflows/minimal-tests.yml
vendored
10
.github/workflows/minimal-tests.yml
vendored
@@ -11,7 +11,7 @@ concurrency:
|
||||
jobs:
|
||||
# Only run if the PR has the minimal-tests label
|
||||
check-label:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 1
|
||||
outputs:
|
||||
should-run: ${{ steps.check.outputs.result }}
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
postgres-app-db-test:
|
||||
needs: check-label
|
||||
if: needs.check-label.outputs.should-run == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: "true"
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
postgres-driver-test:
|
||||
needs: check-label
|
||||
if: needs.check-label.outputs.should-run == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: "true"
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
frontend-unit-tests:
|
||||
needs: check-label
|
||||
if: needs.check-label.outputs.should-run == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
JEST_JUNIT_OUTPUT_DIR: ./target/junit
|
||||
@@ -163,7 +163,7 @@ jobs:
|
||||
- postgres-app-db-test
|
||||
- postgres-driver-test
|
||||
- frontend-unit-tests
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: minimal-tests-result
|
||||
if: always() && !cancelled()
|
||||
|
||||
78
.github/workflows/module-boundaries-stats.yml
vendored
78
.github/workflows/module-boundaries-stats.yml
vendored
@@ -1,78 +0,0 @@
|
||||
name: Module Boundaries Stats
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 40 1 * * 5 # every Friday at 1:40
|
||||
|
||||
jobs:
|
||||
module-boundaries-stats:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
- name: get module boundary stats
|
||||
id: get-stats
|
||||
run: |
|
||||
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));
|
||||
")
|
||||
|
||||
NAMED_DIRS=$(node --input-type=module -e "
|
||||
import { elements } from './frontend/lint/module-boundaries.mjs';
|
||||
const dirs = new Set();
|
||||
for (const el of elements) {
|
||||
const m = el.pattern.match(/^frontend\/src\/metabase\/(\w[\w-]*)\//)
|
||||
if (m) dirs.add(m[1]);
|
||||
}
|
||||
")
|
||||
TOTAL_DIRS=$(ls -d frontend/src/metabase/*/ | xargs -I{} basename {} | wc -l | tr -d ' ')
|
||||
NAMED_COUNT=$(echo "$NAMED_DIRS" | grep -c . || true)
|
||||
UNMODULED=$(($TOTAL_DIRS - $NAMED_COUNT))
|
||||
|
||||
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=$NAMED_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 }}
|
||||
UNMODULED: ${{ steps.get-stats.outputs.UNMODULED }}
|
||||
VIOLATIONS: ${{ steps.get-stats.outputs.VIOLATIONS }}
|
||||
API_KEY: ${{ secrets.STATS_API_KEY }}
|
||||
with:
|
||||
script: | # js
|
||||
const { uploadCsvToMb } = require('${{ github.workspace }}/.github/scripts/csv-to-mb.js');
|
||||
|
||||
console.log("NAMED_MODULES", Number(process.env.NAMED_MODULES));
|
||||
console.log("UNMODULED", Number(process.env.UNMODULED));
|
||||
console.log("VIOLATIONS", Number(process.env.VIOLATIONS));
|
||||
|
||||
const data = [
|
||||
{
|
||||
'Date': new Date().toISOString().slice(0, 10), // YYYY-MM-DD
|
||||
'Modules': Number(process.env.NAMED_MODULES),
|
||||
'Unmoduled folders': Number(process.env.UNMODULED),
|
||||
'Violations': Number(process.env.VIOLATIONS),
|
||||
}
|
||||
];
|
||||
|
||||
uploadCsvToMb({
|
||||
baseUrl: 'https://stats.metabase.com',
|
||||
tableId: 75146,
|
||||
jsonData: data,
|
||||
mode: 'append',
|
||||
}).then(() => {
|
||||
console.log('Data uploaded successfully');
|
||||
}).catch((error) => {
|
||||
console.error('Error uploading data:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
8
.github/workflows/openapi-check.yml
vendored
8
.github/workflows/openapi-check.yml
vendored
@@ -10,7 +10,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
check-openapi-specs:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
if: contains(github.event.pull_request.labels.*.name, 'openapi-self-healing')
|
||||
steps:
|
||||
@@ -22,8 +22,10 @@ jobs:
|
||||
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
# fetch full git history to allow fetching the latest base branch commit and comparing changes
|
||||
fetch-depth: 0
|
||||
- name: Prepare Node + Bun environment
|
||||
uses: ./.github/actions/prepare-node-bun
|
||||
- name: Setup Node.js 22
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Generate version.properties
|
||||
|
||||
14
.github/workflows/openapi.yml
vendored
14
.github/workflows/openapi.yml
vendored
@@ -3,8 +3,8 @@ name: Validate OpenAPI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-**'
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
@@ -14,7 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
openapi: ${{ steps.changes.outputs.openapi }}
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
validate-openapi:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.openapi == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -39,6 +39,8 @@ jobs:
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Generate OpenAPI spec
|
||||
run: bun run generate-openapi
|
||||
run: bun run openapi:generate
|
||||
- name: Validate OpenAPI spec
|
||||
run: bun run lint-openapi
|
||||
run: bun run openapi:lint
|
||||
- name: Split OpenAPI spec
|
||||
run: bun run openapi:split
|
||||
|
||||
2
.github/workflows/optimize-images.yml
vendored
2
.github/workflows/optimize-images.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
optimize-images:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name,'Type:Documentation') }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
4
.github/workflows/perf-test.yml
vendored
4
.github/workflows/perf-test.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
name: Build Metabase Docker image
|
||||
timeout-minutes: 60
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
push: true
|
||||
launch_perf_test:
|
||||
needs: [ build ]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
name: Launch Perf Test
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
2
.github/workflows/pr-env-destroy-manual.yml
vendored
2
.github/workflows/pr-env-destroy-manual.yml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
|
||||
jobs:
|
||||
approve:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
environment: pr-env-admin
|
||||
steps:
|
||||
- name: Approved
|
||||
|
||||
2
.github/workflows/pr-env-destroy.yml
vendored
2
.github/workflows/pr-env-destroy.yml
vendored
@@ -19,7 +19,7 @@ env:
|
||||
|
||||
jobs:
|
||||
destroy_pr:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
name: PR Review ENV Destroy
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
6
.github/workflows/pr-env.yml
vendored
6
.github/workflows/pr-env.yml
vendored
@@ -16,7 +16,7 @@ on:
|
||||
default: false
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
name: Build Metabase Docker image
|
||||
timeout-minutes: 60
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
push: true
|
||||
deploy_pr:
|
||||
needs: [build]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
name: PR Review ENV
|
||||
permissions:
|
||||
id-token: write
|
||||
@@ -188,7 +188,7 @@ jobs:
|
||||
--atomic \
|
||||
--timeout 10m
|
||||
preview_links:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy_pr]
|
||||
steps:
|
||||
- uses: marocchino/sticky-pull-request-comment@v2
|
||||
|
||||
18
.github/workflows/pre-release.yml
vendored
18
.github/workflows/pre-release.yml
vendored
@@ -25,7 +25,7 @@ on:
|
||||
|
||||
jobs:
|
||||
test-mode-config:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
run_mode: ${{ fromJson(steps.config.outputs.result).runMode }}
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
});
|
||||
|
||||
release-artifact:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
|
||||
check-uberjar-health:
|
||||
needs: [release-artifact]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
name: Is ${{ matrix.edition }} (java ${{ matrix.java-version }}) healthy?
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
|
||||
run-sanity-check:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
needs: [test-mode-config, release-artifact]
|
||||
strategy:
|
||||
@@ -258,8 +258,14 @@ jobs:
|
||||
|
||||
tests-complete-message:
|
||||
if: always()
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
needs: [run-sanity-check, check-uberjar-health, containerize-ee, containerize-oss]
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
[
|
||||
run-sanity-check,
|
||||
check-uberjar-health,
|
||||
containerize-ee,
|
||||
containerize-oss,
|
||||
]
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -18,7 +18,7 @@ on:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
backend_presto_kerberos: ${{ steps.changes.outputs.backend_presto_kerberos }}
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
run-presto-kerberos-test:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.backend_presto_kerberos == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 40
|
||||
steps:
|
||||
- name: Install babashka
|
||||
|
||||
2
.github/workflows/pull-request-reminders.yml
vendored
2
.github/workflows/pull-request-reminders.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
external-reminder:
|
||||
name: External PR Helper
|
||||
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
notify-querying-on-call:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
if: |
|
||||
(github.event.label.name == 'Priority:P0' || github.event.label.name == 'Priority:P1' || github.event.label.name == '.Escalation') &&
|
||||
|
||||
68
.github/workflows/release-custom-viz.yml
vendored
68
.github/workflows/release-custom-viz.yml
vendored
@@ -1,68 +0,0 @@
|
||||
name: Release Custom Viz Package
|
||||
run-name: Release @metabase/custom-viz v${{ inputs.version }} from ${{ inputs.branch }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: "Branch to release from"
|
||||
type: string
|
||||
required: true
|
||||
default: "custom-viz"
|
||||
version:
|
||||
description: "Version to publish (e.g. 0.1.0)"
|
||||
type: string
|
||||
required: true
|
||||
npm_tag:
|
||||
description: "NPM dist-tag"
|
||||
type: choice
|
||||
required: true
|
||||
default: "latest"
|
||||
options:
|
||||
- latest
|
||||
- next
|
||||
- canary
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
defaults:
|
||||
run:
|
||||
working-directory: frontend/src/custom-viz
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Setup npm registry auth
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Set package version
|
||||
run: npm version ${{ inputs.version }} --no-git-tag-version
|
||||
|
||||
- name: Build package
|
||||
run: bun run build
|
||||
|
||||
- name: Publish to NPM
|
||||
run: npm publish --tag ${{ inputs.npm_tag }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_RELEASE_TOKEN }}
|
||||
|
||||
- name: Generate workflow summary
|
||||
run: |
|
||||
tee -a $GITHUB_STEP_SUMMARY << EOF
|
||||
## Published @metabase/custom-viz
|
||||
|
||||
- **Version**: ${{ inputs.version }}
|
||||
- **Tag**: ${{ inputs.npm_tag }}
|
||||
- **Branch**: ${{ inputs.branch }}
|
||||
- **Triggered by**: @${{ github.event.sender.login }}
|
||||
EOF
|
||||
20
.github/workflows/release-embedding-sdk.yml
vendored
20
.github/workflows/release-embedding-sdk.yml
vendored
@@ -21,7 +21,7 @@ concurrency:
|
||||
jobs:
|
||||
workflow-summary:
|
||||
name: Log inputs
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Generate workflow summary
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
send-slack-notification-start:
|
||||
name: "Send Slack notification: SDK release started"
|
||||
needs: determine-sdk-version
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
slackMessageId: ${{ steps.send-slack-message-start.outputs.result }}
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
"publish-npm",
|
||||
"git-tag",
|
||||
]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
slackMessageId: ${{ needs.send-slack-notification-start.outputs.slackMessageId }}
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
name: "Send Slack notification: SDK release successful"
|
||||
needs:
|
||||
["determine-sdk-version", "send-slack-notification-start", "publish-npm"]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
slackMessageId: ${{ needs.send-slack-notification-start.outputs.slackMessageId }}
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
});
|
||||
|
||||
determine-sdk-version:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
outputs:
|
||||
sdk_version: ${{ fromJson(steps.new-sdk-version.outputs.result).version }}
|
||||
@@ -219,7 +219,7 @@ jobs:
|
||||
|
||||
check-git-tag:
|
||||
needs: determine-sdk-version
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
tag: embedding-sdk-${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
@@ -243,7 +243,7 @@ jobs:
|
||||
|
||||
test:
|
||||
needs: check-git-tag
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Check out the code using the provided branch
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
|
||||
build-sdk:
|
||||
needs: [test, determine-sdk-version]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Check out the code using the provided branch
|
||||
@@ -297,7 +297,7 @@ jobs:
|
||||
|
||||
publish-npm:
|
||||
needs: [build-sdk, determine-sdk-version]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
sdk_version: ${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
@@ -398,7 +398,7 @@ jobs:
|
||||
|
||||
git-tag:
|
||||
needs: [publish-npm, determine-sdk-version]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
tag: embedding-sdk-${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
|
||||
2
.github/workflows/release-log.yml
vendored
2
.github/workflows/release-log.yml
vendored
@@ -23,7 +23,7 @@ on:
|
||||
|
||||
jobs:
|
||||
update-release-log:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
# don't run this workflow for forks
|
||||
if: ${{ github.event_name != 'push' || github.repository == 'metabase/metabase' }}
|
||||
|
||||
@@ -15,7 +15,7 @@ on:
|
||||
jobs:
|
||||
check-milestone:
|
||||
permissions: write-all
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/release-notes-preview.yml
vendored
2
.github/workflows/release-notes-preview.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
|
||||
jobs:
|
||||
preview-release-notes:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Fail early on the incorrect version format
|
||||
|
||||
10
.github/workflows/release-patch.yml
vendored
10
.github/workflows/release-patch.yml
vendored
@@ -5,22 +5,22 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Major Metabase version (e.g. 45, 52, 68)"
|
||||
description: 'Major Metabase version (e.g. 45, 52, 68)'
|
||||
type: number
|
||||
required: true
|
||||
override_sha:
|
||||
description: DANGEROUSLY allow releasing from this commit sha, regardless of the state of CI (can be empty)
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
# schedule:
|
||||
# - cron: "0 21 * * 1-5" # every weekday at 4 pm EST/5 pm EDT
|
||||
default: ''
|
||||
schedule:
|
||||
- cron: '0 21 * * 1-5' # every weekday at 4 pm EST/5 pm EDT
|
||||
|
||||
jobs:
|
||||
auto-patch-trigger:
|
||||
# don't run this workflow on a cron for forks
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
4
.github/workflows/release-status.yml
vendored
4
.github/workflows/release-status.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
check-release-status:
|
||||
# don't run this workflow on a cron for forks
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
});
|
||||
|
||||
open-backport-reminder:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
12
.github/workflows/release-tag.yml
vendored
12
.github/workflows/release-tag.yml
vendored
@@ -55,7 +55,7 @@ on:
|
||||
|
||||
jobs:
|
||||
green-check-stub:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Stub
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
check-version:
|
||||
# skips everything if the tag_name is 'none' because all later jobs depend on this job
|
||||
if: ${{ inputs.tag_name != 'none' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
ee: ${{ fromJson(steps.canonical_version.outputs.result).ee }}
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
throw new Error("No edition selected to tag");
|
||||
|
||||
copy-to-s3:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-version
|
||||
timeout-minutes: 5
|
||||
strategy:
|
||||
@@ -221,7 +221,7 @@ jobs:
|
||||
fi
|
||||
|
||||
tag-docker-image:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-version
|
||||
timeout-minutes: 5
|
||||
strategy:
|
||||
@@ -257,7 +257,7 @@ jobs:
|
||||
push-git-tags:
|
||||
permissions: write-all
|
||||
needs: check-version
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
@@ -288,7 +288,7 @@ jobs:
|
||||
|
||||
update-version-info:
|
||||
if: ${{ !inputs.dot-x-tag }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-version
|
||||
timeout-minutes: 5
|
||||
strategy:
|
||||
|
||||
8
.github/workflows/release-tests.yml
vendored
8
.github/workflows/release-tests.yml
vendored
@@ -14,7 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
release_source: ${{ steps.changes.outputs.release_source }}
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
release-type-check:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.release_source == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
release-tests-unit:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.release_source == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
release-test-build:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.release_source == 'true'
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
@@ -48,7 +48,7 @@ on:
|
||||
|
||||
jobs:
|
||||
check-version:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
ee: ${{ fromJson(steps.canonical_version.outputs.result).ee }}
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
return versions
|
||||
|
||||
publish-start-message:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [check-version]
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
|
||||
download-uberjar:
|
||||
needs: check-version
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
./version.properties
|
||||
|
||||
upload-to-s3:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: download-uberjar
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
@@ -230,7 +230,7 @@ jobs:
|
||||
--paths "/${{ steps.version_path.outputs.result }}/*"
|
||||
|
||||
verify-s3-download:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: upload-to-s3
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
@@ -287,7 +287,7 @@ jobs:
|
||||
push-tags:
|
||||
permissions: write-all
|
||||
needs: [verify-s3-download, containerize-oss, containerize-ee, check-version]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -320,7 +320,7 @@ jobs:
|
||||
|
||||
get-extra-tags:
|
||||
needs: [ check-version ]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
extra_tags: ${{ steps.taglist.outputs.extra_tags }}
|
||||
steps:
|
||||
@@ -371,7 +371,7 @@ jobs:
|
||||
trigger-cloud-issues:
|
||||
if: ${{ !inputs.auto }}
|
||||
needs: push-tags
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -423,7 +423,7 @@ jobs:
|
||||
needs:
|
||||
- containerize-ee
|
||||
- verify-s3-download
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
@@ -446,7 +446,7 @@ jobs:
|
||||
draft-release-notes:
|
||||
if: ${{ !inputs.auto }}
|
||||
needs: add-extra-tags
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
permissions: write-all
|
||||
env:
|
||||
@@ -514,7 +514,7 @@ jobs:
|
||||
}).catch(console.warn);
|
||||
|
||||
publish-version-info:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [push-tags, check-version]
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
@@ -590,7 +590,7 @@ jobs:
|
||||
tag_oss: true
|
||||
|
||||
publish-complete-message:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [set-release-channel, add-extra-tags]
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
@@ -623,7 +623,7 @@ jobs:
|
||||
manage-milestones:
|
||||
if: ${{ !inputs.auto }}
|
||||
permissions: write-all
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [draft-release-notes, publish-version-info] # this will ensure that the milestone stays open until the release notes are published
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user