Compare commits
130 Commits
thin-datab
...
debug-fail
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b83180e96 | ||
|
|
ffaca12661 | ||
|
|
6acd24fc20 | ||
|
|
2352d01b34 | ||
|
|
90b238f722 | ||
|
|
12e1e7a8ae | ||
|
|
0b42ad89a9 | ||
|
|
857bdadd45 | ||
|
|
eed6736230 | ||
|
|
144b0b33f8 | ||
|
|
dcca252247 | ||
|
|
a183e8b5b4 | ||
|
|
3b837a15a0 | ||
|
|
6679039b45 | ||
|
|
58867cac0b | ||
|
|
2e4b7adbcb | ||
|
|
5aeffcf173 | ||
|
|
721db4d40f | ||
|
|
442fd726a3 | ||
|
|
9e65cc60bf | ||
|
|
44ab6decf0 | ||
|
|
8b369333c3 | ||
|
|
c9e602db2f | ||
|
|
b1fb103a3f | ||
|
|
b631ee88a4 | ||
|
|
2df87d3a7f | ||
|
|
f486c415f9 | ||
|
|
ba5804041b | ||
|
|
9d6564cdaf | ||
|
|
591b453e4c | ||
|
|
5d087d0d18 | ||
|
|
719ebd0a7b | ||
|
|
ff6ec328f3 | ||
|
|
78978219a6 | ||
|
|
7c8a8f3694 | ||
|
|
45abe078f9 | ||
|
|
82b95611cf | ||
|
|
f04fb1bff2 | ||
|
|
f200fbe20c | ||
|
|
3413313d3a | ||
|
|
b5c0d93f8e | ||
|
|
71f7cbc41b | ||
|
|
c2619a5036 | ||
|
|
e4728d22b8 | ||
|
|
2d1b6c4a02 | ||
|
|
b8eb7d65f9 | ||
|
|
28b5fb79f1 | ||
|
|
53cf2ab305 | ||
|
|
135abb44a5 | ||
|
|
8699037548 | ||
|
|
03dad76c9f | ||
|
|
dca22d0865 | ||
|
|
a6f98dbd68 | ||
|
|
2fe9ebdec1 | ||
|
|
c31762e244 | ||
|
|
5585815455 | ||
|
|
2f8cc0f153 | ||
|
|
d00a66f474 | ||
|
|
19ba6a165a | ||
|
|
e0e3099787 | ||
|
|
6d62827416 | ||
|
|
5a8ec6631a | ||
|
|
d5a7dc2e49 | ||
|
|
0a3a826537 | ||
|
|
04cbeab190 | ||
|
|
248a95e704 | ||
|
|
c7e2dcd254 | ||
|
|
fafd6bb3d8 | ||
|
|
4fe0ecbeb9 | ||
|
|
e1c31c0d8e | ||
|
|
a8e592718f | ||
|
|
0b5bbd260f | ||
|
|
38c0aebd00 | ||
|
|
41b5904d52 | ||
|
|
a2975bdb44 | ||
|
|
e996561416 | ||
|
|
10e76e3040 | ||
|
|
25a97838db | ||
|
|
54c23276d7 | ||
|
|
58df0d448e | ||
|
|
35c649f88b | ||
|
|
d981e64f0a | ||
|
|
425856f92a | ||
|
|
5c87a6c12b | ||
|
|
a033c12089 | ||
|
|
39821ae14e | ||
|
|
739b062e7c | ||
|
|
ec73bb3bae | ||
|
|
566d6d1ad6 | ||
|
|
9a981a80b5 | ||
|
|
ea5d731e6a | ||
|
|
cea2da195e | ||
|
|
be1889ac0a | ||
|
|
94d2daaafd | ||
|
|
85633efccf | ||
|
|
e53d67c763 | ||
|
|
7a5d365a4d | ||
|
|
6e0186703f | ||
|
|
713a369825 | ||
|
|
52eeb00a2e | ||
|
|
17291f9d79 | ||
|
|
af139fd003 | ||
|
|
97f104ecbf | ||
|
|
d5e562665f | ||
|
|
b6a8de1d29 | ||
|
|
55abe262c9 | ||
|
|
088a5354fc | ||
|
|
a843870977 | ||
|
|
049723c814 | ||
|
|
25270fea3e | ||
|
|
33f9a3148d | ||
|
|
2b69c463dd | ||
|
|
6845e31f38 | ||
|
|
8b4fb71db4 | ||
|
|
b889dd8e52 | ||
|
|
c461d1296c | ||
|
|
4eda823252 | ||
|
|
aa549d6910 | ||
|
|
d5da0f4d38 | ||
|
|
dded0d225a | ||
|
|
51067eb586 | ||
|
|
cd39f12e79 | ||
|
|
854650cd86 | ||
|
|
9cd92bf39c | ||
|
|
55deb90598 | ||
|
|
679170b832 | ||
|
|
5829365136 | ||
|
|
0de9cf68de | ||
|
|
2e6c7d97e7 | ||
|
|
cdad4c95ff |
75
.claude/skills/_shared/clojure-commands.md
Normal file
@@ -0,0 +1,75 @@
|
||||
## Linting and Formatting
|
||||
|
||||
- **Lint PR:** `./bin/mage kondo-updated master` (or whatever target branch)
|
||||
- Call the command one time at the beginning, record the results, then work through the problems one at a time.
|
||||
- If the solution is obvious, then please apply the fix. Otherwise skip it.
|
||||
- If you fix all the issues (and verify by rerunning the kondo-updated command):
|
||||
- commit the change with a succinct and descriptive commit message
|
||||
- **Lint File:** `./bin/mage kondo <file or files>`
|
||||
- Use the linter as a way to know that you are adhering to conventions in place in the codebase
|
||||
- **Lint Changes:** `./bin/mage kondo-updated HEAD`
|
||||
- **Format:** `./bin/mage cljfmt-files [path]`
|
||||
|
||||
## Testing
|
||||
|
||||
- **Run a test:** `./bin/mage run-tests namespace/test-name`
|
||||
- **Run all tests in a namespace:** `./bin/mage run-tests namespace`
|
||||
|
||||
## Code Readability
|
||||
|
||||
- **Check Code Readability:** `./bin/mage -check-readable <file> [line-number]`
|
||||
- Run after every change to Clojure code
|
||||
- Check specific line first, then entire file if readable
|
||||
|
||||
## REPL Usage
|
||||
|
||||
- **Evaluating Clojure Code:** `./bin/mage -repl '<code>'`
|
||||
- See "Sending Code to the REPL" section for more details
|
||||
|
||||
### Sending Code to the REPL
|
||||
|
||||
- Send code to the metabase process REPL using: `./bin/mage -repl '(+ 1 1)'` where `(+ 1 1)` is your Clojure code.
|
||||
- See `./bin/mage -repl -h` for more details.
|
||||
- If the Metabase backend is not running, you'll see an error message with instructions on how to start it.
|
||||
|
||||
#### Working with Files and Namespaces
|
||||
|
||||
1. **Load a file and call functions with fully qualified names**:
|
||||
|
||||
To call `your.namespace/your-function` on `arg1` and `arg2`:
|
||||
|
||||
```
|
||||
./bin/mage -repl --namespace your.namespace '(your-function arg1 arg2)'
|
||||
```
|
||||
|
||||
DO NOT use "require", "load-file" etc in the code string argument.
|
||||
|
||||
#### Understanding the Response
|
||||
|
||||
The `./bin/mage -repl` command returns three separate, independent outputs:
|
||||
|
||||
- `value`: The return value of the last expression (best for data structures)
|
||||
- `stdout`: Any printed output from `println` etc. (best for messages)
|
||||
- `stderr`: Any error messages (best for warnings and errors)
|
||||
|
||||
Example call:
|
||||
|
||||
```bash
|
||||
./bin/mage -repl '(println "Hello, world!") '\''({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```
|
||||
ns: user
|
||||
session: 32a35206-871c-4553-9bc9-f49491173d1c
|
||||
value: ({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})
|
||||
stdout: Hello, world!
|
||||
stderr:
|
||||
```
|
||||
|
||||
For effective REPL usage:
|
||||
|
||||
- Return data structures as function return values
|
||||
- Use `println` for human-readable messages
|
||||
- Print errors to stderr
|
||||
153
.claude/skills/_shared/clojure-style-guide.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Metabase Clojure Style Guide
|
||||
|
||||
This guide covers Clojure and ClojureScript coding conventions for Metabase. See also: `CLOJURE_STYLE_GUIDE.adoc` for the Community Clojure Style Guide.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
**General Naming:**
|
||||
|
||||
- Acceptable abbreviations: `acc`, `i`, `pred`, `coll`, `n`, `s`, `k`, `f`
|
||||
- Use `kebab-case` for all variables, functions, and constants
|
||||
|
||||
**Function Naming:**
|
||||
|
||||
- Pure functions should be nouns describing the value they return (e.g., `age` not `calculate-age` or `get-age`)
|
||||
- Functions with side effects must end with `!`
|
||||
- Don't repeat namespace alias in function names
|
||||
|
||||
**Destructuring:**
|
||||
|
||||
- Map destructuring should use kebab-case local bindings even if the map uses `snake_case` keys
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
**Docstrings:**
|
||||
|
||||
- Every public var in `src` or `enterprise/backend/src` must have docstring
|
||||
- Format using Markdown conventions
|
||||
- Reference other vars with `[[other-var]]` not backticks
|
||||
|
||||
**Comments:**
|
||||
|
||||
- `TODO` format: `;; TODO (Name M/D/YY) -- description`
|
||||
|
||||
## Code Organization
|
||||
|
||||
**Visibility:**
|
||||
|
||||
- Make everything `^:private` unless it is used elsewhere
|
||||
- Try to organize namespaces to avoid `declare` (put public functions near the end)
|
||||
|
||||
**Size and Structure:**
|
||||
|
||||
- Break up functions > 20 lines
|
||||
- Lines ≤ 120 characters
|
||||
- No blank lines within definition forms (except pairwise `let`/`cond`)
|
||||
|
||||
## Style Conventions
|
||||
|
||||
**Keywords and Metadata:**
|
||||
|
||||
- Prefer namespaced keywords for internal use: `:query-type/normal` not `:normal`
|
||||
- Tag variables with `:arglists` metadata if they're functions but wouldn't otherwise have it
|
||||
|
||||
## Tests
|
||||
|
||||
**Organization:**
|
||||
|
||||
- Break large tests into separate `deftest` forms for logically separate test cases
|
||||
- Test names should end in `-test` or `-test-<number>`
|
||||
|
||||
**Performance:**
|
||||
|
||||
- Mark pure function tests `^:parallel`
|
||||
|
||||
## Modules
|
||||
|
||||
**OSS Modules:**
|
||||
|
||||
- Follow `metabase.<module>.*` pattern
|
||||
- Source in `src/metabase/<module>/`
|
||||
|
||||
**Enterprise Modules:**
|
||||
|
||||
- Follow `metabase-enterprise.<module>.*` pattern
|
||||
- Source in `enterprise/backend/src/metabase_enterprise/<module>/`
|
||||
|
||||
**Module Structure:**
|
||||
|
||||
- REST API endpoints go in `<module>.api` or `<module>.api.*` namespaces
|
||||
- Put module public API in `<module>.core` using Potemkin imports
|
||||
- Put Toucan models in `<module>.models.*`
|
||||
- Put settings in `<module>.settings`
|
||||
- Put schemas in `<module>.schema`
|
||||
|
||||
**Module Linters:**
|
||||
|
||||
- Do not cheat module linters with `:clj-kondo/ignore [:metabase/modules]`
|
||||
|
||||
## REST API Endpoints
|
||||
|
||||
**Required Elements:**
|
||||
|
||||
- All new endpoints must have response schemas (`:- <schema>` after route string)
|
||||
- All endpoints need Malli schemas for parameters (detailed and complete)
|
||||
- All new REST API endpoints MUST HAVE TESTS
|
||||
|
||||
**Naming Conventions:**
|
||||
|
||||
- Query parameters use kebab-case
|
||||
- Request bodies use `snake_case`
|
||||
- Routes use singular nouns (e.g., `/api/dashboard/:id`)
|
||||
|
||||
**Behavior:**
|
||||
|
||||
- `GET` endpoints should not have side effects (except analytics)
|
||||
- `defendpoint` forms should be small wrappers around Toucan model code
|
||||
|
||||
## MBQL (Metabase Query Language)
|
||||
|
||||
**Restrictions:**
|
||||
|
||||
- No raw MBQL introspection outside of `lib`, `lib-be`, or `query-processor` modules
|
||||
- Use Lib and MBQL 5 in new source code; avoid legacy MBQL
|
||||
|
||||
## Database and Models
|
||||
|
||||
**Naming:**
|
||||
|
||||
- Model names and table names should be singular nouns
|
||||
- Application database uses `snake_case` identifiers
|
||||
|
||||
**Best Practices:**
|
||||
|
||||
- Use `t2/select-one-fn` instead of fetching entire rows for one column
|
||||
- Put correct behavior in Toucan methods, not separate helper functions
|
||||
|
||||
## Drivers
|
||||
|
||||
**Documentation:**
|
||||
|
||||
- New driver multimethods must be mentioned in `docs/developers-guide/driver-changelog.md`
|
||||
|
||||
**Implementation:**
|
||||
|
||||
- Driver implementations should pass `driver` argument to other driver multimethods
|
||||
- Don't hardcode driver names in implementations
|
||||
- Minimize logic inside `read-column-thunk` in JDBC-based drivers
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
**Examples:**
|
||||
|
||||
- Example data should be bird-themed if possible
|
||||
|
||||
**Linter Suppressions:**
|
||||
|
||||
- Use proper format for kondo suppressions
|
||||
- No `#_:clj-kondo/ignore` (keyword form)
|
||||
|
||||
**Configurable Options:**
|
||||
|
||||
- Don't define configurable options that can only be set with environment variables
|
||||
- Use `:internal` `defsetting` instead
|
||||
8
.claude/skills/_shared/development-workflow.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Autonomous Development Workflow
|
||||
|
||||
- Do not attempt to read or edit files outside the project folder
|
||||
- Add failing tests first, then fix them
|
||||
- Work autonomously in small, testable increments
|
||||
- Run targeted tests, and lint continuously during development
|
||||
- Prioritize understanding existing patterns before implementing
|
||||
- Don't commit changes, leave it for the user to review and make commits
|
||||
84
.claude/skills/_shared/metabase-style-guide.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Metabase Writing Style Guide
|
||||
|
||||
## Core Principles
|
||||
|
||||
Write like you're talking to a colleague. Be conversational, not formal. Get people what they need quickly. Know your audience and match the complexity.
|
||||
|
||||
## Tone and Voice
|
||||
|
||||
**Do:**
|
||||
|
||||
- Use contractions ("can't" not "cannot")
|
||||
- Say "people" or "companies" instead of "users"
|
||||
- Be friendly but not peppy
|
||||
- Acknowledge limitations honestly ("that's on us, not them")
|
||||
- Jokes and Easter eggs are okay (permit them, don't suggest them)
|
||||
|
||||
**Don't:**
|
||||
|
||||
- Use exclamation points excessively
|
||||
- Rely on tired tropes about nerdiness
|
||||
- Use corporate jargon ("utilize", "offerings", "actionable insights")
|
||||
- Tell people something is cool (show them instead)
|
||||
|
||||
## Structure and Clarity
|
||||
|
||||
**Lead with the important stuff:**
|
||||
|
||||
- Most important information first
|
||||
- Lead with the ask, then provide context
|
||||
- Cut text that adds little value (when in doubt, cut it)
|
||||
- Each paragraph should have one clear purpose
|
||||
|
||||
**Make headings do the work:**
|
||||
|
||||
- Convey your actual point, not just the topic
|
||||
- "Use headings to highlight key points" not "How to write a good heading"
|
||||
- Use sentence case, no punctuation except question marks
|
||||
- No links in headings unless entire heading is a link
|
||||
|
||||
## Instructions and Examples
|
||||
|
||||
**Tell people what to do:**
|
||||
|
||||
- Give the action before explaining why
|
||||
- Put commands in execution order
|
||||
- Provide context, steps, and reasoning
|
||||
- Don't describe tasks as "easy" or "simple"
|
||||
|
||||
**Answer unasked questions:**
|
||||
|
||||
- Include answers to "dumb" questions you had when learning
|
||||
- Don't literally include the question, just give the answer
|
||||
|
||||
## Formatting
|
||||
|
||||
**Code vs. UI elements:**
|
||||
|
||||
- Backticks for code, variable names, parameters only
|
||||
- **Bold** for UI elements and labels (e.g., "Click the **Save** button")
|
||||
- Don't use backticks or quotes for UI elements
|
||||
|
||||
## Writing Mechanics
|
||||
|
||||
- Limit pronouns when introducing new terms (repeat the term to reinforce it)
|
||||
- Ampersands only in proper nouns, never as substitute for "and"
|
||||
|
||||
## Terminology Preferences
|
||||
|
||||
Use familiar terms from tools people already know:
|
||||
|
||||
- "Summarize" (like Excel) instead of "aggregate"
|
||||
- "Take a look at" instead of "reference"
|
||||
- "Filter" instead of technical database terms when appropriate
|
||||
|
||||
## Red Flags
|
||||
|
||||
Avoid these patterns:
|
||||
|
||||
- Multiple exclamation points
|
||||
- Linking "here"
|
||||
- Bullet lists to explain (use prose)
|
||||
- Numbers that will change (guard against change)
|
||||
- Describing things as "easy" or "simple"
|
||||
- Stock photography-worthy content
|
||||
21
.claude/skills/_shared/typescript-commands.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## Linting and Formatting
|
||||
|
||||
- **Lint:** `yarn lint-eslint-pure`
|
||||
- Run ESLint on the codebase
|
||||
- **Format:** `yarn prettier`
|
||||
- Format code using Prettier
|
||||
- **Type Check:** `yarn type-check-pure`
|
||||
- Run TypeScript type checking
|
||||
|
||||
## Testing
|
||||
|
||||
### JavaScript/TypeScript Tests
|
||||
|
||||
- **Test a specific file:** `yarn test-unit-keep-cljs path/to/file.unit.spec.js`
|
||||
- **Test by pattern:** `yarn test-unit-keep-cljs -t "pattern"`
|
||||
- Runs tests matching the given pattern
|
||||
|
||||
### ClojureScript Tests
|
||||
|
||||
- **Test ClojureScript:** `yarn test-cljs`
|
||||
- Run ClojureScript tests
|
||||
144
.claude/skills/clojure-review/SKILL.md
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
name: clojure-review
|
||||
description: Review Clojure and ClojureScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing Clojure/ClojureScript code.
|
||||
allowed-tools: Read, Grep, Bash, Glob
|
||||
---
|
||||
|
||||
# Clojure Code Review Skill
|
||||
|
||||
@./../_shared/clojure-style-guide.md
|
||||
@./../_shared/clojure-commands.md
|
||||
|
||||
## Review guidelines
|
||||
|
||||
**What to flag:**
|
||||
|
||||
- Check compliance with the Metabase Clojure style guide (included above)
|
||||
- If `CLOJURE_STYLE_GUIDE.adoc` exists in the working directory, also check compliance with the community Clojure style guide
|
||||
- Flag all style guide violations
|
||||
|
||||
**What NOT to post:**
|
||||
|
||||
- Do not post comments congratulating someone for trivial changes or for following style guidelines
|
||||
- Do not post comments confirming things "look good" or telling them they did something correctly
|
||||
- Only post comments about style violations or potential issues
|
||||
|
||||
Example bad code review comments to avoid:
|
||||
|
||||
> This TODO comment is properly formatted with author and date - nice work!
|
||||
|
||||
> Good addition of limit 1 to the query - this makes the test more efficient without changing its behavior.
|
||||
|
||||
> The kondo ignore comment is appropriately placed here
|
||||
|
||||
> Test name properly ends with -test as required by the style guide.
|
||||
|
||||
**Special cases:**
|
||||
|
||||
- Do not post comments about missing parentheses (these will be caught by the linter)
|
||||
|
||||
## Quick review checklist
|
||||
|
||||
Use this to scan through changes efficiently:
|
||||
|
||||
### Naming
|
||||
|
||||
- [ ] Descriptive names (no `tbl`, `zs'`)
|
||||
- [ ] Pure functions named as nouns describing their return value
|
||||
- [ ] `kebab-case` for all variables and functions
|
||||
- [ ] Side-effect functions end with `!`
|
||||
- [ ] No namespace-alias repetition in function names
|
||||
|
||||
### Documentation
|
||||
|
||||
- [ ] Public vars in `src` or `enterprise/backend/src` have useful docstrings
|
||||
- [ ] Docstrings use Markdown conventions
|
||||
- [ ] References use `[[other-var]]` not backticks
|
||||
- [ ] `TODO` comments include author and date: `;; TODO (Name 1/1/25) -- description`
|
||||
|
||||
### Code Organization
|
||||
|
||||
- [ ] Everything `^:private` unless used elsewhere
|
||||
- [ ] No `declare` when avoidable (public functions near end)
|
||||
- [ ] Functions under 20 lines when possible
|
||||
- [ ] No blank lines within definition forms (except pairwise constructs in `let`/`cond`)
|
||||
- [ ] Lines ≤ 120 characters
|
||||
|
||||
### Tests
|
||||
|
||||
- [ ] Separate `deftest` forms for distinct test cases
|
||||
- [ ] Pure tests marked `^:parallel`
|
||||
- [ ] Test names end in `-test` or `-test-<number>`
|
||||
|
||||
### Modules
|
||||
|
||||
- [ ] Correct module patterns (OSS: `metabase.<module>.*`, EE: `metabase-enterprise.<module>.*`)
|
||||
- [ ] API endpoints in `<module>.api` namespaces
|
||||
- [ ] Public API in `<module>.core` with Potemkin
|
||||
- [ ] No cheating module linters with `:clj-kondo/ignore [:metabase/modules]`
|
||||
|
||||
### REST API
|
||||
|
||||
- [ ] Response schemas present (`:- <schema>`)
|
||||
- [ ] Query params use kebab-case, bodies use `snake_case`
|
||||
- [ ] Routes use singular nouns (e.g., `/api/dashboard/:id`)
|
||||
- [ ] `GET` has no side effects (except analytics)
|
||||
- [ ] Malli schemas detailed and complete
|
||||
- [ ] All new endpoints have tests
|
||||
|
||||
### MBQL
|
||||
|
||||
- [ ] No raw MBQL manipulation outside `lib`, `lib-be`, or `query-processor` modules
|
||||
- [ ] Uses Lib and MBQL 5, not legacy MBQL
|
||||
|
||||
### Database
|
||||
|
||||
- [ ] Model and table names are singular nouns
|
||||
- [ ] Uses `t2/select-one-fn` instead of selecting full rows for one column
|
||||
- [ ] Logic in Toucan methods, not helper functions
|
||||
|
||||
### Drivers
|
||||
|
||||
- [ ] New multimethods documented in `docs/developers-guide/driver-changelog.md`
|
||||
- [ ] Passes `driver` argument to other driver methods (no hardcoded driver names)
|
||||
- [ ] Minimal logic in `read-column-thunk`
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- [ ] Example data is bird-themed when possible
|
||||
- [ ] Kondo linter suppressions use proper format (not `#_:clj-kondo/ignore` keyword form)
|
||||
|
||||
## Pattern matching table
|
||||
|
||||
Quick scan for common issues:
|
||||
|
||||
| Pattern | Issue |
|
||||
| -------------------------------------------- | ----------------------------------------------------------- |
|
||||
| `calculate-age`, `get-user` | Pure functions should be nouns: `age`, `user` |
|
||||
| `update-db`, `save-model` | Missing `!` for side effects: `update-db!`, `save-model!` |
|
||||
| `snake_case_var` | Should use kebab-case |
|
||||
| Public var without docstring | Add docstring explaining purpose |
|
||||
| `;; TODO fix this` | Missing author/date: `;; TODO (Name 1/1/25) -- description` |
|
||||
| `(defn foo ...)` in namespace used elsewhere | Should be `(defn ^:private foo ...)` |
|
||||
| Function > 20 lines | Consider breaking up into smaller functions |
|
||||
| `/api/dashboards/:id` | Use singular: `/api/dashboard/:id` |
|
||||
| Query params with `snake_case` | Use kebab-case for query params |
|
||||
| New API endpoint without tests | Add tests for the endpoint |
|
||||
|
||||
## Feedback format examples
|
||||
|
||||
**For style violations:**
|
||||
|
||||
> This pure function should be named as a noun describing its return value. Consider `user` instead of `get-user`.
|
||||
|
||||
**For missing documentation:**
|
||||
|
||||
> This public var needs a docstring explaining its purpose, inputs, and outputs.
|
||||
|
||||
**For organization issues:**
|
||||
|
||||
> This function is only used in this namespace, so it should be marked `^:private`.
|
||||
|
||||
**For API conventions:**
|
||||
|
||||
> Query parameters should use kebab-case. Change `user_id` to `user-id`.
|
||||
59
.claude/skills/clojure-write/SKILL.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: clojure-write
|
||||
description: Guide Clojure and ClojureScript development using REPL-driven workflow, coding conventions, and best practices. Use when writing, developing, or refactoring Clojure/ClojureScript code.
|
||||
---
|
||||
|
||||
# Clojure Development Skill
|
||||
|
||||
@./../_shared/development-workflow.md
|
||||
@./../_shared/clojure-style-guide.md
|
||||
@./../_shared/clojure-commands.md
|
||||
|
||||
## REPL-Driven Development Workflow
|
||||
|
||||
- Start with small, fundamental functions:
|
||||
- Identify the core features or functionalities required for your task.
|
||||
- Break each feature down into the smallest, most basic functions that can be developed and tested independently.
|
||||
- Write and test in the REPL:
|
||||
- Write the code for each small function directly in the REPL (Read-Eval-Print Loop).
|
||||
- Test it thoroughly with a variety of inputs, including typical use cases and relevant edge cases, to ensure it
|
||||
behaves as expected.
|
||||
- Integrate into source code:
|
||||
- Once a function works correctly in the REPL, move it from the REPL environment into your source code files (e.g.,
|
||||
within appropriate namespaces).
|
||||
- Gradually increase complexity:
|
||||
- Build upon tested, basic functions to create more complex functions or components.
|
||||
- Compose smaller functions together, testing each new composition in the REPL to verify correctness step by step.
|
||||
- Ensure dependency testing:
|
||||
- Make sure every function is fully tested in the REPL before it is depended upon by other functions.
|
||||
- This ensures that each layer of your application is reliable before you build on it.
|
||||
- Use the REPL fully:
|
||||
- Use the REPL as your primary tool to experiment with different approaches, iterate quickly, and get immediate
|
||||
feedback on your code.
|
||||
- Follow functional programming principles:
|
||||
- Keep functions small, focused, and composable.
|
||||
- Use Clojure's functional programming features—like immutability, higher-order functions, and the standard
|
||||
library—to write concise, effective code.
|
||||
|
||||
## How to Evaluate Code
|
||||
|
||||
### Bottom-up Dev Loop
|
||||
|
||||
1. Write code into a file.
|
||||
2. Evaluate the file's namespace and make sure it loads correctly with:
|
||||
|
||||
```
|
||||
./bin/mage -repl --namespace metabase.app-db.connection
|
||||
```
|
||||
|
||||
3. Call functions in the namespace with test inputs, and observe that the outputs are correct
|
||||
Feel free to copy these REPL session trials into actual test cases using `deftest` and `is`.
|
||||
4. Once you know these functions are good, return to 1, and compose them into the task that you need to build.
|
||||
|
||||
## Critical Rules for Editing
|
||||
|
||||
- Be careful with parentheses counts when editing Clojure code
|
||||
- After EVERY change to Clojure code, verify readability with `-check-readable`
|
||||
- End all files with a newline
|
||||
- When editing tabular code, where the columns line up, try to keep them aligned
|
||||
- Spaces on a line with nothing after it is not allowed
|
||||
168
.claude/skills/docs-review/SKILL.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
name: docs-review
|
||||
description: Review documentation changes for compliance with the Metabase writing style guide. Use when reviewing pull requests, files, or diffs containing documentation markdown files.
|
||||
allowed-tools: Read, Grep, Bash, Glob
|
||||
---
|
||||
|
||||
# Documentation Review Skill
|
||||
|
||||
@./../_shared/metabase-style-guide.md
|
||||
|
||||
## Review mode detection
|
||||
|
||||
**IMPORTANT: Before starting the review, determine which mode to use:**
|
||||
|
||||
1. **PR review mode**: If the `mcp__github__create_pending_pull_request_review` tool is available, you are reviewing a GitHub PR
|
||||
- Use the pending review workflow to post all issues as one cohesive review
|
||||
- Follow the workflow steps in "PR review mode format" below
|
||||
|
||||
2. **Local review mode**: If the MCP tool is NOT available, output issues in the conversation
|
||||
- Format all issues in a numbered markdown list (as described in "Feedback format" below)
|
||||
|
||||
## Review process
|
||||
|
||||
1. **Detect review mode** - Check if `mcp__github__create_pending_pull_request_review` is available
|
||||
2. Read the changes through once to understand intent
|
||||
3. Check all issues that violate style guide or significantly impact readability
|
||||
4. Only flag issues worth mentioning - if it won't make a material difference to the reader, skip it
|
||||
5. **REQUIRED: Number ALL feedback sequentially** - Start from Issue 1 and increment for each issue found
|
||||
|
||||
## Review checklist
|
||||
|
||||
Run through the diff looking for these issues:
|
||||
|
||||
**Tone and voice:**
|
||||
|
||||
- [ ] Formal/corporate language ("utilize" not "use", "offerings", etc.)
|
||||
- [ ] "Users" instead of "people" or "companies"
|
||||
- [ ] Excessive exclamation points or overly peppy tone
|
||||
- [ ] Telling readers something is cool instead of showing them
|
||||
|
||||
**Structure and clarity:**
|
||||
|
||||
- [ ] Important information buried instead of leading
|
||||
- [ ] Verbose text that adds little value
|
||||
- [ ] Paragraphs without clear purpose
|
||||
- [ ] Vague headings that don't convey the point
|
||||
- [ ] Instructions explain "why" before telling "what to do"
|
||||
- [ ] Tasks described as "easy" or "simple"
|
||||
|
||||
**Links and references:**
|
||||
|
||||
- [ ] Linking the word "here" instead of descriptive text
|
||||
- [ ] Links in headings (unless entire heading is a link)
|
||||
|
||||
**Formatting:**
|
||||
|
||||
- [ ] Backticks on UI elements (should use **bold**)
|
||||
- [ ] Backticks used as quotation marks
|
||||
- [ ] Ampersands as "and" substitute (except proper nouns)
|
||||
- [ ] Inconsistent list formatting
|
||||
|
||||
**Code and examples:**
|
||||
|
||||
- [ ] Code examples that don't work or would error
|
||||
- [ ] Commands not in execution order
|
||||
- [ ] Full-width screenshots instead of scoped UI elements
|
||||
- [ ] Excessive or unnecessary images
|
||||
|
||||
**Sentence construction:**
|
||||
|
||||
- [ ] Overuse of pronouns when introducing new terms
|
||||
|
||||
## Quick scan table
|
||||
|
||||
| Pattern | Issue |
|
||||
| ----------------------------- | --------------------------------------------- |
|
||||
| `Button name` or `UI element` | Should use **bold** not backticks |
|
||||
| we can do X, our feature | Should be "Metabase" or "it" |
|
||||
| click here, read more here | Need descriptive link text |
|
||||
| easy, simple, just | Remove condescending qualifiers |
|
||||
| users | Should be "people" or "companies" if possible |
|
||||
|
||||
## Feedback format
|
||||
|
||||
**MANDATORY REQUIREMENT: Every single issue MUST be numbered sequentially starting from Issue 1.**
|
||||
|
||||
This numbered format is NON-NEGOTIABLE. It allows users to efficiently reference specific issues (e.g., "fix issues 1, 3, and 5") and track which feedback has been addressed.
|
||||
|
||||
### Local review mode format
|
||||
|
||||
When outputting issues in the conversation (local mode), use this format:
|
||||
|
||||
```markdown
|
||||
## Issues
|
||||
|
||||
**Issue 1: [Brief title]**
|
||||
Line X: Succinct description of the issue
|
||||
[code or example]
|
||||
Suggested fix or succinct explanation
|
||||
|
||||
**Issue 2: [Brief title]**
|
||||
Line Y: Description of the issue
|
||||
Suggested fix or explanation
|
||||
|
||||
**Issue 3: [Brief title]**
|
||||
...
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
> **Issue 1: Backticks on UI elements**
|
||||
> Line 42: This uses backticks for the UI element. Use **bold** instead: **Filter** not `Filter`.
|
||||
|
||||
> **Issue 2: Formal tone**
|
||||
> Line 15: This could be more conversational. Consider: "You can't..." instead of "You cannot..."
|
||||
|
||||
> **Issue 3: Vague heading**
|
||||
> Line 8: The heading could be more specific. Try stating the point directly: "Run migrations before upgrading" vs "Upgrade process"
|
||||
|
||||
### PR review mode format
|
||||
|
||||
When posting to GitHub (PR mode), use the **pending review workflow**:
|
||||
|
||||
**Workflow steps:**
|
||||
|
||||
1. **Start a review**: Use `mcp__github__create_pending_pull_request_review` to begin a pending review
|
||||
- This creates a draft review that won't be visible until submitted
|
||||
|
||||
2. **Get diff information**: Use `mcp__github__get_pull_request_diff` to understand the code changes and line numbers
|
||||
- This helps you determine the correct file paths and line numbers for comments
|
||||
|
||||
3. **Identify ALL issues**: Read through all changes and identify every issue worth mentioning
|
||||
- Collect all issues before posting any comments
|
||||
- Number them sequentially (Issue 1, Issue 2, Issue 3, etc.)
|
||||
|
||||
4. **Add review comments**: Use `mcp__github__add_pull_request_review_comment_to_pending_review` for each issue
|
||||
- **CRITICAL**: Post ALL comments in a SINGLE response using multiple tool calls in parallel
|
||||
- Each comment should reference a specific file path and line number from the diff
|
||||
- Start each comment body with `**Issue N: [Brief title]**`
|
||||
- Include the description and suggested fix
|
||||
|
||||
5. **Submit the review**: Use `mcp__github__submit_pending_pull_request_review` to publish all comments at once
|
||||
- Use event type `"COMMENT"` (NOT "REQUEST_CHANGES") to make it non-blocking
|
||||
- **Do NOT include a body message** - Leave the body empty or omit it entirely
|
||||
- All comments will appear together as one cohesive review
|
||||
|
||||
**Comment format example:**
|
||||
```
|
||||
**Issue 1: Backticks on UI elements**
|
||||
|
||||
This uses backticks for the UI element. Use **bold** instead: **Filter** not `Filter`.
|
||||
```
|
||||
|
||||
**IMPORTANT**:
|
||||
- Each issue gets its own review comment attached to the pending review
|
||||
- Number ALL comments sequentially (Issue 1, Issue 2, Issue 3, etc.)
|
||||
- Always start the comment body with `**Issue N: [Brief title]**`
|
||||
- **MUST add all comments in parallel in a single response** - Do NOT add them one after another in separate responses
|
||||
- Do NOT output a summary message to the conversation - only post GitHub review comments
|
||||
- When submitting the review, do NOT include a body parameter (or leave it empty) to avoid cluttering the PR with summary text
|
||||
- The review will appear as a single review with multiple comments when submitted
|
||||
|
||||
## Final check
|
||||
|
||||
1. Remove any issues from your assessment that won't make a material difference to the reader if addressed. Only flag issues worth the author's time to fix.
|
||||
2. **Verify all issues are numbered sequentially** starting from Issue 1 with no gaps in numbering.
|
||||
3. Confirm the format exactly matches: `**Issue N: [Brief title]**` where N is the issue number.
|
||||
4. **In PR mode**: Verify each issue was posted as a separate GitHub comment (not output to conversation).
|
||||
96
.claude/skills/docs-write/SKILL.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
name: docs-write
|
||||
description: Write documentation following Metabase's conversational, clear, and user-focused style. Use when creating or editing documentation files (markdown, MDX, etc.).
|
||||
allowed-tools: Read, Write, Grep, Bash, Glob
|
||||
---
|
||||
|
||||
# Documentation Writing Skill
|
||||
|
||||
@./../_shared/metabase-style-guide.md
|
||||
|
||||
## When writing documentation
|
||||
|
||||
### Start here
|
||||
|
||||
1. **Who is this for?** Match complexity to audience. Don't oversimplify hard things or overcomplicate simple ones.
|
||||
2. **What do they need?** Get them to the answer fast. Nobody wants to be in docs longer than necessary.
|
||||
3. **What did you struggle with?** Those common questions you had when learning? Answer them (without literally including the question).
|
||||
|
||||
### Writing process
|
||||
|
||||
**Draft:**
|
||||
|
||||
- Write out the steps/explanation as you'd tell a colleague
|
||||
- Lead with what to do, then explain why
|
||||
- Use headings that state your point: "Set SAML before adding users" not "SAML configuration timing"
|
||||
|
||||
**Edit:**
|
||||
|
||||
- Read aloud. Does it sound like you talking? If it's too formal, simplify.
|
||||
- Cut anything that doesn't directly help the reader
|
||||
- Check each paragraph has one clear purpose
|
||||
- Verify examples actually work (don't give examples that error)
|
||||
|
||||
**Polish:**
|
||||
|
||||
- Make links descriptive (never "here")
|
||||
- Backticks only for code/variables, **bold** for UI elements
|
||||
- American spelling, serial commas
|
||||
- Keep images minimal and scoped tight
|
||||
|
||||
**Format:**
|
||||
|
||||
- Run prettier on the file after making edits: `yarn prettier --write <file-path>`
|
||||
- This ensures consistent formatting across all documentation
|
||||
|
||||
### Common patterns
|
||||
|
||||
**Instructions:**
|
||||
|
||||
```markdown
|
||||
Run:
|
||||
\`\`\`
|
||||
command-to-run
|
||||
\`\`\`
|
||||
|
||||
Then:
|
||||
\`\`\`
|
||||
next-command
|
||||
\`\`\`
|
||||
|
||||
This ensures you're getting the latest changes.
|
||||
```
|
||||
|
||||
Not: "(remember to run X before Y...)" buried in a paragraph.
|
||||
|
||||
**Headings:**
|
||||
|
||||
- "Use environment variables for configuration" ✅
|
||||
- "Environment variables" ❌ (too vague)
|
||||
- "How to use environment variables for configuration" ❌ (too wordy)
|
||||
|
||||
**Links:**
|
||||
|
||||
- "Check out the [SAML documentation](link)" ✅
|
||||
- "Read the docs [here](link)" ❌
|
||||
|
||||
### Watch out for
|
||||
|
||||
- Describing tasks as "easy" (you don't know the reader's context)
|
||||
- Using "we" when talking about Metabase features (use "Metabase" or "it")
|
||||
- Formal language: "utilize", "reference", "offerings"
|
||||
- Too peppy: multiple exclamation points
|
||||
- Burying the action in explanation
|
||||
- Code examples that don't work
|
||||
- Numbers that will become outdated
|
||||
|
||||
### Quick reference
|
||||
|
||||
| Write This | Not This |
|
||||
| -------------------------- | ------------------ |
|
||||
| people, companies | users |
|
||||
| summarize | aggregate |
|
||||
| take a look at | reference |
|
||||
| can't, don't | cannot, do not |
|
||||
| **Filter** button | \`Filter\` button |
|
||||
| Check out [the docs](link) | Click [here](link) |
|
||||
19
.claude/skills/typescript-review/SKILL.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: typescript-review
|
||||
description: Review TypeScript and JavaScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing TypeScript/JavaScript code.
|
||||
allowed-tools: Read, Grep, Bash, Glob
|
||||
---
|
||||
|
||||
# TypeScript/JavaScript Code Review Skill
|
||||
|
||||
@./../_shared/typescript-commands.md
|
||||
|
||||
## Code Review Guidelines
|
||||
|
||||
Review pull requests with a focus on:
|
||||
|
||||
- Compliance with project coding standards and conventions
|
||||
- Code quality and best practices
|
||||
- Clear and correct JSDoc comments
|
||||
- Type safety and proper TypeScript usage
|
||||
- React best practices (when applicable)
|
||||
9
.claude/skills/typescript-write/SKILL.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: typescript-write
|
||||
description: Write TypeScript and JavaScript code following Metabase coding standards and best practices. Use when developing or refactoring TypeScript/JavaScript code.
|
||||
---
|
||||
|
||||
# TypeScript/JavaScript Development Skill
|
||||
|
||||
@./../_shared/development-workflow.md
|
||||
@./../_shared/typescript-commands.md
|
||||
@@ -536,11 +536,11 @@
|
||||
metabase.channel.template.handlebars-test/with-temp-template! hooks.common/with-vec-first-binding
|
||||
metabase.classloader.core/require hooks.clojure.core.require/lint-require
|
||||
metabase.classloader.impl/require hooks.clojure.core.require/lint-require
|
||||
metabase.collections.api-test/with-french-user-and-personal-collection! hooks.common/with-two-top-level-bindings
|
||||
metabase.collections-rest.api-test/with-french-user-and-personal-collection! hooks.common/with-two-top-level-bindings
|
||||
metabase.collections.models.collection-test/with-collection-hierarchy! hooks.common/let-one-with-optional-value
|
||||
metabase.collections.models.collection-test/with-personal-and-impersonal-collections hooks.common/with-two-bindings
|
||||
metabase.dashboards.api-test/with-chain-filter-fixtures hooks.common/let-one-with-optional-value
|
||||
metabase.dashboards.api-test/with-simple-dashboard-with-tabs hooks.common/with-one-binding
|
||||
metabase.dashboards-rest.api-test/with-chain-filter-fixtures hooks.common/let-one-with-optional-value
|
||||
metabase.dashboards-rest.api-test/with-simple-dashboard-with-tabs hooks.common/with-one-binding
|
||||
metabase.dashboards.models.dashboard-tab-test/with-dashtab-in-personal-collection hooks.common/with-one-top-level-binding
|
||||
metabase.dashboards.models.dashboard-test/with-dash-in-collection! hooks.common/with-three-bindings
|
||||
metabase.driver.bigquery-cloud-sdk-test/calculate-bird-scarcity hooks.metabase.query-processor-test.expressions-test/calculate-bird-scarcity
|
||||
@@ -548,10 +548,10 @@
|
||||
metabase.driver.oracle-test/with-temp-user hooks.common/let-one-with-optional-value
|
||||
metabase.driver/register! hooks.metabase.driver/register!
|
||||
metabase.driver-api/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
|
||||
metabase.embedding.api.embed-test/do-response-formats hooks.common/with-two-bindings
|
||||
metabase.embedding.api.embed-test/with-chain-filter-fixtures! hooks.common/let-one-with-optional-value
|
||||
metabase.embedding.api.embed-test/with-temp-card hooks.common/let-one-with-optional-value
|
||||
metabase.embedding.api.embed-test/with-temp-dashcard hooks.common/let-one-with-optional-value
|
||||
metabase.embedding-rest.api.embed-test/do-response-formats hooks.common/with-two-bindings
|
||||
metabase.embedding-rest.api.embed-test/with-chain-filter-fixtures! hooks.common/let-one-with-optional-value
|
||||
metabase.embedding-rest.api.embed-test/with-temp-card hooks.common/let-one-with-optional-value
|
||||
metabase.embedding-rest.api.embed-test/with-temp-dashcard hooks.common/let-one-with-optional-value
|
||||
metabase.legacy-mbql.schema.macros/defclause hooks.metabase.legacy-mbql.schemas.macros/defclause
|
||||
metabase.legacy-mbql.schema.macros/defclause* hooks.metabase.legacy-mbql.schemas.macros/defclause*
|
||||
metabase.legacy-mbql.schema.macros/one-of hooks.metabase.legacy-mbql.schemas.macros/one-of
|
||||
@@ -571,10 +571,10 @@
|
||||
metabase.permissions.core/disable-perms-cache hooks.common/do*
|
||||
metabase.premium-features.core/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
|
||||
metabase.premium-features.defenterprise/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
|
||||
metabase.public-sharing.api-test/with-required-param-card! hooks.common/with-one-binding
|
||||
metabase.public-sharing.api-test/with-temp-public-card hooks.common/let-one-with-optional-value
|
||||
metabase.public-sharing.api-test/with-temp-public-dashboard hooks.common/let-one-with-optional-value
|
||||
metabase.public-sharing.api-test/with-temp-public-dashboard-and-card hooks.common/with-three-bindings
|
||||
metabase.public-sharing-rest.api-test/with-required-param-card! hooks.common/with-one-binding
|
||||
metabase.public-sharing-rest.api-test/with-temp-public-card hooks.common/let-one-with-optional-value
|
||||
metabase.public-sharing-rest.api-test/with-temp-public-dashboard hooks.common/let-one-with-optional-value
|
||||
metabase.public-sharing-rest.api-test/with-temp-public-dashboard-and-card hooks.common/with-three-bindings
|
||||
metabase.pulse.api.alert-test/with-alert-in-collection! hooks.common/with-four-bindings
|
||||
metabase.pulse.dashboard-subscription-test/with-link-card-fixture-for-dashboard hooks.common/let-second
|
||||
metabase.pulse.models.pulse-test/with-dashboard-subscription-in-collection! hooks.common/with-four-bindings
|
||||
@@ -636,7 +636,7 @@
|
||||
metabase.test/with-temp hooks.toucan2.tools.with-temp/with-temp
|
||||
metabase.test/with-temp-file hooks.metabase.test.util/with-temp-file
|
||||
metabase.test/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values
|
||||
metabase.users.api-test/with-temp-user-email! hooks.common/with-one-binding
|
||||
metabase.users-rest.api-test/with-temp-user-email! hooks.common/with-one-binding
|
||||
metabase.util.i18n/deferred-trs hooks.metabase.util.i18n/deferred-trs
|
||||
metabase.util.i18n/deferred-tru hooks.metabase.util.i18n/deferred-tru
|
||||
metabase.util.i18n/trs hooks.metabase.util.i18n/trs
|
||||
@@ -660,43 +660,43 @@
|
||||
metabase.xrays.api.automagic-dashboards-test/with-indexed-model! hooks.metabase.xrays.api.automagic-dashboards-test/with-indexed-model!}
|
||||
|
||||
:macroexpand
|
||||
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
|
||||
clojurewerkz.quartzite.schedule.calendar-interval/build macros.quartz/build-job
|
||||
clojurewerkz.quartzite.schedule.cron/schedule macros.quartz/schedule
|
||||
clojurewerkz.quartzite.schedule.simple/schedule macros.quartz/simple-schedule
|
||||
clojurewerkz.quartzite.triggers/build macros.quartz/build-trigger
|
||||
metabase-enterprise.database-routing.e2e-test/with-temp-dbs! macros.metabase-enterprise.database-routing.e2e-test/with-temp-dbs!
|
||||
metabase-enterprise.impersonation.util-test/with-impersonations! macros.metabase-enterprise.impersonation.util-test/with-impersonations!
|
||||
metabase-enterprise.query-reference-validation.api-test/with-test-setup! macros.metabase-enterprise.query-reference-validation.api-test/with-test-setup!
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase-enterprise.serialization.test-util/with-world macros.metabase-enterprise.serialization.test-util/with-world
|
||||
metabase-enterprise.test/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase-enterprise.test/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase.collections.api-test/with-collection-hierarchy! macros.metabase.collections.api-test/with-collection-hierarchy!
|
||||
metabase.collections.api-test/with-some-children-of-collection! macros.metabase.collections.api-test/with-some-children-of-collection!
|
||||
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-card-referencing! macros.metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-card-referencing!
|
||||
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing! macros.metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing!
|
||||
metabase.lib.common/defop macros.metabase.lib.common/defop
|
||||
metabase.lib.filter/deffilter macros.metabase.lib.filter/deffilter
|
||||
metabase.parameters.chain-filter-test/chain-filter macros.metabase.parameters.chain-filter-test/chain-filter
|
||||
metabase.parameters.chain-filter-test/chain-filter-search macros.metabase.parameters.chain-filter-test/chain-filter
|
||||
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing! macros.metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing!
|
||||
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing! macros.metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing!
|
||||
metabase.queries.api.card-test/with-ordered-items macros.metabase.queries.api.card-test/with-ordered-items
|
||||
metabase.query-analysis.models.query-analysis-test/with-test-setup macros.metabase.query-analysis.models.query-analysis-test/with-test-setup
|
||||
metabase.query-analysis.task.test-setup/with-test-setup! macros.metabase.query-analysis.task.test-setup/with-test-setup!
|
||||
metabase.query-processor.streaming/streaming-response macros.metabase.query-processor.streaming/streaming-response
|
||||
metabase.server.statistics-handler-test/with-server macros.metabase.server.statistics-handler-test/with-server
|
||||
metabase.test.data.users/with-group-for-user macros.metabase.test.data.users/with-group-for-user
|
||||
metabase.test.util/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
|
||||
metabase.test.util/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
|
||||
metabase.test/with-group-for-user macros.metabase.test.data.users/with-group-for-user
|
||||
metabase.test/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
|
||||
metabase.test/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
|
||||
metabase.users.models.user-test/with-groups! macros.metabase.users.models.user-test/with-groups!
|
||||
metabase.util.namespaces/import-fn macros.metabase.util.namespaces/import-fn
|
||||
metabase.xrays.related-test/with-world macros.metabase.xrays.related-test/with-world}}
|
||||
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
|
||||
clojurewerkz.quartzite.schedule.calendar-interval/build macros.quartz/build-job
|
||||
clojurewerkz.quartzite.schedule.cron/schedule macros.quartz/schedule
|
||||
clojurewerkz.quartzite.schedule.simple/schedule macros.quartz/simple-schedule
|
||||
clojurewerkz.quartzite.triggers/build macros.quartz/build-trigger
|
||||
metabase-enterprise.database-routing.e2e-test/with-temp-dbs! macros.metabase-enterprise.database-routing.e2e-test/with-temp-dbs!
|
||||
metabase-enterprise.impersonation.util-test/with-impersonations! macros.metabase-enterprise.impersonation.util-test/with-impersonations!
|
||||
metabase-enterprise.query-reference-validation.api-test/with-test-setup! macros.metabase-enterprise.query-reference-validation.api-test/with-test-setup!
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase-enterprise.serialization.test-util/with-world macros.metabase-enterprise.serialization.test-util/with-world
|
||||
metabase-enterprise.test/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase-enterprise.test/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase.collections-rest.api-test/with-collection-hierarchy! macros.metabase.collections-rest.api-test/with-collection-hierarchy!
|
||||
metabase.collections-rest.api-test/with-some-children-of-collection! macros.metabase.collections-rest.api-test/with-some-children-of-collection!
|
||||
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-card-referencing! macros.metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-card-referencing!
|
||||
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing! macros.metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing!
|
||||
metabase.lib.common/defop macros.metabase.lib.common/defop
|
||||
metabase.lib.filter/deffilter macros.metabase.lib.filter/deffilter
|
||||
metabase.parameters.chain-filter-test/chain-filter macros.metabase.parameters.chain-filter-test/chain-filter
|
||||
metabase.parameters.chain-filter-test/chain-filter-search macros.metabase.parameters.chain-filter-test/chain-filter
|
||||
metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-card-referencing! macros.metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-card-referencing!
|
||||
metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-dashcard-referencing! macros.metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-dashcard-referencing!
|
||||
metabase.queries.api.card-test/with-ordered-items macros.metabase.queries.api.card-test/with-ordered-items
|
||||
metabase.query-analysis.models.query-analysis-test/with-test-setup macros.metabase.query-analysis.models.query-analysis-test/with-test-setup
|
||||
metabase.query-analysis.task.test-setup/with-test-setup! macros.metabase.query-analysis.task.test-setup/with-test-setup!
|
||||
metabase.query-processor.streaming/streaming-response macros.metabase.query-processor.streaming/streaming-response
|
||||
metabase.server.statistics-handler-test/with-server macros.metabase.server.statistics-handler-test/with-server
|
||||
metabase.test.data.users/with-group-for-user macros.metabase.test.data.users/with-group-for-user
|
||||
metabase.test.util/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
|
||||
metabase.test.util/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
|
||||
metabase.test/with-group-for-user macros.metabase.test.data.users/with-group-for-user
|
||||
metabase.test/with-temp-env-var-value! macros.metabase.test.util/with-temp-env-var-value!
|
||||
metabase.test/with-temporary-raw-setting-values macros.metabase.test.util/with-temporary-raw-setting-values
|
||||
metabase.users.models.user-test/with-groups! macros.metabase.users.models.user-test/with-groups!
|
||||
metabase.util.namespaces/import-fn macros.metabase.util.namespaces/import-fn
|
||||
metabase.xrays.related-test/with-world macros.metabase.xrays.related-test/with-world}}
|
||||
|
||||
:config-in-comment
|
||||
{:linters {:unresolved-symbol {:level :off}}}
|
||||
|
||||
@@ -47,14 +47,13 @@
|
||||
{:metabase/modules
|
||||
{actions
|
||||
{:team "Gadget"
|
||||
:api #{metabase.actions.api
|
||||
metabase.actions.args
|
||||
:api #{metabase.actions.args
|
||||
metabase.actions.core
|
||||
metabase.actions.init
|
||||
metabase.actions.schema
|
||||
metabase.actions.types}
|
||||
:uses #{analytics
|
||||
api
|
||||
collections
|
||||
driver
|
||||
events
|
||||
legacy-mbql
|
||||
@@ -63,8 +62,6 @@
|
||||
model-persistence
|
||||
models
|
||||
parameters
|
||||
permissions
|
||||
public-sharing
|
||||
queries
|
||||
query-processor
|
||||
search
|
||||
@@ -72,6 +69,19 @@
|
||||
util
|
||||
warehouses}}
|
||||
|
||||
actions-rest
|
||||
{:team "Gadget"
|
||||
:api #{metabase.actions-rest.api}
|
||||
:uses #{actions
|
||||
analytics
|
||||
api
|
||||
collections
|
||||
lib
|
||||
permissions
|
||||
public-sharing
|
||||
queries
|
||||
util}}
|
||||
|
||||
activity-feed
|
||||
{:team "UX West"
|
||||
:api #{metabase.activity-feed.api
|
||||
@@ -196,6 +206,19 @@
|
||||
task-history
|
||||
util}}
|
||||
|
||||
auth-identity
|
||||
{:team "UX West"
|
||||
:api #{metabase.auth-identity.core
|
||||
metabase.auth-identity.init}
|
||||
:uses #{channel
|
||||
events
|
||||
login-history
|
||||
models
|
||||
notification
|
||||
premium-features
|
||||
request
|
||||
util}}
|
||||
|
||||
auth-provider
|
||||
{:team "UX West"
|
||||
:api #{metabase.auth-provider.core}
|
||||
@@ -316,20 +339,18 @@
|
||||
:uses #{api
|
||||
api-routes
|
||||
app-db
|
||||
auth-identity
|
||||
classloader
|
||||
config
|
||||
|
||||
models
|
||||
query-processor
|
||||
search
|
||||
settings
|
||||
users
|
||||
util}}
|
||||
|
||||
collections
|
||||
{:team "UX West"
|
||||
:api #{metabase.collections.api
|
||||
metabase.collections.core
|
||||
:api #{metabase.collections.core
|
||||
metabase.collections.models.collection
|
||||
metabase.collections.models.collection.root}
|
||||
:uses #{api
|
||||
@@ -337,6 +358,21 @@
|
||||
app-db
|
||||
audit-app
|
||||
config
|
||||
events
|
||||
models
|
||||
permissions
|
||||
premium-features
|
||||
remote-sync
|
||||
search
|
||||
util}}
|
||||
|
||||
collections-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.collections-rest.api}
|
||||
:uses #{api
|
||||
app-db
|
||||
collections
|
||||
config
|
||||
eid-translation
|
||||
events
|
||||
lib-be
|
||||
@@ -345,10 +381,8 @@
|
||||
permissions
|
||||
premium-features
|
||||
queries
|
||||
remote-sync
|
||||
request
|
||||
revisions
|
||||
search
|
||||
upload
|
||||
util}}
|
||||
|
||||
@@ -386,22 +420,43 @@
|
||||
|
||||
dashboards
|
||||
{:team "UX West"
|
||||
:api #{metabase.dashboards.api
|
||||
metabase.dashboards.autoplace
|
||||
:api #{metabase.dashboards.autoplace
|
||||
metabase.dashboards.constants
|
||||
metabase.dashboards.init
|
||||
metabase.dashboards.models.dashboard
|
||||
metabase.dashboards.models.dashboard-card
|
||||
metabase.dashboards.models.dashboard-tab
|
||||
metabase.dashboards.schema}
|
||||
metabase.dashboards.schema
|
||||
metabase.dashboards.settings}
|
||||
:uses #{api
|
||||
app-db
|
||||
audit-app
|
||||
collections
|
||||
config
|
||||
events
|
||||
lib
|
||||
models
|
||||
parameters
|
||||
permissions
|
||||
public-sharing
|
||||
queries
|
||||
query-processor
|
||||
search
|
||||
settings
|
||||
util
|
||||
warehouse-schema}}
|
||||
|
||||
dashboards-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.dashboards-rest.api}
|
||||
:uses #{actions
|
||||
analytics
|
||||
api
|
||||
app-db
|
||||
audit-app
|
||||
channel
|
||||
collections
|
||||
config
|
||||
collections-rest
|
||||
dashboards
|
||||
eid-translation
|
||||
embedding
|
||||
events
|
||||
@@ -417,10 +472,7 @@
|
||||
query-processor
|
||||
request
|
||||
revisions
|
||||
search
|
||||
settings
|
||||
util
|
||||
warehouse-schema
|
||||
xrays}}
|
||||
|
||||
database-routing
|
||||
@@ -506,8 +558,7 @@
|
||||
|
||||
embedding
|
||||
{:team "Embedding"
|
||||
:api #{metabase.embedding.api
|
||||
metabase.embedding.init
|
||||
:api #{metabase.embedding.init
|
||||
metabase.embedding.jwt
|
||||
metabase.embedding.settings
|
||||
metabase.embedding.util
|
||||
@@ -515,19 +566,26 @@
|
||||
:uses #{analytics
|
||||
api
|
||||
config
|
||||
premium-features
|
||||
server
|
||||
settings
|
||||
util}}
|
||||
|
||||
embedding-rest
|
||||
{:team "Embedding"
|
||||
:api #{metabase.embedding-rest.api}
|
||||
:uses #{api
|
||||
dashboards
|
||||
eid-translation
|
||||
embedding
|
||||
events
|
||||
models
|
||||
notification
|
||||
parameters
|
||||
premium-features
|
||||
public-sharing
|
||||
public-sharing-rest
|
||||
queries
|
||||
query-processor
|
||||
request
|
||||
server
|
||||
settings
|
||||
tiles
|
||||
util}}
|
||||
|
||||
@@ -756,6 +814,7 @@
|
||||
:uses #{analytics
|
||||
api
|
||||
appearance
|
||||
auth-identity
|
||||
channel
|
||||
config
|
||||
dashboards
|
||||
@@ -872,22 +931,29 @@
|
||||
|
||||
public-sharing
|
||||
{:team "UX West"
|
||||
:api #{metabase.public-sharing.api
|
||||
metabase.public-sharing.core
|
||||
:api #{metabase.public-sharing.core
|
||||
metabase.public-sharing.init
|
||||
metabase.public-sharing.validation}
|
||||
:uses #{api
|
||||
settings
|
||||
util}}
|
||||
|
||||
public-sharing-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.public-sharing-rest.api}
|
||||
:uses #{actions
|
||||
analytics
|
||||
api
|
||||
dashboards
|
||||
dashboards-rest
|
||||
events
|
||||
lib
|
||||
models
|
||||
parameters
|
||||
public-sharing
|
||||
queries
|
||||
query-processor
|
||||
request
|
||||
settings
|
||||
tiles
|
||||
util}}
|
||||
|
||||
@@ -923,7 +989,7 @@
|
||||
metabase.queries.core
|
||||
metabase.queries.init
|
||||
metabase.queries.models.query ; TODO FIXME
|
||||
metabase.queries.schema} ; TODO FIXME
|
||||
metabase.queries.schema} ; TODO FIXME
|
||||
:uses #{analytics
|
||||
analyze
|
||||
api
|
||||
@@ -1052,7 +1118,7 @@
|
||||
:uses #{api
|
||||
config
|
||||
embedding
|
||||
permissions ; FIXME
|
||||
permissions ; FIXME
|
||||
session
|
||||
settings
|
||||
users
|
||||
@@ -1067,6 +1133,7 @@
|
||||
config
|
||||
dashboards
|
||||
events
|
||||
lib
|
||||
models
|
||||
queries
|
||||
query-permissions
|
||||
@@ -1166,20 +1233,18 @@
|
||||
:api #{metabase.session.api
|
||||
metabase.session.core
|
||||
metabase.session.init
|
||||
metabase.session.models.session ; TODO -- shouldn't be exposed as an API namespace
|
||||
metabase.session.settings} ; TODO -- shouldn't be exposed as an API namespace
|
||||
:uses #{api
|
||||
app-db
|
||||
auth-identity
|
||||
channel
|
||||
config
|
||||
events
|
||||
login-history
|
||||
request
|
||||
settings
|
||||
sso
|
||||
system
|
||||
task
|
||||
users
|
||||
util}}
|
||||
|
||||
settings
|
||||
@@ -1205,20 +1270,25 @@
|
||||
|
||||
setup
|
||||
{:team "UX West"
|
||||
:api #{metabase.setup.api
|
||||
metabase.setup.core
|
||||
:api #{metabase.setup.core
|
||||
metabase.setup.init}
|
||||
:uses #{app-db
|
||||
config
|
||||
settings
|
||||
util}}
|
||||
|
||||
setup-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.setup-rest.api}
|
||||
:uses #{analytics
|
||||
api
|
||||
app-db
|
||||
appearance
|
||||
config
|
||||
auth-identity
|
||||
events
|
||||
request
|
||||
session
|
||||
settings
|
||||
setup
|
||||
system
|
||||
users
|
||||
util}}
|
||||
|
||||
sso
|
||||
@@ -1227,11 +1297,11 @@
|
||||
metabase.sso.core
|
||||
metabase.sso.init}
|
||||
:uses #{api
|
||||
auth-identity
|
||||
config
|
||||
permissions
|
||||
premium-features
|
||||
settings
|
||||
users
|
||||
util}}
|
||||
|
||||
startup
|
||||
@@ -1260,6 +1330,7 @@
|
||||
app-db
|
||||
audit-app
|
||||
config
|
||||
database-routing
|
||||
driver
|
||||
events
|
||||
lib
|
||||
@@ -1375,17 +1446,31 @@
|
||||
|
||||
users
|
||||
{:team "UX West"
|
||||
:api #{metabase.users.api
|
||||
metabase.users.core
|
||||
metabase.users.init
|
||||
:api #{metabase.users.init
|
||||
metabase.users.models.user
|
||||
metabase.users.models.user-parameter-value
|
||||
metabase.users.schema}
|
||||
metabase.users.schema
|
||||
metabase.users.settings}
|
||||
:uses #{api
|
||||
batch-processing
|
||||
channel
|
||||
config
|
||||
events
|
||||
models
|
||||
permissions
|
||||
premium-features
|
||||
settings
|
||||
setup
|
||||
system
|
||||
util}}
|
||||
|
||||
users-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.users-rest.api}
|
||||
:uses #{analytics
|
||||
api
|
||||
appearance
|
||||
batch-processing
|
||||
channel
|
||||
auth-identity
|
||||
collections
|
||||
config
|
||||
events
|
||||
@@ -1393,11 +1478,8 @@
|
||||
permissions
|
||||
premium-features
|
||||
request
|
||||
session
|
||||
settings
|
||||
setup
|
||||
sso
|
||||
system
|
||||
users
|
||||
util
|
||||
enterprise/advanced-permissions}}
|
||||
|
||||
@@ -1436,8 +1518,7 @@
|
||||
|
||||
warehouse-schema
|
||||
{:team "UX West"
|
||||
:api #{metabase.warehouse-schema.api
|
||||
metabase.warehouse-schema.field
|
||||
:api #{metabase.warehouse-schema.field
|
||||
metabase.warehouse-schema.metadata-from-qp
|
||||
metabase.warehouse-schema.metadata-queries
|
||||
metabase.warehouse-schema.models.field
|
||||
@@ -1445,14 +1526,11 @@
|
||||
metabase.warehouse-schema.models.field-values
|
||||
metabase.warehouse-schema.models.table
|
||||
metabase.warehouse-schema.table}
|
||||
:uses #{analytics
|
||||
analyze
|
||||
:uses #{analyze
|
||||
api
|
||||
app-db
|
||||
audit-app
|
||||
database-routing
|
||||
driver
|
||||
events
|
||||
lib
|
||||
lib-be
|
||||
models
|
||||
@@ -1460,25 +1538,58 @@
|
||||
permissions
|
||||
premium-features
|
||||
query-processor
|
||||
request
|
||||
search
|
||||
util
|
||||
warehouses}}
|
||||
|
||||
warehouse-schema-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.warehouse-schema-rest.api}
|
||||
:uses #{analytics
|
||||
api
|
||||
app-db
|
||||
database-routing
|
||||
driver
|
||||
events
|
||||
lib
|
||||
models
|
||||
parameters
|
||||
query-processor
|
||||
request
|
||||
sync
|
||||
types
|
||||
upload
|
||||
util
|
||||
warehouses
|
||||
warehouse-schema
|
||||
warehouses-rest
|
||||
xrays}}
|
||||
|
||||
warehouses
|
||||
{:team "UX West"
|
||||
:api #{metabase.warehouses.api
|
||||
metabase.warehouses.core
|
||||
:api #{metabase.warehouses.core
|
||||
metabase.warehouses.init
|
||||
metabase.warehouses.models.database}
|
||||
:uses #{analytics
|
||||
api
|
||||
app-db
|
||||
audit-app
|
||||
driver
|
||||
lib
|
||||
models
|
||||
permissions
|
||||
premium-features
|
||||
search
|
||||
secrets
|
||||
settings
|
||||
sync
|
||||
util}}
|
||||
|
||||
warehouses-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.warehouses-rest.api}
|
||||
:uses #{analytics
|
||||
api
|
||||
app-db
|
||||
classloader
|
||||
collections
|
||||
config
|
||||
@@ -1493,15 +1604,16 @@
|
||||
queries
|
||||
request
|
||||
sample-data
|
||||
search
|
||||
secrets
|
||||
settings
|
||||
sync
|
||||
upload
|
||||
util
|
||||
warehouse-schema
|
||||
warehouses
|
||||
enterprise/advanced-permissions}}
|
||||
|
||||
|
||||
xrays
|
||||
{:team "Querying"
|
||||
:api #{metabase.xrays.api
|
||||
@@ -1639,10 +1751,15 @@
|
||||
query-processor
|
||||
settings
|
||||
sync
|
||||
users
|
||||
users-rest
|
||||
util
|
||||
enterprise/serialization}}
|
||||
|
||||
enterprise/auth-identity
|
||||
{:team "UX West"
|
||||
:api #{}
|
||||
:uses #{premium-features}}
|
||||
|
||||
enterprise/auth-provider
|
||||
{:team "UX West"
|
||||
:api #{}
|
||||
@@ -1692,6 +1809,7 @@
|
||||
models
|
||||
request
|
||||
users
|
||||
users-rest
|
||||
util}}
|
||||
|
||||
enterprise/content-translation
|
||||
@@ -1748,7 +1866,7 @@
|
||||
settings
|
||||
util
|
||||
warehouse-schema
|
||||
warehouses}}
|
||||
warehouses-rest}}
|
||||
|
||||
enterprise/dependencies
|
||||
{:team "Querying"
|
||||
@@ -1877,7 +1995,8 @@
|
||||
metabase-enterprise.metabot-v3.core
|
||||
metabase-enterprise.metabot-v3.init
|
||||
metabase-enterprise.metabot-v3.tools.api}
|
||||
:uses #{api
|
||||
:uses #{activity-feed
|
||||
api
|
||||
app-db
|
||||
audit-app
|
||||
collections
|
||||
@@ -1900,10 +2019,10 @@
|
||||
sync
|
||||
system
|
||||
task
|
||||
users
|
||||
users-rest
|
||||
util
|
||||
warehouse-schema
|
||||
warehouses
|
||||
warehouses-rest
|
||||
enterprise/dependencies
|
||||
enterprise/documents
|
||||
enterprise/transforms
|
||||
@@ -1932,6 +2051,7 @@
|
||||
events
|
||||
premium-features
|
||||
public-sharing
|
||||
public-sharing-rest
|
||||
query-processor
|
||||
util}}
|
||||
|
||||
@@ -2054,10 +2174,9 @@
|
||||
metabase-enterprise.sso.settings}
|
||||
:uses #{api
|
||||
appearance
|
||||
auth-identity
|
||||
channel
|
||||
embedding
|
||||
events
|
||||
notification
|
||||
premium-features
|
||||
request
|
||||
session
|
||||
@@ -2074,6 +2193,7 @@
|
||||
:uses #{analytics
|
||||
api
|
||||
collections
|
||||
collections-rest
|
||||
embedding
|
||||
premium-features
|
||||
queries
|
||||
@@ -2081,6 +2201,19 @@
|
||||
settings
|
||||
util}}
|
||||
|
||||
enterprise/support-access-grants
|
||||
{:team "UX West"
|
||||
:api #{metabase-enterprise.support-access-grants.api
|
||||
metabase-enterprise.support-access-grants.init}
|
||||
:uses #{api
|
||||
auth-identity
|
||||
events
|
||||
models
|
||||
request
|
||||
settings
|
||||
system
|
||||
util}}
|
||||
|
||||
enterprise/transforms
|
||||
{:team "Querying"
|
||||
:api #{metabase-enterprise.transforms.api
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
metabase.permissions.models.permissions/update-data-perms-graph!
|
||||
metabase.permissions.models.permissions/update-group-permissions!
|
||||
metabase.permissions.test-util/with-restored-perms!
|
||||
metabase.public-sharing.api-test/add-card-to-dashboard!
|
||||
metabase.public-sharing-rest.api-test/add-card-to-dashboard!
|
||||
metabase.pulse.send/send-notifications!
|
||||
metabase.pulse.send/send-pulse!
|
||||
metabase.query-processor.streaming.interface/begin!
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(ns macros.metabase.collections.api-test)
|
||||
(ns macros.metabase.collections-rest.api-test)
|
||||
|
||||
(defmacro with-collection-hierarchy! [bindings & body]
|
||||
`(let [~'a nil
|
||||
@@ -1,4 +1,4 @@
|
||||
(ns macros.metabase.embedding.api.embed-test)
|
||||
(ns macros.metabase.embedding-rest.api.embed-test)
|
||||
|
||||
(defmacro with-embedding-enabled-and-temp-card-referencing!
|
||||
[table-kw field-kw [card-binding] & body]
|
||||
@@ -1,4 +1,4 @@
|
||||
(ns macros.metabase.public-sharing.api-test)
|
||||
(ns macros.metabase.public-sharing-rest.api-test)
|
||||
|
||||
(defmacro with-sharing-enabled-and-temp-card-referencing!
|
||||
[table-kw field-kw [card-binding] & body]
|
||||
628
.cljfmt.edn
@@ -38,321 +38,321 @@
|
||||
;; zero get indented 2 spaces if not on the first line regardless of number of elements on the first line.
|
||||
:extra-indents
|
||||
{;; clojure.core stuff
|
||||
fn* [[:inner 0]]
|
||||
let* [[:block 1]]
|
||||
with-meta [[:default]]
|
||||
fn* [[:inner 0]]
|
||||
let* [[:block 1]]
|
||||
with-meta [[:default]]
|
||||
;; library stuff. Libraries that specify their own `:style/indent` specs are included in the section after this
|
||||
;; since it is generated automatically.
|
||||
clojure.core.logic/fresh [[:block 1]]
|
||||
clojure.core.logic/matcha [[:block 1]]
|
||||
clojure.core.logic/run [[:block 2]]
|
||||
clojure.core.match/match [[:block 1]]
|
||||
clojure.test.check.properties/for-all [[:block 1]]
|
||||
clojure.test.check.properties/for-all* [[:block 1]]
|
||||
clojure.test.check/quick-check [[:block 1]]
|
||||
clojure.tools.macro/macrolet [[:block 1] [:inner 2 0]]
|
||||
clojurewerkz.quartzite.jobs/build [[:default]]
|
||||
clojurewerkz.quartzite.jobs/with-description [[:default]]
|
||||
clojurewerkz.quartzite.jobs/with-identity [[:default]]
|
||||
clojurewerkz.quartzite.schedule.cron/schedule [[:default]]
|
||||
clojurewerkz.quartzite.schedule.cron/with-misfire-handling-instruction-do-nothing [[:default]]
|
||||
clojurewerkz.quartzite.triggers/build [[:default]]
|
||||
clojurewerkz.quartzite.triggers/with-description [[:default]]
|
||||
clojurewerkz.quartzite.triggers/with-identity [[:default]]
|
||||
clojurewerkz.quartzite.triggers/with-priority [[:default]]
|
||||
clojurewerkz.quartzite.triggers/with-schedule [[:default]]
|
||||
def-map-type [[:block 2] [:inner 1]]
|
||||
defprotocol+ [[:block 1] [:inner 1]]
|
||||
defrecord+ [[:block 2] [:inner 1]]
|
||||
deftype+ [[:block 2] [:inner 1]]
|
||||
net.cgrand.macrovich/case [[:block 0]]
|
||||
potemkin.namespaces/import-vars [[:default]]
|
||||
potemkin.types/def-map-type [[:block 2] [:inner 1]]
|
||||
potemkin.types/defprotocol+ [[:block 1] [:inner 1]]
|
||||
potemkin.types/defrecord+ [[:block 2] [:inner 1]]
|
||||
potemkin.types/deftype+ [[:block 2] [:inner 1]]
|
||||
potemkin/def-map-type [[:block 2] [:inner 1]]
|
||||
potemkin/defprotocol+ [[:block 1] [:inner 1]]
|
||||
potemkin/defrecord+ [[:block 2] [:inner 1]]
|
||||
potemkin/deftype+ [[:block 2] [:inner 1]]
|
||||
potemkin/import-vars [[:default]]
|
||||
taoensso.nippy/extend-freeze [[:inner 0]]
|
||||
taoensso.nippy/extend-thaw [[:inner 0]]
|
||||
throttle.core/with-throttling [[:block 1]]
|
||||
clojure.core.logic/fresh [[:block 1]]
|
||||
clojure.core.logic/matcha [[:block 1]]
|
||||
clojure.core.logic/run [[:block 2]]
|
||||
clojure.core.match/match [[:block 1]]
|
||||
clojure.test.check.properties/for-all [[:block 1]]
|
||||
clojure.test.check.properties/for-all* [[:block 1]]
|
||||
clojure.test.check/quick-check [[:block 1]]
|
||||
clojure.tools.macro/macrolet [[:block 1] [:inner 2 0]]
|
||||
clojurewerkz.quartzite.jobs/build [[:default]]
|
||||
clojurewerkz.quartzite.jobs/with-description [[:default]]
|
||||
clojurewerkz.quartzite.jobs/with-identity [[:default]]
|
||||
clojurewerkz.quartzite.schedule.cron/schedule [[:default]]
|
||||
clojurewerkz.quartzite.schedule.cron/with-misfire-handling-instruction-do-nothing [[:default]]
|
||||
clojurewerkz.quartzite.triggers/build [[:default]]
|
||||
clojurewerkz.quartzite.triggers/with-description [[:default]]
|
||||
clojurewerkz.quartzite.triggers/with-identity [[:default]]
|
||||
clojurewerkz.quartzite.triggers/with-priority [[:default]]
|
||||
clojurewerkz.quartzite.triggers/with-schedule [[:default]]
|
||||
def-map-type [[:block 2] [:inner 1]]
|
||||
defprotocol+ [[:block 1] [:inner 1]]
|
||||
defrecord+ [[:block 2] [:inner 1]]
|
||||
deftype+ [[:block 2] [:inner 1]]
|
||||
net.cgrand.macrovich/case [[:block 0]]
|
||||
potemkin.namespaces/import-vars [[:default]]
|
||||
potemkin.types/def-map-type [[:block 2] [:inner 1]]
|
||||
potemkin.types/defprotocol+ [[:block 1] [:inner 1]]
|
||||
potemkin.types/defrecord+ [[:block 2] [:inner 1]]
|
||||
potemkin.types/deftype+ [[:block 2] [:inner 1]]
|
||||
potemkin/def-map-type [[:block 2] [:inner 1]]
|
||||
potemkin/defprotocol+ [[:block 1] [:inner 1]]
|
||||
potemkin/defrecord+ [[:block 2] [:inner 1]]
|
||||
potemkin/deftype+ [[:block 2] [:inner 1]]
|
||||
potemkin/import-vars [[:default]]
|
||||
taoensso.nippy/extend-freeze [[:inner 0]]
|
||||
taoensso.nippy/extend-thaw [[:inner 0]]
|
||||
throttle.core/with-throttling [[:block 1]]
|
||||
;; the rest of the stuff in this list is generated automatically based on `:style/indent` specs
|
||||
;; by running [[dev.generate-cljfmt-config/specs]]... use that code to update this list
|
||||
metabase-enterprise.cache.config-test/with-temp-persist-models [[:block 1]]
|
||||
metabase-enterprise.impersonation.util-test/with-impersonations! [[:inner 0]]
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps! [[:inner 0]]
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! [[:inner 0]]
|
||||
metabase-enterprise.sandbox.test-util/with-user-attributes [[:block 2]]
|
||||
metabase-enterprise.serialization.test-util/with-dbs [[:inner 0]]
|
||||
metabase-enterprise.serialization.test-util/with-random-dump-dir [[:block 1]]
|
||||
metabase-enterprise.serialization.test-util/with-world [[:block 0]]
|
||||
metabase-enterprise.sso.integrations.jwt-test/with-users-with-email-deleted [[:block 1]]
|
||||
metabase-enterprise.test/with-gtaps! [[:inner 0]]
|
||||
metabase-enterprise.test/with-gtaps-for-user! [[:inner 0]]
|
||||
metabase-enterprise.test/with-user-attributes [[:block 2]]
|
||||
metabase.actions.actions-test/with-actions-test-data-and-actions-permissively-enabled [[:block 0]]
|
||||
metabase.actions.test-util/with-actions [[:block 1]]
|
||||
metabase.actions.test-util/with-actions-disabled [[:block 0]]
|
||||
metabase.actions.test-util/with-actions-enabled [[:block 0]]
|
||||
metabase.actions.test-util/with-actions-test-data [[:inner 0]]
|
||||
metabase.actions.test-util/with-actions-test-data-and-actions-enabled [[:block 0]]
|
||||
metabase.actions.test-util/with-actions-test-data-tables [[:block 1]]
|
||||
metabase.actions.test-util/with-empty-db [[:inner 0]]
|
||||
metabase.actions.test-util/with-temp-test-data [[:inner 0]]
|
||||
metabase.api.common/check [[:default]]
|
||||
metabase.api.common/define-routes [[:block 0]]
|
||||
metabase.api.common/let-404 [[:block 1]]
|
||||
metabase.app-db.core/with-application-db [[:inner 0]]
|
||||
metabase.app-db.liquibase/with-liquibase [[:block 1]]
|
||||
metabase.app-db.liquibase/with-scope-locked [[:block 1]]
|
||||
metabase.app-db.schema-migrations-test.impl/test-migrations [[:block 2]]
|
||||
metabase.cmd.copy/step [[:block 1]]
|
||||
metabase.cmd.copy/with-connection-rollback-only [[:block 1]]
|
||||
metabase.cmd.copy/with-disabled-db-constraints [[:block 2]]
|
||||
metabase.collections.api-test/with-collection-hierarchy [[:block 1]]
|
||||
metabase.collections.api-test/with-french-user-and-personal-collection [[:block 2]]
|
||||
metabase.collections.api-test/with-some-children-of-collection [[:block 1]]
|
||||
metabase.collections.models.collection-test/with-collection-hierarchy [[:block 1]]
|
||||
metabase.collections.models.collection-test/with-current-user-perms-for-collections [[:block 1]]
|
||||
metabase.collections.models.collection-test/with-personal-and-impersonal-collections [[:block 1]]
|
||||
metabase.config/build-type-case [[:block 0]]
|
||||
metabase.dashboards.models.dashboard-test/with-dash-in-collection [[:inner 0]]
|
||||
metabase.driver-api.core/match [[:inner 0]]
|
||||
metabase.driver-api.core/match-one [[:inner 0]]
|
||||
metabase.driver-api.core/replace [[:inner 0]]
|
||||
metabase.driver-api.core/replace-in [[:inner 0]]
|
||||
metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]]
|
||||
metabase.driver.druid.client/DELETE [[:default]]
|
||||
metabase.driver.druid.client/GET [[:default]]
|
||||
metabase.driver.druid.client/POST [[:default]]
|
||||
metabase.driver.druid.query-processor-test/druid-query [[:block 0]]
|
||||
metabase.driver.druid.query-processor-test/druid-query-returning-rows [[:block 0]]
|
||||
metabase.driver.mongo.query-processor/mongo-let [[:block 1]]
|
||||
metabase.driver.sql-jdbc.actions/with-jdbc-transaction [[:block 1]]
|
||||
metabase.driver.sql-jdbc.connection/with-connection-spec-for-testing-connection [[:block 1]]
|
||||
metabase.driver.sql-jdbc.execute.diagnostic/capturing-diagnostic-info [[:block 1]]
|
||||
metabase.driver.sql.query-processor/apply-top-level-clause [[:default]]
|
||||
metabase.driver/with-driver [[:block 1]]
|
||||
metabase.embedding.api.embed-test/do-response-formats [[:block 1]]
|
||||
metabase.embedding.api.embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
|
||||
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-card-referencing [[:block 3]]
|
||||
metabase.embedding.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing [[:block 3]]
|
||||
metabase.embedding.api.embed-test/with-new-secret-key [[:block 0]]
|
||||
metabase.embedding.api.embed-test/with-temp-card [[:block 1]]
|
||||
metabase.embedding.api.embed-test/with-temp-dashcard [[:block 1]]
|
||||
metabase.embedding.api.preview-embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
|
||||
metabase.embedding.api.preview-embed-test/with-new-secret-key [[:block 0]]
|
||||
metabase.lib.binning/with-binning [[:default]]
|
||||
metabase.lib.core/with-binning [[:default]]
|
||||
metabase.lib.core/with-join-alias [[:default]]
|
||||
metabase.lib.core/with-join-conditions [[:default]]
|
||||
metabase.lib.join/with-join-alias [[:default]]
|
||||
metabase.lib.join/with-join-conditions [[:default]]
|
||||
metabase.lib.test-util.macros/$ids [[:inner 0]]
|
||||
metabase.lib.test-util.macros/mbql-5-query [[:inner 0]]
|
||||
metabase.lib.test-util.macros/mbql-query [[:inner 0]]
|
||||
metabase.lib.util.match/match [[:inner 0]]
|
||||
metabase.lib.util.match/match-one [[:inner 0]]
|
||||
metabase.lib.util.match/match-lite [[:inner 0]]
|
||||
metabase.lib.util.match/match-lite-recursive [[:inner 0]]
|
||||
metabase.lib.util.match/replace [[:inner 0]]
|
||||
metabase.lib.util.match/replace-in [[:inner 0]]
|
||||
metabase.models.interface/define-batched-hydration-method [[:inner 0]]
|
||||
metabase.models.interface/define-simple-hydration-method [[:inner 0]]
|
||||
metabase.parameters.chain-filter-test/with-clean-field-values-for-field [[:block 1]]
|
||||
metabase.parameters.chain-filter-test/with-fk-field-to-field-remapping! [[:block 0]]
|
||||
metabase.parameters.chain-filter-test/with-human-readable-values-remapping! [[:block 0]]
|
||||
metabase.permissions.core/allow-changing-all-users-group-members [[:block 0]]
|
||||
metabase.permissions.core/disable-perms-cache [[:block 0]]
|
||||
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-card-referencing [[:block 3]]
|
||||
metabase.public-sharing.api-test/with-sharing-enabled-and-temp-dashcard-referencing [[:block 3]]
|
||||
metabase.public-sharing.api-test/with-temp-public-card [[:block 1]]
|
||||
metabase.public-sharing.api-test/with-temp-public-dashboard [[:block 1]]
|
||||
metabase.public-sharing.api-test/with-temp-public-dashboard-and-card [[:block 1]]
|
||||
metabase.pulse.api.alert-test/with-alert-in-collection [[:block 1]]
|
||||
metabase.pulse.dashboard-subscription-test/with-link-card-fixture-for-dashboard [[:block 2]]
|
||||
metabase.pulse.models.pulse-test/with-dashboard-subscription-in-collection [[:block 1]]
|
||||
metabase.pulse.models.pulse-test/with-pulse-in-collection [[:inner 0]]
|
||||
metabase.queries.api.card-test/with-cards-in-readable-collection [[:block 1]]
|
||||
metabase.queries.api.card-test/with-cards-in-writeable-collection [[:block 1]]
|
||||
metabase.queries.api.card-test/with-ordered-items [[:inner 0]]
|
||||
metabase.queries.api.card-test/with-temp-native-card [[:block 1]]
|
||||
metabase.queries.api.card-test/with-temp-native-card-with-params [[:block 1]]
|
||||
metabase.query-processor-test.pipeline-queries-test/pmbql-query [[:block 1]]
|
||||
metabase.query-processor-test.pipeline-queries-test/run-pmbql-query [[:block 1]]
|
||||
metabase.query-processor.debug/debug> [[:block 0]]
|
||||
metabase.query-processor.error-type/deferror [[:block 1]]
|
||||
metabase.query-processor.middleware.cache-backend.interface/with-cached-results [[:block 4]]
|
||||
metabase.query-processor.middleware.process-userland-query-test/with-query-execution [[:block 1]]
|
||||
metabase.query-processor.store/cached [[:block 1]]
|
||||
metabase.query-processor.store/with-metadata-provider [[:inner 0]]
|
||||
metabase.query-processor.streaming/streaming-response [[:block 1]]
|
||||
metabase.query-processor.test-util/format-rows-by [[:default]]
|
||||
metabase.request.core/as-admin [[:block 0]]
|
||||
metabase.request.core/with-current-request [[:inner 0]]
|
||||
metabase.request.core/with-current-user [[:inner 0]]
|
||||
metabase.request.core/with-limit-and-offset [[:inner 0]]
|
||||
metabase.request.session/as-admin [[:block 0]]
|
||||
metabase.request.session/with-current-user [[:inner 0]]
|
||||
metabase.sample-data.impl-test/with-temp-sample-database-db [[:block 1]]
|
||||
metabase.search.api-test/do-test-users [[:block 1]]
|
||||
metabase.server.middleware.session/as-admin [[:block 0]]
|
||||
metabase.server.middleware.session/with-current-user [[:inner 0]]
|
||||
metabase.server.streaming-response-test/with-streaming-response-thread-pool [[:block 0]]
|
||||
metabase.server.streaming-response-test/with-test-driver-db! [[:block 0]]
|
||||
metabase.server.streaming-response/streaming-response [[:block 2]]
|
||||
metabase.session.api/http-401-on-error [[:block 0]]
|
||||
metabase.settings.core/define-multi-setting [[:inner 0]]
|
||||
metabase.settings.core/defsetting [[:block 1]]
|
||||
metabase.settings.models.setting.multi-setting/define-multi-setting [[:inner 0]]
|
||||
metabase.settings.models.setting/defsetting [[:block 1]]
|
||||
metabase.sync.fetch-metadata/log-if-error [[:block 1]]
|
||||
metabase.sync.sync-metadata.fields-test/with-test-db [[:block 0]]
|
||||
metabase.sync.util/do-with-start-and-finish-debug-logging [[:default]]
|
||||
metabase.sync.util/sum-for [[:block 1]]
|
||||
metabase.sync.util/sync-operation [[:block 3]]
|
||||
metabase.sync.util/with-db-logging-disabled [[:default]]
|
||||
metabase.sync.util/with-duplicate-ops-prevented [[:default]]
|
||||
metabase.sync.util/with-emoji-progress-bar [[:block 1]]
|
||||
metabase.sync.util/with-error-handling [[:block 1]]
|
||||
metabase.sync.util/with-start-and-finish-logging [[:default]]
|
||||
metabase.sync.util/with-start-and-finish-logging* [[:default]]
|
||||
metabase.sync.util/with-sync-events [[:default]]
|
||||
metabase.task-history.models.task-history/with-task-history [[:block 1]]
|
||||
metabase.task.core/rerun-on-error [[:block 1]]
|
||||
metabase.task.impl/rerun-on-error [[:block 1]]
|
||||
metabase.test.data.datasets/test-driver [[:inner 0]]
|
||||
metabase.test.data.datasets/test-drivers [[:inner 0]]
|
||||
metabase.test.data.one-off-dbs/with-blank-db [[:block 0]]
|
||||
metabase.test.data.one-off-dbs/with-blueberries-db [[:block 0]]
|
||||
metabase.test.data.users/with-test-user [[:inner 0]]
|
||||
metabase.test.data/$ids [[:inner 0]]
|
||||
metabase.test.data/dataset [[:inner 0]]
|
||||
metabase.test.data/mbql-query [[:inner 0]]
|
||||
metabase.test.data/query [[:block 1]]
|
||||
metabase.test.data/run-mbql-query [[:inner 0]]
|
||||
metabase.test.data/with-empty-h2-app-db! [[:block 0]]
|
||||
metabase.test.data/with-temp-copy-of-db [[:block 0]]
|
||||
metabase.test.util.public-settings/with-additional-premium-features [[:block 1]]
|
||||
metabase.test.util.public-settings/with-premium-features [[:block 1]]
|
||||
metabase.test.util.thread-local/test-helpers-set-global-values! [[:block 0]]
|
||||
metabase.test.util/discard-setting-changes [[:block 1]]
|
||||
metabase.test.util/let-url [[:block 1]]
|
||||
metabase.test.util/throw-if-called! [[:block 1]]
|
||||
metabase.test.util/with-temp-scheduler [[:block 0]]
|
||||
metabase.test.util/with-temp-vals-in-db [[:block 3]]
|
||||
metabase.test.util/with-user-in-groups [[:inner 0]]
|
||||
metabase.test/$ids [[:inner 0]]
|
||||
metabase.test/as-admin [[:inner 0]]
|
||||
metabase.test/dataset [[:inner 0]]
|
||||
metabase.test/discard-setting-changes [[:block 1]]
|
||||
metabase.test/format-rows-by [[:default]]
|
||||
metabase.test/let-url [[:block 1]]
|
||||
metabase.test/mbql-query [[:inner 0]]
|
||||
metabase.test/query [[:block 1]]
|
||||
metabase.test/run-mbql-query [[:inner 0]]
|
||||
metabase.test/test-driver [[:inner 0]]
|
||||
metabase.test/test-drivers [[:inner 0]]
|
||||
metabase.test/test-helpers-set-global-values! [[:block 0]]
|
||||
metabase.test/throw-if-called! [[:block 1]]
|
||||
metabase.test/with-actions [[:block 1]]
|
||||
metabase.test/with-actions-disabled [[:block 0]]
|
||||
metabase.test/with-actions-enabled [[:block 0]]
|
||||
metabase.test/with-actions-test-data [[:inner 0]]
|
||||
metabase.test/with-actions-test-data-and-actions-enabled [[:block 0]]
|
||||
metabase.test/with-actions-test-data-tables [[:block 1]]
|
||||
metabase.test/with-additional-premium-features [[:block 1]]
|
||||
metabase.test/with-current-user [[:inner 0]]
|
||||
metabase.test/with-driver [[:block 1]]
|
||||
metabase.test/with-empty-db [[:inner 0]]
|
||||
metabase.test/with-empty-h2-app-db! [[:block 0]]
|
||||
metabase.test/with-metadata-provider [[:inner 0]]
|
||||
metabase.test/with-premium-features [[:block 1]]
|
||||
metabase.test/with-temp [[:inner 0]]
|
||||
metabase.test/with-temp-copy-of-db [[:block 0]]
|
||||
metabase.test/with-temp-scheduler [[:block 0]]
|
||||
metabase.test/with-temp-test-data [[:inner 0]]
|
||||
metabase.test/with-temp-vals-in-db [[:block 3]]
|
||||
metabase.test/with-test-user [[:inner 0]]
|
||||
metabase.test/with-user-in-groups [[:inner 0]]
|
||||
metabase.timeseries-query-processor-test.util/test-timeseries-drivers [[:block 0]]
|
||||
metabase.timeseries-query-processor-test/run-mbql-query [[:inner 0]]
|
||||
metabase.upload.impl-test/with-upload-table! [[:inner 0]]
|
||||
metabase.upload.types/with-currency [[:default]]
|
||||
metabase.upload.types/with-parens [[:default]]
|
||||
metabase.util/for-map [[:block 1]]
|
||||
metabase.util.async/cancelable-thread [[:block 0]]
|
||||
metabase.util.date-2/with-time-zone-same-instant [[:default]]
|
||||
metabase.util.encryption-test/with-secret-key [[:block 1]]
|
||||
metabase.util.honey-sql-2/with-database-type-info [[:default]]
|
||||
metabase.util.i18n/deferred-trs [[:default]]
|
||||
metabase.util.i18n/deferred-tru [[:default]]
|
||||
metabase.util.i18n/trs [[:default]]
|
||||
metabase.util.i18n/trsn [[:default]]
|
||||
metabase.util.i18n/tru [[:default]]
|
||||
metabase.util.i18n/trun [[:default]]
|
||||
metabase.util.jvm/auto-retry [[:block 1]]
|
||||
metabase.util.jvm/varargs [[:block 1]]
|
||||
metabase.util.jvm/with-us-locale [[:block 0]]
|
||||
metabase.util.malli/disable-enforcement [[:block 0]]
|
||||
metabase.util.malli/with [[:default]]
|
||||
metabase.util.malli/with-api-error-message [[:default]]
|
||||
metabase.util.namespaces/import-fns [[:default]]
|
||||
metabase.util.queue-test/await-test-while [[:block 1]]
|
||||
metabase.util/auto-retry [[:block 1]]
|
||||
metabase.util/case-enum [[:block 1]]
|
||||
metabase.util/ignore-exceptions [[:block 0]]
|
||||
metabase.util/or-with [[:block 1]]
|
||||
metabase.util/profile [[:block 1]]
|
||||
metabase.util/prog1 [[:inner 0]]
|
||||
metabase.util/varargs [[:inner 0]]
|
||||
metabase.util/with-us-locale [[:block 0]]
|
||||
metabuild-common.core/exit-when-finished-nonzero-on-exception [[:block 0]]
|
||||
metabuild-common.core/step [[:block 1]]
|
||||
metabuild-common.entrypoint/exit-when-finished-nonzero-on-exception [[:block 0]]
|
||||
metabuild-common.steps/step [[:block 1]]
|
||||
methodical.core/defmethod [[:inner 0]]
|
||||
methodical.core/defmulti [[:inner 0]]
|
||||
methodical.core/with-dispatcher [[:default]]
|
||||
methodical.core/with-method-table [[:default]]
|
||||
methodical.core/with-prefers [[:default]]
|
||||
methodical.interface/with-dispatcher [[:default]]
|
||||
methodical.interface/with-method-table [[:default]]
|
||||
methodical.interface/with-prefers [[:default]]
|
||||
methodical.macros/defmethod [[:inner 0]]
|
||||
methodical.macros/defmulti [[:inner 0]]
|
||||
saml20-clj.sp.response/validate-confirmation-datas [[:block 1]]
|
||||
toucan2.connection/with-transaction [[:block 1]]
|
||||
toucan2.core/build [[:block 0]]
|
||||
toucan2.core/compile [[:block 0]]
|
||||
toucan2.core/debug [[:block 0]]
|
||||
toucan2.core/define-after-insert [[:inner 0]]
|
||||
toucan2.core/define-after-select [[:inner 0]]
|
||||
toucan2.core/define-after-update [[:inner 0]]
|
||||
toucan2.core/define-before-delete [[:inner 0]]
|
||||
toucan2.core/define-before-insert [[:inner 0]]
|
||||
toucan2.core/define-before-select [[:inner 0]]
|
||||
toucan2.core/define-default-fields [[:inner 0]]
|
||||
toucan2.core/define-named-query [[:block 1]]
|
||||
toucan2.core/deftransforms [[:block 1]]
|
||||
toucan2.core/with-transaction [[:block 1]]
|
||||
toucan2.tools.after-insert/define-after-insert [[:inner 0]]
|
||||
toucan2.tools.after-select/define-after-select [[:inner 0]]
|
||||
toucan2.tools.after-update/define-after-update [[:inner 0]]
|
||||
toucan2.tools.after/define-after-impl [[:default]]
|
||||
toucan2.tools.before-delete/define-before-delete [[:inner 0]]
|
||||
toucan2.tools.before-insert/define-before-insert [[:inner 0]]
|
||||
toucan2.tools.before-select/define-before-select [[:inner 0]]
|
||||
toucan2.tools.compile/build [[:block 0]]
|
||||
toucan2.tools.compile/compile [[:block 0]]
|
||||
toucan2.tools.compile/resolved [[:block 0]]
|
||||
toucan2.tools.debug/debug [[:block 0]]
|
||||
toucan2.tools.default-fields/define-default-fields [[:inner 0]]
|
||||
toucan2.tools.named-query/define-named-query [[:block 1]]
|
||||
toucan2.tools.simple-out-transform/define-out-transform [[:inner 0]]
|
||||
toucan2.tools.transformed/deftransforms [[:block 1]]
|
||||
toucan2.tools.with-temp/with-temp [[:inner 0]]
|
||||
toucan2.util/try-with-error-context [[:inner 0]]}}
|
||||
metabase-enterprise.cache.config-test/with-temp-persist-models [[:block 1]]
|
||||
metabase-enterprise.impersonation.util-test/with-impersonations! [[:inner 0]]
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps! [[:inner 0]]
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! [[:inner 0]]
|
||||
metabase-enterprise.sandbox.test-util/with-user-attributes [[:block 2]]
|
||||
metabase-enterprise.serialization.test-util/with-dbs [[:inner 0]]
|
||||
metabase-enterprise.serialization.test-util/with-random-dump-dir [[:block 1]]
|
||||
metabase-enterprise.serialization.test-util/with-world [[:block 0]]
|
||||
metabase-enterprise.sso.integrations.jwt-test/with-users-with-email-deleted [[:block 1]]
|
||||
metabase-enterprise.test/with-gtaps! [[:inner 0]]
|
||||
metabase-enterprise.test/with-gtaps-for-user! [[:inner 0]]
|
||||
metabase-enterprise.test/with-user-attributes [[:block 2]]
|
||||
metabase.actions.actions-test/with-actions-test-data-and-actions-permissively-enabled [[:block 0]]
|
||||
metabase.actions.test-util/with-actions [[:block 1]]
|
||||
metabase.actions.test-util/with-actions-disabled [[:block 0]]
|
||||
metabase.actions.test-util/with-actions-enabled [[:block 0]]
|
||||
metabase.actions.test-util/with-actions-test-data [[:inner 0]]
|
||||
metabase.actions.test-util/with-actions-test-data-and-actions-enabled [[:block 0]]
|
||||
metabase.actions.test-util/with-actions-test-data-tables [[:block 1]]
|
||||
metabase.actions.test-util/with-empty-db [[:inner 0]]
|
||||
metabase.actions.test-util/with-temp-test-data [[:inner 0]]
|
||||
metabase.api.common/check [[:default]]
|
||||
metabase.api.common/define-routes [[:block 0]]
|
||||
metabase.api.common/let-404 [[:block 1]]
|
||||
metabase.app-db.core/with-application-db [[:inner 0]]
|
||||
metabase.app-db.liquibase/with-liquibase [[:block 1]]
|
||||
metabase.app-db.liquibase/with-scope-locked [[:block 1]]
|
||||
metabase.app-db.schema-migrations-test.impl/test-migrations [[:block 2]]
|
||||
metabase.cmd.copy/step [[:block 1]]
|
||||
metabase.cmd.copy/with-connection-rollback-only [[:block 1]]
|
||||
metabase.cmd.copy/with-disabled-db-constraints [[:block 2]]
|
||||
metabase.collections-rest.api-test/with-collection-hierarchy [[:block 1]]
|
||||
metabase.collections-rest.api-test/with-french-user-and-personal-collection [[:block 2]]
|
||||
metabase.collections-rest.api-test/with-some-children-of-collection [[:block 1]]
|
||||
metabase.collections.models.collection-test/with-collection-hierarchy [[:block 1]]
|
||||
metabase.collections.models.collection-test/with-current-user-perms-for-collections [[:block 1]]
|
||||
metabase.collections.models.collection-test/with-personal-and-impersonal-collections [[:block 1]]
|
||||
metabase.config/build-type-case [[:block 0]]
|
||||
metabase.dashboards.models.dashboard-test/with-dash-in-collection [[:inner 0]]
|
||||
metabase.driver-api.core/match [[:inner 0]]
|
||||
metabase.driver-api.core/match-one [[:inner 0]]
|
||||
metabase.driver-api.core/replace [[:inner 0]]
|
||||
metabase.driver-api.core/replace-in [[:inner 0]]
|
||||
metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]]
|
||||
metabase.driver.druid.client/DELETE [[:default]]
|
||||
metabase.driver.druid.client/GET [[:default]]
|
||||
metabase.driver.druid.client/POST [[:default]]
|
||||
metabase.driver.druid.query-processor-test/druid-query [[:block 0]]
|
||||
metabase.driver.druid.query-processor-test/druid-query-returning-rows [[:block 0]]
|
||||
metabase.driver.mongo.query-processor/mongo-let [[:block 1]]
|
||||
metabase.driver.sql-jdbc.actions/with-jdbc-transaction [[:block 1]]
|
||||
metabase.driver.sql-jdbc.connection/with-connection-spec-for-testing-connection [[:block 1]]
|
||||
metabase.driver.sql-jdbc.execute.diagnostic/capturing-diagnostic-info [[:block 1]]
|
||||
metabase.driver.sql.query-processor/apply-top-level-clause [[:default]]
|
||||
metabase.driver/with-driver [[:block 1]]
|
||||
metabase.embedding-rest.api.embed-test/do-response-formats [[:block 1]]
|
||||
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
|
||||
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-card-referencing [[:block 3]]
|
||||
metabase.embedding-rest.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing [[:block 3]]
|
||||
metabase.embedding-rest.api.embed-test/with-new-secret-key [[:block 0]]
|
||||
metabase.embedding-rest.api.embed-test/with-temp-card [[:block 1]]
|
||||
metabase.embedding-rest.api.embed-test/with-temp-dashcard [[:block 1]]
|
||||
metabase.embedding-rest.api.preview-embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
|
||||
metabase.embedding-rest.api.preview-embed-test/with-new-secret-key [[:block 0]]
|
||||
metabase.lib.binning/with-binning [[:default]]
|
||||
metabase.lib.core/with-binning [[:default]]
|
||||
metabase.lib.core/with-join-alias [[:default]]
|
||||
metabase.lib.core/with-join-conditions [[:default]]
|
||||
metabase.lib.join/with-join-alias [[:default]]
|
||||
metabase.lib.join/with-join-conditions [[:default]]
|
||||
metabase.lib.test-util.macros/$ids [[:inner 0]]
|
||||
metabase.lib.test-util.macros/mbql-5-query [[:inner 0]]
|
||||
metabase.lib.test-util.macros/mbql-query [[:inner 0]]
|
||||
metabase.lib.util.match/match [[:inner 0]]
|
||||
metabase.lib.util.match/match-one [[:inner 0]]
|
||||
metabase.lib.util.match/match-lite [[:inner 0]]
|
||||
metabase.lib.util.match/match-lite-recursive [[:inner 0]]
|
||||
metabase.lib.util.match/replace [[:inner 0]]
|
||||
metabase.lib.util.match/replace-in [[:inner 0]]
|
||||
metabase.models.interface/define-batched-hydration-method [[:inner 0]]
|
||||
metabase.models.interface/define-simple-hydration-method [[:inner 0]]
|
||||
metabase.parameters.chain-filter-test/with-clean-field-values-for-field [[:block 1]]
|
||||
metabase.parameters.chain-filter-test/with-fk-field-to-field-remapping! [[:block 0]]
|
||||
metabase.parameters.chain-filter-test/with-human-readable-values-remapping! [[:block 0]]
|
||||
metabase.permissions.core/allow-changing-all-users-group-members [[:block 0]]
|
||||
metabase.permissions.core/disable-perms-cache [[:block 0]]
|
||||
metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-card-referencing [[:block 3]]
|
||||
metabase.public-sharing-rest.api-test/with-sharing-enabled-and-temp-dashcard-referencing [[:block 3]]
|
||||
metabase.public-sharing-rest.api-test/with-temp-public-card [[:block 1]]
|
||||
metabase.public-sharing-rest.api-test/with-temp-public-dashboard [[:block 1]]
|
||||
metabase.public-sharing-rest.api-test/with-temp-public-dashboard-and-card [[:block 1]]
|
||||
metabase.pulse.api.alert-test/with-alert-in-collection [[:block 1]]
|
||||
metabase.pulse.dashboard-subscription-test/with-link-card-fixture-for-dashboard [[:block 2]]
|
||||
metabase.pulse.models.pulse-test/with-dashboard-subscription-in-collection [[:block 1]]
|
||||
metabase.pulse.models.pulse-test/with-pulse-in-collection [[:inner 0]]
|
||||
metabase.queries.api.card-test/with-cards-in-readable-collection [[:block 1]]
|
||||
metabase.queries.api.card-test/with-cards-in-writeable-collection [[:block 1]]
|
||||
metabase.queries.api.card-test/with-ordered-items [[:inner 0]]
|
||||
metabase.queries.api.card-test/with-temp-native-card [[:block 1]]
|
||||
metabase.queries.api.card-test/with-temp-native-card-with-params [[:block 1]]
|
||||
metabase.query-processor-test.pipeline-queries-test/pmbql-query [[:block 1]]
|
||||
metabase.query-processor-test.pipeline-queries-test/run-pmbql-query [[:block 1]]
|
||||
metabase.query-processor.debug/debug> [[:block 0]]
|
||||
metabase.query-processor.error-type/deferror [[:block 1]]
|
||||
metabase.query-processor.middleware.cache-backend.interface/with-cached-results [[:block 4]]
|
||||
metabase.query-processor.middleware.process-userland-query-test/with-query-execution [[:block 1]]
|
||||
metabase.query-processor.store/cached [[:block 1]]
|
||||
metabase.query-processor.store/with-metadata-provider [[:inner 0]]
|
||||
metabase.query-processor.streaming/streaming-response [[:block 1]]
|
||||
metabase.query-processor.test-util/format-rows-by [[:default]]
|
||||
metabase.request.core/as-admin [[:block 0]]
|
||||
metabase.request.core/with-current-request [[:inner 0]]
|
||||
metabase.request.core/with-current-user [[:inner 0]]
|
||||
metabase.request.core/with-limit-and-offset [[:inner 0]]
|
||||
metabase.request.session/as-admin [[:block 0]]
|
||||
metabase.request.session/with-current-user [[:inner 0]]
|
||||
metabase.sample-data.impl-test/with-temp-sample-database-db [[:block 1]]
|
||||
metabase.search.api-test/do-test-users [[:block 1]]
|
||||
metabase.server.middleware.session/as-admin [[:block 0]]
|
||||
metabase.server.middleware.session/with-current-user [[:inner 0]]
|
||||
metabase.server.streaming-response-test/with-streaming-response-thread-pool [[:block 0]]
|
||||
metabase.server.streaming-response-test/with-test-driver-db! [[:block 0]]
|
||||
metabase.server.streaming-response/streaming-response [[:block 2]]
|
||||
metabase.session.api/http-401-on-error [[:block 0]]
|
||||
metabase.settings.core/define-multi-setting [[:inner 0]]
|
||||
metabase.settings.core/defsetting [[:block 1]]
|
||||
metabase.settings.models.setting.multi-setting/define-multi-setting [[:inner 0]]
|
||||
metabase.settings.models.setting/defsetting [[:block 1]]
|
||||
metabase.sync.fetch-metadata/log-if-error [[:block 1]]
|
||||
metabase.sync.sync-metadata.fields-test/with-test-db [[:block 0]]
|
||||
metabase.sync.util/do-with-start-and-finish-debug-logging [[:default]]
|
||||
metabase.sync.util/sum-for [[:block 1]]
|
||||
metabase.sync.util/sync-operation [[:block 3]]
|
||||
metabase.sync.util/with-db-logging-disabled [[:default]]
|
||||
metabase.sync.util/with-duplicate-ops-prevented [[:default]]
|
||||
metabase.sync.util/with-emoji-progress-bar [[:block 1]]
|
||||
metabase.sync.util/with-error-handling [[:block 1]]
|
||||
metabase.sync.util/with-start-and-finish-logging [[:default]]
|
||||
metabase.sync.util/with-start-and-finish-logging* [[:default]]
|
||||
metabase.sync.util/with-sync-events [[:default]]
|
||||
metabase.task-history.models.task-history/with-task-history [[:block 1]]
|
||||
metabase.task.core/rerun-on-error [[:block 1]]
|
||||
metabase.task.impl/rerun-on-error [[:block 1]]
|
||||
metabase.test.data.datasets/test-driver [[:inner 0]]
|
||||
metabase.test.data.datasets/test-drivers [[:inner 0]]
|
||||
metabase.test.data.one-off-dbs/with-blank-db [[:block 0]]
|
||||
metabase.test.data.one-off-dbs/with-blueberries-db [[:block 0]]
|
||||
metabase.test.data.users/with-test-user [[:inner 0]]
|
||||
metabase.test.data/$ids [[:inner 0]]
|
||||
metabase.test.data/dataset [[:inner 0]]
|
||||
metabase.test.data/mbql-query [[:inner 0]]
|
||||
metabase.test.data/query [[:block 1]]
|
||||
metabase.test.data/run-mbql-query [[:inner 0]]
|
||||
metabase.test.data/with-empty-h2-app-db! [[:block 0]]
|
||||
metabase.test.data/with-temp-copy-of-db [[:block 0]]
|
||||
metabase.test.util.public-settings/with-additional-premium-features [[:block 1]]
|
||||
metabase.test.util.public-settings/with-premium-features [[:block 1]]
|
||||
metabase.test.util.thread-local/test-helpers-set-global-values! [[:block 0]]
|
||||
metabase.test.util/discard-setting-changes [[:block 1]]
|
||||
metabase.test.util/let-url [[:block 1]]
|
||||
metabase.test.util/throw-if-called! [[:block 1]]
|
||||
metabase.test.util/with-temp-scheduler [[:block 0]]
|
||||
metabase.test.util/with-temp-vals-in-db [[:block 3]]
|
||||
metabase.test.util/with-user-in-groups [[:inner 0]]
|
||||
metabase.test/$ids [[:inner 0]]
|
||||
metabase.test/as-admin [[:inner 0]]
|
||||
metabase.test/dataset [[:inner 0]]
|
||||
metabase.test/discard-setting-changes [[:block 1]]
|
||||
metabase.test/format-rows-by [[:default]]
|
||||
metabase.test/let-url [[:block 1]]
|
||||
metabase.test/mbql-query [[:inner 0]]
|
||||
metabase.test/query [[:block 1]]
|
||||
metabase.test/run-mbql-query [[:inner 0]]
|
||||
metabase.test/test-driver [[:inner 0]]
|
||||
metabase.test/test-drivers [[:inner 0]]
|
||||
metabase.test/test-helpers-set-global-values! [[:block 0]]
|
||||
metabase.test/throw-if-called! [[:block 1]]
|
||||
metabase.test/with-actions [[:block 1]]
|
||||
metabase.test/with-actions-disabled [[:block 0]]
|
||||
metabase.test/with-actions-enabled [[:block 0]]
|
||||
metabase.test/with-actions-test-data [[:inner 0]]
|
||||
metabase.test/with-actions-test-data-and-actions-enabled [[:block 0]]
|
||||
metabase.test/with-actions-test-data-tables [[:block 1]]
|
||||
metabase.test/with-additional-premium-features [[:block 1]]
|
||||
metabase.test/with-current-user [[:inner 0]]
|
||||
metabase.test/with-driver [[:block 1]]
|
||||
metabase.test/with-empty-db [[:inner 0]]
|
||||
metabase.test/with-empty-h2-app-db! [[:block 0]]
|
||||
metabase.test/with-metadata-provider [[:inner 0]]
|
||||
metabase.test/with-premium-features [[:block 1]]
|
||||
metabase.test/with-temp [[:inner 0]]
|
||||
metabase.test/with-temp-copy-of-db [[:block 0]]
|
||||
metabase.test/with-temp-scheduler [[:block 0]]
|
||||
metabase.test/with-temp-test-data [[:inner 0]]
|
||||
metabase.test/with-temp-vals-in-db [[:block 3]]
|
||||
metabase.test/with-test-user [[:inner 0]]
|
||||
metabase.test/with-user-in-groups [[:inner 0]]
|
||||
metabase.timeseries-query-processor-test.util/test-timeseries-drivers [[:block 0]]
|
||||
metabase.timeseries-query-processor-test/run-mbql-query [[:inner 0]]
|
||||
metabase.upload.impl-test/with-upload-table! [[:inner 0]]
|
||||
metabase.upload.types/with-currency [[:default]]
|
||||
metabase.upload.types/with-parens [[:default]]
|
||||
metabase.util/for-map [[:block 1]]
|
||||
metabase.util.async/cancelable-thread [[:block 0]]
|
||||
metabase.util.date-2/with-time-zone-same-instant [[:default]]
|
||||
metabase.util.encryption-test/with-secret-key [[:block 1]]
|
||||
metabase.util.honey-sql-2/with-database-type-info [[:default]]
|
||||
metabase.util.i18n/deferred-trs [[:default]]
|
||||
metabase.util.i18n/deferred-tru [[:default]]
|
||||
metabase.util.i18n/trs [[:default]]
|
||||
metabase.util.i18n/trsn [[:default]]
|
||||
metabase.util.i18n/tru [[:default]]
|
||||
metabase.util.i18n/trun [[:default]]
|
||||
metabase.util.jvm/auto-retry [[:block 1]]
|
||||
metabase.util.jvm/varargs [[:block 1]]
|
||||
metabase.util.jvm/with-us-locale [[:block 0]]
|
||||
metabase.util.malli/disable-enforcement [[:block 0]]
|
||||
metabase.util.malli/with [[:default]]
|
||||
metabase.util.malli/with-api-error-message [[:default]]
|
||||
metabase.util.namespaces/import-fns [[:default]]
|
||||
metabase.util.queue-test/await-test-while [[:block 1]]
|
||||
metabase.util/auto-retry [[:block 1]]
|
||||
metabase.util/case-enum [[:block 1]]
|
||||
metabase.util/ignore-exceptions [[:block 0]]
|
||||
metabase.util/or-with [[:block 1]]
|
||||
metabase.util/profile [[:block 1]]
|
||||
metabase.util/prog1 [[:inner 0]]
|
||||
metabase.util/varargs [[:inner 0]]
|
||||
metabase.util/with-us-locale [[:block 0]]
|
||||
metabuild-common.core/exit-when-finished-nonzero-on-exception [[:block 0]]
|
||||
metabuild-common.core/step [[:block 1]]
|
||||
metabuild-common.entrypoint/exit-when-finished-nonzero-on-exception [[:block 0]]
|
||||
metabuild-common.steps/step [[:block 1]]
|
||||
methodical.core/defmethod [[:inner 0]]
|
||||
methodical.core/defmulti [[:inner 0]]
|
||||
methodical.core/with-dispatcher [[:default]]
|
||||
methodical.core/with-method-table [[:default]]
|
||||
methodical.core/with-prefers [[:default]]
|
||||
methodical.interface/with-dispatcher [[:default]]
|
||||
methodical.interface/with-method-table [[:default]]
|
||||
methodical.interface/with-prefers [[:default]]
|
||||
methodical.macros/defmethod [[:inner 0]]
|
||||
methodical.macros/defmulti [[:inner 0]]
|
||||
saml20-clj.sp.response/validate-confirmation-datas [[:block 1]]
|
||||
toucan2.connection/with-transaction [[:block 1]]
|
||||
toucan2.core/build [[:block 0]]
|
||||
toucan2.core/compile [[:block 0]]
|
||||
toucan2.core/debug [[:block 0]]
|
||||
toucan2.core/define-after-insert [[:inner 0]]
|
||||
toucan2.core/define-after-select [[:inner 0]]
|
||||
toucan2.core/define-after-update [[:inner 0]]
|
||||
toucan2.core/define-before-delete [[:inner 0]]
|
||||
toucan2.core/define-before-insert [[:inner 0]]
|
||||
toucan2.core/define-before-select [[:inner 0]]
|
||||
toucan2.core/define-default-fields [[:inner 0]]
|
||||
toucan2.core/define-named-query [[:block 1]]
|
||||
toucan2.core/deftransforms [[:block 1]]
|
||||
toucan2.core/with-transaction [[:block 1]]
|
||||
toucan2.tools.after-insert/define-after-insert [[:inner 0]]
|
||||
toucan2.tools.after-select/define-after-select [[:inner 0]]
|
||||
toucan2.tools.after-update/define-after-update [[:inner 0]]
|
||||
toucan2.tools.after/define-after-impl [[:default]]
|
||||
toucan2.tools.before-delete/define-before-delete [[:inner 0]]
|
||||
toucan2.tools.before-insert/define-before-insert [[:inner 0]]
|
||||
toucan2.tools.before-select/define-before-select [[:inner 0]]
|
||||
toucan2.tools.compile/build [[:block 0]]
|
||||
toucan2.tools.compile/compile [[:block 0]]
|
||||
toucan2.tools.compile/resolved [[:block 0]]
|
||||
toucan2.tools.debug/debug [[:block 0]]
|
||||
toucan2.tools.default-fields/define-default-fields [[:inner 0]]
|
||||
toucan2.tools.named-query/define-named-query [[:block 1]]
|
||||
toucan2.tools.simple-out-transform/define-out-transform [[:inner 0]]
|
||||
toucan2.tools.transformed/deftransforms [[:block 1]]
|
||||
toucan2.tools.with-temp/with-temp [[:inner 0]]
|
||||
toucan2.util/try-with-error-context [[:inner 0]]}}
|
||||
|
||||
23
.codespellignore
Normal file
@@ -0,0 +1,23 @@
|
||||
Brite
|
||||
afterAll
|
||||
ags
|
||||
altho
|
||||
cancelation
|
||||
cancelations
|
||||
datas
|
||||
edn
|
||||
eduction
|
||||
hasTables
|
||||
horizontalX
|
||||
horizontalY
|
||||
juxt
|
||||
methodd
|
||||
notIn
|
||||
pass-thru
|
||||
ser
|
||||
serieses
|
||||
sortings
|
||||
trs
|
||||
trsn
|
||||
tru
|
||||
trun
|
||||
3
.codespellrc
Normal file
@@ -0,0 +1,3 @@
|
||||
[codespell]
|
||||
skip = *.csv,*.edn,*.geojson,*.json,*.tsv,*.xml,docs/developers-guide/drivers/driver-tests.md,docs/developers-guide/e2e-tests.md,docs/questions/query-builder/expressions-list.md,docs/questions/visualizations/country-codes.md,e2e/test/scenarios/admin/i18n/content-translation/constants.ts,frontend/src/cljs,frontend/src/metabase/querying/expressions/tokenizer/lezer.js,frontend/src/metabase/static-viz/constants/char-sizes.ts,frontend/src/metabase/visualizations/lib/mapping_codes.ts,modules/drivers/mongo/src/metabase/driver/mongo/operators.clj
|
||||
ignore-words = .codespellignore
|
||||
@@ -115,7 +115,6 @@ module.exports = {
|
||||
"And",
|
||||
"When",
|
||||
"Then",
|
||||
"describeWithSnowplow",
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -181,6 +180,7 @@ module.exports = {
|
||||
"no-unconditional-metabase-links-render": "error",
|
||||
"no-color-literals": "error",
|
||||
"no-literal-metabase-strings": "error",
|
||||
"no-oss-reinitialize-import": "error",
|
||||
"depend/ban-dependencies": [
|
||||
"error",
|
||||
{
|
||||
@@ -345,5 +345,11 @@ module.exports = {
|
||||
"import/no-commonjs": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.stories.tsx", "**/preview.tsx"],
|
||||
rules: {
|
||||
"import/no-default-export": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
5
.github/actions/get-cache-keys/action.yml
vendored
@@ -13,6 +13,9 @@ outputs:
|
||||
eslint-cache-key:
|
||||
description: "The cache key for Eslint cache"
|
||||
value: ${{ steps.keys-factory.outputs.eslint-cache-key }}
|
||||
cypress-cache-key:
|
||||
description: "The cache key for Cypress cache"
|
||||
value: ${{ steps.keys-factory.outputs.cypress-cache-key }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -23,10 +26,12 @@ runs:
|
||||
M2_CACHE_PREFIX='M2'
|
||||
NODE_CACHE_PREFIX='node-modules'
|
||||
ESLINT_CACHE_PREFIX='eslint'
|
||||
CYPRESS_CACHE_PREFIX='cypress'
|
||||
WEEK_OF_YEAR=$(/bin/date "+%G-%V")
|
||||
CACHE_SUFFIX="${{ runner.os }}-$WEEK_OF_YEAR"
|
||||
|
||||
echo "m2-cache-key=$M2_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "node-cache-key=$NODE_CACHE_PREFIX-$CACHE_SUFFIX" >> $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
|
||||
|
||||
5
.github/actions/prepare-backend/action.yml
vendored
@@ -25,11 +25,6 @@ runs:
|
||||
curl -O https://download.clojure.org/install/linux-install-${{ inputs.clojure-version }}.sh &&
|
||||
sudo bash ./linux-install-${{ inputs.clojure-version }}.sh
|
||||
|
||||
- name: Fetch CI config
|
||||
shell: bash
|
||||
run: |
|
||||
curl -o ci-test-config.json https://raw.githubusercontent.com/metabase/metabase/refs/heads/master/ci-test-config.json
|
||||
|
||||
- name: Get M2 cache key
|
||||
uses: ./.github/actions/get-cache-keys
|
||||
id: get-cache-keys
|
||||
|
||||
14
.github/actions/prepare-cypress/action.yml
vendored
@@ -22,16 +22,16 @@ runs:
|
||||
chrome-version: 1097615
|
||||
id: setup-chrome
|
||||
|
||||
- name: Get current week of year
|
||||
id: get-date
|
||||
run: |
|
||||
echo "week-of-year=$(/bin/date "+%G-%V")" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
- name: Get cache keys
|
||||
uses: ./.github/actions/get-cache-keys
|
||||
id: get-cache-keys
|
||||
|
||||
- name: Get Cypress cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ~/.cache/Cypress
|
||||
key: ${{ runner.os }}-Cypress-${{ steps.get-date.outputs.week-of-year }}
|
||||
key: ${{ steps.get-cache-keys.outputs.cypress-cache-key }}
|
||||
|
||||
- name: Ensure that Cypress executable is ready
|
||||
run: |
|
||||
yarn cypress install
|
||||
|
||||
14
.github/actions/prepare-frontend/action.yml
vendored
@@ -6,7 +6,7 @@ inputs:
|
||||
apply-patches:
|
||||
description: "Whether to patch frontend dependencies with patch-package."
|
||||
required: true
|
||||
default: 'true'
|
||||
default: "true"
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
@@ -28,11 +28,21 @@ runs:
|
||||
~/.gitlibs
|
||||
key: ${{ steps.get-cache-keys.outputs.m2-cache-key }}
|
||||
|
||||
- name: Get yarn cache directory
|
||||
id: yarn-cache-dir
|
||||
run: |
|
||||
YARN_CACHE_DIR=$(yarn cache dir)
|
||||
echo "Yarn cache directory: $YARN_CACHE_DIR"
|
||||
echo "dir=$YARN_CACHE_DIR" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Restore node_modules cache
|
||||
uses: actions/cache/restore@v4
|
||||
id: node-modules-cache
|
||||
with:
|
||||
path: node_modules
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir.outputs.dir }}
|
||||
node_modules
|
||||
key: ${{ steps.get-cache-keys.outputs.node-cache-key }}
|
||||
|
||||
# postinstall lifecycle hook can be dangerous in CI so we only run it locally (see package.json)
|
||||
|
||||
41
.github/actions/test-driver/action.yml
vendored
@@ -10,21 +10,54 @@ inputs:
|
||||
required: true
|
||||
default: false
|
||||
|
||||
outputs:
|
||||
quarantine-skipped:
|
||||
description: Whether the driver test was skipped due to quarantine
|
||||
value: ${{ steps.check-driver.outputs.skip }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Check if driver is ignored
|
||||
id: check-driver
|
||||
shell: bash
|
||||
run: |
|
||||
# If DRIVERS env var is not set, proceed with tests
|
||||
if [ -z "$DRIVERS" ]; then
|
||||
echo "No DRIVERS env var set. Proceeding with tests."
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Fetch CI config and check if driver should be skipped
|
||||
curl -sSf -o ci-test-config.json https://raw.githubusercontent.com/metabase/ci-test-config/refs/heads/master/ci-test-config.json || true
|
||||
|
||||
# Check if the driver is in the ignored list (gracefully handle missing/invalid config)
|
||||
IS_IGNORED=$(jq --arg driver "$DRIVERS" '.ignored.drivers // [] | contains([$driver])' ci-test-config.json 2>/dev/null || echo "false")
|
||||
|
||||
if [ "$IS_IGNORED" = "true" ]; then
|
||||
echo "⚠️ Driver '$DRIVERS' is in the ignored list. Skipping tests."
|
||||
echo "View the ignored list here: https://github.com/metabase/ci-test-config/blob/master/ci-test-config.json"
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Driver '$DRIVERS' is not ignored. Proceeding with tests."
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Prepare back-end environment
|
||||
if: steps.check-driver.outputs.skip != 'true'
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Prepare front-end environment
|
||||
if: ${{ steps.check-driver.outputs.skip != 'true' && inputs.build-static-viz == 'true' }}
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
if: ${{ inputs.build-static-viz == 'true' }}
|
||||
- name: Build static viz frontend
|
||||
if: ${{ steps.check-driver.outputs.skip != 'true' && inputs.build-static-viz == 'true' }}
|
||||
run: yarn build-static-viz
|
||||
shell: bash
|
||||
if: ${{ inputs.build-static-viz == 'true' }}
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Test database driver
|
||||
if: steps.check-driver.outputs.skip != 'true'
|
||||
run: clojure -X:dev:ci:ee:ee-dev:drivers:drivers-dev:test ${{ inputs.test-args }}
|
||||
shell: bash
|
||||
id: run-test
|
||||
@@ -37,7 +70,7 @@ runs:
|
||||
# As per the documentation: https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#steps-context
|
||||
# test.outcome gives us access to the result before `continue-on-error` overrides it and sets it to 'success'.
|
||||
# We want test reports even if the test step gets cancelled (e.g. times out), hence we match anything that is not a 'success'.
|
||||
if: steps.run-test.outcome != 'success'
|
||||
if: ${{ steps.run-test.outcome != 'success' && steps.check-driver.outputs.skip != 'true' }}
|
||||
with:
|
||||
path: "target/junit/**/*_test.xml"
|
||||
name: JUnit Test Report ${{ inputs.junit-name }}
|
||||
@@ -46,7 +79,7 @@ runs:
|
||||
list-tests: failed
|
||||
fail-on-error: false
|
||||
- name: Upload Logs on Failure
|
||||
if: always() && steps.run-test.outcome != 'success'
|
||||
if: always() && steps.run-test.outcome != 'success' && steps.check-driver.outputs.skip != 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-logs-${{ github.job }}
|
||||
|
||||
18
.github/file-paths.yaml
vendored
@@ -16,11 +16,6 @@ frontend_ci: &frontend_ci
|
||||
- ".github/actions/prepare-backend/**"
|
||||
- ".github/workflows/frontend.yml"
|
||||
|
||||
frontend_embedding_sdk_ci: &frontend_embedding_sdk_ci
|
||||
- ".github/actions/prepare-frontend/**"
|
||||
- ".github/actions/prepare-backend/**"
|
||||
- ".github/workflows/*embedding-sdk*.yml"
|
||||
|
||||
frontend_configs: &frontend_configs
|
||||
- "yarn.lock"
|
||||
- "**/tsconfig*.json"
|
||||
@@ -127,14 +122,19 @@ e2e_ci: &e2e_ci
|
||||
e2e_specs: &e2e_specs
|
||||
- e2e/!(test-host-app)**/*.cy.*.{js,ts}
|
||||
|
||||
e2e_embedding_sdk:
|
||||
- *frontend_embedding_sdk_ci
|
||||
embedding_sdk_ci: &embedding_sdk_ci
|
||||
- ".github/actions/prepare-frontend/**"
|
||||
- ".github/actions/prepare-backend/**"
|
||||
- ".github/workflows/embedding-sdk*.yml"
|
||||
|
||||
embedding_sdk_components:
|
||||
- *embedding_sdk_ci
|
||||
- *sources
|
||||
- "e2e/component/**/*.cy.*.{js,ts}"
|
||||
|
||||
e2e_embedding_sdk_compatibility:
|
||||
embedding_sdk_host_sample_apps:
|
||||
- *default
|
||||
- *frontend_embedding_sdk_ci
|
||||
- *embedding_sdk_ci
|
||||
- *frontend_sources
|
||||
- *backend_sources
|
||||
- "e2e/embedding-sdk-host-apps/**"
|
||||
|
||||
3
.github/team.json
vendored
@@ -106,7 +106,8 @@
|
||||
"chodorowicz",
|
||||
"kamilmielnik",
|
||||
"stasgavrylov",
|
||||
"uladzimirdev"
|
||||
"uladzimirdev",
|
||||
"egorgrushin"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
15
.github/workflows/backend.yml
vendored
@@ -48,13 +48,11 @@ jobs:
|
||||
- name: Enterprise Tests
|
||||
edition: ee
|
||||
test-group-name: ee
|
||||
build-static-viz: 'false'
|
||||
test-args: >-
|
||||
:only '"enterprise/backend/test"'
|
||||
- name: EE App DB Tests (Part 1)
|
||||
edition: ee
|
||||
test-group-name: ee-app-db-1
|
||||
build-static-viz: 'true'
|
||||
test-args: >-
|
||||
:only '["test" ".clj-kondo/test"]'
|
||||
:partition/total 2
|
||||
@@ -62,7 +60,6 @@ jobs:
|
||||
- name: EE App DB Tests (Part 2)
|
||||
edition: ee
|
||||
test-group-name: ee-app-db-2
|
||||
build-static-viz: 'true'
|
||||
test-args: >-
|
||||
:only '["test" ".clj-kondo/test"]'
|
||||
:partition/total 2
|
||||
@@ -70,7 +67,6 @@ jobs:
|
||||
- name: OSS App DB Tests (Part 1)
|
||||
edition: oss
|
||||
test-group-name: oss-app-db-1
|
||||
build-static-viz: 'true'
|
||||
test-args: >-
|
||||
:only '["test" ".clj-kondo/test"]'
|
||||
:partition/total 2
|
||||
@@ -78,7 +74,6 @@ jobs:
|
||||
- name: OSS App DB Tests (Part 2)
|
||||
edition: oss
|
||||
test-group-name: oss-app-db-2
|
||||
build-static-viz: 'true'
|
||||
test-args: >-
|
||||
:only '["test" ".clj-kondo/test"]'
|
||||
:partition/total 2
|
||||
@@ -103,25 +98,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
if: ${{ matrix.job.build-static-viz == 'true' }}
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
java-version: "${{ matrix.java-version }}"
|
||||
# Depending on which files changed, we might either have to build static-viz
|
||||
# from scratch or download the previously built artifact
|
||||
- name: Build static-viz frontend
|
||||
if: ${{ matrix.job.build-static-viz == 'true' && !inputs.skip }}
|
||||
run: yarn build-static-viz
|
||||
env:
|
||||
MB_EDITION: ${{ matrix.job.edition }}
|
||||
- name: Download Static Viz Bundle Artifact
|
||||
if: ${{ matrix.job.build-static-viz == 'true' && !inputs.skip }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static-viz-bundle-${{ github.sha }}
|
||||
path: resources/frontend_client/app/dist
|
||||
|
||||
- name: "Test Java ${{ matrix.java-version }} ${{ matrix.job.name }}"
|
||||
id: run-java-tests
|
||||
|
||||
37
.github/workflows/cache-generator.yml
vendored
@@ -30,8 +30,8 @@ name: Cache Generator
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 3 * * 1' # Mondays at 03:00 UTC
|
||||
workflow_dispatch: # manually update the cache
|
||||
- cron: "0 3 * * 1" # Mondays at 03:00 UTC
|
||||
workflow_dispatch: # manually update the cache
|
||||
|
||||
concurrency:
|
||||
group: cache-generator
|
||||
@@ -49,6 +49,7 @@ jobs:
|
||||
m2-cache-key: ${{ steps.get-cache-keys.outputs.m2-cache-key }}
|
||||
node-cache-key: ${{ steps.get-cache-keys.outputs.node-cache-key }}
|
||||
eslint-cache-key: ${{ steps.get-cache-keys.outputs.eslint-cache-key }}
|
||||
cypress-cache-key: ${{ steps.get-cache-keys.outputs.cypress-cache-key }}
|
||||
|
||||
generate-m2-cache:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -63,7 +64,7 @@ jobs:
|
||||
|
||||
# Add or remove aliases to affect the cache size and its contents.
|
||||
# Adjust to taste, then redeploy this workflow.
|
||||
- name: Pre-resolve dependencies
|
||||
- name: Pre-resolve Clojure dependencies
|
||||
run: clojure -A:build:dev:ee:ee-dev:drivers:drivers-dev:cljs -P
|
||||
|
||||
- name: Update M2 cache
|
||||
@@ -81,6 +82,7 @@ jobs:
|
||||
env:
|
||||
NODE_CACHE_KEY: ${{ needs.get-cache-keys.outputs.node-cache-key }}
|
||||
ESLINT_CACHE_KEY: ${{ needs.get-cache-keys.outputs.eslint-cache-key }}
|
||||
CYPRESS_CACHE_KEY: ${{ needs.get-cache-keys.outputs.cypress-cache-key }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# `prepare-frontend` action (1) prepares Node.js and (2) fetches the node_modules cache if it exists.
|
||||
@@ -92,16 +94,39 @@ jobs:
|
||||
- name: Install front-end dependencies
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
apply-patches: 'false'
|
||||
apply-patches: "false"
|
||||
|
||||
- name: Get yarn cache directory
|
||||
id: yarn-cache-dir
|
||||
run: |
|
||||
YARN_CACHE_DIR=$(yarn cache dir)
|
||||
echo "Yarn cache directory: $YARN_CACHE_DIR"
|
||||
echo "dir=$YARN_CACHE_DIR" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Ensure that Cypress executable is ready
|
||||
run: |
|
||||
yarn cypress install
|
||||
yarn cypress cache path
|
||||
yarn cypress cache list
|
||||
yarn cypress verify
|
||||
shell: bash
|
||||
|
||||
- name: Update node_modules cache
|
||||
uses: ./.github/actions/update-cache
|
||||
with:
|
||||
path: node_modules
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir.outputs.dir }}
|
||||
node_modules
|
||||
key: ${{ env.NODE_CACHE_KEY }}
|
||||
|
||||
- name: Update eslint cache
|
||||
uses: ./.github/actions/update-cache
|
||||
with:
|
||||
path: .eslintcache
|
||||
path: ${{ github.workspace}}/.eslintcache
|
||||
key: ${{ env.ESLINT_CACHE_KEY }}
|
||||
|
||||
- name: Update Cypress cache
|
||||
uses: ./.github/actions/update-cache
|
||||
with:
|
||||
path: ~/.cache/Cypress
|
||||
key: ${{ env.CYPRESS_CACHE_KEY }}
|
||||
|
||||
12
.github/workflows/claude-code-bug-fix.yml
vendored
@@ -159,15 +159,15 @@ jobs:
|
||||
1. **Analyze the issue description and comments** - Understand what the issue is describing
|
||||
a. Pay particular attention to reproduction steps and if there's an agreed upon fix.
|
||||
2. **Search the codebase** - Use Grep and Glob tools to find relevant files
|
||||
3. **Implement a fix** - Follow existing code patterns and conventions
|
||||
4. **Test the fix** - Run appropriate tests using the commands in CLAUDE.md:
|
||||
- JavaScript/TypeScript: `yarn test-unit path/to/file.unit.spec.js`
|
||||
- Clojure: `clojure -X:dev:test :only namespace/test-name`
|
||||
- Lint: `yarn lint-eslint-pure` for JS/TS, `./bin/mage kondo <file>` for Clojure
|
||||
3. **Implement a fix** - Use the appropriate write skill based on the file types:
|
||||
- Use clojure-write for Clojure/ClojureScript files
|
||||
- Use typescript-write for TypeScript/JavaScript files
|
||||
- Follow the coding standards and patterns in each skill
|
||||
4. **Test the fix** - Run appropriate tests as specified in the skills
|
||||
5. **Verify the fix** - Ensure tests pass and code follows conventions
|
||||
|
||||
## Important Guidelines
|
||||
- Follow the development patterns in CLAUDE.md
|
||||
- Use the appropriate write skill for the language you're working in
|
||||
- Write failing tests first, then fix them
|
||||
- Work in small, testable increments
|
||||
- Don't commit changes - leave that for the workflow
|
||||
|
||||
15
.github/workflows/claude-code-review.yaml
vendored
@@ -79,17 +79,14 @@ jobs:
|
||||
REPO: ${{ github.repository }}
|
||||
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||
|
||||
Review this pull request with a focus on:
|
||||
Review this pull request for compliance with project coding standards and conventions.
|
||||
|
||||
- Compliance with project coding standards and conventions
|
||||
- Code quality and best practices
|
||||
- Clear and correct docstrings
|
||||
- Clear and correct comments explaining what the code is doing
|
||||
**Use the appropriate review skill based on the file types changed:**
|
||||
- Use clojure-review for Clojure/ClojureScript files
|
||||
- Use typescript-review for TypeScript/JavaScript files
|
||||
- Follow the review guidelines and checklists in each skill
|
||||
|
||||
DO NOT post any comments confirming that something is correct or congratulating someone for following our
|
||||
guidelines -- only post comments for style violations or concerns.
|
||||
|
||||
Follow additional guidelines in the "Code Review Guidelines" section of CLAUDE.md .
|
||||
DO NOT post any comments confirming that something is correct - only post comments for style violations or concerns.
|
||||
|
||||
Note: The PR branch is already checked out in the current working directory.
|
||||
|
||||
|
||||
100
.github/workflows/claude-docs-review.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: "Claude Docs Review"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
paths:
|
||||
- 'docs/**/*.md'
|
||||
- 'docs/**/*.mdx'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to review'
|
||||
required: true
|
||||
type: number
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-claude-docs-review
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
# Make sure the value of GITHUB_TOKEN will not be persisted in repo's config
|
||||
persist-credentials: false
|
||||
|
||||
- name: Generate GitHub App Token for Claude
|
||||
uses: actions/create-github-app-token@v1
|
||||
id: claude-app-token
|
||||
with:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
permission-contents: read # Required for reading repository files and PR diffs
|
||||
permission-pull-requests: write # Required for creating inline comments and PR comments
|
||||
|
||||
- name: Delete old Claude docs review comments
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.claude-app-token.outputs.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
|
||||
shell: bash
|
||||
run: |
|
||||
# Delete pending reviews first (these can be deleted entirely)
|
||||
pending_review_ids=$(gh api "/repos/$REPO/pulls/$PR_NUMBER/reviews" --jq '.[] | select(.user.login=="metabase-bot[bot]" and .state=="PENDING") | .id')
|
||||
if [ -n "$pending_review_ids" ]; then
|
||||
echo "Deleting pending reviews: $pending_review_ids"
|
||||
for id in $pending_review_ids; do
|
||||
gh api -X DELETE "/repos/$REPO/pulls/$PR_NUMBER/reviews/$id"
|
||||
echo "Deleted pending review $id"
|
||||
done
|
||||
fi
|
||||
|
||||
# Delete individual inline comments from submitted reviews (can't delete the review itself)
|
||||
old_line_comment_ids=$(gh api "/repos/$REPO/pulls/$PR_NUMBER/comments" --jq '.[] | select(.user.login=="metabase-bot[bot]") | .id')
|
||||
if [ -n "$old_line_comment_ids" ]; then
|
||||
echo "Deleting line comments: $old_line_comment_ids"
|
||||
for id in $old_line_comment_ids; do
|
||||
gh api -X DELETE "/repos/$REPO/pulls/comments/$id"
|
||||
echo "Deleted line comment $id"
|
||||
done
|
||||
fi
|
||||
|
||||
# Delete top-level review comments
|
||||
old_top_level_comment_ids=$(gh api "/repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login=="metabase-bot[bot]") | .id')
|
||||
if [ -n "$old_top_level_comment_ids" ]; then
|
||||
echo "Deleting top-level comments: $old_top_level_comment_ids"
|
||||
for id in $old_top_level_comment_ids; do
|
||||
gh api -X DELETE "/repos/$REPO/issues/comments/$id"
|
||||
echo "Deleted top-level comment $id"
|
||||
done
|
||||
fi
|
||||
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
show_full_output: true
|
||||
prompt: |
|
||||
REPO: ${{ github.repository }}
|
||||
PR NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
|
||||
|
||||
Review the documentation changes in this pull request.
|
||||
|
||||
Use the docs-review skill to review all changed documentation files for compliance with the Metabase writing style guide.
|
||||
|
||||
DO NOT post any comments confirming that something is correct - only post comments for style violations or concerns.
|
||||
|
||||
claude_args: |
|
||||
--allowedTools "Skill,mcp__github__get_pull_request,mcp__github__get_pull_request_files,mcp__github__create_pending_pull_request_review,mcp__github__get_pull_request_diff,mcp__github__add_comment_to_pending_review,mcp__github__add_pull_request_review_comment_to_pending_review,mcp__github__delete_pending_pull_request_review,mcp__github__submit_pending_pull_request_review"
|
||||
19
.github/workflows/codespell.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Codespell
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: codespell-project/actions-codespell@v1
|
||||
with:
|
||||
# keep this list in sync with the list in `mage/src/mage/codespell.yml`
|
||||
path: >-
|
||||
docs
|
||||
6
.github/workflows/docs-links.yml
vendored
@@ -2,8 +2,6 @@ name: Check doc links
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -15,7 +13,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
sparse-checkout: |
|
||||
docs
|
||||
@@ -29,7 +27,7 @@ jobs:
|
||||
|
||||
- name: Run link checker
|
||||
id: link-check
|
||||
uses: lycheeverse/lychee-action@v2
|
||||
uses: lycheeverse/lychee-action@v2.3.0
|
||||
with:
|
||||
fail: true
|
||||
args: docs --config ./.lychee/config.toml --offline
|
||||
|
||||
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.56.x"
|
||||
default: "release-x.57.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.56.x' }}
|
||||
target_branch: ${{ inputs.target_branch || 'release-x.57.x' }}
|
||||
secrets:
|
||||
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
280
.github/workflows/drivers.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
determine-driver-skips:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
skip: ${{ steps.check_team_and_branch.outputs.skip }}
|
||||
skip: ${{ steps.check-branch-and-files-changed.outputs.skip }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
@@ -25,49 +25,31 @@ jobs:
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.base.ref != ''
|
||||
run: |
|
||||
# Fetch the base branch to ensure its history is available for diffing
|
||||
git fetch origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }}
|
||||
git fetch --depth=1 origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }}
|
||||
# Only run this if it's a pull request and the base ref exists
|
||||
- name: Check if tier3 drivers should run
|
||||
id: check_team_and_branch
|
||||
- name: Check if driver tests should run
|
||||
id: check-branch-and-files-changed
|
||||
run: |
|
||||
IS_MASTER_OR_RELEASE="${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') }}"
|
||||
HAS_TEAM_LABEL="${{ contains(github.event.pull_request.labels.*.name, '.Team/Drivers') || contains(github.event.pull_request.labels.*.name, '.Team/Querying') || contains(github.event.pull_request.labels.*.name, '.Team/SemanticLayer') || contains(github.event.pull_request.labels.*.name, '.Team/DevEx') }}"
|
||||
|
||||
|
||||
echo "Is master or release: ${IS_MASTER_OR_RELEASE}"
|
||||
echo "Has '.Team/Drivers' or '.Team/Querying' or '.Team/SemanticLayer' label: ${HAS_TEAM_LABEL}"
|
||||
|
||||
if [[ "${IS_MASTER_OR_RELEASE}" == "true" ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "Merging into master or a release branch. Tier 3 driver tests will run."
|
||||
elif [[ "${HAS_TEAM_LABEL}" == "true" ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "A relevant team label is present. Tier 3 driver tests will run."
|
||||
echo "Merging into master or a release branch. All driver tests will run."
|
||||
else
|
||||
echo "Checking for changes in relevant folders..."
|
||||
CHANGED_FILES=$(git diff --name-only $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) ${{ github.sha }})
|
||||
|
||||
# Initialize an empty array to store folders with changes
|
||||
CHANGED_FOLDERS=()
|
||||
for folder in "src/metabase/lib" "src/metabase/query_processor" "modules/drivers" "src/metabase/driver"; do
|
||||
if echo "$CHANGED_FILES" | grep -q "$folder"; then
|
||||
CHANGED_FOLDERS+=("$folder")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#CHANGED_FOLDERS[@]} -gt 0 ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "Changes detected in the following folders: ${CHANGED_FOLDERS[*]}."
|
||||
echo "Tier 3 driver tests will run."
|
||||
echo "Checking for changes in relevant modules..."
|
||||
if ./bin/mage can-skip-driver-tests "${{ github.event.pull_request.base.ref }}"; then
|
||||
echo 'skip=true' >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
echo "No relevant team label, not merging into master/release, and no changes in relevant folders. Tier 3 driver tests will be skipped."
|
||||
echo 'skip=false' >> $GITHUB_OUTPUT
|
||||
fi
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
be-tests-athena-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -85,6 +67,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Athena driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-athena-ee'
|
||||
@@ -93,7 +76,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -104,8 +87,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-bigquery-cloud-sdk-ee:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -146,7 +130,7 @@ jobs:
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -163,8 +147,9 @@ jobs:
|
||||
logs-artifact-suffix: ${{ matrix.job.name }} ${{ matrix.version.name }}
|
||||
|
||||
be-tests-clickhouse-ee:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -216,7 +201,7 @@ jobs:
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -235,7 +220,7 @@ jobs:
|
||||
be-tests-druid-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -251,6 +236,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Druid driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-druid-ee'
|
||||
@@ -259,7 +245,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -272,7 +258,7 @@ jobs:
|
||||
be-tests-druid-jdbc-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -288,6 +274,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Druid JDBC driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-druid-jdbc-ee'
|
||||
@@ -296,7 +283,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -309,7 +296,7 @@ jobs:
|
||||
be-tests-databricks-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -325,6 +312,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Databricks driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-databricks-ee'
|
||||
@@ -333,7 +321,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -346,7 +334,7 @@ jobs:
|
||||
be-tests-databricks-multi-catalog-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -363,6 +351,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Databricks driver with multi-catalog
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-databricks-multi-catalog-ee'
|
||||
@@ -371,7 +360,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -383,7 +372,7 @@ jobs:
|
||||
|
||||
be-tests-h2:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -400,7 +389,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -411,8 +400,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mysql-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -528,7 +518,7 @@ jobs:
|
||||
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -547,7 +537,7 @@ jobs:
|
||||
be-tests-mongo:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -597,7 +587,7 @@ jobs:
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -616,7 +606,7 @@ jobs:
|
||||
be-tests-mongo-ssl:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -660,6 +650,7 @@ jobs:
|
||||
curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metaca.crt \
|
||||
-o ./test_resources/ssl/mongo/metaca.crt
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
@@ -668,7 +659,7 @@ jobs:
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -681,7 +672,7 @@ jobs:
|
||||
be-tests-mongo-sharded-cluster-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -695,6 +686,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test MongoDB driver (Sharded cluster)
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mongo-sharded-cluster-ee'
|
||||
@@ -702,7 +694,7 @@ jobs:
|
||||
:only metabase.driver.mongo.sharded-cluster-test
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -715,7 +707,7 @@ jobs:
|
||||
be-tests-oracle:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -767,6 +759,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
@@ -775,7 +768,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -786,8 +779,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-postgres:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -877,7 +871,7 @@ jobs:
|
||||
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -896,7 +890,7 @@ jobs:
|
||||
be-tests-presto-jdbc-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -945,6 +939,7 @@ jobs:
|
||||
-file /tmp/presto-ssl-ca.der
|
||||
-trustcacerts
|
||||
- name: Test Presto JDBC driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-presto-jdbc-ee'
|
||||
@@ -953,7 +948,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -964,8 +959,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-redshift-ee:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -1015,7 +1011,7 @@ jobs:
|
||||
${{ matrix.job.test-args }}
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: ${{ !matrix.job.skip && !cancelled() }}
|
||||
if: ${{ !matrix.job.skip && !cancelled() && steps.test-driver.outputs.quarantine-skipped != 'true' }}
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -1031,80 +1027,80 @@ jobs:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: ${{ matrix.job.name }} ${{ matrix.version.name }}
|
||||
|
||||
# dan sutton 2025-10-23: disable snowflake because of account
|
||||
# issues. https://linear.app/metabase/issue/DEV-997/reenable-snowflake-in-ci. Note that the transforms tests were
|
||||
# disabled previously
|
||||
# be-tests-snowflake-ee:
|
||||
# if: ${{ !inputs.skip }}
|
||||
# runs-on: ubuntu-22.04
|
||||
# timeout-minutes: 60
|
||||
# strategy:
|
||||
# fail-fast: false
|
||||
# matrix:
|
||||
# job:
|
||||
# - name: Driver Tests
|
||||
# test-args: >-
|
||||
# :only-tags [:mb/driver-tests]
|
||||
# :exclude-tags [:mb/transforms-python-test]
|
||||
be-tests-snowflake-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
|
||||
# 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
|
||||
# MB_SNOWFLAKE_TEST_USER: METABASE CI
|
||||
# MB_SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.MB_SNOWFLAKE_TEST_ACCOUNT }}
|
||||
# MB_SNOWFLAKE_TEST_PASSWORD: ${{ secrets.MB_SNOWFLAKE_TEST_PASSWORD }}
|
||||
# MB_SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.MB_SNOWFLAKE_TEST_WAREHOUSE }}
|
||||
# MB_SNOWFLAKE_TEST_PK_USER: METABASE PK
|
||||
# MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY }}
|
||||
# MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2 }}
|
||||
# MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY }}
|
||||
# MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2 }}
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: snowflake
|
||||
MB_SNOWFLAKE_TEST_USER: METABASE CI
|
||||
MB_SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.MB_SNOWFLAKE_TEST_ACCOUNT }}
|
||||
MB_SNOWFLAKE_TEST_PASSWORD: ${{ secrets.MB_SNOWFLAKE_TEST_PASSWORD }}
|
||||
MB_SNOWFLAKE_TEST_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PRIVATE_KEY }}
|
||||
MB_SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.MB_SNOWFLAKE_TEST_WAREHOUSE }}
|
||||
MB_SNOWFLAKE_TEST_PK_USER: METABASE PK
|
||||
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY }}
|
||||
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2 }}
|
||||
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY }}
|
||||
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2 }}
|
||||
|
||||
# # RSA Role testing:
|
||||
# MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DEFAULT_USER: RSA_ROLE_TEST_DEFAULT_USER
|
||||
# MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_CUSTOM_USER: RSA_ROLE_TEST_CUSTOM_USER
|
||||
# MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_ROLE: RSA_ROLE_TEST_ROLE
|
||||
# MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DB: RSA_ROLE_TEST_DB
|
||||
# name: Snowflake ${{ matrix.job.name }}
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - name: Setup python-runner
|
||||
# if: ${{ matrix.job.needs-python-runner == true }}
|
||||
# uses: ./.github/actions/setup-python-runner
|
||||
# with:
|
||||
# metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
# - name: Test Snowflake driver
|
||||
# id: test-driver
|
||||
# uses: ./.github/actions/test-driver
|
||||
# with:
|
||||
# junit-name: 'be-tests-snowflake-ee'
|
||||
# test-args: ${{ matrix.job.test-args }}
|
||||
# - name: Upload Test Results
|
||||
# uses: ./.github/actions/upload-test-results
|
||||
# if: always()
|
||||
# with:
|
||||
# input-path: ./target/junit/
|
||||
# output-name: ${{ github.job }}
|
||||
# bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
# aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
# aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
# aws-region: ${{ vars.AWS_REGION }}
|
||||
# trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
# - name: Cleanup python-runner
|
||||
# if: ${{ always() && matrix.job.needs-python-runner == true }}
|
||||
# uses: ./.github/actions/cleanup-python-runner
|
||||
# with:
|
||||
# upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
# logs-artifact-suffix: ${{ matrix.job.name }} ${{ matrix.version.name }}
|
||||
# RSA Role testing:
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DEFAULT_USER: RSA_ROLE_TEST_DEFAULT_USER
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_CUSTOM_USER: RSA_ROLE_TEST_CUSTOM_USER
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_ROLE: RSA_ROLE_TEST_ROLE
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DB: RSA_ROLE_TEST_DB
|
||||
name: Snowflake ${{ matrix.job.name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
if: ${{ matrix.job.needs-python-runner == true }}
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test Snowflake driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-snowflake-ee'
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: ${{ always() && matrix.job.needs-python-runner == true }}
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: ${{ matrix.job.name }} ${{ matrix.version.name }}
|
||||
|
||||
be-tests-sparksql-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -1118,6 +1114,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Spark driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-sparksql-ee'
|
||||
@@ -1126,7 +1123,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -1139,7 +1136,7 @@ jobs:
|
||||
be-tests-sqlite-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -1148,6 +1145,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test SQLite driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-sqlite-ee'
|
||||
@@ -1156,7 +1154,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -1169,7 +1167,7 @@ jobs:
|
||||
be-tests-sqlserver:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -1221,7 +1219,7 @@ jobs:
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -1241,7 +1239,7 @@ jobs:
|
||||
# be-tests-starburst-ee:
|
||||
# needs: determine-driver-skips
|
||||
# if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
# runs-on: ubuntu-22.04
|
||||
# runs-on: ubuntu-latest
|
||||
# timeout-minutes: 60
|
||||
# env:
|
||||
# CI: 'true'
|
||||
@@ -1279,7 +1277,7 @@ jobs:
|
||||
login-to-ecr:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
id-token: write
|
||||
@@ -1309,7 +1307,7 @@ jobs:
|
||||
be-tests-vertica-ee:
|
||||
needs: [determine-driver-skips, login-to-ecr]
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -1328,6 +1326,7 @@ jobs:
|
||||
- name: Make plugins directory
|
||||
run: mkdir plugins
|
||||
- name: Test Vertica driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-vertica-ee'
|
||||
@@ -1336,7 +1335,7 @@ jobs:
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: always() && steps.test-driver.outputs.quarantine-skipped != 'true'
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -1363,8 +1362,7 @@ jobs:
|
||||
- be-tests-postgres
|
||||
- be-tests-presto-jdbc-ee
|
||||
- be-tests-redshift-ee
|
||||
# dan: disabled for account reasons. https://linear.app/metabase/issue/DEV-997/reenable-snowflake-in-ci
|
||||
# - be-tests-snowflake-ee
|
||||
- be-tests-snowflake-ee
|
||||
- be-tests-sparksql-ee
|
||||
- be-tests-sqlite-ee
|
||||
- be-tests-sqlserver
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
# This workflow does the backward compatibility tests
|
||||
# between the Embedding SDK package of version N and Embedding SDK Bundle of version N - 1
|
||||
# N - 1 is a target of a PR
|
||||
name: Embedding SDK backward-compatibility E2E Component Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cached-embedding-sdk-dist-artifact-name:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: "SDK backward-compatibility E2E > Check which files changed"
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
decide-target-ref:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
target-ref: ${{ steps.get-target-ref.outputs.value }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Decide ref to checkout
|
||||
id: get-target-ref
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "value=${{ github.base_ref }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "value=$(git rev-parse HEAD^1)" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# if this is a test on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
name: "SDK backward-compatibility E2E > [Backport only] Get build requirements"
|
||||
needs: [decide-target-ref]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.base_ref || github.ref, 'release-x')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
|
||||
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get build dependencies
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const {
|
||||
getBuildRequirements,
|
||||
getVersionFromReleaseBranch,
|
||||
} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const targetBranchName = '${{ github.base_ref || github.ref }}';
|
||||
|
||||
const version = getVersionFromReleaseBranch(targetBranchName);
|
||||
const requirements = getBuildRequirements(version);
|
||||
|
||||
return {
|
||||
java_version: requirements.java,
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
e2e-tests:
|
||||
name: "SDK backward-compatibility E2E > Run e2e tests"
|
||||
needs: [files-changed, decide-target-ref, get-build-requirements]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.files-changed.outputs.e2e_embedding_sdk == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
MB_RUN_MODE: "e2e"
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
|
||||
|
||||
- name: Build uberjar with ./bin/build.sh
|
||||
run: ./bin/build.sh
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.cached-embedding-sdk-dist-artifact-name }}
|
||||
path: ${{ github.workspace }}/resources/embedding-sdk
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
- name: Make app db snapshot
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js component \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }} \
|
||||
--env grepTags="-@skip-backward-compatibility",grepOmitFiltered=true
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-embedding-sdk
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-embedding-sdk-cross-version
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const {
|
||||
generateReport,
|
||||
parseReport,
|
||||
formatSummary
|
||||
} = require("./.github/scripts/handle-mochawesome-report.js");
|
||||
|
||||
const report = await generateReport();
|
||||
const results = parseReport(report);
|
||||
const summary = formatSummary(results);
|
||||
|
||||
await core.summary.addRaw(summary).write();
|
||||
@@ -1,204 +0,0 @@
|
||||
name: E2E Component Tests for Embedding SDK
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cached-embedding-sdk-dist-artifact-name:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: "SDK e2e > Check which files changed"
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
# if this is a test on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
name: "SDK e2e > [Backport only] Get build requirements"
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.base_ref || github.ref, 'release-x')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
|
||||
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get build dependencies
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const {
|
||||
getBuildRequirements,
|
||||
getVersionFromReleaseBranch,
|
||||
} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const targetBranchName = '${{ github.base_ref || github.ref }}';
|
||||
|
||||
const version = getVersionFromReleaseBranch(targetBranchName);
|
||||
const requirements = getBuildRequirements(version);
|
||||
|
||||
return {
|
||||
java_version: requirements.java,
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
build-uberjar:
|
||||
name: "SDK e2e > Build uberjar"
|
||||
needs: [files-changed, get-build-requirements]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.files-changed.outputs.e2e_embedding_sdk == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
INTERACTIVE: false
|
||||
# make sure that builds on release branches get licenses, because we use them for releases
|
||||
SKIP_LICENSES: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
|
||||
|
||||
- name: Build uberjar with ./bin/build.sh
|
||||
run: ./bin/build.sh
|
||||
|
||||
- name: Prepare uberjar artifact
|
||||
uses: ./.github/actions/prepare-uberjar-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-sdk-tests
|
||||
|
||||
e2e-tests:
|
||||
name: "SDK e2e > Run e2e tests (React ${{ matrix.react-version }})"
|
||||
needs: [get-build-requirements, build-uberjar]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.build-uberjar.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
react-version: [18, 19]
|
||||
fail-fast: false
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
MB_RUN_MODE: "e2e"
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-sdk-tests
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare JDK ${{ needs.get-build-requirements.outputs.java_version || 21 }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ needs.get-build-requirements.outputs.java_version || 21 }}
|
||||
distribution: "temurin"
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.cached-embedding-sdk-dist-artifact-name }}
|
||||
path: ${{ github.workspace }}/resources/embedding-sdk
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
- name: Make app db snapshot
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
# Has to be run after the app db snapshot is made, as it is not compatible with React 19
|
||||
- name: Change React version to ${{ matrix.react-version }}
|
||||
run: ./bin/embedding-sdk/change-react-version.bash ${{ matrix.react-version }}
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
run: node e2e/runner/run_cypress_ci.js component --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-embedding-sdk-react-${{ matrix.react-version }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: "SDK e2e > Upload Cypress Artifacts upon failure"
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-embedding-sdk-react-${{ matrix.react-version }}
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: "SDK e2e > Publish Summary"
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const {
|
||||
generateReport,
|
||||
parseReport,
|
||||
formatSummary
|
||||
} = require("./.github/scripts/handle-mochawesome-report.js");
|
||||
|
||||
const report = await generateReport();
|
||||
const results = parseReport(report);
|
||||
const summary = formatSummary(results);
|
||||
|
||||
await core.summary.addRaw(summary).write();
|
||||
29
.github/workflows/e2e-stress-test-flake-fix.yml
vendored
@@ -37,26 +37,26 @@ on:
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
determine-suite:
|
||||
name: Determine test suite
|
||||
determine-test-type:
|
||||
name: Determine test type
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
suite: ${{ steps.detect.outputs.suite }}
|
||||
type: ${{ steps.detect.outputs.type }}
|
||||
steps:
|
||||
- name: Detect suite from spec path
|
||||
- name: Detect test type from spec path
|
||||
id: detect
|
||||
run: |
|
||||
SPEC='${{ inputs.spec }}'
|
||||
if [[ "$SPEC" == e2e/test-component/scenarios/embedding-sdk/* ]]; then
|
||||
echo "suite=component" >> $GITHUB_OUTPUT
|
||||
echo "type=component" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "suite=e2e" >> $GITHUB_OUTPUT
|
||||
echo "type=e2e" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
workflow-summary:
|
||||
name: Stress test inputs
|
||||
needs: [determine-suite]
|
||||
needs: [determine-test-type]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `branch`: ${{ github.ref_name }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `spec`: ${{ inputs.spec }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `test_suite` (inferred): ${{ needs.determine-suite.outputs.suite }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `test_type` (inferred): ${{ needs.determine-test-type.outputs.type }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `burn_in`: ${{ inputs.burn_in }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `grep`: "${{ inputs.grep }}"' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `qa_db`: "${{ inputs.qa_db }}"' >> $GITHUB_STEP_SUMMARY
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
echo 'triggered by: @${{ github.event.sender.login }}' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
stress-test-flake-fix:
|
||||
needs: [determine-suite]
|
||||
needs: [determine-test-type]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
name: Stress test E2E flake fix
|
||||
@@ -122,12 +122,12 @@ jobs:
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare back-end environment
|
||||
if: ${{ needs.determine-suite.outputs.suite == 'component' }}
|
||||
if: ${{ needs.determine-test-type.outputs.type == 'component' }}
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
- name: Build Embedding SDK package
|
||||
if: ${{ needs.determine-suite.outputs.suite == 'component' }}
|
||||
if: ${{ needs.determine-test-type.outputs.type == 'component' }}
|
||||
run: yarn build-embedding-sdk-package
|
||||
|
||||
- name: Prepare JDK 21
|
||||
@@ -148,13 +148,12 @@ jobs:
|
||||
- name: Stress-test ${{ github.event.inputs.spec }} ${{ github.event.inputs.burn_in }} times
|
||||
env:
|
||||
GREP: ${{ github.event.inputs.grep }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: ${{ needs.determine-suite.outputs.suite == 'component' }}
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js ${{ needs.determine-suite.outputs.suite }} \
|
||||
node e2e/runner/run_cypress_ci.js ${{ needs.determine-test-type.outputs.type }} \
|
||||
--spec '${{ github.event.inputs.spec }}' \
|
||||
--env burn=${{ github.event.inputs.burn_in }} \
|
||||
--config-file e2e/support/cypress-stress-test.config.js \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
--config-file e2e/support/cypress-stress-test.config.js
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
14
.github/workflows/e2e-test.yml
vendored
@@ -120,6 +120,8 @@ jobs:
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
- name: Generate database snapshots
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot
|
||||
|
||||
- name: Run auto-split Cypress tests on ${{ inputs.name }}
|
||||
@@ -133,13 +135,13 @@ jobs:
|
||||
# do not write cypress-split summary to github
|
||||
SPLIT_SUMMARY: false
|
||||
SPEC_PATH: ${{ env.specs || inputs.specs }}
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Running tests with specs: $SPEC_PATH"
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="-@mongo+-@python+-@OSS+-@skip",grepOmitFiltered=true \
|
||||
--spec "$SPEC_PATH" \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
--env grepTags="-@mongo+-@python+-@OSS+-@skip" \
|
||||
--spec "$SPEC_PATH"
|
||||
# Trunk Quarantine controls the final state of the run in the CI!
|
||||
# Even if this step fails we let it pass, and then `trunk-io/analytics-uploader` determines
|
||||
# whether to fail or to pass the whole job, based on the quarantine list.
|
||||
@@ -150,12 +152,12 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
SPEC_PATH: ${{ env.specs || inputs.specs }}
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: |
|
||||
echo "Running tests with specs: $SPEC_PATH"
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="${{inputs.tags && format('{0}+-@skip', inputs.tags) || '-@skip'}}",grepOmitFiltered=true \
|
||||
--spec "$SPEC_PATH" \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
--env grepTags="${{inputs.tags && format('{0}+-@skip', inputs.tags) || '-@skip'}}" \
|
||||
--spec "$SPEC_PATH"
|
||||
# Trunk Quarantine controls the final state of the run in the CI!
|
||||
# Even if this step fails we let it pass, and then `trunk-io/analytics-uploader` determines
|
||||
# whether to fail or to pass the whole job, based on the quarantine list.
|
||||
|
||||
250
.github/workflows/embedding-sdk-component-tests.yml
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
name: Embedding SDK Component Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
sdk-component-tests:
|
||||
name: "SDK component tests (React ${{ matrix.react-version }})"
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
react-version: [18, 19]
|
||||
fail-fast: false
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
MB_RUN_MODE: "e2e"
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
extract-path: "target/uberjar"
|
||||
output-name: "metabase.jar"
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: ${{ github.workspace }}/resources/embedding-sdk
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: "temurin"
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
- name: Make app db snapshot
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot
|
||||
|
||||
# Has to be run after the app db snapshot is made, as it is not compatible with React 19
|
||||
- name: Change React version to ${{ matrix.react-version }}
|
||||
run: ./bin/embedding-sdk/change-react-version.bash ${{ matrix.react-version }}
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: node e2e/runner/run_cypress_ci.js component
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-embedding-sdk-react-${{ matrix.react-version }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: "SDK component tests > Upload Cypress Artifacts upon failure"
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-embedding-sdk-react-${{ matrix.react-version }}
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: "SDK component tests > Publish Summary"
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const {
|
||||
generateReport,
|
||||
parseReport,
|
||||
formatSummary
|
||||
} = require("./.github/scripts/handle-mochawesome-report.js");
|
||||
|
||||
const report = await generateReport();
|
||||
const results = parseReport(report);
|
||||
const summary = formatSummary(results);
|
||||
|
||||
await core.summary.addRaw(summary).write();
|
||||
|
||||
decide-target-ref:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
target-ref: ${{ steps.get-target-ref.outputs.value }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Decide ref to checkout
|
||||
id: get-target-ref
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "value=${{ github.base_ref }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "value=$(git rev-parse HEAD^1)" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# This job ensures that the Embedding SDK package from the current commit (N) works with the
|
||||
# Embedding SDK Bundle (Metabase JAR) from the previous commit (N-1).
|
||||
# N - 1 is a target of a pull request, or a literal previous commit when this job is run on push in a branch.
|
||||
sdk-component-tests-backward-compatibility:
|
||||
name: "SDK component tests (Backward compatibility)"
|
||||
needs: [decide-target-ref]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
MB_RUN_MODE: "e2e"
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
|
||||
steps:
|
||||
- name: Check out the code from the previous commit
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
|
||||
|
||||
- name: Get the actual commit SHA from the checked out branch
|
||||
id: get-commit-sha
|
||||
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Attempt to retrieve the ee uberjar artifact from the previous commit
|
||||
id: download-uberjar
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ steps.get-commit-sha.outputs.sha }}-uberjar
|
||||
extract-path: "target/uberjar"
|
||||
output-name: "metabase.jar"
|
||||
continue-on-error: true # do not break the whole job if this step doesn't succeed
|
||||
|
||||
# Build the uberjar from the previous commit only if we couldn't fetch it.
|
||||
# `step.foo.OUTCOME` is important because it looks at the step status *before* continue-on-error is applied.
|
||||
# Do not confuse it with the `step.foo.conclusion`.
|
||||
- name: Build MB uberjar from the commit N-1
|
||||
if: steps.download-uberjar.outcome != 'success'
|
||||
run: ./bin/build.sh
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact built from the current commit N
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: ${{ github.workspace }}/resources/embedding-sdk
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
- name: Make app db snapshot
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js component \
|
||||
--env grepTags="-@skip-backward-compatibility"
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-embedding-sdk
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-embedding-sdk-cross-version
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const {
|
||||
generateReport,
|
||||
parseReport,
|
||||
formatSummary
|
||||
} = require("./.github/scripts/handle-mochawesome-report.js");
|
||||
|
||||
const report = await generateReport();
|
||||
const results = parseReport(report);
|
||||
const summary = formatSummary(results);
|
||||
|
||||
await core.summary.addRaw(summary).write();
|
||||
@@ -1,78 +1,14 @@
|
||||
name: "[NOT REQUIRED] SDK Compatibility tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_embedding_sdk_compatibility: ${{ steps.changes.outputs.e2e_embedding_sdk_compatibility }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
# if this is a test on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.base_ref || github.ref, 'release-x')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
|
||||
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get build dependencies
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const {
|
||||
getBuildRequirements,
|
||||
getVersionFromReleaseBranch,
|
||||
} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const targetBranchName = '${{ github.base_ref || github.ref }}';
|
||||
|
||||
const version = getVersionFromReleaseBranch(targetBranchName);
|
||||
const requirements = getBuildRequirements(version);
|
||||
|
||||
return {
|
||||
java_version: requirements.java,
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
get-sample-apps-data:
|
||||
if: |
|
||||
!cancelled()
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
# we must support last two stable releases
|
||||
# we must support the last two stable releases
|
||||
SUPPORTED_RELEASES_COUNT: 2
|
||||
TEST_SUITES: |
|
||||
[
|
||||
@@ -145,51 +81,6 @@ jobs:
|
||||
matrix: []
|
||||
};
|
||||
|
||||
build:
|
||||
needs: [files-changed, get-build-requirements, get-sample-apps-data]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.files-changed.outputs.e2e_embedding_sdk_compatibility == 'true' &&
|
||||
fromJSON(needs.get-sample-apps-data.outputs.matrix)[0] != null
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
INTERACTIVE: false
|
||||
# make sure that builds on release branches get licenses, because we use them for releases
|
||||
SKIP_LICENSES: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
|
||||
|
||||
- name: Build uberjar with ./bin/build.sh
|
||||
run: ./bin/build.sh
|
||||
|
||||
- name: Prepare uberjar artifact
|
||||
uses: ./.github/actions/prepare-uberjar-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
- name: Build Embedding SDK package
|
||||
run: |
|
||||
# Remove the matchers to not report false positives on every PR
|
||||
# unrelated to embedding (EMB-765)
|
||||
echo "::remove-matcher owner=tsc::"
|
||||
yarn build-embedding-sdk-package
|
||||
|
||||
- name: Prepare Embedding SDK artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: resources/embedding-sdk
|
||||
|
||||
# In order to check compatibility between Sample Apps and Embedding SDK, the job for each sample app:
|
||||
# - pulls a Sample App
|
||||
# - starts it
|
||||
@@ -197,13 +88,13 @@ jobs:
|
||||
# The job is not required, though if it's failing better to be sure that it's not caused by a breaking change.
|
||||
# For more details see `Sample Apps compatibility with Embedding SDK tests` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
|
||||
e2e-sample-apps-tests:
|
||||
needs: [get-build-requirements, get-sample-apps-data, build]
|
||||
needs: [get-sample-apps-data]
|
||||
strategy:
|
||||
matrix:
|
||||
test_suite: ${{ fromJSON(needs.get-sample-apps-data.outputs.matrix) }}
|
||||
environment: ["production", "development"]
|
||||
if: |
|
||||
!cancelled() && needs.build.result == 'success'
|
||||
!cancelled() && needs.get-sample-apps-data.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-sample-app-${{ matrix.test_suite }}-tests-${{ matrix.environment }}-bundle
|
||||
@@ -216,7 +107,6 @@ jobs:
|
||||
MB_RUN_MODE: e2e
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
SAMPLE_APP_BRANCH_NAME: ${{ needs.get-sample-apps-data.outputs.branch_name }}
|
||||
@@ -241,9 +131,11 @@ jobs:
|
||||
echo "sample_app_name=${test_suite%-e2e}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: actions/download-artifact@v4
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
extract-path: "target/uberjar"
|
||||
output-name: "metabase.jar"
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -299,7 +191,10 @@ jobs:
|
||||
id: run-e2e-tests
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
|
||||
continue-on-error: true
|
||||
run: node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }} --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -323,7 +218,6 @@ jobs:
|
||||
# The job is not required, though if it's failing better to be sure that it's not caused by a breaking change.
|
||||
# For more details see `Embedding SDK integration tests with Host Apps` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
|
||||
e2e-host-apps-tests:
|
||||
needs: [get-build-requirements, build]
|
||||
strategy:
|
||||
matrix:
|
||||
test_suite:
|
||||
@@ -334,8 +228,6 @@ jobs:
|
||||
"angular-20-host-app-e2e",
|
||||
]
|
||||
environment: ["production", "development"]
|
||||
if: |
|
||||
!cancelled() && needs.build.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-host-app-${{ matrix.test_suite }}-tests-${{ matrix.environment }}-bundle
|
||||
@@ -348,7 +240,6 @@ jobs:
|
||||
MB_RUN_MODE: e2e
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
HOST_APP_ENVIRONMENT: ${{ matrix.environment }}
|
||||
@@ -359,9 +250,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: actions/download-artifact@v4
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
extract-path: "target/uberjar"
|
||||
output-name: "metabase.jar"
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -394,7 +287,9 @@ jobs:
|
||||
- name: Run e2e tests for Host App
|
||||
id: run-e2e-tests
|
||||
continue-on-error: true
|
||||
run: node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }} --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
31
.github/workflows/embedding-sdk-package-build.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Embedding SDK Package Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: embedding-sdk-package-${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-sdk-package:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Build Embedding SDK package
|
||||
run: |
|
||||
# Remove the matchers to not report false positives on every PR
|
||||
# unrelated to embedding (EMB-765)
|
||||
echo "::remove-matcher owner=tsc::"
|
||||
yarn build-embedding-sdk-package
|
||||
- name: Prepare Embedding SDK artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: resources/embedding-sdk
|
||||
103
.github/workflows/embedding-sdk.yml
vendored
@@ -2,61 +2,35 @@ name: SDK
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cli-snippets-type-check:
|
||||
description: "Whether to run cli snippets type check."
|
||||
required: true
|
||||
type: boolean
|
||||
docs-snippets-type-check:
|
||||
description: "Whether to run docs snippets type check."
|
||||
required: true
|
||||
type: boolean
|
||||
documentation-validation:
|
||||
description: "Whether to run documentation generation validation check."
|
||||
required: true
|
||||
type: boolean
|
||||
component-tests:
|
||||
description: "Whether to run component tests."
|
||||
required: true
|
||||
type: boolean
|
||||
host-sample-apps-tests:
|
||||
description: "Whether to run host and sample apps compatibility tests."
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
|
||||
frontend_embedding_sdk_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_sources }}
|
||||
embedding_documentation: ${{ steps.changes.outputs.embedding_documentation }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
build:
|
||||
needs: [files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
(needs.files-changed.outputs.e2e_embedding_sdk == 'true' ||
|
||||
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' ||
|
||||
needs.files-changed.outputs.embedding_documentation == 'true')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Build Embedding SDK package
|
||||
run: |
|
||||
# Remove the matchers to not report false positives on every PR
|
||||
# unrelated to embedding (EMB-765)
|
||||
echo "::remove-matcher owner=tsc::"
|
||||
yarn build-embedding-sdk-package
|
||||
- name: Prepare Embedding SDK artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: resources/embedding-sdk
|
||||
|
||||
embedding-sdk-cli-snippets-type-check:
|
||||
needs: [files-changed, build]
|
||||
if: |
|
||||
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' &&
|
||||
needs.build.result == 'success'
|
||||
if: inputs.cli-snippets-type-check
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
@@ -72,10 +46,7 @@ jobs:
|
||||
run: yarn run embedding-sdk:cli-snippets:type-check
|
||||
|
||||
embedding-sdk-docs-snippets-type-check:
|
||||
needs: [files-changed, build]
|
||||
if: |
|
||||
needs.files-changed.outputs.embedding_documentation == 'true' &&
|
||||
needs.build.result == 'success'
|
||||
if: inputs.docs-snippets-type-check
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
@@ -91,10 +62,7 @@ jobs:
|
||||
run: yarn run embedding-sdk:docs-snippets:type-check
|
||||
|
||||
embedding-sdk-api-documentation-generation-validation:
|
||||
needs: [files-changed, build]
|
||||
if: |
|
||||
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' &&
|
||||
needs.build.result == 'success'
|
||||
if: inputs.documentation-validation
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
@@ -140,29 +108,24 @@ jobs:
|
||||
run: exit 1
|
||||
|
||||
# See `Component e2e tests` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
|
||||
sdk-e2e-tests:
|
||||
name: "SDK e2e tests"
|
||||
uses: ./.github/workflows/e2e-component-tests-embedding-sdk.yml
|
||||
needs: [build]
|
||||
embedding-sdk-component-tests:
|
||||
name: "SDK component tests"
|
||||
if: inputs.component-tests
|
||||
uses: ./.github/workflows/embedding-sdk-component-tests.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
cached-embedding-sdk-dist-artifact-name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
e2e-component-tests-embedding-sdk-backward-compatibility:
|
||||
name: "SDK e2e backward compatibility tests"
|
||||
uses: ./.github/workflows/e2e-component-tests-embedding-sdk-backward-compatibility.yml
|
||||
needs: [build]
|
||||
not-required-embedding-sdk-apps-compatibility-tests:
|
||||
name: "SDK e2e tests for compatibility with (host/sample) apps"
|
||||
if: inputs.host-sample-apps-tests
|
||||
uses: ./.github/workflows/embedding-sdk-host-sample-apps-tests.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
cached-embedding-sdk-dist-artifact-name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
embedding-sdk-tests-result:
|
||||
needs:
|
||||
- embedding-sdk-cli-snippets-type-check
|
||||
- embedding-sdk-docs-snippets-type-check
|
||||
- embedding-sdk-api-documentation-generation-validation
|
||||
- sdk-e2e-tests
|
||||
- e2e-component-tests-embedding-sdk-backward-compatibility
|
||||
- embedding-sdk-component-tests
|
||||
if: always() && !cancelled()
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
2
.github/workflows/frontend.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
- name: Restore ESLint cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: .eslintcache
|
||||
path: ${{ github.workspace}}/.eslintcache
|
||||
key: ${{ steps.get-cache-keys.outputs.eslint-cache-key }}
|
||||
|
||||
- name: Compile CLJS
|
||||
|
||||
227
.github/workflows/minimal-tests.yml
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
name: Minimal Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Only run if the PR has the minimal-tests label
|
||||
check-label:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 1
|
||||
outputs:
|
||||
should-run: ${{ steps.check.outputs.result }}
|
||||
steps:
|
||||
- name: Check for minimal-tests label
|
||||
id: check
|
||||
run: |
|
||||
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'minimal-tests') }}" == "true" ]]; then
|
||||
echo "result=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "result=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Postgres app-db tests - single version, single job
|
||||
postgres-app-db-test:
|
||||
needs: check-label
|
||||
if: needs.check-label.outputs.should-run == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: postgres
|
||||
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
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
name: Postgres Latest EE App DB Tests (Part 1)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Postgres App DB
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: true
|
||||
junit-name: 'minimal-be-tests-postgres-ee'
|
||||
test-args: >-
|
||||
:only '["test" "enterprise/backend/test"]'
|
||||
:exclude-tags '[:mb/driver-tests :mb/transforms-python-test]'
|
||||
:partition/total 2
|
||||
:partition/index 0
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
# Postgres driver tests
|
||||
postgres-driver-test:
|
||||
needs: check-label
|
||||
if: needs.check-label.outputs.should-run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: postgres
|
||||
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
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
name: Postgres Latest Driver Tests
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Postgres Driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'minimal-be-tests-postgres-driver-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
# Frontend unit tests
|
||||
frontend-unit-tests:
|
||||
needs: check-label
|
||||
if: needs.check-label.outputs.should-run == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
JEST_JUNIT_OUTPUT_DIR: ./target/junit
|
||||
JEST_JUNIT_OUTPUT_NAME: test-report-frontend-unit.xml
|
||||
name: Frontend Unit Tests
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Run frontend unit tests
|
||||
run: yarn run test-unit --silent
|
||||
continue-on-error: true
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: frontend-unit
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
# Summary job
|
||||
minimal-tests-result:
|
||||
needs:
|
||||
- postgres-app-db-test
|
||||
- postgres-driver-test
|
||||
- frontend-unit-tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: minimal-tests-result
|
||||
if: always() && !cancelled()
|
||||
steps:
|
||||
- name: Check job status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
with:
|
||||
script: | # js
|
||||
const needs = JSON.parse(process.env.needs);
|
||||
|
||||
const jobs = Object.entries(needs).map(
|
||||
([jobName, jobValues]) => ({
|
||||
name: jobName,
|
||||
result: jobValues.result
|
||||
}));
|
||||
|
||||
// Check if all jobs either succeeded or were skipped
|
||||
// Skipped jobs are acceptable (they were intentionally skipped when the label is absent)
|
||||
const allPassedOrSkipped = jobs.every(job =>
|
||||
job.result === 'success' || job.result === 'skipped'
|
||||
);
|
||||
|
||||
// Check if any jobs actually failed (not skipped, not cancelled)
|
||||
const anyFailed = jobs.some(job =>
|
||||
job.result === 'failure'
|
||||
);
|
||||
|
||||
if (allPassedOrSkipped && !anyFailed) {
|
||||
console.log("");
|
||||
console.log(" _------. ");
|
||||
console.log(" / , \_ ");
|
||||
console.log(" / / /{}\ |o\_ ");
|
||||
console.log(" / \ `--' /-' \ ");
|
||||
console.log(" | \ \ | ");
|
||||
console.log(" | |`-, | ");
|
||||
console.log(" / /__/)/ ");
|
||||
console.log(" | | ");
|
||||
console.log("");
|
||||
console.log("All minimal tests have passed. Fast feedback achieved!");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// otherwise, something failed
|
||||
console.log("");
|
||||
console.log(" .::::::::::. .::::::::::. ");
|
||||
console.log(" .::::''''''::::. .::::''''''::::. ");
|
||||
console.log(" .:::' `::::.... ....::::' `:::. ");
|
||||
console.log(" .::' `:::::::| |:::::::' `::. ");
|
||||
console.log(" .::| |::::::|_ ___ __|::::::| |::. ");
|
||||
console.log(" `--' |::::::|_()__()_|::::::| `--' ");
|
||||
console.log(" ::: |::-o::| |::o-::| ::: ");
|
||||
console.log(" `::. .|::::::| |::::::|. .::' ");
|
||||
console.log(" `:::. .::\-----' `-----/::. .:::' ");
|
||||
console.log(" `::::......::::' `::::......::::' ");
|
||||
console.log(" `::::::::::' `::::::::::' ");
|
||||
console.log("");
|
||||
console.log("Minimal tests have failed.");
|
||||
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success' && job.result !== 'skipped') {
|
||||
console.log(`${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
|
||||
process.exit(1);
|
||||
11
.github/workflows/pr-env-close-unlabel.yml
vendored
@@ -10,6 +10,15 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
pr-env:
|
||||
if: ${{ github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'PR-Env') || github.event.label.name == 'PR-Env' }}
|
||||
if: ${{ (github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'PR-Env') && !contains(github.event.pull_request.labels.*.name, 'PR-Env-self-hosting')) || github.event.label.name == 'PR-Env' }}
|
||||
uses: ./.github/workflows/pr-env-destroy.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
self_hosting: false
|
||||
|
||||
pr-env-self-hosting:
|
||||
if: ${{ (github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'PR-Env-self-hosting')) || github.event.label.name == 'PR-Env-self-hosting' }}
|
||||
uses: ./.github/workflows/pr-env-destroy.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
self_hosting: true
|
||||
|
||||
17
.github/workflows/pr-env-destroy.yml
vendored
@@ -3,6 +3,12 @@ run-name: Destroying Dynamic PR Environment for ${{ github.ref_name }} by @${{ g
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
self_hosting:
|
||||
description: 'Self-hosting stack'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
destroy_pr:
|
||||
@@ -54,9 +60,20 @@ jobs:
|
||||
namespace: default
|
||||
user: oidc-token
|
||||
- name: Destroy PR Review ENV
|
||||
if: ${{ !inputs.self_hosting }}
|
||||
run: |
|
||||
kubectl delete metabase -n hosting-pr${{ github.event.number }} hosting-pr${{ github.event.number }}
|
||||
kubectl delete ns hosting-pr${{ github.event.number }}
|
||||
- name: Setup Helm
|
||||
if: ${{ inputs.self_hosting }}
|
||||
uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: 'latest'
|
||||
- name: Destroy PR Review ENV (self-hosting)
|
||||
if: ${{ inputs.self_hosting }}
|
||||
run: |
|
||||
helm uninstall hosting-pr${{ github.event.number }} --namespace hosting-pr${{ github.event.number }} || true
|
||||
kubectl delete ns hosting-pr${{ github.event.number }} || true
|
||||
- name: Setup psql client
|
||||
run: |
|
||||
sudo apt-get update
|
||||
|
||||
12
.github/workflows/pr-env-labeled.yml
vendored
@@ -10,7 +10,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
uberjar:
|
||||
if: ${{ github.event.label.name == 'PR-Env' }}
|
||||
if: ${{ github.event.label.name == 'PR-Env' || github.event.label.name == 'PR-Env-self-hosting' }}
|
||||
uses: ./.github/workflows/uberjar.yml
|
||||
secrets: inherit
|
||||
|
||||
@@ -19,3 +19,13 @@ jobs:
|
||||
if: ${{ github.event.label.name == 'PR-Env' }}
|
||||
uses: ./.github/workflows/pr-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
self_hosting: false
|
||||
|
||||
pr-env-self-hosting:
|
||||
needs: [uberjar]
|
||||
if: ${{ github.event.label.name == 'PR-Env-self-hosting' }}
|
||||
uses: ./.github/workflows/pr-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
self_hosting: true
|
||||
|
||||
53
.github/workflows/pr-env.yml
vendored
@@ -3,6 +3,12 @@ run-name: Building Dynamic PR Environment for ${{ github.ref_name }} by @${{ git
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
self_hosting:
|
||||
description: 'Deploy self-hosting stack'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -113,8 +119,10 @@ jobs:
|
||||
| grep -q 1 \
|
||||
|| psql -c "CREATE DATABASE hosting_pr${{ github.event.number }}"
|
||||
- name: Download Deployment YAML template
|
||||
if: ${{ !inputs.self_hosting }}
|
||||
run: aws s3 cp s3://metabase-pr-env/metabase.yml.tmpl ./metabase.yml.tmpl
|
||||
- name: Render Deployment YAML
|
||||
if: ${{ !inputs.self_hosting }}
|
||||
uses: nowactions/envsubst@v1
|
||||
with:
|
||||
input: ./metabase.yml.tmpl
|
||||
@@ -125,8 +133,52 @@ jobs:
|
||||
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.PR_ENV_MB_PREMIUM_EMBEDDING_TOKEN }}
|
||||
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Deploy PR Review ENV
|
||||
if: ${{ !inputs.self_hosting }}
|
||||
run: |
|
||||
kubectl apply -f ./metabase.yml
|
||||
- name: Download Helm values template (self-hosting)
|
||||
if: ${{ inputs.self_hosting }}
|
||||
run: aws s3 cp s3://metabase-pr-env/values.yaml.tmpl ./values.yaml.tmpl
|
||||
- name: Render Helm values (self-hosting)
|
||||
if: ${{ inputs.self_hosting }}
|
||||
uses: nowactions/envsubst@v1
|
||||
with:
|
||||
input: ./values.yaml.tmpl
|
||||
output: ./values.yaml
|
||||
env:
|
||||
IMAGE_TAG: pr${{ github.event.number }}
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.PR_ENV_MB_PREMIUM_EMBEDDING_TOKEN }}
|
||||
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
|
||||
QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
|
||||
OPEN_AI_API_KEY: ${{ secrets.OPEN_AI_API_KEY }}
|
||||
DB_HOST: ${{ secrets.PR_ENV_DB_HOST }}
|
||||
DB_USERNAME: ${{ secrets.PR_ENV_DB_USER }}
|
||||
DB_PASSWORD: ${{ secrets.PR_ENV_DB_PASSWORD }}
|
||||
CONFIG: ${{ secrets.PR_ENV_CONFIG }}
|
||||
|
||||
- name: Setup Helm
|
||||
if: ${{ inputs.self_hosting }}
|
||||
uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: 'latest'
|
||||
- name: Login to Quay.io
|
||||
if: ${{ inputs.self_hosting }}
|
||||
run: |
|
||||
helm registry login quay.io \
|
||||
--username ${{ secrets.QUAY_USERNAME }} \
|
||||
--password ${{ secrets.QUAY_PASSWORD }}
|
||||
- name: Deploy PR Review ENV (self-hosting)
|
||||
if: ${{ inputs.self_hosting }}
|
||||
run: |
|
||||
helm upgrade --install hosting-pr${{ github.event.number }} \
|
||||
oci://quay.io/enterprisemetabase/chart-metabase-stack \
|
||||
--namespace hosting-pr${{ github.event.number }} \
|
||||
--create-namespace \
|
||||
--values ./values.yaml \
|
||||
--atomic \
|
||||
--timeout 10m
|
||||
preview_links:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy_pr]
|
||||
@@ -139,3 +191,4 @@ jobs:
|
||||
✅ Preview:
|
||||
https://pr${{ github.event.number }}.coredev.metabase.com
|
||||
🗒️ [login instructions](https://www.notion.so/metabase/Ephemeral-PR-Environments-c74a3710c87a460dbf4fc0007a001458)
|
||||
📦 Deployment type: ${{ inputs.self_hosting && 'PR-Env-self-hosting' || 'PR-Env' }}
|
||||
|
||||
14
.github/workflows/pre-release.yml
vendored
@@ -194,19 +194,21 @@ jobs:
|
||||
|
||||
- name: Run a few important OSS Cypress tests as sanity check
|
||||
if: ${{ matrix.edition == 'oss' }}
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="@smoke+-@EE",grepOmitFiltered=true \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
--env grepTags="@smoke+-@EE" \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)'
|
||||
|
||||
- name: Run a few important EE Cypress tests as sanity check
|
||||
if: ${{ matrix.edition == 'ee' }}
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="@smoke+-@OSS",grepOmitFiltered=true \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
--env grepTags="@smoke+-@OSS" \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)'
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
80
.github/workflows/run-tests.yml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') }}
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') && !contains(github.event.pull_request.labels.*.name,'minimal-tests') }}
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
@@ -25,6 +25,11 @@ jobs:
|
||||
frontend_all: ${{ steps.changes.outputs.frontend_all }}
|
||||
frontend_sources: ${{ steps.changes.outputs.frontend_sources }}
|
||||
documentation: ${{ steps.changes.outputs.documentation }}
|
||||
frontend_embedding_sdk_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_sources }}
|
||||
embedding_documentation: ${{ steps.changes.outputs.embedding_documentation }}
|
||||
embedding_sdk_components: ${{ steps.changes.outputs.embedding_sdk_components }}
|
||||
embedding_sdk_host_sample_apps: ${{ steps.changes.outputs.embedding_sdk_host_sample_apps }}
|
||||
embedding_sdk_ci: ${{ steps.changes.outputs.embedding_sdk_ci }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
@@ -36,49 +41,19 @@ jobs:
|
||||
list-files: csv
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
static-viz-files-changed:
|
||||
name: Check whether static-viz files changed
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
static_viz: ${{ steps.static_viz.outputs.static_viz }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Build static-viz frontend
|
||||
run: yarn build-static-viz
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Upload Static Viz Bundle Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-viz-bundle-${{ github.sha }}
|
||||
path: resources/frontend_client/app/dist
|
||||
|
||||
- name: Check for static viz changes
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: static_viz
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/static-viz-sources.yaml
|
||||
|
||||
uberjar:
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
needs: [files-changed]
|
||||
if: ${{ !cancelled() && !(needs.files-changed.outputs.documentation == 'true' && needs.files-changed.outputs.backend_all == 'false' && needs.files-changed.outputs.frontend_all == 'false' && needs.files-changed.outputs.e2e_all == 'false') }}
|
||||
uses: ./.github/workflows/uberjar.yml
|
||||
secrets: inherit
|
||||
|
||||
backend-tests:
|
||||
if: ${{ !cancelled() && needs.files-changed.result == 'success' }}
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
needs: [files-changed]
|
||||
uses: ./.github/workflows/backend.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.backend_all != 'true' && needs.static-viz-files-changed.outputs.static_viz != 'true' }}
|
||||
skip: ${{ needs.files-changed.outputs.backend_all != 'true' }}
|
||||
|
||||
semantic-search-tests:
|
||||
needs: files-changed
|
||||
@@ -122,18 +97,51 @@ jobs:
|
||||
# list of changed specs files, then only specs are changed and we can run only those specs
|
||||
specs: ${{ needs.files-changed.outputs.e2e_all_files == needs.files-changed.outputs.e2e_specs_files && needs.files-changed.outputs.e2e_specs_files || '' }}
|
||||
|
||||
embedding-sdk-package:
|
||||
needs: [files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
(needs.files-changed.outputs.embedding_sdk_components == 'true' ||
|
||||
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' ||
|
||||
needs.files-changed.outputs.embedding_documentation == 'true' ||
|
||||
needs.files-changed.outputs.embedding_sdk_ci == 'true')
|
||||
uses: ./.github/workflows/embedding-sdk-package-build.yml
|
||||
secrets: inherit
|
||||
|
||||
sdk-tests:
|
||||
needs: files-changed
|
||||
needs: [uberjar, embedding-sdk-package, files-changed]
|
||||
if: |
|
||||
needs.embedding-sdk-package.result == 'success' &&
|
||||
needs.uberjar.result == 'success'
|
||||
uses: ./.github/workflows/embedding-sdk.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
cli-snippets-type-check: ${{ needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' }}
|
||||
docs-snippets-type-check: ${{ needs.files-changed.outputs.embedding_documentation == 'true' }}
|
||||
documentation-validation: ${{ needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' }}
|
||||
component-tests: ${{ needs.files-changed.outputs.embedding_sdk_components == 'true' }}
|
||||
host-sample-apps-tests: ${{ needs.files-changed.outputs.embedding_sdk_host_sample_apps == 'true' }}
|
||||
|
||||
pr-env:
|
||||
needs: [uberjar]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.event.pull_request.labels.*.name, 'PR-Env')
|
||||
contains(github.event.pull_request.labels.*.name, 'PR-Env') &&
|
||||
!contains(github.event.pull_request.labels.*.name, 'PR-Env-self-hosting')
|
||||
uses: ./.github/workflows/pr-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
self_hosting: false
|
||||
|
||||
pr-env-self-hosting:
|
||||
needs: [uberjar]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.event.pull_request.labels.*.name, 'PR-Env-self-hosting')
|
||||
uses: ./.github/workflows/pr-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
self_hosting: true
|
||||
|
||||
containerize:
|
||||
needs: [uberjar]
|
||||
|
||||
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,5 +1,8 @@
|
||||
accept = ["200..=204", "429"]
|
||||
exclude_path = ["docs/embedding/sdk"]
|
||||
exclude = ["\\.html(#.*)?$"]
|
||||
exclude_path = ["docs/util"]
|
||||
no_progress = true
|
||||
include_fragments = true
|
||||
verbose = "error"
|
||||
verbose = "error"
|
||||
include_verbatim = false
|
||||
cache = false
|
||||
|
||||
@@ -2,4 +2,4 @@ frontend/src/cljs
|
||||
docs/configuring-metabase/environment-variables.md
|
||||
docs/api/*
|
||||
docs/api-documentation.md
|
||||
enterprise/frontend/src/embedding-sdk-shared/.storybook/msw-public/mockServiceWorker.js
|
||||
frontend/src/embedding-sdk-shared/.storybook/msw-public/mockServiceWorker.js
|
||||
|
||||
724
CLAUDE.md
@@ -1,722 +1,20 @@
|
||||
# Metabase Development Guide
|
||||
|
||||
## Autonomous Development Workflow
|
||||
# Skills
|
||||
|
||||
- Do not attempt to read or edit files outside the project folder
|
||||
- Add failing tests first, then fix them
|
||||
- Work autonomously in small, testable increments
|
||||
- Run targeted tests, and lint continuously during development
|
||||
- Prioritize understanding existing patterns before implementing
|
||||
- Don't commit changes, leave it for the user to review and make commits
|
||||
For detailed guidance on writing and reviewing code and documentation, see the skills in [.claude/skills/](.claude/skills/):
|
||||
|
||||
## Quick Commands
|
||||
## Clojure
|
||||
|
||||
### JavaScript/TypeScript
|
||||
- **[clojure-write](.claude/skills/clojure-write/SKILL.md)** - Clojure/ClojureScript development with REPL-driven workflow and coding conventions
|
||||
- **[clojure-review](.claude/skills/clojure-review/SKILL.md)** - Clojure/ClojureScript code review guidelines and style enforcement
|
||||
|
||||
- **Lint:** `yarn lint-eslint-pure`
|
||||
- **Test:** `yarn test-unit-keep-cljs path/to/file.unit.spec.js` or `yarn test-unit-keep-cljs -t "pattern"`
|
||||
- **Format:** `yarn prettier`
|
||||
- **Type Check:** `yarn type-check-pure`
|
||||
## TypeScript
|
||||
|
||||
### Clojure
|
||||
- **[typescript-write](.claude/skills/typescript-write/SKILL.md)** - TypeScript/JavaScript development and best practices
|
||||
- **[typescript-review](.claude/skills/typescript-review/SKILL.md)** - TypeScript/JavaScript code review guidelines
|
||||
|
||||
- **Lint PR:** `./bin/mage kondo-updated master` (or whatever target branch)
|
||||
- Call the command one time at the beginning, record the results, then work through the problems one at a time.
|
||||
- If the solution is obvious, then please apply the fix. Otherwise skip it.
|
||||
- If you fix all the issues (and verify by rerunning the kondo-updated command):
|
||||
- commit the change with a succinct and descriptive commit message
|
||||
- **Lint File:** `./bin/mage kondo <file or files>` (or whatever target branch)
|
||||
- Use the linter as a way to know that you are adhering to conventions in place in the codebase
|
||||
- **Lint Changes:** `./bin/mage kondo-updated HEAD`
|
||||
- **Format:** `./bin/mage cljfmt-files [path]`
|
||||
- **Run a test:** `./bin-mage run-tests namespace/test-name`
|
||||
- **Run all tests in a namespace:** `./bin-mage run-tests namespace`
|
||||
- **Check Code Readability** `./bin/mage -check-readable` with optional line-number
|
||||
- Run this after every change to Clojure code, only accept readable code
|
||||
- **Evaluating Clojure Code** `./bin/mage -repl '<code>'`
|
||||
- See `Sending code to the REPL` for more details
|
||||
## Documentation
|
||||
|
||||
### ClojureScript
|
||||
|
||||
- **Test:** `yarn test-cljs`
|
||||
|
||||
## Clojure REPL-driven development
|
||||
|
||||
- Start with small, fundamental functions:
|
||||
- Identify the core features or functionalities required for your task.
|
||||
- Break each feature down into the smallest, most basic functions that can be developed and tested independently.
|
||||
- Write and test in the REPL:
|
||||
- Write the code for each small function directly in the REPL (Read-Eval-Print Loop).
|
||||
- Test it thoroughly with a variety of inputs, including typical use cases and relevant edge cases, to ensure it
|
||||
behaves as expected.
|
||||
- Integrate into source code:
|
||||
- Once a function works correctly in the REPL, move it from the REPL environment into your source code files (e.g.,
|
||||
within appropriate namespaces).
|
||||
- Gradually increase complexity:
|
||||
- Build upon tested, basic functions to create more complex functions or components.
|
||||
- Compose smaller functions together, testing each new composition in the REPL to verify correctness step by step.
|
||||
- Ensure dependency testing:
|
||||
- Make sure every function is fully tested in the REPL before it is depended upon by other functions.
|
||||
- This ensures that each layer of your application is reliable before you build on it.
|
||||
- Leverage the REPL fully:
|
||||
- Use the REPL as your primary tool to experiment with different approaches, iterate quickly, and get immediate
|
||||
feedback on your code.
|
||||
- Follow functional programming principles:
|
||||
- Keep functions small, focused, and composable.
|
||||
- Leverage Clojure's functional programming features—like immutability, higher-order functions, and the standard
|
||||
library—to write concise, effective code.
|
||||
|
||||
### How to evaluate code
|
||||
|
||||
#### Keeping Code Readable
|
||||
|
||||
The `./bin/mage -check-readable <file> <optional: line-number>` command checks if your Clojure code can be properly parsed.
|
||||
This ensures your changes maintain valid syntax and structure.
|
||||
|
||||
- Edit Clojure files one step at a time.
|
||||
- After EVERY change to a Clojure form, call `mage -check-readable src/metabase/thefile.clj <line-number>` with the line number.
|
||||
- If it's readable then call `mage -check-readable dev/src/dev.clj` without the line number to check the entire file.
|
||||
- If the change results in unreadable code, try again until it is readable.
|
||||
- To overcome errors about parens, pay close attention to them. Count opening/closing parens you add/remove.
|
||||
|
||||
#### Bottom-up dev loop
|
||||
|
||||
1. Write code into a file.
|
||||
2. Evaluate the file's namespace and make sure it loads correctly with:
|
||||
|
||||
```
|
||||
mage -repl --namespace metabase.app-db.connection
|
||||
```
|
||||
|
||||
3. Call functions in the namespace with test inputs, and observe that the outputs are correct 3.1
|
||||
Feel free to copy these REPL session trials into actual test cases using `deftest` and `is`.
|
||||
4. Once you know these functions are good, return to 1, and compose them into the task that you need to build.
|
||||
|
||||
#### Sending code to the REPL
|
||||
|
||||
- Send code to the metabase process REPL using: `./bin/mage -repl '(+ 1 1)'` where `(+ 1 1)` is your Clojure code.
|
||||
- See `./bin/mage -repl -h` for more details.
|
||||
- If the Metabase backend is not running, you'll see an error message with instructions on how to start it.
|
||||
|
||||
##### Working with files and namespaces
|
||||
|
||||
1. **Load a file and call functions with fully qualified names**:
|
||||
|
||||
To call `your.namespace/your-function` on `arg1` and `arg2`:
|
||||
|
||||
```
|
||||
./bin/mage -repl --namespace your.namespace '(your-function arg1 arg2)'
|
||||
```
|
||||
|
||||
DO NOT use "require", "load-file" etc in the code string argument.
|
||||
|
||||
##### Understanding the response
|
||||
|
||||
The `./bin/mage -repl` command returns three separate, independent outputs:
|
||||
|
||||
- `value`: The return value of the last expression (best for data structures)
|
||||
- `stdout`: Any printed output from `println` etc. (best for messages)
|
||||
- `stderr`: Any error messages (best for warnings and errors)
|
||||
|
||||
Example call:
|
||||
|
||||
```bash
|
||||
./bin/mage -repl '(println "Hello, world!") '\''({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```
|
||||
ns: user
|
||||
session: 32a35206-871c-4553-9bc9-f49491173d1c
|
||||
value: ({0 1, 1 3, 2 0, 3 2} {0 2, 1 0, 2 3, 3 1})
|
||||
stdout: Hello, world!
|
||||
stderr:
|
||||
```
|
||||
|
||||
For effective REPL usage:
|
||||
|
||||
- Return data structures as function return values
|
||||
- Use `println` for human-readable messages
|
||||
- Print errors to stderr
|
||||
|
||||
## Tips
|
||||
|
||||
- End all files with a newline.
|
||||
- When editing tabular code, where the columns line up, try to keep them aligned.
|
||||
- Spaces on a line with nothing after it is not allowed
|
||||
|
||||
## Critical REPL Usage Rules
|
||||
|
||||
- Be careful with parentheses counts when editing Clojure code
|
||||
- After EVERY change to Clojure code, verify readability with `-check-readable`
|
||||
|
||||
# Code Review Guidelines
|
||||
|
||||
Review pull requests with a focus on:
|
||||
|
||||
- Compliance with project coding standards and conventions
|
||||
|
||||
- Code quality and best practices
|
||||
|
||||
- Clear and correct docstrings
|
||||
|
||||
- Clear and correct comments explaining what the code is doing
|
||||
|
||||
## More Instructions
|
||||
|
||||
- Carefully read the Community Clojure Style Guide from `CLOJURE_STYLE_GUIDE.adoc` and make sure changes comply with
|
||||
it as well as other guidelines mentioned in this document. Flag _all_ violations of the Community Clojure Style
|
||||
Guide.
|
||||
|
||||
- Do not post comments congratulating someone for trivial changes, or for following the style guidelines, or
|
||||
confirming that things "look good", or telling them that they did something correctly; only post comments about
|
||||
style violations or potential issues. Example bad code review comments to avoid:
|
||||
|
||||
> This TODO comment is properly formatted with author and date - nice work!
|
||||
|
||||
> Good addition of limit 1 to the query - this makes the test more efficient without changing its behavior.
|
||||
|
||||
> The kondo ignore comment is appropriately placed here
|
||||
|
||||
> The kondo ignore is properly used here. The comment on lines 42-45 explains why hardcoded driver names are needed,
|
||||
> which is good context.
|
||||
|
||||
> Test name properly ends with -test as required by the style guide.
|
||||
|
||||
- Do not post comments about missing parentheses.
|
||||
|
||||
# Code Conventions and Style Guide
|
||||
|
||||
## Clojure and ClojureScript
|
||||
|
||||
### General Conventions
|
||||
|
||||
- Prefer longer, more verbose names for functions and variables; avoid abbreviations unless they are well-known and
|
||||
conventional in the Clojure world. `acc`, `i`, `pred`, `coll`, `n`, `s`, `k`, and `f` are examples of well-known
|
||||
conventions; any Clojure developer has seen them before and can tell you what they mean. Avoid unconventional
|
||||
abbreviations like `tbl` and unclear variable names like `zs'`. A good function or variable name should make its
|
||||
purpose immediately clear. Error on the side of longer variable names if needed.
|
||||
|
||||
```clj
|
||||
;; too cryptic
|
||||
(defn mayb+1 [n]
|
||||
(when n
|
||||
(inc n)))
|
||||
|
||||
;; just right
|
||||
(defn maybe-inc [n]
|
||||
(when n
|
||||
(inc n)))
|
||||
```
|
||||
|
||||
**Why?** Code is read many more times than it is written, and clearer variable names make using and tweaking your
|
||||
code easier for others.
|
||||
|
||||
Conventions used widely in our codebase (like `mp` for a Metadata Provider) are acceptable; one-offs like `zs'` are
|
||||
not.
|
||||
|
||||
- Avoid misleading variable and function names. The names of a variable or function should clearly and unambiguously
|
||||
describe its purpose and match what it does.
|
||||
|
||||
```clj
|
||||
;; bad
|
||||
(defn nil-or-maplist? [v] ; coll would be a better variable name because it's more specific
|
||||
(or (nil? v)
|
||||
(and (sequential? v) ; v can actually be an array, vector, list, or lazy seq
|
||||
(every? map? v))))
|
||||
|
||||
;; good
|
||||
(defn nil-or-sequence-of-maps? [coll]
|
||||
(or (nil? coll)
|
||||
(and (sequential? coll)
|
||||
(every? map? coll))))
|
||||
```
|
||||
|
||||
**Why?** Poorly-named functions are prone to being used in cases where they're inappropriate or avoided in cases
|
||||
when they would be suitable.
|
||||
|
||||
- Pure function names should be nouns describing the value they return.
|
||||
|
||||
For example, a function to compute a user's age based on their birthdate should be called `age`, not `calculate-age`
|
||||
or `get-age`.
|
||||
|
||||
**Why?** A pure function is one which can be replaced with its value without affecting the result, so the name
|
||||
should reflect that.
|
||||
|
||||
- Don't repeat the usual alias of the namespace a function belongs to in the name of a function itself.
|
||||
|
||||
```clj
|
||||
(ns metabase.config)
|
||||
|
||||
;; bad
|
||||
(defn config-is-dev? [] ...)
|
||||
|
||||
;; good
|
||||
(defn is-dev? [] ...)
|
||||
```
|
||||
|
||||
**Why?** It's obvious that `is-dev?` in the example above is referring to `dev`, because it's in the `config`
|
||||
namespace. It's also needlessly noisy when using the function in another namespace:
|
||||
|
||||
```clj
|
||||
;; bad
|
||||
(when (config/config-is-dev?)
|
||||
...)
|
||||
|
||||
;; good
|
||||
(when (config/is-dev?)
|
||||
...)
|
||||
```
|
||||
|
||||
In some cases, following this rule will require you to use a `(:refer-clojure :exclude [...])` form in your namespace
|
||||
declaration. This is acceptable, and should be taken as a sign that you're following this rule correctly.
|
||||
|
||||
- If you shadow a `clojure.core` var, make sure you use `(:refer-clojure :exclude ...)` in the `ns` form.
|
||||
|
||||
- Make everything `^:private` unless it is used elsewhere.
|
||||
|
||||
Don't make things public just for the sake of tests. Use the var form (e.g. `#'redshift/execute!`) instead in your
|
||||
tests.
|
||||
|
||||
**Why?** It's much easier to read and refactor code when you know its scope is limited to the current namespace.
|
||||
|
||||
- Tag variables with `:arglists` metadata if they are functions but wouldn't otherwise have it, such as when using
|
||||
`def` to define partial functions or function compositions e.g.
|
||||
|
||||
```clj
|
||||
(def ^{:arglists '([n])} plus-one (partial + 1))
|
||||
```
|
||||
|
||||
**Why?** Good editors use this metadata show the expected arguments to a function as you're writing code.
|
||||
|
||||
- Try to organize namespaces in such a way that you don't need to use `declare`. This usually means putting the public
|
||||
portion of a namespace near the end of a file.
|
||||
|
||||
**Why?** Avoiding declare when unnecessary forces us to read and write code in a consistent manner, that is, from
|
||||
top to bottom. When code is written in this consistent order we can safely assume referenced functions sit somewhere
|
||||
above their reference in the namespace; this makes the code easier to navigate.
|
||||
|
||||
- Don't mark things `^:const` unless you have a really good reason for doing so. Add a comment explaining why you
|
||||
marked it `^:const`.
|
||||
|
||||
- Every public var in `src` or `enterprise/backend/src` must have a _useful_ docstring. A useful docstring
|
||||
should clearly explain the purpose of the function, its inputs and outputs, and anything else that is otherwise not
|
||||
immediately clear. If there are other functions that have similar purposes, explain how the use-cases for this
|
||||
function differ.
|
||||
|
||||
Tests and other vars in the `test` or `enterprise/backend/test` do not _require_ docstrings, but general helper
|
||||
functions used across many namespaces it should have docstrings.
|
||||
|
||||
Schemas defined with `mr/def` e.g. `(mr/def ::my-schema)` do not require docstrings.
|
||||
|
||||
- Format docstrings according to Markdown conventions. (https://guide.clojure.style/#markdown-docstrings)
|
||||
|
||||
- Mentions of other vars in docstrings should use `[[some-other-var]]` (for vars in the same namespace) or
|
||||
`[[metabase.namespace/some-other-var]]` (for vars in a different namespace) instead of backticks; references in
|
||||
docstrings should be valid (i.e., point to something that exists).
|
||||
(https://guide.clojure.style/#document-references)
|
||||
|
||||
- Judiciously use comments to explain sections of code that would not immediately be clear to someone else. Avoid
|
||||
comments that do little more than repeat what the code already says.
|
||||
|
||||
- Make sure to update comments and docstrings when you change the code they describe.
|
||||
|
||||
- `TODO` comments should include the author and date, for example
|
||||
|
||||
```clj
|
||||
;; TODO (Cam 10/7/25) -- this is a properly formatted TODO comment
|
||||
(...)
|
||||
```
|
||||
|
||||
- Break up larger functions (> 20 lines) in source code (`src` or `enterprise/backend/src`) whenever possible. Small
|
||||
functions are much easier to test, understand, and tweak. Tests are allowed to be longer, especially when they
|
||||
contain mock data, but they should probably never be more than 100 lines.
|
||||
|
||||
- Try to keep lines 120 characters wide or less; use this as a guideline when formatting docstrings or comments.
|
||||
|
||||
- **No Blank Lines Within Definition Forms** Do not place blank lines in the middle of a function or macro definition.
|
||||
An exception can be made to indicate grouping of pairwise constructs as found in e.g. `let` and `cond`, in case
|
||||
those don’t fit on the same line. `deftest` is **NOT** an exception to this rule.
|
||||
(https://guide.clojure.style/#no-blank-lines-within-def-forms) Verify that the line in question is **actually
|
||||
blank** (i.e., only contains whitespace for the **entire line**) before warning about this. **A line that contains
|
||||
any non-whitespace character is not a blank line.** You will be shut down if you make this mistake too many times,
|
||||
so be careful.
|
||||
|
||||
- Use `kebab-case` names for variables and defs, including constants.
|
||||
(https://guide.clojure.style/#naming-functions-and-variables)
|
||||
|
||||
```clj
|
||||
;;; BAD
|
||||
(def MY_CONSTANT 100)
|
||||
|
||||
;;; GOOD
|
||||
(def my-constant 100)
|
||||
|
||||
;;; BAD
|
||||
(defn myFunction [my_arg] ...)
|
||||
|
||||
;;; GOOD
|
||||
(defn my-function [my-arg] ...)
|
||||
```
|
||||
|
||||
- Map destructuring should use kebab-case local bindings even if the map it was destructured from uses `snake_case`
|
||||
keys or if it is returned as a value for a `snake_case` key.
|
||||
|
||||
```clj
|
||||
;; Good
|
||||
(let [{database-id :database_id} some-object]
|
||||
{:database_id database-id, :table_id 100})
|
||||
|
||||
;; Bad
|
||||
(let [{database_id :database_id} some-object]
|
||||
{:database_id database_id, :table_id 100})
|
||||
```
|
||||
|
||||
- Prefer namespaced keywords for keywords that are used internally (i.e., not returned by the REST API or persisted by
|
||||
the app DB):
|
||||
|
||||
```clj
|
||||
;;; good
|
||||
(defn query-type [x]
|
||||
(if (some-pred? x)
|
||||
:query-type/normal
|
||||
:query-type/crazy))
|
||||
|
||||
;;; bad
|
||||
(defn query-type [x]
|
||||
(if (some-pred? x)
|
||||
:normal
|
||||
:crazy))
|
||||
```
|
||||
|
||||
These are easy to search across the entire application and makes their origin clearer.
|
||||
|
||||
- Functions that have side-effects such as writing to the application database or mutating the global state of the
|
||||
application should have names that end in exclamation points. Exclamation points should be considered "sticky", so
|
||||
if a function uses another function with a name ending in an exclamation point, it too should have a name that ends
|
||||
in an exclamation point. (https://guide.clojure.style/#naming-unsafe-functions)
|
||||
|
||||
An exception is functions that write log messages or other output to the console; these don't need exclamation
|
||||
points.
|
||||
|
||||
### Tests
|
||||
|
||||
- Large tests should be broken out into separate `deftest` forms when they consist of several logically separate test
|
||||
cases.
|
||||
|
||||
```clj
|
||||
(deftest ^:parallel my-test
|
||||
(testing "Some logically discrete test case"
|
||||
(is ...)))
|
||||
|
||||
(deftest ^:parallel my-other-test
|
||||
(testing "Another logically discrete test case"
|
||||
(is ...)))
|
||||
|
||||
(deftest ^:parallel my-third-different-test
|
||||
(testing "A third logically discrete test case"
|
||||
(is ...)))
|
||||
|
||||
(deftest ^:parallel my-amazing-test
|
||||
(testing "A fourth logically discrete test case"
|
||||
(is ...)))
|
||||
```
|
||||
|
||||
is preferable to
|
||||
|
||||
```clj
|
||||
(deftest ^:parallel my-test
|
||||
(testing "Some logically discrete test case"
|
||||
(is ...))
|
||||
|
||||
(testing "Another logically discrete test case"
|
||||
(is ...))
|
||||
|
||||
(testing "A third logically discrete test case"
|
||||
(is ...))
|
||||
|
||||
(testing "A fourth logically discrete test case"
|
||||
(is ...)))
|
||||
```
|
||||
|
||||
- Mark pure function tests `^:parallel`.
|
||||
|
||||
- Test utility functions that are not thread-safe/safe in `^:parallel` tests should have names that end in an
|
||||
exclamation mark.
|
||||
|
||||
- Test names in `deftest` forms should end in `-test` or `-test-<number>` e.g. `whatever-test` or `whatever-test-2` or
|
||||
even `whatever-test-2b`. (https://guide.clojure.style/#test-naming)
|
||||
|
||||
### Modules
|
||||
|
||||
- The backend codebase is broken out into separate modules.
|
||||
|
||||
- The module configuration file lives in `.clj-kondo/config/modules/config.edn`. There is one entry for each module.
|
||||
The entry has several keys, but the important ones are `:api` -- the list of namespaces this module provides for use
|
||||
outside of the module -- and `:uses` -- the list of modules this module directly relies on.
|
||||
|
||||
- An OSS follows the pattern `metabase.<module>.*` (for the Clojure namespace) and `src/metabase/<module>/**`(for the
|
||||
source files) with tests inside the corresponding `test/metabase/<module>/` directory, e.g. the `dashboards` module
|
||||
is everything inside `src/metabase/dashboards/` and `test/metabase/dashboards/`; it might have a
|
||||
`metabase.dashboards.api` namespace that corresponds to the file `src/metabase/dashboards/api.clj`.
|
||||
|
||||
- An EE module follows the pattern `metabase-enterprise.<module>.*` (for the Clojure namespaces) and
|
||||
`enterprise/backend/src/metabase_enterprise/<module>/**` (for the source files). The module name uses the
|
||||
`enterprise/` prefix. For example, `enterprise/billing` is everything in the
|
||||
`enterprise/backend/src/metabase_enterprise/billing/` directory (for source code) and
|
||||
`enterprise/backend/test/metabase_enterprise/billing/` (for tests); it might have a namespace called
|
||||
`metabase-enterprise.billing.api` which corresponds to the file
|
||||
`enterprise/backend/src/metabase_enterprise/billing/api.clj`.
|
||||
|
||||
- REST API endpoints (defined by the `defendpoint` macro) should live in a `<module>.api` or `<module>.api.*`
|
||||
namespace, e.g. `metabase.dashboards.api` or `metabase.dashboards.api.x`.
|
||||
|
||||
- When you add a new API endpoint namespace, you need to add a mapping for it in `metabase.api-routes.routes`.
|
||||
|
||||
- Put any functions used by other modules (the API meant for the rest of the backend code) in `<module>.core` .
|
||||
`.core` should import stuff with Potemkin/ `metabase.util.namespace` and not be used inside the module itself. It’s
|
||||
also nice to put a `:consistent-alias` entry for this namespace in the Kondo config.
|
||||
|
||||
- The `<module>.core` namespace should generally be empty other than importing things from other namespaces with
|
||||
Potemkin (`potemkin/import-vars` and the like).
|
||||
|
||||
- Put Toucan models related to a feature in `<module>.models.*` and add mappings in `metabase.models.resolution`.
|
||||
|
||||
- Put scheduled Quartz tasks in `<module>.task.*`.
|
||||
|
||||
- Put event handlers (things that use the events subsystem in `metabase.events.core`) in `<module>.event.*`.
|
||||
|
||||
- Put `defsetting`s (Settings) in `<module>.settings`.
|
||||
|
||||
- Quartz tasks, event handlers, and Settings all need to be loaded on launch, so if you have any of the above add
|
||||
them to a `<module>.init` namespace and require it in `metabase[-enterprise].core.init`.
|
||||
|
||||
- Don't try to cheat the module linters by using things like `#_{:clj-kondo/ignore [:metabase/modules]}`. Note that
|
||||
this does not apply to using `:clj-kondo/ignore` to disable warnings for other linters besides `:metabase/modules`.
|
||||
|
||||
- Put Malli schemas in `<module>.schema`.
|
||||
|
||||
- Aim to keep the number of namespaces used outside of a module small. A module should only need at most a subset of
|
||||
`<module>.api`, `<module>.settings`, `<module>.schema`, `<module>.init`, and `<module>.core`.
|
||||
|
||||
- Try to minimize the direct dependencies of a module as well as the indirect dependencies. Modules that are used by
|
||||
lots of other modules (such as `util`) ideally will have no dependencies on other modules. Our goal is to make as
|
||||
many modules as possible be "leaf nodes".
|
||||
|
||||
- Module names should be match the customer-facing name of the feature they concern, usually a plural noun, for
|
||||
example, `dashboards` rather than `dashboard`.
|
||||
|
||||
### Settings
|
||||
|
||||
- Don't define configurable options that can only be set with environment variables; use an `:internal` `defsetting`
|
||||
instead. We have lots of tooling around `defsetting`.
|
||||
|
||||
### REST API Endpoints
|
||||
|
||||
- All new REST API Endpoints (defined by `defendpoint`) should have a response schema (denoted by `:- <schema>` after
|
||||
the route string).
|
||||
|
||||
```clj
|
||||
;;; BAD
|
||||
(api.macros/defendpoint :get "/"
|
||||
"Get a list of all transform tags."
|
||||
...)
|
||||
|
||||
;;; GOOD
|
||||
(api.macros/defendpoint :get "/" :- [:sequential ::whatever-this-returns]
|
||||
"Get a list of all transform tags."
|
||||
...)
|
||||
```
|
||||
|
||||
- Query parameters should also use kebab-case e.g. `GET /api/dashboards?include-archived=true` is good while `GET
|
||||
/api/dashboards?include_archived=true` or `GET /api/dashboards?includeArchived=true` is bad.
|
||||
|
||||
- HTTP request bodies should use `snake_case`.
|
||||
|
||||
- REST API endpoints should have routes that use singular nouns, for example `GET /api/dashboard/:id` to get a
|
||||
Dashboard rather than `GET /api/dashboards/:id`.
|
||||
|
||||
- `GET` endpoints should not have side effects outside of analytics like updating last-viewed-at timestamps or
|
||||
recording usage metrics. A `GET` endpoint should not be creating new rows in the application database representing
|
||||
user-facing objects, for example `GET /api/dashboard` should not attempt to populate empty Dashboards with content
|
||||
by creating new rows in the application database.
|
||||
|
||||
- `defendpoint` forms should be small wrappers around Toucan model code. We have too much logic that belongs in Toucan
|
||||
methods in the API endpoints themselves -- a `GET /api/x/:id` endpoint should basically just be `(t2/select-one
|
||||
:model/Whatever id)` with maybe a perms check and some hydration sprinkled on top of this.
|
||||
|
||||
- All API endpoints should have Malli schemas for any parameters that aren't ignored. `_route-params` doesn't need a
|
||||
schema, but `{:keys [x]}` should have one.
|
||||
|
||||
- Malli schemas for REST API endpoints should be detailed enough that someone can look at them and know how to use the
|
||||
endpoint.
|
||||
|
||||
Example: for an endpoint like this:
|
||||
|
||||
```clj
|
||||
(api.macros/defendpoint :post "/check_transform"
|
||||
"Check a proposed edit to a transform, and return the card, transform, etc. IDs for things that will break."
|
||||
[_route-params
|
||||
_query-params
|
||||
{:keys [id source target] :as _body} :- ::transform-body]
|
||||
...)
|
||||
```
|
||||
|
||||
This is a bad schema for the request body:
|
||||
|
||||
```clj
|
||||
;;; Bad, does not specify the shape of :source and :target in any real detail
|
||||
(mr/def ::transform-body
|
||||
[:map
|
||||
[:id {:optional false} ms/PositiveInt]
|
||||
[:name {:optional true} :string]
|
||||
[:source {:optional true} [:maybe ms/Map]]
|
||||
[:target {:optional true} [:maybe ms/Map]]])
|
||||
```
|
||||
|
||||
While this is better:
|
||||
|
||||
```clj
|
||||
;;; Better, because it tells us the expected shape of :source and :target
|
||||
(mr/def ::transform-body
|
||||
[:map
|
||||
[:id {:optional false} ms/PositiveInt]
|
||||
[:name {:optional true} :string]
|
||||
[:source {:optional true} [:maybe [:map [:x pos-int?] [:y pos-int?]]]]
|
||||
[:target {:optional true} [:maybe [:map [:a pos-int?] [:b pos-int?]]]]])
|
||||
```
|
||||
|
||||
- All new REST API endpoints **MUST HAVE TESTS**.
|
||||
|
||||
### MBQL
|
||||
|
||||
- No raw MBQL introspection or manipulation should be done outside of Lib (the `lib` and `lib-be` modules) or the
|
||||
Query Processor (the `query-processor` module) modules. MBQL maps include the `:model/Card` `dataset_query`, you can
|
||||
usually recognize it when you see a map with a `:database` key and either `:type` or `:lib/type`. You should treat
|
||||
this map as an opaque object outside of the aforementioned modules; pretend you didn't know it has a `:database` or
|
||||
`:type` key.
|
||||
|
||||
- Use Lib and MBQL 5 in all new **source** code instead of legacy MBQL; avoid use of the `legacy-mbql` module, or the
|
||||
`metabase.query-processor.store` namespace, in new code. Any code that checks whether a query `:type` is `:native`
|
||||
or `:query` is a gigantic code smell. Exception: we will allow legacy MBQL in tests... for now.
|
||||
|
||||
- Use of `qp.store/with-metadata-provider` or `mt/with-metadata-provider` should be discouraged and phased out.
|
||||
|
||||
## Models and the Application Database
|
||||
|
||||
- Model names should be singular nouns, e.g. `:model/Dashboard` and not `:model/Dashboards`.
|
||||
|
||||
- Table names in the application database should be singular nouns, e.g. `transform` instead of `transforms`.
|
||||
|
||||
- The application database should use `snake_case` identifiers for table and column names.
|
||||
|
||||
## Using Toucan
|
||||
|
||||
- Never fetch an entire row from the application database only to immediately discard everything except for the value
|
||||
of one column, this is super inefficient and also icky. Use `t2/select-one-fn` instead.
|
||||
|
||||
```clj
|
||||
;;; Bad
|
||||
(:database_id (t2/select-one :model/Card :id 1))
|
||||
|
||||
;;; Good
|
||||
(t2/select-one-fn :database_id :model/Card :id 1)
|
||||
|
||||
;;; Even better -- this does SELECT database_id instead of SELECT *
|
||||
(t2/select-one-fn :database_id [:model/Card :database_id] :id 1)
|
||||
```
|
||||
|
||||
- One of the big ideas behind Toucan is there's never supposed to be a question of "what function to I use to create
|
||||
an X correctly" or "what function do I use to update a Y correctly" -- the answer is always supposed to be
|
||||
`t2/select`, `t2/update!`, etc. That's why they're backed by multimethods, so you can put the correct behavior in
|
||||
the model's method and then it becomes impossible for you to forget to call the correct function. Avoid adding
|
||||
functions like `select-dashboards` or `update-dashboard!` -- put this functionality in Toucan methods for the model
|
||||
in question. This includes things like firing off events.
|
||||
|
||||
- Avoid the `mi/define-batched-hydration-method` and `mi/define-simple-hydration-method` helpers in new code; define
|
||||
method implementations with `(methodical/defmethod t2/batched-hydrate ...)` directly instead.
|
||||
|
||||
### Drivers
|
||||
|
||||
- All new driver multimethods must be mentioned in `docs/developers-guide/driver-changelog.md`.
|
||||
|
||||
- All new driver multimethods should use Lib-style kebab-cased metadata and MBQL 5 queries.
|
||||
|
||||
- Driver multimethod implementations (`defmethod` forms) should explicitly pass any `driver` argument to any other
|
||||
driver multimethods it invokes, rather than hardcoding the driver name.
|
||||
|
||||
```clj
|
||||
;;; Good
|
||||
(defmethod driver/driver-method :postgres
|
||||
[driver x]
|
||||
(driver/some-other-method driver x))
|
||||
|
||||
;;; Bad
|
||||
(defmethod driver/driver-method :postgres
|
||||
[_driver x]
|
||||
(driver/some-other-method :postgres x))
|
||||
```
|
||||
|
||||
This is because other drivers may inherit from this driver and we need to propagate the driver name passed in to any
|
||||
method implementations it inherits.
|
||||
|
||||
- Drivers should only use methods from the `driver` or `driver-api` modules.
|
||||
|
||||
- Be sure to minimize the amount of logic you're doing inside the `read-column-thunk` in JDBC-based drivers as much as
|
||||
possible. Here's our Athena code for java.sql.Types/TIMESTAMP_WITH_TIMEZONE for example:
|
||||
|
||||
```clj
|
||||
(defmethod sql-jdbc.execute/read-column-thunk [:athena Types/TIMESTAMP_WITH_TIMEZONE]
|
||||
[_driver ^ResultSet rs _rs-meta ^Long i]
|
||||
(fn []
|
||||
;; Using ZonedDateTime if available to conform tests first. OffsetDateTime if former is not available.
|
||||
(when-some [^Timestamp timestamp (.getObject rs i Timestamp)]
|
||||
(let [timestamp-instant (.toInstant timestamp)
|
||||
results-timezone (driver-api/results-timezone-id)]
|
||||
...))))
|
||||
```
|
||||
|
||||
Here's a tiny change that will avoid calling (driver-api/results-timezone-id) potentially 10 million times (for a
|
||||
potential million-row result with 10 timestamp columns)
|
||||
|
||||
```clj
|
||||
(defmethod sql-jdbc.execute/read-column-thunk [:athena Types/TIMESTAMP_WITH_TIMEZONE]
|
||||
[_driver ^ResultSet rs _rs-meta ^Long i]
|
||||
(let [results-timezone (driver-api/results-timezone-id)]
|
||||
(fn []
|
||||
;; Using ZonedDateTime if available to conform tests first. OffsetDateTime if former is not available.
|
||||
(when-some [^Timestamp timestamp (.getObject rs i Timestamp)]
|
||||
(let [timestamp-instant (.toInstant timestamp)]
|
||||
...)))))
|
||||
```
|
||||
|
||||
- Avoid defining new datasets with `defdataset` as much as possible, since loading test data in cloud-based databases
|
||||
is crazy slow.
|
||||
|
||||
### Misc
|
||||
|
||||
- Example data should be bird-themed if possible.
|
||||
|
||||
- Any comments that are written by or mention `Cam` should be given bonus points.
|
||||
|
||||
### Kondo
|
||||
|
||||
- Kondo linter warnings are suppressed with a comment proceeding the form to ignore warnings in e.g.
|
||||
|
||||
```clj
|
||||
#_{:clj-kondo/ignore [:metabase/disallow-hardcoded-driver-names-in-tests]}
|
||||
(do-x ...)
|
||||
```
|
||||
|
||||
to ignore the `:metabase/disallow-hardcoded-driver-names-in-tests` Linter inside the `(do-x ...)` form. You do not
|
||||
need to point these out to us, we know what they do.
|
||||
|
||||
```clj
|
||||
^{:clj-kondo/ignore [:metabase/disallow-hardcoded-driver-names-in-tests]}
|
||||
(do-x ...)
|
||||
```
|
||||
|
||||
is an acceptable alternative.
|
||||
|
||||
- Avoid ignoring **everything** with a `#_:clj-kondo/ignore` (a keyword instead of a map).
|
||||
- **[docs-write](.claude/skills/docs-write/SKILL.md)** - Documentation writing with Metabase style guide
|
||||
- **[docs-review](.claude/skills/docs-review/SKILL.md)** - Documentation review checklist
|
||||
|
||||
26
bb.edn
@@ -83,6 +83,18 @@
|
||||
:requires [[mage.check]]
|
||||
:task (task! (mage.check/check (:arguments parsed)))}
|
||||
|
||||
codespell
|
||||
{:doc "Check for misspellings in the source code."
|
||||
:examples [["./bin/mage codespell"]]
|
||||
:requires [[mage.codespell]]
|
||||
:task (task! (mage.codespell/codespell (:arguments parsed)))}
|
||||
|
||||
codespell-interactive
|
||||
{:doc "Check for misspellings in the source code, and interactively prompt to fix them."
|
||||
:examples [["./bin/mage codespell-interactive"]]
|
||||
:requires [[mage.codespell]]
|
||||
:task (task! (mage.codespell/codespell-interactive (:arguments parsed)))}
|
||||
|
||||
start-maildev
|
||||
{:doc "Start Maildev"
|
||||
:examples [["./bin/mage start-maildev -h" "print help for start-maildev"]]
|
||||
@@ -192,6 +204,20 @@
|
||||
test)]
|
||||
(nrepl-eval "mb.hawk.core" (str code) (:port options)))}
|
||||
|
||||
affected-modules
|
||||
{:doc "Prints information about which backend modules have changes compared to a Git ref e.g. `master`"
|
||||
:examples [["./bin/mage affected-modules master"]
|
||||
["./bin/mage affected-modules"]]
|
||||
:requires [[mage.modules]]
|
||||
:task (task! (mage.modules/cli-print-affected-modules (:arguments parsed)))}
|
||||
|
||||
can-skip-driver-tests
|
||||
{:doc "Returns zero (success) if we can skip driver tests based on changes related to a Git ref e.g. `master`"
|
||||
:examples [["./bin/mage can-skip-driver-tests master"]
|
||||
["./bin/mage can-skip-driver-tests"]]
|
||||
:requires [[mage.modules]]
|
||||
:task (task! (mage.modules/cli-can-skip-driver-tests (:arguments parsed)))}
|
||||
|
||||
ls {:doc "List all mage public and private tasks"
|
||||
:examples [["./bin/mage ls" "List all tasks"]]
|
||||
:requires [[mage.util :as u]]
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
["target/classes/metabase/request/util.clj"] true
|
||||
;; Both a FE and a BE path
|
||||
["frontend/src/metabase/browse/components/TableBrowser/TableBrowser.jsx:145"
|
||||
"metabase/warehouses/api.clj:178"] true
|
||||
"metabase/warehouses_rest/api.clj:178"] true
|
||||
;; FE-only paths
|
||||
["frontend/src/metabase/components/ActionButton.jsx:31"] false
|
||||
["frontend/src/metabase/entities/collections/forms.js:22"] false
|
||||
|
||||
@@ -20,8 +20,9 @@ const REPLACES_MAP = {
|
||||
"metabase-types": "frontend/src/metabase-types",
|
||||
metabase: "frontend/src/metabase",
|
||||
"embedding-sdk-package": "enterprise/frontend/src/embedding-sdk-package",
|
||||
"embedding-sdk-bundle": "enterprise/frontend/src/embedding-sdk-bundle",
|
||||
"embedding-sdk-shared": "enterprise/frontend/src/embedding-sdk-shared",
|
||||
"embedding-sdk-bundle": "frontend/src/embedding-sdk-bundle",
|
||||
"embedding-sdk-shared": "frontend/src/embedding-sdk-shared",
|
||||
"embedding-sdk-ee": "enterprise/frontend/src/embedding-sdk-ee",
|
||||
cljs: "target/cljs_release",
|
||||
};
|
||||
|
||||
@@ -31,13 +32,15 @@ const API_EXTRACTOR_CONFIG_PATH = path.join(
|
||||
);
|
||||
|
||||
const getLogger = (prefix) => {
|
||||
const verbose = process.env.SDK_FIXUP_VERBOSE_LOGS === "true";
|
||||
return {
|
||||
verbose: (message) => verbose && console.log(`[${prefix}] ${message}`),
|
||||
log: (message) => console.log(`[${prefix}] ${message}`),
|
||||
error: (message) => console.error(`[${prefix}] ${message}`),
|
||||
};
|
||||
};
|
||||
|
||||
const { log } = getLogger("dts fixup");
|
||||
const { log, verbose } = getLogger("dts fixup");
|
||||
|
||||
const getRelativePath = (fromPath, toPath) => {
|
||||
const relativePath = path.relative(path.dirname(fromPath), toPath);
|
||||
@@ -67,7 +70,7 @@ const replaceAliasedImports = (filePath) => {
|
||||
});
|
||||
|
||||
fs.writeFileSync(filePath, fileContent, { encoding: "utf-8" });
|
||||
log(`Edited file: ${filePath}`);
|
||||
verbose(`Edited file: ${filePath}`);
|
||||
};
|
||||
|
||||
const removeUnresolvedReexports = (filePath) => {
|
||||
@@ -94,7 +97,7 @@ const removeUnresolvedReexports = (filePath) => {
|
||||
!fs.existsSync(`${path.resolve(dirPath, target)}.d.ts`);
|
||||
|
||||
if (isUnresolved) {
|
||||
log(`Removing unresolved re-export: ${line}`);
|
||||
verbose(`Removing unresolved re-export: ${line}`);
|
||||
isModified = true;
|
||||
}
|
||||
|
||||
@@ -117,7 +120,7 @@ const fixupTypesAfterCompilation = ({ isWatchMode }) => {
|
||||
removeUnresolvedReexports(filePath);
|
||||
});
|
||||
|
||||
log("Done!");
|
||||
console.log("[dts fixup] Done!");
|
||||
|
||||
if (!isWatchMode) {
|
||||
generateDtsRollup();
|
||||
@@ -176,7 +179,7 @@ const generateDtsRollup = () => {
|
||||
});
|
||||
});
|
||||
|
||||
log("Dts rollup done!");
|
||||
console.log("[dts rollup] Dts rollup done!");
|
||||
};
|
||||
|
||||
const watchFilesAndFixThem = () => {
|
||||
|
||||
42
deps.edn
@@ -74,7 +74,7 @@
|
||||
:exclusions [commons-beanutils/commons-beanutils
|
||||
commons-digester/commons-digester
|
||||
commons-logging/commons-logging]}
|
||||
compojure/compojure {:mvn/version "1.7.1" ; HTTP Routing library built on Ring
|
||||
compojure/compojure {:mvn/version "1.7.2" ; HTTP Routing library built on Ring
|
||||
:exclusions [ring/ring-codec]}
|
||||
crypto-random/crypto-random {:mvn/version "1.2.1"} ; library for generating cryptographically secure random bytes and strings
|
||||
diehard/diehard {:mvn/version "0.12.0"}
|
||||
@@ -85,9 +85,9 @@
|
||||
hiccup/hiccup {:mvn/version "2.0.0"} ; HTML templating
|
||||
inflections/inflections {:mvn/version "0.15.0"} ; Clojure/Script library used for prularizing words
|
||||
instaparse/instaparse {:mvn/version "1.5.0"} ; Make your own parser
|
||||
io.github.camsaul/toucan2 {:mvn/version "1.0.568"}
|
||||
io.github.camsaul/toucan2 {:mvn/version "1.0.569"}
|
||||
io.github.eerohele/pp {#_#_:git/tag "2024-11-13.77" ; super fast pretty-printing library
|
||||
:git/sha "4998a1fdd9d3713d7034d04509e9212204773bf2"
|
||||
:git/sha "ffc4edc482c057cd9412c62f018a41e9140c618b"
|
||||
:git/url "https://github.com/eerohele/pp"}
|
||||
;; TODO: replace back to normal artifact
|
||||
io.github.metabase/macaw {:mvn/version "0.2.29"} ; Parse native SQL queries
|
||||
@@ -108,7 +108,7 @@
|
||||
org.bouncycastle/bcprov-jdk18on]}
|
||||
metabase/throttle {:mvn/version "1.0.2"} ; Tools for throttling access to API endpoints and other code pathways
|
||||
methodical/methodical {:mvn/version "1.0.127"} ; drop-in replacements for Clojure multimethods and adds several advanced features
|
||||
metosin/malli {:mvn/version "0.19.2"} ; Data-driven Schemas for Clojure/Script and babashka
|
||||
metosin/malli {:mvn/version "0.20.0"} ; Data-driven Schemas for Clojure/Script and babashka
|
||||
nano-id/nano-id {:mvn/version "1.1.0"} ; NanoID generator for generating entity_ids
|
||||
net.cgrand/macrovich {:mvn/version "0.2.2"} ; utils for writing macros for both Clojure & ClojureScript
|
||||
net.clojars.wkok/openai-clojure {:mvn/version "0.23.0"
|
||||
@@ -121,15 +121,15 @@
|
||||
net.thisptr/jackson-jq {:mvn/version "1.6.0"} ; Java implementation of the JQ json query language
|
||||
org.apache.commons/commons-compress {:mvn/version "1.28.0"} ; compression utils
|
||||
org.apache.commons/commons-lang3 {:mvn/version "3.18.0"} ; helper methods for working with java.lang stuff
|
||||
org.apache.logging.log4j/log4j-1.2-api {:mvn/version "2.25.1"} ; apache logging framework
|
||||
org.apache.logging.log4j/log4j-api {:mvn/version "2.25.1"} ; add compatibility with log4j 1.2
|
||||
org.apache.logging.log4j/log4j-core {:mvn/version "2.25.1"} ; apache logging framework
|
||||
org.apache.logging.log4j/log4j-jcl {:mvn/version "2.25.1"} ; allows the commons-logging API to work with log4j 2
|
||||
org.apache.logging.log4j/log4j-jul {:mvn/version "2.25.1"} ; java.util.logging (JUL) -> Log4j2 adapter
|
||||
org.apache.logging.log4j/log4j-1.2-api {:mvn/version "2.25.2"} ; apache logging framework
|
||||
org.apache.logging.log4j/log4j-api {:mvn/version "2.25.2"} ; add compatibility with log4j 1.2
|
||||
org.apache.logging.log4j/log4j-core {:mvn/version "2.25.2"} ; apache logging framework
|
||||
org.apache.logging.log4j/log4j-jcl {:mvn/version "2.25.2"} ; allows the commons-logging API to work with log4j 2
|
||||
org.apache.logging.log4j/log4j-jul {:mvn/version "2.25.2"} ; java.util.logging (JUL) -> Log4j2 adapter
|
||||
org.apache.logging.log4j/log4j-layout-template-json
|
||||
{:mvn/version "2.25.1"} ; allows the custom json logging format
|
||||
{:mvn/version "2.25.2"} ; allows the custom json logging format
|
||||
org.apache.logging.log4j/log4j-slf4j2-impl
|
||||
{:mvn/version "2.25.1"} ; allows the slf4j2 API to work with log4j 2
|
||||
{:mvn/version "2.25.2"} ; allows the slf4j2 API to work with log4j 2
|
||||
org.apache.poi/poi {:mvn/version "5.4.1"} ; Work with Office documents (e.g. Excel spreadsheets) -- newer version than one specified by Docjure
|
||||
org.apache.poi/poi-ooxml {:mvn/version "5.4.1"
|
||||
:exclusions [org.bouncycastle/bcpkix-jdk15on
|
||||
@@ -138,10 +138,10 @@
|
||||
org.apache.sshd/sshd-core {:mvn/version "3.0.0-M2" ; ssh tunneling and test server
|
||||
:exclusions [org.slf4j/slf4j-api
|
||||
org.slf4j/jcl-over-slf4j]}
|
||||
org.apache.tika/tika-core {:mvn/version "3.2.2"}
|
||||
org.apache.tika/tika-core {:mvn/version "3.2.3"}
|
||||
org.apache.xmlgraphics/batik-all {:mvn/version "1.19"} ; SVG -> image
|
||||
org.bouncycastle/bcpkix-jdk18on {:mvn/version "1.81"} ; Bouncy Castle crypto library -- explicit version of BC specified to resolve illegal reflective access errors
|
||||
org.bouncycastle/bcprov-jdk18on {:mvn/version "1.81"}
|
||||
org.bouncycastle/bcpkix-jdk18on {:mvn/version "1.82"} ; Bouncy Castle crypto library -- explicit version of BC specified to resolve illegal reflective access errors
|
||||
org.bouncycastle/bcprov-jdk18on {:mvn/version "1.82"}
|
||||
org.clj-commons/claypoole {:mvn/version "1.2.2"} ; Threadpool tools for Clojure
|
||||
org.clj-commons/dirigiste {:mvn/version "1.0.4"} ; Instrumented object pooling
|
||||
org.clj-commons/hickory {:mvn/version "0.7.7" ; Parse HTML into Clojure data structures
|
||||
@@ -180,7 +180,7 @@
|
||||
org.mariadb.jdbc/mariadb-java-client ^:antq/exclude ; 3.x only support jdbc:mariadb, so using 2.x for now
|
||||
{:mvn/version "2.7.10"} ; MySQL/MariaDB driver
|
||||
org.mindrot/jbcrypt {:mvn/version "0.4"} ; Crypto library
|
||||
org.postgresql/postgresql {:mvn/version "42.7.7"} ; Postgres driver
|
||||
org.postgresql/postgresql {:mvn/version "42.7.8"} ; Postgres driver
|
||||
org.slf4j/slf4j-api {:mvn/version "2.0.17"} ; abstraction for logging frameworks -- allows end user to plug in desired logging framework at deployment time
|
||||
org.tcrawley/dynapath {:mvn/version "1.1.0"} ; Dynamically add Jars (e.g. Oracle or Vertica) to classpath
|
||||
org.threeten/threeten-extra {:mvn/version "1.8.0"} ; extra Java 8 java.time classes like DayOfMonth and Quarter
|
||||
@@ -192,8 +192,8 @@
|
||||
redux/redux {:git/url "https://github.com/metabase/redux"
|
||||
:sha "4a37feaf817a2a6b5ef688c927f6fd4375964433"}
|
||||
riddley/riddley {:mvn/version "0.2.0"} ; code walking lib -- used interally by Potemkin, manifold, etc.
|
||||
ring/ring-core {:mvn/version "1.14.2"} ; HTTP abstraction
|
||||
ring/ring-jetty-adapter {:mvn/version "1.14.2" ; Ring adapter for Jetty
|
||||
ring/ring-core {:mvn/version "1.15.3"} ; HTTP abstraction
|
||||
ring/ring-jetty-adapter {:mvn/version "1.15.3" ; Ring adapter for Jetty
|
||||
:exclusions [org.eclipse.jetty/jetty-server
|
||||
org.eclipse.jetty/jetty-unixdomain-server
|
||||
org.eclipse.jetty.ee9/jetty-ee9-servlet
|
||||
@@ -249,7 +249,7 @@
|
||||
{cider/cider-nrepl {:mvn/version "0.58.0"} ; nREPL server for CIDER
|
||||
clj-http-fake/clj-http-fake {:mvn/version "1.0.4"
|
||||
:exclusions [slingshot/slingshot]}
|
||||
clj-kondo/clj-kondo {:mvn/version "2025.07.28"} ; included here mainly to facilitate working on the stuff in `.clj-kondo/src`
|
||||
clj-kondo/clj-kondo {:mvn/version "2025.10.23"} ; included here mainly to facilitate working on the stuff in `.clj-kondo/src`
|
||||
cloverage/cloverage {:mvn/version "1.2.4"}
|
||||
com.clojure-goes-fast/clj-async-profiler
|
||||
{:mvn/version "1.6.2"} ; Enables local profiling and heat map generation
|
||||
@@ -274,7 +274,7 @@
|
||||
:exclusions [org.clojure/clojure
|
||||
org.clojure/clojurescript]} ; lets you write test fixtures that are clear, concise, and easy to maintain (clojure.spec)
|
||||
rewrite-clj/rewrite-clj {:mvn/version "1.2.50"} ; clojure source parsing and rewriting
|
||||
ring/ring-devel {:mvn/version "1.14.2"} ; reload clojure files on the fly
|
||||
ring/ring-devel {:mvn/version "1.15.3"} ; reload clojure files on the fly
|
||||
ring/ring-mock {:mvn/version "0.6.2"} ; creating Ring request maps for testing purposes
|
||||
talltale/talltale {:mvn/version "0.5.14"}} ; generates fake data, useful for prototyping or load testing
|
||||
:extra-paths ["dev/src" "local/src" "test" "test_resources" ".clj-kondo/src" ".clj-kondo/test"]
|
||||
@@ -419,7 +419,7 @@
|
||||
org.clojars.mmb90/cljs-cache {:mvn/version "0.1.4"}
|
||||
refactor-nrepl/refactor-nrepl {:mvn/version "3.11.0"}
|
||||
;; keep it in sync with package.json
|
||||
thheller/shadow-cljs {:mvn/version "3.2.0"}}}
|
||||
thheller/shadow-cljs {:mvn/version "3.2.1"}}}
|
||||
|
||||
;; for local dev -- include the drivers locally with :dev:drivers
|
||||
:drivers
|
||||
@@ -467,7 +467,7 @@
|
||||
;; Use this to only run Kondo against specific files.
|
||||
:kondo
|
||||
{:replace-deps
|
||||
{clj-kondo/clj-kondo {:mvn/version "2025.07.28"}}
|
||||
{clj-kondo/clj-kondo {:mvn/version "2025.10.23"}}
|
||||
|
||||
:main-opts
|
||||
["-m" "clj-kondo.main"]}
|
||||
|
||||
@@ -356,8 +356,8 @@
|
||||
|
||||
([deps module-x module-y]
|
||||
(let [module-x-ns->module-y-ns (->> (external-usages deps module-y)
|
||||
(filter #(= (:module %) module-x))
|
||||
(map (juxt :namespace :depends-on-namespace)))]
|
||||
(filter #(= (:module %) module-x))
|
||||
(map (juxt :namespace :depends-on-namespace)))]
|
||||
(reduce
|
||||
(fn [m [module-x-ns module-y-ns]]
|
||||
(update m module-x-ns (fn [deps]
|
||||
@@ -433,10 +433,10 @@
|
||||
ddiff/minimize)))
|
||||
|
||||
(defn print-kondo-config-diff
|
||||
"Print the diff between how the config would look if regenerated with [[generate-config]] versus how it looks in
|
||||
"Print the diff between how the config would look if regenerated with [[generate-config]] versus how it looks in
|
||||
reality ([[kondo-config]]). Use this to suggest updates to make to the config file."
|
||||
[]
|
||||
(ddiff/pretty-print (kondo-config-diff)))
|
||||
[]
|
||||
(ddiff/pretty-print (kondo-config-diff)))
|
||||
|
||||
(comment
|
||||
(external-usages 'core)
|
||||
@@ -461,12 +461,12 @@
|
||||
([deps module]
|
||||
(all-module-deps-paths deps module (sorted-map) (atom #{}) []))
|
||||
([deps module acc already-seen path]
|
||||
(let [module-deps (module-dependencies deps module)
|
||||
new-deps (remove @already-seen module-deps)
|
||||
acc (into acc
|
||||
(map (fn [dep]
|
||||
[dep path]))
|
||||
new-deps)]
|
||||
(let [module-deps (module-dependencies deps module)
|
||||
new-deps (remove @already-seen module-deps)
|
||||
acc (into acc
|
||||
(map (fn [dep]
|
||||
[dep path]))
|
||||
new-deps)]
|
||||
(swap! already-seen into new-deps)
|
||||
(reduce
|
||||
(fn [acc new-dep]
|
||||
@@ -532,7 +532,46 @@
|
||||
affect `module`."
|
||||
[module]
|
||||
(let [deps (dependencies)
|
||||
all-modules (into (sorted-set) (map :module) deps)]
|
||||
(set/difference
|
||||
all-modules
|
||||
(set (keys (all-module-deps-paths deps module))))))
|
||||
all-modules (into (sorted-set) (map :module) deps)
|
||||
module-deps (set (keys (all-module-deps-paths deps module)))]
|
||||
(printf "Module %s depends on %d/%d (%.1f%%) other modules.\n"
|
||||
module
|
||||
(count module-deps)
|
||||
(count all-modules)
|
||||
(double (* (/ (count module-deps) (count all-modules)) 100)))
|
||||
(flush)
|
||||
(set/difference all-modules module-deps)))
|
||||
|
||||
(defn- simulate-rename
|
||||
"Create a new version of `deps` as they would appear if you renamed namespace(s).
|
||||
|
||||
(simulate-rename (dependencies) '{metabase.users.api metabase.users-rest.api})"
|
||||
([deps old-namespace new-namespace]
|
||||
(for [dep deps]
|
||||
(-> dep
|
||||
(cond-> (= (:namespace dep) old-namespace)
|
||||
(assoc :namespace new-namespace))
|
||||
(update :deps (fn [deps]
|
||||
(for [dep deps]
|
||||
(if (= (:namespace dep) old-namespace)
|
||||
{:namespace new-namespace, :module (module new-namespace)}
|
||||
dep)))))))
|
||||
|
||||
([deps old-namespace->new-namespace]
|
||||
(reduce
|
||||
(fn [deps [old-namespace new-namespace]]
|
||||
(simulate-rename deps old-namespace new-namespace))
|
||||
deps
|
||||
old-namespace->new-namespace)))
|
||||
|
||||
(defn dependencies-eliminated-by-renaming-namespaces
|
||||
"Calculate the set of dependencies of `module` (both explicit and transient) that would be eliminated by renaming
|
||||
`old-namespaces->new-namespaces`.
|
||||
|
||||
(dependencies-eliminated-by-renaming-namespaces 'users '{metabase.users.api metabase.users-rest.api})"
|
||||
[module old-namespace->new-namespace]
|
||||
(let [deps (dependencies)
|
||||
old-module-deps (into (sorted-set) (keys (all-module-deps-paths deps module)))
|
||||
new-deps (simulate-rename deps old-namespace->new-namespace)
|
||||
new-module-deps (into (sorted-set) (keys (all-module-deps-paths new-deps module)))]
|
||||
(set/difference old-module-deps new-module-deps)))
|
||||
|
||||
@@ -3853,7 +3853,7 @@
|
||||
"required" : [ "type", "subject", "body" ]
|
||||
} ]
|
||||
},
|
||||
"metabase.collections.api.DashboardQuestionCandidate" : {
|
||||
"metabase.collections-rest.api.DashboardQuestionCandidate" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"description" : {
|
||||
@@ -3885,13 +3885,13 @@
|
||||
},
|
||||
"required" : [ "id", "name", "sole_dashboard_info" ]
|
||||
},
|
||||
"metabase.collections.api.DashboardQuestionCandidatesResponse" : {
|
||||
"metabase.collections-rest.api.DashboardQuestionCandidatesResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"data" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"$ref" : "#/components/schemas/metabase.collections.api.DashboardQuestionCandidate"
|
||||
"$ref" : "#/components/schemas/metabase.collections-rest.api.DashboardQuestionCandidate"
|
||||
}
|
||||
},
|
||||
"total" : {
|
||||
@@ -3900,7 +3900,7 @@
|
||||
},
|
||||
"required" : [ "data", "total" ]
|
||||
},
|
||||
"metabase.collections.api.MoveDashboardQuestionCandidatesResponse" : {
|
||||
"metabase.collections-rest.api.MoveDashboardQuestionCandidatesResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"moved" : {
|
||||
@@ -9211,7 +9211,7 @@
|
||||
"type" : "string",
|
||||
"enum" : [ "events" ]
|
||||
},
|
||||
"metabase.users.api.user-group-membership" : {
|
||||
"metabase.users-rest.api.user-group-membership" : {
|
||||
"description" : "Group Membership info of a User.\n In which :is_group_manager is only included if `advanced-permissions` is enabled.",
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
@@ -9267,12 +9267,12 @@
|
||||
},
|
||||
"required" : [ "schedule_type" ]
|
||||
},
|
||||
"metabase.warehouse-schema.api.table.data-authority-write" : {
|
||||
"metabase.warehouse-schema-rest.api.table.data-authority-write" : {
|
||||
"description" : "Schema for writing a valid table data authority.",
|
||||
"type" : "string",
|
||||
"enum" : [ "computed", "authoritative", "unconfigured", "ingested" ]
|
||||
},
|
||||
"metabase.warehouses.api.available-settings" : {
|
||||
"metabase.warehouses-rest.api.available-settings" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : {
|
||||
"type" : "object",
|
||||
@@ -12530,7 +12530,7 @@
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/metabase.collections.api.DashboardQuestionCandidatesResponse"
|
||||
"$ref" : "#/components/schemas/metabase.collections-rest.api.DashboardQuestionCandidatesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12656,7 +12656,7 @@
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/metabase.collections.api.MoveDashboardQuestionCandidatesResponse"
|
||||
"$ref" : "#/components/schemas/metabase.collections-rest.api.MoveDashboardQuestionCandidatesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12914,7 +12914,7 @@
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/metabase.collections.api.DashboardQuestionCandidatesResponse"
|
||||
"$ref" : "#/components/schemas/metabase.collections-rest.api.DashboardQuestionCandidatesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13049,7 +13049,7 @@
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/metabase.collections.api.MoveDashboardQuestionCandidatesResponse"
|
||||
"$ref" : "#/components/schemas/metabase.collections-rest.api.MoveDashboardQuestionCandidatesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15119,7 +15119,7 @@
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"settings" : {
|
||||
"$ref" : "#/components/schemas/metabase.warehouses.api.available-settings"
|
||||
"$ref" : "#/components/schemas/metabase.warehouses-rest.api.available-settings"
|
||||
}
|
||||
},
|
||||
"required" : [ "settings" ]
|
||||
@@ -16252,7 +16252,7 @@
|
||||
"required" : true,
|
||||
"schema" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "metabase-ai", "python-execution" ]
|
||||
"enum" : [ "metabase-ai", "metabase-ai-tiered", "python-execution" ]
|
||||
}
|
||||
} ],
|
||||
"responses" : {
|
||||
@@ -16272,6 +16272,9 @@
|
||||
"schema" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"quantity" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
"terms_of_service" : {
|
||||
"type" : "boolean"
|
||||
}
|
||||
@@ -20141,7 +20144,7 @@
|
||||
},
|
||||
"remote-sync-type" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "production", "development" ]
|
||||
"enum" : [ "read-only", "read-write" ]
|
||||
},
|
||||
"remote-sync-url" : {
|
||||
"type" : "string"
|
||||
@@ -28674,25 +28677,6 @@
|
||||
"tags" : [ "/api/setup" ]
|
||||
}
|
||||
},
|
||||
"/api/setup/user_defaults" : {
|
||||
"get" : {
|
||||
"summary" : "GET /api/setup/user_defaults",
|
||||
"description" : "Returns object containing default user details for initial setup, if configured,\n and if the provided token value matches the token in the configuration value.",
|
||||
"parameters" : [ ],
|
||||
"responses" : {
|
||||
"2XX" : {
|
||||
"description" : "Successful response"
|
||||
},
|
||||
"4XX" : {
|
||||
"description" : "Client error response"
|
||||
},
|
||||
"5XX" : {
|
||||
"description" : "Server error response"
|
||||
}
|
||||
},
|
||||
"tags" : [ "/api/setup" ]
|
||||
}
|
||||
},
|
||||
"/api/slack/bug-report" : {
|
||||
"post" : {
|
||||
"summary" : "POST /api/slack/bug-report",
|
||||
@@ -28830,7 +28814,7 @@
|
||||
"enum" : [ "technical", "hidden", "cruft" ]
|
||||
},
|
||||
"data_authority" : {
|
||||
"$ref" : "#/components/schemas/metabase.warehouse-schema.api.table.data-authority-write"
|
||||
"$ref" : "#/components/schemas/metabase.warehouse-schema-rest.api.table.data-authority-write"
|
||||
},
|
||||
"show_in_getting_started" : {
|
||||
"type" : "boolean"
|
||||
@@ -28993,7 +28977,7 @@
|
||||
"enum" : [ "technical", "hidden", "cruft" ]
|
||||
},
|
||||
"data_authority" : {
|
||||
"$ref" : "#/components/schemas/metabase.warehouse-schema.api.table.data-authority-write"
|
||||
"$ref" : "#/components/schemas/metabase.warehouse-schema-rest.api.table.data-authority-write"
|
||||
},
|
||||
"show_in_getting_started" : {
|
||||
"type" : "boolean"
|
||||
@@ -30489,7 +30473,7 @@
|
||||
"user_group_memberships" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"$ref" : "#/components/schemas/metabase.users.api.user-group-membership"
|
||||
"$ref" : "#/components/schemas/metabase.users-rest.api.user-group-membership"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -30627,7 +30611,7 @@
|
||||
"user_group_memberships" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"$ref" : "#/components/schemas/metabase.users.api.user-group-membership"
|
||||
"$ref" : "#/components/schemas/metabase.users-rest.api.user-group-membership"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ For example, say you're running two Metabase instances, A and B, that both use t
|
||||
|
||||
Then the number of user accounts would total 8 for that day.
|
||||
|
||||
If you're on Metabase Cloud, counting active users works the same: each day we count the maxium number of user accounts at any one time over the previous 36 hours.
|
||||
If you're on Metabase Cloud, counting active users works the same: each day we count the maximum number of user accounts at any one time over the previous 36 hours.
|
||||
|
||||
We refresh the user count you see in your [Metabase Store account page](https://store.metabase.com) every day. Since the refresh only happens once a day, there might be a delay between when you adjust the number of user accounts in your Metabase and when your accounts sync with your Store page.
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ If you're migrating from a Metabase Cloud Pro plan to a self-hosted Pro plan, yo
|
||||
|
||||
2. **Download the snapshot from Metabase Store**. The email from Metabase Success Engineering will have a link to your download. You'll need to log in to the Metabase Store to access the download.
|
||||
|
||||
The snapshot of your Metabase Cloud application database will be an H2 file database: a file with the extension `.mv.db`. (Metabase Cloud instances use PostgresSQL for application databases, but snapshots are saved in H2 format, so that you can restore to either PostgresSQL or MysQL application databases).
|
||||
The snapshot of your Metabase Cloud application database will be an H2 file database: a file with the extension `.mv.db`. (Metabase Cloud instances use PostgreSQL for application databases, but snapshots are saved in H2 format, so that you can restore to either PostgreSQL or MysQL application databases).
|
||||
|
||||
Snapshots expire quickly, but don't sweat it; if your snapshot has expired, just request a new one.
|
||||
|
||||
|
||||
@@ -209,6 +209,7 @@ config:
|
||||
not-behind-proxy: false
|
||||
notification-link-base-url: null
|
||||
notification-system-event-thread-pool-size: 5
|
||||
notification-temp-file-size-max-bytes: 10485760
|
||||
notification-thread-pool-size: 3
|
||||
persisted-model-refresh-cron-schedule: 0 0 0/6 * * ? *
|
||||
persisted-models-enabled: false
|
||||
|
||||
@@ -1329,7 +1329,7 @@ Force all traffic to use HTTPS via a redirect, if the site URL is HTTPS.
|
||||
- Default: `false`
|
||||
- [Configuration file name](./config-file.md): `remote-sync-auto-import`
|
||||
|
||||
Whether to automatically import from the remote git repository. Only applies if remote-sync-type is :production.
|
||||
Whether to automatically import from the remote git repository. Only applies if remote-sync-type is :read-only.
|
||||
|
||||
### `MB_REMOTE_SYNC_AUTO_IMPORT_RATE`
|
||||
|
||||
@@ -1337,7 +1337,7 @@ Whether to automatically import from the remote git repository. Only applies if
|
||||
- Default: `5`
|
||||
- [Configuration file name](./config-file.md): `remote-sync-auto-import-rate`
|
||||
|
||||
If remote-sync-type is :production and remote-sync-auto-import is true, the rate (in minutes) at which to check for updates to import. Defaults to 5.
|
||||
If remote-sync-type is :read-only and remote-sync-auto-import is true, the rate (in minutes) at which to check for updates to import. Defaults to 5.
|
||||
|
||||
### `MB_REMOTE_SYNC_TASK_TIME_LIMIT_MS`
|
||||
|
||||
@@ -1599,7 +1599,7 @@ Used for encrypting and checking whether SDK requests are signed.
|
||||
- Default: `null`
|
||||
|
||||
When using the appdb engine against postgresql, override the language used for stemming in to_tsvector.
|
||||
Value must be a valid configured langauge option in your database such as 'english' or 'simple'.
|
||||
Value must be a valid configured language option in your database such as 'english' or 'simple'.
|
||||
|
||||
### `MB_SEARCH_TYPEAHEAD_ENABLED`
|
||||
|
||||
|
||||
@@ -139,4 +139,4 @@ This setting doesn't affect [SQL queries](../questions/native-editor/writing-sql
|
||||
|
||||
## Localizing embedded Metabase
|
||||
|
||||
You can tranlate both Metabase UI elements (like button labels) and content (like dashboard names) in static embeds. See [Setting the language for static embeds](../embedding/static-embedding-parameters.md#setting-the-language-for-a-static-embed) and [Translating embedded dashboards and questions](../embedding/translations.md).
|
||||
You can translate both Metabase UI elements (like button labels) and content (like dashboard names) in static embeds. See [Setting the language for static embeds](../embedding/static-embedding-parameters.md#setting-the-language-for-a-static-embed) and [Translating embedded dashboards and questions](../embedding/translations.md).
|
||||
|
||||
@@ -55,7 +55,7 @@ If performance degrades, or you'd rather keep the JSON contained in the original
|
||||
4. Select the database that contains the field you want to update.
|
||||
5. Select the table that contains the field.
|
||||
6. Select the field containing the original JSON
|
||||
7. In the **Behavior** section, locate the **Unfold JSON** option and select **Yes** or **No**. If the column was unfolded, Metabase will have hidden this JSON columnn from view, so if you want the JSON column to be visible again, you'll need to change the column's visibility to **Everywhere**.
|
||||
7. In the **Behavior** section, locate the **Unfold JSON** option and select **Yes** or **No**. If the column was unfolded, Metabase will have hidden this JSON column from view, so if you want the JSON column to be visible again, you'll need to change the column's visibility to **Everywhere**.
|
||||
8. Scroll to the top and click the **Filter values** button and select **Re-scan this field**.
|
||||
|
||||

|
||||
|
||||
@@ -65,7 +65,7 @@ You'll add an ampersand (`&`) to separate each parameter. For example, In the **
|
||||
sslmode=verify-full&sslrootcert=/path/to/certificate.pem
|
||||
```
|
||||
|
||||
Replace `/path/to/certifcate.pem` with the full path for the certificate you downloaded from your provider.
|
||||
Replace `/path/to/certificate.pem` with the full path for the certificate you downloaded from your provider.
|
||||
|
||||
You can learn more about [SSL support for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ GRANT analytics TO metabase;
|
||||
-- GRANT SELECT ON "your_table" IN SCHEMA "your_schema" TO analytics;
|
||||
```
|
||||
|
||||
Depending on how you use Metabase, you can also additonally grant:
|
||||
Depending on how you use Metabase, you can also additionally grant:
|
||||
|
||||
- `TEMPORARY` privileges to create temp tables.
|
||||
- `EXECUTE` privileges to use stored procedures or user-defined functions.
|
||||
|
||||
@@ -79,7 +79,7 @@ title: API changelog
|
||||
- `POST /api/user/:id/send_invite` has been removed.
|
||||
- `GET /:id/fields` now includes the Table ID.
|
||||
|
||||
- APIs under `/api/pulse` and `/api/alert` will be removed in a future version as we're tranitioning to a new architecture.
|
||||
- APIs under `/api/pulse` and `/api/alert` will be removed in a future version as we're transitioning to a new architecture.
|
||||
|
||||
## Metabase 0.51.0
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ several properties that can be set as environment variables or in a `deps.edn`.
|
||||
"-Dmb.db.pass="]}}}
|
||||
```
|
||||
|
||||
You could also pass a full conection string in as the `mb.db.connection.uri`:
|
||||
You could also pass a full connection string in as the `mb.db.connection.uri`:
|
||||
|
||||
```
|
||||
"-Dmb.db.connection.uri=postgres://<user>:<password>@localhost:5432/<dbname>"
|
||||
|
||||
@@ -157,13 +157,13 @@ title: Driver interface changelog
|
||||
|
||||
## Metabase 0.54.11
|
||||
|
||||
- The multimethods `metabase.driver.sql-jdbc.sync.interface/active-tables` and `metabase.driver.sql-jdbc.sync.interface/filtered-syncable-schemas`, aswell as the functions
|
||||
- The multimethods `metabase.driver.sql-jdbc.sync.interface/active-tables` and `metabase.driver.sql-jdbc.sync.interface/filtered-syncable-schemas`, as well as the functions
|
||||
`metabase.driver.sql-jdbc.sync.describe_database/fast-active-tables`, `metabase.driver.sql-jdbc.sync.describe_database/have-select-privilege-fn` and `metabase.driver.sql-jdbc.sync.describe_database/db-tables` now take a database spec instead of a `java.sql.Connection` object.
|
||||
|
||||
## Metabase 0.54.10
|
||||
|
||||
- Add `metabase.driver/table-known-to-not-exist?` for drivers to test if an exception is due to a query on a table that no longer exists
|
||||
- Add `metabase.driver.sql-jdbc/impl-table-known-to-not-exist?` for JDBC drivers. This is the implemenation of table-known-to-not-exist for jdbc and allows testing directly against `java.sql.SQLException` throwables without worrying about the exception cause chain.
|
||||
- Add `metabase.driver.sql-jdbc/impl-table-known-to-not-exist?` for JDBC drivers. This is the implementation of table-known-to-not-exist for jdbc and allows testing directly against `java.sql.SQLException` throwables without worrying about the exception cause chain.
|
||||
|
||||
## Metabase 0.54.0
|
||||
|
||||
@@ -195,7 +195,7 @@ title: Driver interface changelog
|
||||
## Metabase 0.53.12
|
||||
|
||||
- Add `metabase.driver/query-canceled?` for drivers to test if an exception is due to a query being canceled due to user action
|
||||
- Add `metabase.driver.sql-jdbc/impl-query-canceled?` for JDBC drivers. This is the implemenation of query-canceled for jdbc and allows testing directly against `java.sql.SQLException` throwables without worrying about the exception cause chain.
|
||||
- Add `metabase.driver.sql-jdbc/impl-query-canceled?` for JDBC drivers. This is the implementation of query-canceled for jdbc and allows testing directly against `java.sql.SQLException` throwables without worrying about the exception cause chain.
|
||||
|
||||
## Metabase 0.53.10
|
||||
|
||||
@@ -641,7 +641,7 @@ title: Driver interface changelog
|
||||
to be assigned to specific database roles which are set before any queries are executed, so that access to tables can
|
||||
be restricted at the database level instead of (or in conjunction with) Metabase's built-in permissions system.
|
||||
|
||||
- The multimethod `metabase.driver.sql-jdbc.sync.describe-table/get-table-pks` is changed to return a vector instea
|
||||
- The multimethod `metabase.driver.sql-jdbc.sync.describe-table/get-table-pks` is changed to return a vector instead
|
||||
of a set.
|
||||
|
||||
- The function `metabase.query-processor.timezone/report-timezone-id-if-supported` has been updated to take an additional
|
||||
@@ -839,8 +839,8 @@ differences between the library versions.
|
||||
|
||||
**Note: we expect these breaking changes to be fixed before 0.46.0 ships. This will be updated if they are.**
|
||||
|
||||
The classes `metabase.util.honeysql_extensions.Identifer` and `metabase.util.honeysql_extensions.TypedHoneySQLForm`
|
||||
have been moved to `metabase.util.honey_sql_1.Identifer` and `metabase.util.honey_sql_1.TypedHoneySQLForm`,
|
||||
The classes `metabase.util.honeysql_extensions.Identifier` and `metabase.util.honeysql_extensions.TypedHoneySQLForm`
|
||||
have been moved to `metabase.util.honey_sql_1.Identifier` and `metabase.util.honey_sql_1.TypedHoneySQLForm`,
|
||||
respectively. On the off chance that your driver directly referencing these class names, you may need to update things
|
||||
to use the new class names.
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ You'll almost certainly be running your database in a local Docker container. Ra
|
||||
|
||||
Tells Metabase to look for the environment variable `MB_MYSQL_TEST_USER`; if not found, default to `"root"`. The name of the environment variable follows the pattern `MB_<driver>_TEST_<property>`, as passed into the function as first and second args, respectively. You don't need to specify a default value for `tx/db-test-env-var`; perhaps `user` is an optional parameter; and if `MB_MYSQL_TEST_USER` isn't specified, you don't need to specify it in the connection details.
|
||||
|
||||
But what about properties you want to require, but do not have sane defaults? In those cases, you can use `tx/db-test-env-var-or-throw`. It the corresponding enviornment variable isn't set, these will throw an Exception, ultimately causing tests to fail.
|
||||
But what about properties you want to require, but do not have sane defaults? In those cases, you can use `tx/db-test-env-var-or-throw`. It the corresponding environment variable isn't set, these will throw an Exception, ultimately causing tests to fail.
|
||||
|
||||
```clj
|
||||
;; If MB_SQLSERVER_TEST_USER is unset, the test suite will quit with a message saying something like
|
||||
@@ -190,7 +190,7 @@ Besides `tx/db-test-env-var`, `metabase.test.data.interface` has several other h
|
||||
|
||||
There's a few other things Metabase needs to know when comparing test results. For example, different databases name tables and columns in different ways; methods exist to let Metabase know it should expect something like the `venues` table in the `test-data` Database Definition to come back as `VENUES` for a database that uppercases everything. (We consider such minor variations in naming to still mean the same thing.) Take a look at `tx/format-name` and other methods like that and see which ones you need to implement.
|
||||
|
||||
## What about DBMSes that don't let you create new databases programatically?
|
||||
## What about DBMSes that don't let you create new databases programmatically?
|
||||
|
||||
This is actually a common problem, and luckily we have figured out how to work around it. The solution is usually something like using different _schemas_ in place of different databases, or prefixing table names with the database name, and creating everything in the same database. For SQL-based databases, you can implement `sql.tx/qualified-name-components` to have tests use a different identifier instead of what they would normally use, for example `"shared_db"."test-data_venues".id` instead of `"test-data".venues.id`. The SQL Server and Oracle test extensions are good examples of such black magic in action.
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ driver:
|
||||
|
||||
# Whether loading this driver can be deferred until the first
|
||||
# attempt to connect to a database of this type. Default: true. Only
|
||||
# set this to false if absolutely neccesary.
|
||||
# set this to false if absolutely necessary.
|
||||
lazy-load: true
|
||||
|
||||
# Parent driver, if any.
|
||||
|
||||
@@ -4,7 +4,7 @@ title: End-to-end tests with Cypress
|
||||
|
||||
# End-to-end tests with Cypress
|
||||
|
||||
Metabase uses Cypress for “end-to-end testing”, that is, tests that are executed against the application as a whole, including the frontend, backend, and application database. These tests are essentially scripts written in JavaScript that run in the web browser: visit different URLs, click various UI elements, type text, and assert that things happen as expected (for example, an element appearing on screen, or a network request occuring).
|
||||
Metabase uses Cypress for “end-to-end testing”, that is, tests that are executed against the application as a whole, including the frontend, backend, and application database. These tests are essentially scripts written in JavaScript that run in the web browser: visit different URLs, click various UI elements, type text, and assert that things happen as expected (for example, an element appearing on screen, or a network request occurring).
|
||||
|
||||
_Please, get familiar with the [Cypress best practices](https://docs.cypress.io/app/core-concepts/best-practices) before you proceed._
|
||||
|
||||
@@ -155,17 +155,15 @@ export MB_SNOWPLOW_URL=http://localhost:9090
|
||||
|
||||
We have a few helpers for dealing with tests involving snowplow
|
||||
|
||||
1. You can use `describeWithSnowplow` (or `describeWithSnowplowEE` for EE edition) method to define tests that only
|
||||
run when a Snowplow instance is running
|
||||
1. Use `resetSnowplow()` test helper before each test to clear the queue of processed events.
|
||||
1. Use `expectSnowplowEvent({ ...payload }, count=n)` to assert that exactly `count` snowplow events match (partially)
|
||||
2. Use `expectSnowplowEvent({ ...payload }, count=n)` to assert that exactly `count` snowplow events match (partially)
|
||||
the payload provided (count defaults to 1)
|
||||
1. Use `expectUnstructuredSnowplowEvent` to assert that exactly `count` snowplow events are unstructured events that
|
||||
3. Use `expectUnstructuredSnowplowEvent` to assert that exactly `count` snowplow events are unstructured events that
|
||||
partial-match the payload provided. This is simply a convenience function for comparing
|
||||
`event.unstruct_event.data.data` rather than the entire `event`. Most of our events are unstructured events, so this is handy.
|
||||
1. Use `assertNoUnstructuredSnowplowEvent({ ...eventData })` is the inverse of `expectUnstructuredSnowplowEvent`, and asserts that
|
||||
4. Use `assertNoUnstructuredSnowplowEvent({ ...eventData })` is the inverse of `expectUnstructuredSnowplowEvent`, and asserts that
|
||||
_no_ unstructured events match the payload.
|
||||
1. Use `expectNoBadSnowplowEvents()` after each test to assert that no invalid events have been sent.
|
||||
5. Use `expectNoBadSnowplowEvents()` after each test to assert that no invalid events have been sent.
|
||||
|
||||
### Running tests that require SMTP server
|
||||
|
||||
@@ -175,6 +173,10 @@ Some of our tests depend on the email being set up, and require a local SMTP ser
|
||||
docker run -d -p 1080:1080 -p 1025:1025 maildev/maildev:latest
|
||||
```
|
||||
|
||||
### Running tests that require translation dictionaries
|
||||
|
||||
Some of the tests are checking content translation functionality. These tests require to run `./bin/i18n/build-translation-resources` command before running the tests to precompile JSON files with translations.
|
||||
|
||||
### Cypress comes with `Lodash` for free
|
||||
|
||||
We don't need to have [Lodash](https://lodash.com/) in our direct dependencies to be able to [use it with Cypress](https://docs.cypress.io/api/utilities/_). It is aliased with an underscore and its methods can be accessed with `Cypress._.method()`. We can use `_.times` method to stress-test a certain test (or a set of tests) locally.
|
||||
|
||||
@@ -128,7 +128,6 @@ For the most part we follow the [Airbnb React/JSX Style Guide](https://github.co
|
||||
- Prefer React [function components over class components](https://reactjs.org/docs/components-and-props.html#function-and-class-components)
|
||||
- Avoid creating new components within the `containers` folder, as this approach has been deprecated. Instead, store both connected and view components in the `components` folder for a more unified and efficient organization. If a connected component grows substantially in size and you need to extract a view component, opt for using the `View` suffix.
|
||||
- For control components, typically we use `value` and `onChange`. Controls that have options (e.x. `Radio`, `Select`) usually take an `options` array of objects with `name` and `value` properties.
|
||||
- Components named like `FooModal` and `FooPopover` typically refer to the modal/popover _content_ which should be used inside a `Modal`/`ModalWithTrigger` or `Popover`/`PopoverWithTrigger`
|
||||
- Components named like `FooWidget` typically include a `FooPopover` inside a `PopoverWithTrigger` with some sort of trigger element, often `FooName`
|
||||
|
||||
- Use arrow function instance properties if you need to bind a method in a class (instead of `this.method = this.method.bind(this);` in the constructor), but only if the function needs to be bound (e.x. if you're passing it as a prop to a React component)
|
||||
|
||||
@@ -4,9 +4,7 @@ title: Metabase release versioning
|
||||
|
||||
# Metabase release versioning
|
||||
|
||||
We follow our own flavor of the [semantic versioning guidelines](https://semver.org/) in order to distinguish the [open-source version](https://www.metabase.com/product/starter) of Metabase from the paid, source-available version of Metabase (available in the [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans).
|
||||
|
||||
Semantic versioning typically follows the format: `Major.Minor.Point.Patch`. For example, version `3.15.2` or `3.15.2.1`.
|
||||
We follow our own flavor of the [semantic versioning guidelines](https://semver.org/). Standard semantic versioning uses `Major.Minor.Patch` format, but we've adapted it to distinguish the [open-source version](https://www.metabase.com/product/starter) of Metabase from the paid, source-available version of Metabase (available in the [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans).
|
||||
|
||||
With Metabase releases, we prefix the version with a `0` or `1`, depending on the license.
|
||||
|
||||
@@ -16,13 +14,13 @@ With Metabase releases, we prefix the version with a `0` or `1`, depending on th
|
||||
License.Major.Point.Hotfix
|
||||
```
|
||||
|
||||
E.g.,
|
||||
For example:
|
||||
|
||||
```
|
||||
v0.46.3.1
|
||||
v0.57.3.1
|
||||
```
|
||||
|
||||
`v0.46.3.1` would be for a hotfix (`1`) for the third (`3`) point release of Metabase `46`, the open-source edition (`0`).
|
||||
`v0.57.3.1` would be for a hotfix (`1`) for the third (`3`) point release of Metabase `57`, the open-source edition (`0`).
|
||||
|
||||
### License
|
||||
|
||||
@@ -31,33 +29,39 @@ v0.46.3.1
|
||||
|
||||
### Major
|
||||
|
||||
We release major version when we introduce new features or breaking changes.
|
||||
We release major versions when we introduce new features or breaking changes. See [release announcements](https://www.metabase.com/releases).
|
||||
|
||||
### Point
|
||||
|
||||
Sometimes called a minor release, we issue point releases when we add bug fixes and refinements to existing features.
|
||||
Sometimes called a minor release, we issue point releases when we add bug fixes and refinements to existing features. See the [changelog](https://www.metabase.com/changelog)
|
||||
|
||||
### Hotfix
|
||||
|
||||
Sometimes called a patch release, we issue these hotfix releases to fix security issues in a timely manner, or to undo a horrific regression.
|
||||
Sometimes called a patch release, we issue these releases nightly so we can fix bugs or security issues as soon as we can.
|
||||
|
||||
You'll see releases ending as `.x` in [releases on GitHub](https://github.com/metabase/metabase/releases). These are placeholder links. The actual link will go to the latest hotfix. For example:
|
||||
|
||||
- `metabase/metabase:v0.57.2.x` will automatically pull the latest patch version in for the latest minor version (like `v0.57.2.1`, `v0.57.2.2`, etc.).
|
||||
|
||||
## Other release terms
|
||||
|
||||
### The Gold Release
|
||||
### The beta release
|
||||
|
||||
The gold release is the first release of a new major version of Metabase. So for Metabase version 46, the gold releases would be:
|
||||
The beta release is the beta release of a new major version of Metabase. So for Metabase version 57, the gold releases would be:
|
||||
|
||||
- `v0.46.0` (the OSS version)
|
||||
- `v1.46.0` (the EE version)
|
||||
- `v0.57.0` (the OSS version)
|
||||
- `v1.57.0` (the EE version)
|
||||
|
||||
### Release Candidates
|
||||
### The gold release
|
||||
|
||||
We usually publish release candidates to kick the tires on new features before releasing a new major version (a gold release). To distinguish these release candidates, we append an `-RC#` at the end.
|
||||
The gold release is the first stable release of a new major version of Metabase. So for Metabase version 57, the gold releases would be:
|
||||
|
||||
E.g.,
|
||||
- `v0.57.1` (the OSS version)
|
||||
- `v1.57.1` (the EE version)
|
||||
|
||||
- `v1.46.0-RC1` (the first release candidate for the EE version)
|
||||
- `v0.46.0-RC3` (the third release candidate for the OSS version)
|
||||
### Cloud builds
|
||||
|
||||
If you see a version followed by an `-X`, like `v1.56.2-X01`, these are version numbers related to builds for customers on [Metabase Cloud](https://www.metabase.com/cloud/).
|
||||
|
||||
## Further reading
|
||||
|
||||
|
||||
BIN
docs/documents/images/supporting-text.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
@@ -5,7 +5,7 @@ summary: Create documents to combine text and charts for long-form analysis. Add
|
||||
|
||||
# Documents
|
||||
|
||||
{% include plans-blockquote.html feature="Documents" %}
|
||||
{% include plans-blockquote.html feature="Documents" is_plural=true%}
|
||||
|
||||

|
||||
|
||||
@@ -23,35 +23,52 @@ Documents let you tell a story with your data by combining text, charts, and oth
|
||||
Type `/` in your document to see available commands:
|
||||
|
||||
- **Ask Metabot** to chat with [Metabot](../ai/metabot.md) in the document.
|
||||
- **Chart** to insert a table or chart from an existing question or model. You can't add dashboards or other documents, but you can mention them (see the next section).
|
||||
- **Link**. You can mention other items in your Metabase.
|
||||
- **Chart** to insert a table or chart from an existing question or model, or create a new question directly in the document. You can't add dashboards or other documents, but you can mention them (see the next section).
|
||||
- **Link** to mention other items in your Metabase.
|
||||
|
||||
## Mention items with `@`
|
||||
|
||||
You can type `@` to mention items from your Metabase. (The mentions get a fancy little icon next to them).
|
||||
You can type `@` to mention items from your Metabase (mentions get a fancy little icon next to them).
|
||||
|
||||
## Add charts to documents
|
||||
|
||||
You can add [questions](../questions/start.md) and [models](../data-modeling/models.md) to a document.
|
||||
You can add [questions](../questions/start.md) and [models](../data-modeling/models.md) to a document. You can either insert existing questions from your collections or create new questions directly in the document.
|
||||
|
||||
### Creating new questions in documents
|
||||
|
||||
To create a new question directly in a document:
|
||||
|
||||
1. Type `/` to open the command menu.
|
||||
2. Select **Chart**.
|
||||
3. Select **New chart**.
|
||||
4. Choose either:
|
||||
- **New Question** to use the [query builder](../questions/query-builder/editor.md).
|
||||
- **New SQL query** to use the [native SQL editor](../questions/native-editor/writing-sql.md).
|
||||
5. Build your question and click **Save and use** to add it to the document.
|
||||
|
||||
Questions created this way are saved directly to the document and don't appear in your collections unless you explicitly save them there later. Deleting charts made in documents is permanent. You can't recover deleted charts from the [trash](../exploration-and-organization/delete-and-restore.md).
|
||||
|
||||
To enter the full-screen editor for a question, click on the question's title.
|
||||
|
||||
### Adding a chart copies it to the document
|
||||
|
||||
When you add a chart from a collection, Metabase creates a copy in the document. The original question remains in the collection. Changes to one copy won't affect the other; they're independent.
|
||||
|
||||
If however, you delete the document's copy, that copy is permanently deleted. It can't be recovered from the [trash](../exploration-and-organization/delete-and-restore.md).
|
||||
If you delete the document's copy, that copy is permanently deleted and can't be recovered from the [trash](../exploration-and-organization/delete-and-restore.md).
|
||||
|
||||
### Charts generated by Metabot only exist in the document
|
||||
### Charts created in documents only exist in the document
|
||||
|
||||
If [Metabot](../ai/metabot.md) generates a chart, Metabase saves that question to the document itself. If you delete it, it's gone forever. You won't be able to recover the question from the trash.
|
||||
Questions that are created directly in the document (either through [Metabot](../ai/metabot.md) or by creating a new question via the Chart command) are saved to the document itself and don't appear in your collections. If you delete these questions from the document, they're permanently deleted and can't be recovered from the trash.
|
||||
|
||||
## Editing charts
|
||||
|
||||
To edit a chart, click the **three-dot** menu in the chart's upper right. You can:
|
||||
|
||||
- Edit visualization
|
||||
- Edit query
|
||||
- Replace the chart with another question or model
|
||||
- [Download results](../questions/exporting-results.md#exporting-results-from-document-cards)
|
||||
- **Add supporting text** to provide context next to the chart
|
||||
- **Edit visualization** to change how the chart displays data
|
||||
- **Edit query** to modify the underlying question
|
||||
- **Replace** the chart with another question or model
|
||||
- [**Download results**](../questions/exporting-results.md#exporting-results-from-document-cards)
|
||||
|
||||
Edits only affect the chart in the document. If you added the chart from a collection, Metabase will have created a copy of the chart. Any changes made to the chart in the document won't affect the original question.
|
||||
|
||||
@@ -61,16 +78,31 @@ Edits only affect the chart in the document. If you added the chart from a colle
|
||||
|
||||
To rearrange charts, simply click and hold on the chart and drag it to where you want it in the document.
|
||||
|
||||
You can put up to three charts side by side (more than three charts side-by-side gets crowded). To resize charts, you can click on the edges to adjust their size.
|
||||
|
||||
When there are multiple charts in the same row, you can resize them horizontally by clicking between the charts and sliding left or right to resize.
|
||||
You can put up to three items side by side in a group. This includes both charts and supporting text blocks. When you have multiple items in the same row, you can resize them horizontally by clicking between the items and sliding left or right to resize.
|
||||
|
||||
To resize a chart's height, click the bottom of the chart and resize the chart up or down.
|
||||
|
||||
For now, you can only put charts side-by-side (not text elements).
|
||||
|
||||
After arranging your charts, save your changes!
|
||||
|
||||
## Adding supporting text to charts
|
||||
|
||||

|
||||
|
||||
You can add a supporting text block next to charts to provide context.
|
||||
|
||||
To add supporting text to a chart:
|
||||
|
||||
1. Hover over the chart.
|
||||
2. Click the **three-dot** menu in the chart's upper right.
|
||||
3. Select **Add supporting text**.
|
||||
4. Type your text in the supporting text block.
|
||||
|
||||
You can only have one supporting text block per group of charts. A group can contain up to three items total (charts and supporting text block combined).
|
||||
|
||||
You can drag and drop a supporting text block to reorder them within a group of charts. You can only move supporting text within its current group; you can't drag it out to create a standalone text block.
|
||||
|
||||
Supporting text blocks support the same Markdown formatting as the rest of the document, including headings, lists, blockquotes, and code blocks. But you can't add charts in supporting text.
|
||||
|
||||
## Formatting text
|
||||
|
||||
Documents support Markdown syntax for:
|
||||
@@ -84,7 +116,22 @@ You can also access formatting options by typing `/`.
|
||||
|
||||
## Links
|
||||
|
||||
If you paste a link into a document, Metabase will turn it into a clickable link. For now, Markdown links aren't supported, and you can't change the link text.
|
||||
You can add links to documents in several ways:
|
||||
|
||||
- **Select text and add a link**: Select any text, then click the **Link** button in the formatting menu. Enter a URL and press Enter.
|
||||
- **Paste a URL**: Paste a link into a document, and Metabase will turn it into a clickable link.
|
||||
- **Use Markdown syntax**: Type a link using Markdown format: `[link text](url)`. When you type the closing parenthesis, Metabase will convert it to a clickable link.
|
||||
|
||||
### Editing links
|
||||
|
||||
To edit a link:
|
||||
|
||||
1. Hover over the link.
|
||||
2. Click the **pencil icon** in the popup.
|
||||
3. Update the URL.
|
||||
4. Click the **check icon** to save, or the **trash icon** to remove the link entirely.
|
||||
|
||||
To edit the link's visible text, edit the text in the document itself.
|
||||
|
||||
## Commenting on documents
|
||||
|
||||
@@ -118,7 +165,7 @@ If you never want to see a comment again:
|
||||
3. Click on the **three-dot** menu.
|
||||
4. Select **Delete**.
|
||||
|
||||
This deletion is permanent.
|
||||
This deletion is permanent.
|
||||
|
||||
## Printing documents
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ Until the Embedded analytics SDK is fully loaded and initialized, the hook retur
|
||||
|
||||
### `CreateDashboardModal`
|
||||
|
||||
#### API Refernce
|
||||
#### API Reference
|
||||
|
||||
- [Component](./api/CreateDashboardModal.html)
|
||||
- [Props](./api/CreateDashboardModalProps.html)
|
||||
|
||||
@@ -26,9 +26,11 @@ Here's the [Shoppy source code](https://github.com/metabase/shoppy).
|
||||
|
||||
## Quickstarts
|
||||
|
||||
- [Quickstart](./quickstart.md) (If you have Metabase and an app)
|
||||
- [Quickstart CLI](./quickstart-cli.md) (If you have an app, but no Metabase)
|
||||
- [Quickstart with sample React app](./quickstart-with-sample-app.md) (If you don't have either)
|
||||
The best way to get started with Embedded Analytics SDK depends on what you already have:
|
||||
|
||||
- You have an app and a Metabase instance: go to [main quickstart](./quickstart.md)
|
||||
- You have an app but no Metabase: go to [quickstart with CLI](./quickstart-cli.md)
|
||||
- You don't have an app: go to [quickstart with a sample React app](./quickstart-with-sample-app.md)
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ Once the database status is `Available`, you need to click on the DB identifier:
|
||||
|
||||

|
||||
|
||||
On the page that appears after you click on the database identifer, you will see on the center of the page the **Connectivity & Security** section. This section will provide you with the **Endpoint** that Metabase needs to connect to the application database we just created.
|
||||
On the page that appears after you click on the database identifier, you will see on the center of the page the **Connectivity & Security** section. This section will provide you with the **Endpoint** that Metabase needs to connect to the application database we just created.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ summary: Create development instances of Metabase for testing without paying per
|
||||
|
||||
On Pro and Enterprise plans, you can purchase a development instance or development token as an add-on to your plan.
|
||||
|
||||
- **Self-hosted**: you can purchase a dev token so you don't get billed for every test user acccount. You can use the same dev token for one or more self-hosted development instances (meaning: you can reuse the same token for multiple dev instances at no additional cost). The development token will have access to the same set of paid features as your production token.
|
||||
- **Self-hosted**: you can purchase a dev token so you don't get billed for every test user account. You can use the same dev token for one or more self-hosted development instances (meaning: you can reuse the same token for multiple dev instances at no additional cost). The development token will have access to the same set of paid features as your production token.
|
||||
- **Cloud**: you can purchase a development instance hosted on [Metabase Cloud](https://www.metabase.com/cloud/). The development instance will have access to the same set of paid features as your production token.
|
||||
|
||||
Dev instances are only available to Pro and Enterprise plans; they're not included in the base plans. Dev instances must be purchased as an add-on to your plan, after you've completed your trial and made your first payment.
|
||||
|
||||
|
Before Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 148 KiB |
BIN
docs/installation-and-operation/images/read-only-settings.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
docs/installation-and-operation/images/read-only-view.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
docs/installation-and-operation/images/read-write-settings.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
@@ -17,11 +17,11 @@ Remote Sync lets you develop analytics content in a collection in a development
|
||||
|
||||
Here's a basic remote-sync workflow:
|
||||
|
||||
1. Create a dashboard in a **Metabase configured in Development mode**.
|
||||
1. Create a dashboard in a **Metabase configured in Read-write mode**.
|
||||
2. Push it to a Git branch.
|
||||
3. Open a pull request for review.
|
||||
4. Merge the PR to production.
|
||||
5. Your **Metabase configured in Production mode** automatically pulls in the changes.
|
||||
5. Your **Metabase configured in Read-only mode** automatically pulls in the changes.
|
||||
|
||||
We'll cover [setting up Remote Sync](#setting-up-remote-sync), an [example dev-to-production workflow](#an-example-dev-to-production-workflow), [branch management](#branch-management), and some other odds and ends.
|
||||
|
||||
@@ -29,10 +29,10 @@ We'll cover [setting up Remote Sync](#setting-up-remote-sync), an [example dev-t
|
||||
|
||||
**Remote sync has two modes for different roles**:
|
||||
|
||||
- **Development mode**: Create and edit content. You can [push](#pushing-changes-to-git) and [pull](#pulling-changes-from-git) changes to and from your repository. Multiple Metabase instances can connect in Development mode, each working on [different branches](#branch-management).
|
||||
- **Production mode**: Serves read-only content to users. Production instances only [pull](#pulling-changes-from-git) changes (typically from your main branch) and don't allow direct editing of synced content. You can set up [auto-sync](#pulling-changes-automatically) to automatically pull approved changes every five minutes.
|
||||
- **Read-write mode**: Create and edit content. You can [push](#pushing-changes-to-git) and [pull](#pulling-changes-from-git) changes to and from your repository. Multiple Metabase instances can connect in Read-write mode, each working on [different branches](#branch-management).
|
||||
- **Read-only mode**: Serves read-only content to users. Read-only instances only [pull](#pulling-changes-from-git) changes (typically from your main branch) and don't allow direct editing of synced content. You can set up [auto-sync](#pulling-changes-automatically) to automatically pull approved changes every five minutes.
|
||||
|
||||
**Only the synced collection is tracked**: Each Metabase connected to Remote Sync has one special [collection that syncs with Git](#how-the-synced-collection-works-in-development-mode). When you connect in Development mode, Metabase creates a collection called "Synced Collection" (you can rename it). Everything inside this collection (including sub-collections) is versioned and synchronized with your repository.
|
||||
**Only the synced collection is tracked**: Each Metabase connected to Remote Sync has one special [collection that syncs with Git](#how-the-synced-collection-works-in-read-write-mode). When you connect in Read-write mode, Metabase creates a collection called "Synced Collection" (you can rename it). Everything inside this collection (including sub-collections) is versioned and synchronized with your repository.
|
||||
|
||||
**The synced collection must be self-contained**: Everything a dashboard or question needs must be [inside the synced collection](#items-in-the-synced-collection-cant-depend-on-items-outside-of-it). Content outside the synced collection won't sync to your repository or appear in other Metabase instances.
|
||||
|
||||
@@ -67,9 +67,9 @@ Copy the token immediately after generating it.
|
||||
|
||||
### 3. Connect your development Metabase to your repository
|
||||
|
||||

|
||||

|
||||
|
||||
You can put any Metabase into development mode. We also offer [Development instances](./development-instance.md) that you can use for Remote Sync or any other kind of development.
|
||||
You can put any Metabase into Read-write mode. We also offer [Development instances](./development-instance.md) that you can use for Remote Sync or any other kind of development.
|
||||
|
||||
In the Metabase instance that you use for development:
|
||||
|
||||
@@ -79,7 +79,7 @@ In the Metabase instance that you use for development:
|
||||
|
||||
- For example, `https://github.com/your-org/your-repo`. The repository must already exist and be initialized with at least one commit.
|
||||
|
||||
3. Select **Development mode**.
|
||||
3. Select **Read-write mode**.
|
||||
|
||||
4. Add your access token:
|
||||
|
||||
@@ -91,7 +91,7 @@ In the Metabase instance that you use for development:
|
||||
|
||||
### 4. Add an item to your synced collection
|
||||
|
||||
When you first connect in Development mode, Metabase automatically creates a **synced collection** called "Synced Collection"—any content you add to it will be tracked in Git and can be pushed to your repository.
|
||||
When you first connect in Read-write mode, Metabase automatically creates a **synced collection** called "Synced Collection"—any content you add to it will be tracked in Git and can be pushed to your repository.
|
||||
|
||||
You can rename the Synced Collection if you want, and you can add sub-collections within it to organize your content.
|
||||
|
||||
@@ -130,7 +130,7 @@ Copy the token immediately after generating it — you'll need to paste it into
|
||||
|
||||
### 7. Connect your production Metabase to your repository
|
||||
|
||||

|
||||

|
||||
|
||||
In your production Metabase instance:
|
||||
|
||||
@@ -140,7 +140,7 @@ In your production Metabase instance:
|
||||
|
||||
- Use the same repository as your development Metabase, for example, `https://github.com/your-org/your-repo`.
|
||||
|
||||
3. Select **Production mode**.
|
||||
3. Select **Read-only mode**.
|
||||
|
||||
4. Add your access token:
|
||||
|
||||
@@ -154,9 +154,9 @@ In your production Metabase instance:
|
||||
- Click "Pull changes" to immediately sync content from your repository.
|
||||
- To keep your production instance automatically updated, toggle on "Auto-sync with Git". Metabase will pull changes from your main branch every five minutes.
|
||||
|
||||
In Production mode, the synced collection appears in the regular collections list with a special icon to indicate it's versioned and read-only.
|
||||
In Read-only mode, the synced collection appears in the regular collections list with a special icon to indicate it's versioned and read-only.
|
||||
|
||||

|
||||

|
||||
|
||||
At this point, you should be all set up. Exit Admin settings, then reload your browser. You should see your synced collection in your production Metabase.
|
||||
|
||||
@@ -195,7 +195,7 @@ On your production Metabase instance:
|
||||
2. The "Megafauna Analytics" dashboard appears in production with all its questions.
|
||||
3. The content is read-only for users (they can view and use it, but can't edit it).
|
||||
|
||||
## How the synced collection works in Development mode
|
||||
## How the synced collection works in Read-write mode
|
||||
|
||||
- [The synced collection in the UI](#the-synced-collection-in-the-ui)
|
||||
- [Moving and deleting content in the synced collection](#moving-and-deleting-content-in-the-synced-collection)
|
||||
@@ -203,13 +203,13 @@ On your production Metabase instance:
|
||||
|
||||
### The synced collection in the UI
|
||||
|
||||
When you first connect in Development mode, Metabase creates a synced collection called "Synced Collection". You can add items and sub-collections to it. The synced collection shows its current state with visual indicators: a yellow dot indicates unsynced local changes that need to be committed, and up/down arrows provide sync controls for pulling and pushing changes.
|
||||
When you first connect in Read-write mode, Metabase creates a synced collection called "Synced Collection". You can add items and sub-collections to it. The synced collection shows its current state with visual indicators: a yellow dot indicates unsynced local changes that need to be committed, and up/down arrows provide sync controls for pulling and pushing changes.
|
||||
|
||||
In Production mode, the synced collection appears in the regular collections list (not in a separate "Synced Collections" section) with a special icon to indicate it's versioned and read-only.
|
||||
In Read-only mode, the synced collection appears in the regular collections list (not in a separate "Synced Collections" section) with a special icon to indicate it's versioned and read-only.
|
||||
|
||||
### Moving and deleting content in the synced collection
|
||||
|
||||
**Deletions sync to production:** When you remove content from the synced collection in Development mode and push that change, the content will also be removed from your Production instance when it syncs. This applies to moving content out of the synced collection or deleting it entirely.
|
||||
**Deletions sync to production:** When you remove content from the synced collection in Read-write mode and push that change, the content will also be removed from your Production instance when it syncs. This applies to moving content out of the synced collection or deleting it entirely.
|
||||
|
||||
Content in other Metabases that depended on this item may break since the dependency will no longer be in the synced collection.
|
||||
|
||||
@@ -239,7 +239,7 @@ If you're using Metabase for embedding, you might want synced content to appear
|
||||
|
||||
For groups with these permissions, the sub-collections they can access will appear at the top level of navigation, as if they were root-level collections. They won't see the top-level Synced Collection that you have in your development Metabase.
|
||||
|
||||
What you see in Development mode:
|
||||
What you see in Read-write mode:
|
||||
|
||||
```
|
||||
Collections
|
||||
@@ -248,7 +248,7 @@ Collections
|
||||
├── Giant Sloth Statistics
|
||||
```
|
||||
|
||||
What embedding groups see in Production mode (with no access to Synced Collection, but access to Mammoth Statistics and Giant Sloth Statistics):
|
||||
What embedding groups see in Read-only mode (with no access to Synced Collection, but access to Mammoth Statistics and Giant Sloth Statistics):
|
||||
|
||||
```
|
||||
Collections
|
||||
@@ -280,7 +280,7 @@ Remote Sync uses the same serialization format as the [Metabase CLI serializatio
|
||||
|
||||
## Branch management
|
||||
|
||||
Branching is only available in Development mode.
|
||||
Branching is only available in Read-write mode.
|
||||
|
||||
### Creating a branch
|
||||
|
||||
@@ -313,13 +313,13 @@ If you have unsynced changes, Metabase will show a dialog asking what you want t
|
||||
|
||||
The dialog shows you exactly which items have changed, so you can make an informed decision.
|
||||
|
||||
If you switch modes (from Development to Production or vice versa) with unpushed changes, you'll be prompted to save or discard them. You cannot switch to Production mode with uncommitted changes.
|
||||
If you switch modes (from Read-write to Read-only or vice versa) with unpushed changes, you'll be prompted to save or discard them. You cannot switch to Read-only mode with uncommitted changes.
|
||||
|
||||
If changes don't appear after switching modes: Hard refresh your browser (Cmd/Ctrl + Shift + R).
|
||||
|
||||
## Pushing changes to Git
|
||||
|
||||
You can only push changes in a Metabase with Remote Sync set to Development mode.
|
||||
You can only push changes in a Metabase with Remote Sync set to Read-write mode.
|
||||
|
||||
### Committing and pushing your changes
|
||||
|
||||
@@ -329,13 +329,13 @@ When you make changes to items in the synced collection, a yellow dot appears on
|
||||
2. Enter a descriptive commit message explaining your changes.
|
||||
3. Click "Continue" to push your changes to Git.
|
||||
|
||||
If you see a message that "Remote is ahead of local", that means someone else pushed to the branch from another Metabase in Development mode. Pull the latest changes before pushing again.
|
||||
If you see a message that "Remote is ahead of local", that means someone else pushed to the branch from another Metabase in Read-write mode. Pull the latest changes before pushing again.
|
||||
|
||||
## Pulling changes from Git
|
||||
|
||||
You can pull changes when in Development or Production mode.
|
||||
You can pull changes when in Read-write or Read-only mode.
|
||||
|
||||
In Development mode, you can get the latest changes from your Git repository:
|
||||
In Read-write mode, you can get the latest changes from your Git repository:
|
||||
|
||||
1. Click the down arrow (pull) icon next to your synced collection in the left sidebar.
|
||||
2. Review any summary of incoming changes if shown.
|
||||
@@ -348,7 +348,7 @@ If changes don't appear after pulling:
|
||||
- Hard refresh your browser (Cmd/Ctrl + Shift + R).
|
||||
- If you encounter sync errors, review error messages in the sync dialog, manually resolve conflicts in your Git repository, then pull again.
|
||||
|
||||
In Production mode, go to **Admin settings** > **Settings** > **Remote sync** and click **Pull changes**.
|
||||
In Read-only mode, go to **Admin settings** > **Settings** > **Remote sync** and click **Pull changes**.
|
||||
|
||||
### Handling unsynced changes
|
||||
|
||||
@@ -362,7 +362,7 @@ When in doubt, create a new branch and push changes to that branch. That way you
|
||||
|
||||
### Pulling changes automatically
|
||||
|
||||
In Production mode, you can set Metabase to auto-sync changes from your main branch.
|
||||
In Read-only mode, you can set Metabase to auto-sync changes from your main branch.
|
||||
|
||||
1. Navigate to **Admin settings** > **Settings** > **Remote sync**.
|
||||
2. Enable Auto-sync with Git.
|
||||
@@ -396,7 +396,7 @@ Keep doing what you're doing.
|
||||
|
||||
If you want to switch fully to Remote Sync, we recommend starting with a new repo:
|
||||
|
||||
1. Check out a new branch in your Metabase instance in Development mode.
|
||||
1. Check out a new branch in your Metabase instance in Read-write mode.
|
||||
2. Import your data to your Metabase instance with the serialization command as you normally would.
|
||||
3. Move the content you want to sync into the synced collection.
|
||||
4. Push up your changes to the new repo.
|
||||
|
||||
@@ -136,7 +136,7 @@ Metabase will do all this automatically.
|
||||
|
||||
If you need to downgrade after a major version upgrade, you'll either need to restore from a backup, or manually migrate to a lower version, otherwise Metabase may refuse to start (see the next section).
|
||||
|
||||
Durning a **minor version upgrade** (e.g., 54.1 to 54.2), the new Metabase container or Jar will just work. Only in rare cases will it have to perform a migration, but, like with major version upgrades, Metabase will perform the migration automatically. And of course, you're backing up your application database each time you upgrade, right?
|
||||
During a **minor version upgrade** (e.g., 54.1 to 54.2), the new Metabase container or Jar will just work. Only in rare cases will it have to perform a migration, but, like with major version upgrades, Metabase will perform the migration automatically. And of course, you're backing up your application database each time you upgrade, right?
|
||||
|
||||
## Rolling back an upgrade or to an older version
|
||||
|
||||
@@ -181,7 +181,7 @@ Once the migration process completes, start up Metabase using the JAR or contain
|
||||
|
||||
If you're running Metabase in a cluster:
|
||||
|
||||
1. Reduce the number of nodes to a single node. You can't upgrade all nodes at the same time because the ugprade process works by acquiring a migration lock on the application database from a single client, which performs the migration. If you keep more than one node active when you do a major version upgrade, the application won't behave correctly, as schema changes to the application database could cause problems for nodes that are still running the older version of Metabase.
|
||||
1. Reduce the number of nodes to a single node. You can't upgrade all nodes at the same time because the upgrade process works by acquiring a migration lock on the application database from a single client, which performs the migration. If you keep more than one node active when you do a major version upgrade, the application won't behave correctly, as schema changes to the application database could cause problems for nodes that are still running the older version of Metabase.
|
||||
2. Perform the upgrade as normal (as outlined above).
|
||||
3. Raise the number of nodes to the same number you had before.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ This page covers the [View data](./data.md#view-data-permissions) permission lev
|
||||
For now, impersonation access is only available for the following databases:
|
||||
- ClickHouse
|
||||
- MySQL
|
||||
- PostgreSQL. If you're using views in PostgresSQL, the row-level security policies on views will only work on Postgres versions 15 and higher.
|
||||
- PostgreSQL. If you're using views in PostgreSQL, the row-level security policies on views will only work on Postgres versions 15 and higher.
|
||||
- Redshift
|
||||
- Snowflake
|
||||
- SQL Server
|
||||
@@ -179,6 +179,6 @@ Blue group's more permissive access would override the impersonated access.
|
||||
|
||||
Admins won't ever see the effects of impersonation effects, because their privileges will override those of any other group they're a member of.
|
||||
|
||||
Metabase's default Administrators group has "Can view" access to all databases, and Metabase uses the most permissive access for any person in multple groups, so any admin will have "Can view" - not "Impersonated" - access to the database.
|
||||
Metabase's default Administrators group has "Can view" access to all databases, and Metabase uses the most permissive access for any person in multiple groups, so any admin will have "Can view" - not "Impersonated" - access to the database.
|
||||
|
||||
To test impersonation, create a test user, assign them a user attribute with the database role, and add them to the impersonated group. Then, log in as the test user and verify the data access.
|
||||
|
||||