diff --git a/.circleci/config.yml b/.circleci/config.yml
index 5617d1b77d39..3ca727a12dc6 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,911 +1,16 @@
version: 2.1
-########################################################################################################################
-# EXECUTORS #
-########################################################################################################################
-
-executors:
- # CircleCI base Node + Headless browsers + Clojure CLI - big one
-
- builder:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
-
- # Java 11 tests also test Metabase with the at-rest encryption enabled. See
- # https://metabase.com/docs/latest/operations-guide/encrypting-database-details-at-rest.html for an explanation of
- # what this means.
- java-11:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_ENCRYPTION_SECRET_KEY: Orw0AAyzkO/kPTLJRxiyKoBHXa/d6ZcO+p+gpZO/wSQ=
-
- java-17:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-17-clj-1.11.0.1100.04-2022-build
-
- postgres-9-6:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_DB_TYPE: postgres
- MB_DB_PORT: 5432
- MB_DB_HOST: localhost
- MB_DB_DBNAME: circle_test
- MB_DB_USER: circle_test
- MB_POSTGRESQL_TEST_USER: circle_test
- - image: circleci/postgres:9.6-alpine
- environment:
- POSTGRES_USER: circle_test
- POSTGRES_DB: circle_test
-
- postgres-latest:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_DB_TYPE: postgres
- MB_DB_PORT: 5432
- MB_DB_HOST: localhost
- MB_DB_DBNAME: metabase_test
- MB_DB_USER: metabase_test
- MB_POSTGRESQL_TEST_USER: metabase_test
- - image: circleci/postgres:latest
- environment:
- POSTGRES_USER: metabase_test
- POSTGRES_DB: metabase_test
- POSTGRES_HOST_AUTH_METHOD: trust
-
- mysql-5-7:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_DB_TYPE: mysql
- MB_DB_HOST: localhost
- MB_DB_PORT: 3306
- MB_DB_DBNAME: circle_test
- MB_DB_USER: root
- MB_MYSQL_TEST_USER: root
- - image: circleci/mysql:5.7.23
-
- mysql-latest:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_DB_TYPE: mysql
- MB_DB_HOST: localhost
- MB_DB_PORT: 3306
- MB_DB_DBNAME: circle_test
- MB_DB_USER: root
- MB_MYSQL_TEST_USER: root
- - image: circleci/mysql:latest
-
- mariadb-10-2:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_DB_TYPE: mysql
- MB_DB_HOST: localhost
- MB_DB_PORT: 3306
- MB_DB_DBNAME: circle_test
- MB_DB_USER: root
- MB_MYSQL_TEST_USER: root
- - image: circleci/mariadb:10.2.23
-
- mariadb-latest:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_DB_TYPE: mysql
- MB_DB_HOST: localhost
- MB_DB_PORT: 3306
- MB_DB_DBNAME: circle_test
- MB_DB_USER: root
- MB_MYSQL_TEST_USER: root
- - image: circleci/mariadb:latest
- environment:
- # MYSQL_DATABASE: metabase_test
- # MYSQL_USER: root
- # MYSQL_ALLOW_EMPTY_PASSWORD: yes
-
- mongo-4-0:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- - image: metabase/qa-databases:mongo-sample-4.0
-
- mongo-4-0-ssl:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_TEST_MONGO_REQUIRES_SSL: true
- - image: metabase/qa-databases:mongo-sample-4.0
- command: mongod --dbpath /data/db2/ --sslMode requireSSL --sslPEMKeyFile /etc/mongo/metamongo.pem --sslCAFile /etc/mongo/metaca.crt
-
- mongo-5-0:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- - image: metabase/qa-databases:mongo-sample-5.0
-
- mongo-5-0-ssl:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_TEST_MONGO_REQUIRES_SSL: true
- - image: metabase/qa-databases:mongo-sample-5.0
- command: mongod --dbpath /data/db2/ --tlsMode requireTLS --tlsCertificateKeyFile /etc/mongo/metamongo.pem --tlsCAFile /etc/mongo/metaca.crt
-
- mongo-latest:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- - image: circleci/mongo:latest
- environment:
- MONGO_INITDB_ROOT_USERNAME: metabase
- MONGO_INITDB_ROOT_PASSWORD: metasample123
-
-
- presto-186:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- - image: metabase/presto-mb-ci:0.186
- environment:
- JAVA_TOOL_OPTIONS: "-Xmx2g"
- # Run instance with 8GB or RAM instead of the default 4GB for medium instances. The Presto Docker image runs
- # OOM sometimes with the default medium size.
- resource_class: large
-
- presto-jdbc-env:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- - image: metabase/presto-mb-ci:latest # version 0.254
- environment:
- JAVA_TOOL_OPTIONS: "-Xmx2g"
- MB_PRESTO_JDBC_TEST_CATALOG: test_data
- MB_PRESTO_JDBC_TEST_HOST: localhost
- MB_PRESTO_JDBC_TEST_PORT: 8443
- MB_PRESTO_JDBC_TEST_SSL: true
- MB_PRESTO_JDBC_TEST_USER: metabase
- MB_PRESTO_JDBC_TEST_PASSWORD: metabase
- MB_ENABLE_PRESTO_JDBC_DRIVER: true
- MB_PRESTO_JDBC_TEST_ADDITIONAL_OPTIONS: >
- SSLTrustStorePath=/tmp/cacerts-with-presto-ssl.jks&SSLTrustStorePassword=changeit
- # (see above)
- resource_class: large
-
- sparksql:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- - image: metabase/spark:3.2.1
- resource_class: large
-
- vertica:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- - image: sumitchawla/vertica
-
- sqlserver:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- environment:
- MB_SQLSERVER_TEST_HOST: localhost
- MB_SQLSERVER_TEST_PASSWORD: 'P@ssw0rd'
- MB_SQLSERVER_TEST_USER: SA
- - image: mcr.microsoft.com/mssql/server:2017-latest
- environment:
- ACCEPT_EULA: Y
- SA_PASSWORD: 'P@ssw0rd'
- MSSQL_MEMORY_LIMIT_MB: 1024
-
- druid:
- working_directory: /home/circleci/metabase/metabase/
- docker:
- - image: metabase/ci:java-11-clj-1.11.0.1100.04-2022-build
- - image: metabase/druid:0.20.2
- environment:
- CLUSTER_SIZE: nano-quickstart
- # Run Docker images with 8GB or RAM instead of the default 4GB for medium instances. The Druid Docker image runs
- # OOM all the time with the default medium size.
- resource_class: large
-
-########################################################################################################################
-# MAP FRAGMENTS AND CACHE KEYS #
-########################################################################################################################
-
-# `default_parameters` isn't a key that CircleCI uses, but this form lets us reuse parameter definitions
-default_parameters: &Params
- edition:
- type: enum
- enum: ["oss", "ee"]
- default: "oss"
-
-# .BACKEND-CHECKSUMS and .MODULE-CHECKSUMS are created during the checkout step; see that step
-# for exact details as to what they contain.
-#
-# To support cache busting, we create a file named .CACHE-PREFIX in the checkout step and use its checksum as the
-# prefix for every cache key. If the commit message DOES NOT include [ci nocache], we create an empty file; the
-# checksum will always be the same for this file. If the commit message DOES include [ci nocache], we'll write the
-# unique ID of the current pipeline to .CACHE-PREFIX which will effectively bust our caches whenever it's used.
-
-### Deps Keys ###
-
-# Why don't we use fallback keys for backend deps? We used to, but it allowed the cache to grow
-# uncontrollably since old deps would continue to accumulate. Restoring big caches is really slow in Circle. It's
-# actually faster to recreate the deps cache from scratch whenever we need to which keeps the size down.
-cache-key-backend-deps: &CacheKeyBackendDeps
- # TODO -- this should actually include the Java source files and the Spark SQL AOT source files as well since we now
- # compile those as part of this step. FIXME
- key: v5-{{ checksum ".CACHE-PREFIX" }}-be-deps-{{ checksum "deps.edn" }}-{{ checksum ".SCRIPTS-DEPS-CHECKSUMS" }}
-
-# Key used for implementation of run-on-change -- this is the cache key that contains the .SUCCESS dummy file
-# By default the key ALWAYS includes the name of the test job itself ($CIRCLE_JOB) so you don't need to add that yourself.
-cache-key-run-on-change: &CacheKeyRunOnChange
- key: v5-{{ checksum ".CACHE-PREFIX" }}-run-on-change-{{ .Environment.CIRCLE_JOB }}-<< parameters.checksum >>
-
-########################################################################################################################
-# COMMANDS #
-########################################################################################################################
-
-commands:
- attach-workspace:
- steps:
- - attach_workspace:
- at: /home/circleci/
-
- # For the restore-deps-cache commands below, only restore the cache if there's an exact match. This means whatever
- # is in the cache will be exactly what's used and the cache won't keep growing uncontrollably going forward.
-
- restore-be-deps-cache:
- steps:
- - restore_cache:
- name: Restore cached backend dependencies
- <<: *CacheKeyBackendDeps
-
- # run-on-change lets you only run steps if changes have happened to relevant files since the last time it was run
- # successfully. Uses a cache key to record successful runs -- cache key should be unique for job and relevant source
- # files -- use a checksum! It works like this:
- #
- # 1. Calculate a cache key using a checksum of relevant files for the step in question, e.g. a backend linter step
- # might use a checksum of all .clj files.
- #
- # 2. When the step completes successfully, create a dummy file .SUCCESS and cache it with that cache key.
- #
- # 3. On subsequent runs:
- #
- # a. Attempt to restore the cache using an exact match for this cache key
- #
- # b. If we have a cache entry for that key, .SUCCESS will get restored
- #
- # c. If this command has the skip-job-if-commit-message-includes-ci-quick option enabled, and commit message includes
- # [ci quick], create a dummy file .SUCCESS if not already present. Ignored for master/release branches.
- #
- # d. If commit message includes [ci noskip], delete .SUCCESS so the job will be forced to run.
- #
- # e. If .SUCCESS is present, we can skip the rest of the job, including potentially slow steps like restoring
- # dependency caches or the like. This logs a link to the last successful (not skipped) run of the job
- #
- # Important! If this step is skipped because no changes have happened, the entire JOB will halt with a success
- # status -- no steps that happen AFTER run-on-change will be ran. Keep this in mind!
- #
- # f. If .SUCCESS is not present, proceed as normal, and create and cache .SUCCESS if the job succeeds
- run-on-change:
- parameters:
- checksum:
- type: string
- default: ""
- steps:
- type: steps
- # Whether to skip the rest of the job if commit message includes [ci quick]
- skip-job-if-commit-message-includes-ci-quick:
- type: boolean
- default: false
- steps:
- - restore_cache:
- name: Restore dummy file .SUCCESS if it exists for cache key << parameters.checksum >>
- <<: *CacheKeyRunOnChange
- - when:
- condition: << parameters.skip-job-if-commit-message-includes-ci-quick >>
- steps:
- - run:
- name: "Skip tests (create dummy file .SUCCESS) if commit message contains [ci quick] and branch isn't a master/release branch"
- command: |
- if [[ "$CIRCLE_BRANCH" =~ ^master|release-.+$ ]]; then
- echo "branch '$CIRCLE_BRANCH' is a master or release branch: ignoring [ci quick]"
- elif [[ `cat .COMMIT` == *"[ci quick]"* ]]; then
- echo 'Commit message includes [ci quick]. Creating dummy file .SUCCESS'
- touch .SUCCESS
- else
- echo 'Commit message does not include [ci quick]'
- fi
- - run:
- name: "Force test run (delete dummy file .SUCCESS) if commit message includes [ci noskip]"
- command: |
- if [[ `cat .COMMIT` == *"[ci noskip]"* ]]; then
- echo 'Commit message includes [ci noskip] -- forcing test run (delete .SUCCESS)'
- rm -f .SUCCESS
- else
- echo 'Commit message does not include [ci noskip]'
- fi
- - run:
- name: Skip rest of job if .SUCCESS exists
- command: |
- if [ -f .SUCCESS ]; then
- echo '.SUCCESS is present: skipping rest of job.'
- echo "Link to last successful run (if available): $(cat .SUCCESS)"
- circleci-agent step halt
- fi
- - steps: << parameters.steps >>
- - run:
- name: Create dummy file .SUCCESS
- command: |
- echo "$CIRCLE_BUILD_URL" > .SUCCESS
- - save_cache:
- name: Persist dummy file .SUCCESS to cache with key << parameters.checksum >>
- <<: *CacheKeyRunOnChange
- paths:
- - /home/circleci/metabase/metabase/.SUCCESS
- - run:
- name: Delete dummy file .SUCCESS so subsequent steps don't see it
- command: rm /home/circleci/metabase/metabase/.SUCCESS
-
- # Creates a file that contains checksums for all the files found using the find command with supplied arguments.
- # You can use a checksum of the checksum file for cache keys including run-on-change cache keys.
- create-checksum-file:
- parameters:
- filename:
- type: string
- find-args:
- type: string
- steps:
- - run:
- name: Create << parameters.filename >> checksum file
- command: |
- for file in `find << parameters.find-args >> | sort`; do
- echo `md5sum "$file"` >> "<< parameters.filename >>"
- done
- if [ ! -f "<< parameters.filename >>" ]; then
- echo 'Error: no matching files. Did you remember to attach the workspace?'
- exit 1
- fi
- echo "Created checksums for $(cat << parameters.filename >> | wc -l) files"
-
- run-clojure-command:
- parameters:
- before-steps:
- type: steps
- default: []
- clojure-args:
- type: string
- after-steps:
- type: steps
- default: []
- <<: *Params
- steps:
- - restore-be-deps-cache
- - steps: << parameters.before-steps >>
- - run:
- name: clojure << parameters.clojure-args >>:<< parameters.edition >>:<< parameters.edition >>-dev
- command: |
- clojure << parameters.clojure-args >>:<< parameters.edition >>:<< parameters.edition >>-dev
- no_output_timeout: 15m
- - steps: << parameters.after-steps >>
- - store_test_results:
- path: /home/circleci/metabase/metabase/target/junit
-
- wait-for-port:
- parameters:
- port:
- type: integer
- steps:
- - run:
- name: Wait for port << parameters.port >> to be ready
- command: |
- while ! nc -z localhost << parameters.port >>; do sleep 0.1; done
- no_output_timeout: 15m
-
- fetch-jdbc-driver:
- parameters:
- source:
- type: string
- dest:
- type: string
- steps:
- - run:
- name: Make plugins dir
- command: mkdir /home/circleci/metabase/metabase/plugins
- - run:
- name: Download JDBC driver JAR << parameters.dest >>
- command: |
- wget --output-document=plugins/<< parameters.dest >> ${<< parameters.source >>}
- no_output_timeout: 15m
-
- run-command:
- parameters:
- command:
- type: string
- steps:
- - run:
- name: Run command
- command: << parameters.command >>
-
+workflows:
+ empty:
+ jobs:
+ - empty
jobs:
+ empty:
+ docker:
+ - image: cimg/base:stable
-########################################################################################################################
-# CHECKOUT ETC. #
-########################################################################################################################
-
- checkout:
- executor: builder
steps:
- - checkout
- - attach-workspace
- # .BACKEND-CHECKSUMS is every Clojure source file as well as dependency files like deps.edn and plugin manifests
- - create-checksum-file:
- filename: .BACKEND-CHECKSUMS
- find-args: ". -type f -name '*.clj' -or -name '*.cljc' -or -name '*.java' -or -name '*.edn' -or -name '*.yaml' -or -name sample-dataset.db.mv.db"
- # .SCRIPTS-DEPS-CHECKSUMS is all the deps.edn files inside ./bin
- - create-checksum-file:
- filename: .SCRIPTS-DEPS-CHECKSUMS
- find-args: "bin -type f -name 'deps.edn'"
- # .MODULES-CHECKSUMS is every Clojure source file in the modules/ directory as well as plugin manifests
- - create-checksum-file:
- filename: .MODULES-CHECKSUMS
- find-args: "./modules -type f -name '*.clj' -or -name metabase-plugin.yaml"
- - run:
- name: Save last git commit message to .COMMIT
- command: git log -1 > .COMMIT
- - run:
- name: Determine what to do to .git directory
- command: |
- if [[ $CIRCLE_BRANCH == release* ]]; then
- echo 'This is a release branch; preserving .git directory to determine version'
- else
- echo 'This is not a release branch; removing .git directory (not needed for tests)'
- rm -rf /home/circleci/metabase/metabase/.git
- fi
-
- - run:
- name: Remove ./OSX directory (not needed for tests)
- command: rm -rf /home/circleci/metabase/metabase/OSX
- # .CACHE-PREFIX is described above in the Cache Keys section of this file
- - run:
- name: 'Create cache key prefix .CACHE-PREFIX to bust caches if commit message includes [ci nocache]'
- command: |
- if [[ `cat .COMMIT` == *"[ci nocache]"* ]]; then
- echo 'Commit message includes [ci nocache]; using cache-busting prefix'
- echo '<< pipeline.id >>' > .CACHE-PREFIX
- else
- echo '' > .CACHE-PREFIX
- fi
- run:
- name: 'If $CIRCLE_JOB is unset then bust the cache (see #24128 for details)'
- command: |
- if [ -z "$CIRCLE_JOB" ]; then
- echo '$CIRCLE_JOB is unset. Using cache-busting prefix.'
- echo '<< pipeline.id >>' > .CACHE-PREFIX
- fi
- - run:
- name: Create static visualization js bundle
- command: yarn build-static-viz
- - run:
- name: 'Check for branch name to bust caches if it is a release branch'
- command: |
- if [[ $CIRCLE_BRANCH == release* || $CIRCLE_BRANCH == master ]]; then
- echo 'This is a release or master branch; using cache-busting prefix'
- echo '<< pipeline.id >>' > .CACHE-PREFIX
- fi
- - run:
- name: Make SSL certificates for Mongo available
- command: >-
- curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metabase.crt
- -o /home/circleci/metabase/metabase/test_resources/ssl/mongo/metabase.crt
- https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metabase.key
- -o /home/circleci/metabase/metabase/test_resources/ssl/mongo/metabase.key
- https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metaca.crt
- -o /home/circleci/metabase/metabase/test_resources/ssl/mongo/metaca.crt
- - persist_to_workspace:
- root: /home/circleci/
- paths:
- - metabase/metabase
-
-########################################################################################################################
-# BACKEND #
-########################################################################################################################
-
- be-deps:
- executor: builder
- parameters:
- <<: *Params
- steps:
- - attach-workspace
- # This step is pretty slow, even with the cache, so only run it if deps.edn has changed
- - run-on-change:
- checksum: 'v5-{{ checksum "deps.edn" }}-{{ checksum ".SCRIPTS-DEPS-CHECKSUMS" }}'
- steps:
- - restore-be-deps-cache
- - run:
- name: Compile Java source file(s)
- command: clojure -X:deps prep
- - run:
- name: Fetch dependencies
- command: clojure -P -X:dev:ci:ee:ee-dev:drivers:drivers-dev
- - run:
- name: Fetch dependencies (./bin/build/build-mb)
- command: cd /home/circleci/metabase/metabase/bin/build-mb && clojure -P -M:test
- # Not sure why this is needed since you would think build-mb would fetch this stuff as well. It doesn't
- # seem to fetch everything tho. :shrug:
- - run:
- name: Fetch dependencies (./bin/build/build-drivers)
- command: cd /home/circleci/metabase/metabase/bin/build-drivers && clojure -P -M:test
- - save_cache:
- name: Cache backend dependencies
- <<: *CacheKeyBackendDeps
- paths:
- - /home/circleci/.m2
- - /home/circleci/.gitlibs
- - /home/circleci/metabase/metabase/java/target/classes
- - /home/circleci/metabase/metabase/modules/drivers/sparksql/target/classes
-
- clojure:
- parameters:
- e:
- type: executor
- default: builder
- before-steps:
- type: steps
- default: []
- clojure-args:
- type: string
- after-steps:
- type: steps
- default: []
- skip-when-no-change:
- type: boolean
- default: false
- java-version:
- type: string
- default: ""
- version:
- type: string
- default: ""
- <<: *Params
- executor: << parameters.e >>
- steps:
- - attach-workspace
- - when:
- condition: << parameters.skip-when-no-change >>
- steps:
- - run-on-change:
- checksum: '{{ checksum ".BACKEND-CHECKSUMS" }}'
- steps:
- - run-clojure-command:
- before-steps: << parameters.before-steps >>
- clojure-args: << parameters.clojure-args >>
- after-steps: << parameters.after-steps >>
- edition: << parameters.edition >>
- - unless:
- condition: << parameters.skip-when-no-change >>
- steps:
- - run-clojure-command:
- before-steps: << parameters.before-steps >>
- clojure-args: << parameters.clojure-args >>
- after-steps: << parameters.after-steps >>
- edition: << parameters.edition >>
-
- be-linter-reflection-warnings:
- executor: builder
- steps:
- - attach-workspace
- - run-on-change:
- checksum: '{{ checksum ".BACKEND-CHECKSUMS" }}-{{ checksum "bin/reflection-linter" }}'
- steps:
- - restore-be-deps-cache
- - run:
- name: Run reflection warnings checker
- command: ./bin/reflection-linter
- no_output_timeout: 15m
-
- test-driver:
- parameters:
- e:
- type: executor
- default: builder
- driver:
- type: string
- timeout:
- type: string
- default: 20m
- before-steps:
- type: steps
- default: []
- after-steps:
- type: steps
- default: []
- description:
- type: string
- default: ""
- extra-env:
- type: string
- default: ""
- test-args:
- type: string
- default: ""
- version:
- type: string
- default: ""
- executor: << parameters.e >>
- steps:
- - attach-workspace
- - run-on-change:
- checksum: '{{ checksum ".BACKEND-CHECKSUMS" }}'
- skip-job-if-commit-message-includes-ci-quick: true
- steps:
- - restore-be-deps-cache
- - steps: << parameters.before-steps >>
- - run:
- name: Test << parameters.driver >> driver << parameters.description >>
- environment:
- DRIVERS: << parameters.driver >>
- command: >
- << parameters.extra-env >> clojure -X:dev:ci:ee:ee-dev:drivers:drivers-dev:test
- << parameters.test-args >>
- no_output_timeout: << parameters.timeout >>
- - store_test_results:
- path: /home/circleci/metabase/metabase/target/junit
- - steps: << parameters.after-steps >>
-
-########################################################################################################################
-# WORKFLOWS #
-########################################################################################################################
-
-# `default_matrix` isn't a key that CircleCI uses, but this form lets us reuse the matrix: block
-default_matrix: &Matrix
- matrix:
- parameters:
- edition: ["ee", "oss"]
-
-workflows:
- version: 2
- build:
- jobs:
- - checkout
-
- - be-deps:
- requires:
- - checkout
-
- - clojure:
- matrix:
- parameters:
- edition: ["ee"]
- java-version: ["java-11"]
- name: be-tests-<< matrix.java-version >>-<< matrix.edition >>
- requires:
- - be-deps
- e: << matrix.java-version >>
- clojure-args: -X:dev:ci:test
- skip-when-no-change: true
-
- - clojure:
- name: be-linter-cloverage
- requires:
- - be-deps
- # TODO FIXME
- clojure-args: -X:dev:ee:ee-dev:test:cloverage
- after-steps:
- - run:
- name: Upload code coverage to codecov.io
- command: bash <(curl -s https://codecov.io/bash) -F back-end
-
- skip-when-no-change: true
-
- - test-driver:
- matrix:
- parameters:
- driver: ["bigquery-cloud-sdk", "googleanalytics", "sqlite"]
- name: be-tests-<< matrix.driver >>-ee
- requires:
- - be-tests-java-11-ee
- driver: << matrix.driver >>
-
- - test-driver:
- matrix:
- parameters:
- driver: ["sqlserver", "druid"]
- name: be-tests-<< matrix.driver >>-ee
- requires:
- - be-tests-java-11-ee
- e: << matrix.driver >>
- driver: << matrix.driver >>
-
- - test-driver:
- name: be-google-related-drivers-classpath-test
- requires:
- - be-tests-java-11-ee
- driver: googleanalytics,bigquery-cloud-sdk
- test-args: >-
- :only "[metabase.query-processor-test.expressions-test metabase.driver.google-test
- metabase.driver.googleanalytics-test]"
-
- - test-driver:
- matrix:
- parameters:
- version: ["mongo-4-0", "mongo-5-0", "mongo-latest", "mongo-4-0-ssl", "mongo-5-0-ssl"]
- name: be-tests-<< matrix.version >>-ee
- description: "(<< matrix.version >>)"
- requires:
- - be-tests-java-11-ee
- e: << matrix.version >>
- driver: mongo
-
- - test-driver:
- matrix:
- parameters:
- version: ["mysql-5-7", "mariadb-10-2", "mariadb-latest"]
- name: be-tests-<< matrix.version >>-ee
- description: "(<< matrix.version >>)"
- requires:
- - be-tests-java-11-ee
- e:
- name: << matrix.version >>
- driver: mysql
-
- - test-driver:
- name: be-tests-mysql-latest-ee
- description: "(MySQL latest)"
- requires:
- - be-tests-java-11-ee
- e:
- name: mysql-latest
- driver: mysql
- # set up env vars for something named "MYSQL_SSL" to run MySQL SSL tests verifying connectivity with PEM cert
- # they are deliberately given a different name to prevent them from affecting the regular test run against
- # the configured MySQL instance, but there is one particular test (mysql-connect-with-ssl-and-pem-cert-test)
- # that overrides the MB_MYSQL_TEST_* values with them
- # the MYSQL_RDS_SSL_INSTANCE vars are secret and/or changeable, so they are defined in the CircleCI settings
- timeout: 30m
- extra-env: >-
- MB_MYSQL_SSL_TEST_HOST=$MYSQL_RDS_SSL_INSTANCE_HOST
- MB_MYSQL_SSL_TEST_SSL=true
- MB_MYSQL_SSL_TEST_ADDITIONAL_OPTIONS='verifyServerCertificate=true'
- MB_MYSQL_SSL_TEST_SSL_CERT="$(cat /home/circleci/metabase/metabase/resources/certificates/rds-combined-ca-bundle.pem)"
- MB_MYSQL_SSL_TEST_USER=metabase
- MB_MYSQL_SSL_TEST_PASSWORD=$MYSQL_RDS_SSL_INSTANCE_PASSWORD
-
- - test-driver:
- name: be-tests-oracle-ee
- requires:
- - be-tests-java-11-ee
- before-steps:
- - fetch-jdbc-driver:
- source: ORACLE_JDBC_JAR
- dest: ojdbc8.jar
- - run:
- name: Ensure truststore file
- command: ls /home/circleci/metabase/metabase/resources/certificates/rds_root_ca_truststore.jks
- driver: oracle
- extra-env: >-
- MB_ORACLE_SSL_TEST_SSL=true
- MB_ORACLE_SSL_TEST_PORT=2484
- MB_ORACLE_SSL_TEST_SSL_USE_TRUSTSTORE=true
- MB_ORACLE_SSL_TEST_SSL_TRUSTSTORE_PATH=/home/circleci/metabase/metabase/resources/certificates/rds_root_ca_truststore.jks
- MB_ORACLE_SSL_TEST_SSL_TRUSTSTORE_OPTIONS=local
- MB_ORACLE_SSL_TEST_SSL_TRUSTSTORE_PASSWORD_VALUE=metabase
-
- - test-driver:
- name: be-tests-postgres-ee
- description: "(9.6)"
- requires:
- - be-tests-java-11-ee
- e: postgres-9-6
- driver: postgres
-
- - test-driver:
- name: be-tests-postgres-latest-ee
- description: "(Latest)"
- requires:
- - be-tests-java-11-ee
- e: postgres-latest
- driver: postgres
- extra-env: >-
- MB_POSTGRES_SSL_TEST_SSL=true
- MB_POSTGRES_SSL_TEST_SSL_MODE=verify-full
- MB_POSTGRES_SSL_TEST_SSL_ROOT_CERT_PATH=/home/circleci/metabase/metabase/test-resources/certificates/us-east-2-bundle.pem
-
- - test-driver:
- name: be-tests-presto-ee
- requires:
- - be-tests-java-11-ee
- e: presto-186
- before-steps:
- - wait-for-port:
- port: 8080
- driver: presto
-
- - test-driver:
- name: be-tests-presto-jdbc-ee
- requires:
- - be-tests-java-11-ee
- e: presto-jdbc-env # specific env for running Presto JDBC tests (newer Presto version, SSL, etc.)
- before-steps:
- - wait-for-port:
- port: 8443
- - run:
- name: Create temp cacerts file based on bundled JDK one
- command: cp $JAVA_HOME/lib/security/cacerts /tmp/cacerts-with-presto-ssl.jks
- - run:
- name: Capture Presto server self signed CA
- command: |
- while [[ ! -s /tmp/presto-ssl-ca.pem ]];
- do echo "Waiting to capture SSL CA" \
- && openssl s_client -connect localhost:8443 2>/dev/null /tmp/presto-ssl-ca.pem \
- && sleep 1; done
- - run:
- name: Convert Presto CA from PEM to DER
- command: openssl x509 -outform der -in /tmp/presto-ssl-ca.pem -out /tmp/presto-ssl-ca.der
- - run:
- name: Add write permission on cacerts file
- command: chmod u+w /tmp/cacerts-with-presto-ssl.jks
- - run:
- name: Import Presto CA into temp cacerts file
- command: |
- keytool -noprompt -import -alias presto -keystore /tmp/cacerts-with-presto-ssl.jks \
- -storepass changeit -file /tmp/presto-ssl-ca.der -trustcacerts
- after-steps:
- - run:
- name: Capture max memory usage
- command: cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
- when: always
- driver: presto-jdbc
-
- - test-driver:
- name: be-tests-redshift-ee
- requires:
- - be-tests-java-11-ee
- driver: redshift
- timeout: 15m
-
- - test-driver:
- name: be-tests-snowflake-ee
- requires:
- - be-tests-java-11-ee
- driver: snowflake
- timeout: 115m
-
- - test-driver:
- name: be-tests-sparksql-ee
- requires:
- - be-tests-java-11-ee
- e: sparksql
- before-steps:
- - wait-for-port:
- port: 10000
- driver: sparksql
-
- - test-driver:
- name: be-tests-vertica-ee
- requires:
- - be-tests-java-11-ee
- e: vertica
- before-steps:
- - fetch-jdbc-driver:
- source: VERTICA_JDBC_JAR
- dest: vertica-jdbc-7.1.2-0.jar
- driver: vertica
+ name: echo
+ command: echo "Hello, World!"
diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn
index 853a8d409d68..97b8ac9e32d1 100644
--- a/.clj-kondo/config.edn
+++ b/.clj-kondo/config.edn
@@ -224,13 +224,14 @@
metabase.driver.mongo.parameters mongo.params
metabase.driver.mongo.query-processor mongo.qp
metabase.driver.mongo.util mongo.util
+ metabase.driver.sql-jdbc.common sql-jdbc.common
metabase.driver.sql-jdbc.connection sql-jdbc.conn
metabase.driver.sql-jdbc.execute sql-jdbc.execute
metabase.driver.sql-jdbc.execute.diagnostic sql-jdbc.execute.diagnostic
metabase.driver.sql-jdbc.execute.legacy-impl sql-jdbc.legacy
metabase.driver.sql-jdbc.execute.old-impl sql-jdbc.execute.old
metabase.driver.sql-jdbc.sync sql-jdbc.sync
- metabase.driver.sql-jdbc.sync.common sql-jdbc.common
+ metabase.driver.sql-jdbc.sync.common sql-jdbc.sync.common
metabase.driver.sql-jdbc.sync.describe-database sql-jdbc.describe-database
metabase.driver.sql-jdbc.sync.describe-table sql-jdbc.describe-table
metabase.driver.sql-jdbc.sync.interface sql-jdbc.sync.interface
@@ -401,6 +402,7 @@
metabase.integrations.ldap/with-ldap-connection clojure.core/fn
metabase.mbql.schema.macros/defclause clj-kondo.lint-as/def-catch-all
metabase.models.collection-test/with-collection-in-location clojure.core/let
+ metabase.models.json-migration/def-json-migration clj-kondo.lint-as/def-catch-all
metabase.models.setting.multi-setting/define-multi-setting clojure.core/def
metabase.models.setting/defsetting clj-kondo.lint-as/def-catch-all
metabase.public-settings.premium-features/defenterprise clj-kondo.lint-as/def-catch-all
@@ -572,7 +574,7 @@
metabase.query-processor-test.expressions-test/calculate-bird-scarcity hooks.metabase.query-processor-test.expressions-test/calculate-bird-scarcity
metabase.query-processor-test.filter-test/count-with-filter-clause hooks.metabase.test.data/$ids
metabase.query-processor.middleware.cache-test/with-mock-cache hooks.common/with-two-bindings
- metabase.sample-database-test/with-temp-sample-database-db hooks.common/with-one-binding
+ metabase.sample-data-test/with-temp-sample-database-db hooks.common/with-one-binding
metabase.test.data.datasets/test-drivers hooks.common/do*
metabase.test.data.users/with-group hooks.common/let-one-with-optional-value
metabase.test.data/$ids hooks.metabase.test.data/$ids
diff --git a/.github/actions/find-squashed-commit/action.yml b/.github/actions/find-squashed-commit/action.yml
index c8469da135c2..853df00c5913 100644
--- a/.github/actions/find-squashed-commit/action.yml
+++ b/.github/actions/find-squashed-commit/action.yml
@@ -22,7 +22,7 @@ runs:
COMMIT=$(env -i git log $BASE_REF --grep="(#$PULL_REQUEST_NUMBER)" --format="%H")
echo "commit SHA $COMMIT"
- echo "::set-output name=commit::$COMMIT"
+ echo "commit=$COMMIT" >> $GITHUB_OUTPUT
id: find-squashed-commit
shell: bash
env:
diff --git a/.github/actions/test-driver/action.yml b/.github/actions/test-driver/action.yml
new file mode 100644
index 000000000000..4162e98d01cb
--- /dev/null
+++ b/.github/actions/test-driver/action.yml
@@ -0,0 +1,28 @@
+name: Test database driver
+inputs:
+ junit-name:
+ required: true
+ default: 'driver'
+ test-args:
+ required: false
+
+runs:
+ using: "composite"
+ steps:
+ - 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
+ shell: bash
+ - name: Test database driver
+ run: clojure -X:dev:ci:ee:ee-dev:drivers:drivers-dev:test ${{ inputs.test-args }}
+ shell: bash
+ - name: Publish Test Report (JUnit)
+ uses: dorny/test-reporter@v1
+ if: always()
+ with:
+ path: 'target/junit/**/*_test.xml'
+ name: JUnit Test Report ${{ inputs.junit-name }}
+ reporter: java-junit
diff --git a/.github/workflows/auto-backport.yml b/.github/workflows/auto-backport.yml
index dfd68c67a0d0..3155339b846e 100644
--- a/.github/workflows/auto-backport.yml
+++ b/.github/workflows/auto-backport.yml
@@ -43,13 +43,13 @@ jobs:
CONFLICTS=$(git ls-files -u | wc -l)
if [ "$CONFLICTS" -gt 0 ]; then
echo "Could not cherry pick because of a conflict"
- echo "::set-output name=has-conflicts::true"
+ echo "has-conflicts=true" >> $GITHUB_OUTPUT
git cherry-pick --abort
git checkout master
exit 0
fi
- echo "::set-output name=has-conflicts::false"
+ echo "has-conflicts=false" >> $GITHUB_OUTPUT
git checkout master
git push -u origin ${BACKPORT_BRANCH}
@@ -60,7 +60,7 @@ jobs:
BACKPORT_PR_NUMBER=${BACKPORT_PR_URL##*/}
- echo "::set-output name=backport_pr_number::$BACKPORT_PR_NUMBER"
+ echo "backport_pr_number=$BACKPORT_PR_NUMBER" >> $GITHUB_OUTPUT
env:
TARGET_BRANCH: ${{ steps.get_latest_release_branch.outputs.branch-name }}
ORIGINAL_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
diff --git a/.github/workflows/backend-skipped-checks.yml b/.github/workflows/backend-skipped-checks.yml
index 83f1fc1648dc..e1506b2d087b 100644
--- a/.github/workflows/backend-skipped-checks.yml
+++ b/.github/workflows/backend-skipped-checks.yml
@@ -4,12 +4,23 @@
name: Backend
on:
+ push:
+ branches:
+ - 'master'
+ - 'release-**'
+ paths:
+ - "docs/**"
+ - "**.md"
+ # frontend and E2E tests
+ - "**/frontend/test/**"
+ - "**/frontend/**.unit.*"
pull_request:
paths:
- "docs/**"
- "**.md"
- - "frontend/test/**"
- - "enterprise/frontend/test/**"
+ # frontend and E2E tests
+ - "**/frontend/test/**"
+ - "**/frontend/**.unit.*"
jobs:
diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml
index 5dd66f163dd8..362509beca2d 100644
--- a/.github/workflows/backend.yml
+++ b/.github/workflows/backend.yml
@@ -5,15 +5,43 @@ on:
branches:
- 'master'
- 'release-**'
+ paths-ignore:
+ - "docs/**"
+ - "**.md"
+ # frontend and E2E tests
+ - "**/frontend/test/**"
+ - "**/frontend/**.unit.*"
pull_request:
+ types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- "docs/**"
- "**.md"
- - "frontend/test/**"
- - "enterprise/frontend/test/**"
+ # frontend and E2E tests
+ - "**/frontend/test/**"
+ - "**/frontend/**.unit.*"
jobs:
+ be-linter-cloverage:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ steps:
+ - uses: actions/checkout@v3
+ - name: Build static viz frontend
+ run: yarn build-static-viz
+ - name: Prepare back-end environment
+ uses: ./.github/actions/prepare-backend
+ with:
+ m2-cache-key: 'cloverage'
+ - name: Collect the test coverage
+ run: clojure -X:dev:ee:ee-dev:test:cloverage
+ - name: Upload coverage to codecov.io
+ uses: codecov/codecov-action@v2
+ with:
+ files: ./target/coverage/codecov.json
+ flags: back-end
+
be-linter-clj-kondo:
runs-on: ubuntu-20.04
timeout-minutes: 10
@@ -65,6 +93,7 @@ jobs:
/work/modules/drivers/presto-jdbc/test
be-linter-eastwood:
+ if: github.event.pull_request.draft == false
runs-on: ubuntu-20.04
timeout-minutes: 20
steps:
@@ -76,10 +105,41 @@ jobs:
- run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood
name: Run Eastwood linter
+ # Because it's not possible to conditionally run only `java-11-ee` test in the draft mode,
+ # we have to extract that job manually here. Backend developers have requested that this
+ # test runs at all times to give them an early warning sign is something is broken.
+ be-tests-java-11-ee-pre-check:
+ if: github.event.pull_request.draft == true
+ runs-on: ubuntu-20.04
+ name: be-tests-java-11-ee-pre-check
+ timeout-minutes: 25
+ steps:
+ - uses: actions/checkout@v3
+ - name: Prepare front-end environment
+ uses: ./.github/actions/prepare-frontend
+ - name: Prepare back-end environment
+ uses: ./.github/actions/prepare-backend
+
+ - run: yarn install --frozen-lockfile --prefer-offline
+ - name: Build static viz frontend
+ run: yarn build-static-viz
+
+ - name: Run tests
+ run: clojure -X:dev:ci:test:ee:ee-dev
+
+ - name: Publish Test Report (JUnit)
+ uses: dorny/test-reporter@v1
+ if: always()
+ with:
+ path: 'target/junit/**/*_test.xml'
+ name: JUnit Test Report be-tests-java-11-ee-pre-check
+ reporter: java-junit
+
be-tests:
+ if: github.event.pull_request.draft == false
runs-on: ubuntu-20.04
name: be-tests-java-${{ matrix.java-version }}-${{ matrix.edition }}
- timeout-minutes: 20
+ timeout-minutes: 25
strategy:
fail-fast: false
matrix:
@@ -96,25 +156,30 @@ jobs:
- name: Build static viz frontend
run: yarn build-static-viz
- - name: Compile Java source file(s)
- run: clojure -X:deps prep
- - name: Compile driver AOT namespaces
- working-directory: modules/drivers
- run: clojure -X:deps prep
- - name: Fetch dependencies
- run: clojure -P -X:dev:ci:ee:ee-dev:drivers:drivers-dev
- - name: Fetch dependencies (./bin/build/build-mb)
- working-directory: bin/build-mb
- run: clojure -P -M:test
- - name: Fetch dependencies (./bin/build/build-drivers)
- working-directory: bin/build-drivers
- run: clojure -P -M:test
-
- name: Run tests
run: clojure -X:dev:ci:test:${{ matrix.edition }}:${{ matrix.edition }}-dev
+
- name: Publish Test Report (JUnit)
- uses: mikepenz/action-junit-report@v2
+ uses: dorny/test-reporter@v1
if: always()
with:
- report_paths: 'target/junit/**/*_test.xml'
- check_name: JUnit Test Report be-tests-java-${{ matrix.java-version }}-${{ matrix.edition }}
+ path: 'target/junit/**/*_test.xml'
+ name: JUnit Test Report be-tests-java-${{ matrix.java-version }}-${{ matrix.edition }}
+ reporter: java-junit
+
+ # checks that all the namespaces we actually ship can be compiled, without any dependencies that we don't ship (such
+ # as `:dev` dependencies). See #27009 for more context.
+ be-check:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ name: be-check-java-${{ matrix.java-version }}
+ timeout-minutes: 10
+ strategy:
+ matrix:
+ java-version: [11, 17, 19]
+ steps:
+ - uses: actions/checkout@v3
+ - name: Prepare backend
+ uses: ./.github/actions/prepare-backend
+ - name: Check namespaces
+ run: clojure -M:ee:drivers:check
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 4eb8ea05c006..2285d20b57f1 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -117,7 +117,7 @@ jobs:
BACKPORT_PR_NUMBER=${BACKPORT_PR_URL##*/}
- echo "::set-output name=backport_pr_number::$BACKPORT_PR_NUMBER"
+ echo "backport_pr_number=$BACKPORT_PR_NUMBER" >> $GITHUB_OUTPUT
echo "New PR has been created"
fi
env:
diff --git a/.github/workflows/drivers-skipped-checks.yml b/.github/workflows/drivers-skipped-checks.yml
new file mode 100644
index 000000000000..91b6c034da94
--- /dev/null
+++ b/.github/workflows/drivers-skipped-checks.yml
@@ -0,0 +1,175 @@
+# Required checks with path filtering rules will block pull requests from merging if they change only the excluded files.
+# This is a workaround to allow the PR to be merged.
+# https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
+name: Driver Tests
+
+on:
+ push:
+ branches:
+ - 'master'
+ - 'release-**'
+ paths:
+ - "docs/**"
+ - "**.md"
+ # frontend and E2E tests
+ - "**/frontend/test/**"
+ - "**/frontend/**.unit.*"
+ pull_request:
+ paths:
+ - "docs/**"
+ - "**.md"
+ # frontend and E2E tests
+ - "**/frontend/test/**"
+ - "**/frontend/**.unit.*"
+
+jobs:
+
+ be-tests-athena-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-bigquerycloud-sdk-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-druid-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-googleanalytics-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-google-related-classpath-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mariadb-10-2-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mariadb-latest-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mongo-4-0-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mongo-4-0-ssl-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mongo-5-0-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mongo-5-0-ssl-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mongo-latest-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mysql-5-7-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-mysql-latest-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-oracle-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-postgres-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-postgres-latest-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-presto-186-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-presto-jdbc-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-redshift-jdbc-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-snowflake-jdbc-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-sparksql-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-sqlite-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-sqlserver-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
+
+ be-tests-vertica-ee:
+ runs-on: ubuntu-20.04
+ steps:
+ - run: |
+ echo "Didn't run due to conditional filtering"
diff --git a/.github/workflows/drivers.yml b/.github/workflows/drivers.yml
new file mode 100644
index 000000000000..c37929d14e98
--- /dev/null
+++ b/.github/workflows/drivers.yml
@@ -0,0 +1,643 @@
+name: Driver Tests
+
+on:
+ push:
+ branches:
+ - 'master'
+ - 'release-**'
+ paths-ignore:
+ - "docs/**"
+ - "**.md"
+ # frontend and E2E tests
+ - "**/frontend/test/**"
+ - "**/frontend/**.unit.*"
+ pull_request:
+ types: [opened, synchronize, reopened, ready_for_review]
+ paths-ignore:
+ - "docs/**"
+ - "**.md"
+ # frontend and E2E tests
+ - "**/frontend/test/**"
+ - "**/frontend/**.unit.*"
+
+concurrency:
+ group: ${{ github.head_ref || github.run_id}}
+ cancel-in-progress: true
+
+jobs:
+
+ be-tests-athena-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: athena
+ MB_ATHENA_TEST_REGION: us-east-1
+ MB_ATHENA_TEST_ACCESS_KEY: ${{ secrets.MB_ATHENA_TEST_ACCESS_KEY }}
+ MB_ATHENA_TEST_SECRET_KEY: ${{ secrets.MB_ATHENA_TEST_SECRET_KEY }}
+ MB_ATHENA_TEST_S3_STAGING_DIR: ${{ secrets.MB_ATHENA_TEST_S3_STAGING_DIR }}
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Athena driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-athena-ee'
+
+ be-tests-bigquery-cloud-sdk-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: bigquery-cloud-sdk
+ MB_BIGQUERY_TEST_PROJECT_ID: ${{ secrets.BIGQUERY_TEST_PROJECT_ID }}
+ MB_BIGQUERY_TEST_CLIENT_ID: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_ID }}
+ MB_BIGQUERY_TEST_CLIENT_SECRET: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_SECRET }}
+ MB_BIGQUERY_TEST_ACCESS_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_ACCESS_TOKEN }}
+ MB_BIGQUERY_TEST_REFRESH_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_REFRESH_TOKEN }}
+ MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON }}
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test BigQuery Cloud SDK driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-bigquery-cloud-sdk-ee'
+
+ be-tests-druid-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: buildjet-2vcpu-ubuntu-2004
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: druid
+ services:
+ druid:
+ image: metabase/druid:0.20.2
+ ports:
+ - "8082:8082"
+ env:
+ CLUSTER_SIZE: nano-quickstart
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Druid driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-druid-ee'
+
+ be-tests-googleanalytics-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: googleanalytics
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Google Analytics driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-googleanalytics-ee'
+
+ be-tests-google-related-classpath-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ strategy:
+ matrix:
+ driver: ['googleanalytics', 'bigquery-cloud-sdk']
+ env:
+ CI: 'true'
+ DRIVERS: ${{ matrix.driver }}
+ MB_BIGQUERY_TEST_PROJECT_ID: ${{ secrets.BIGQUERY_TEST_PROJECT_ID }}
+ MB_BIGQUERY_TEST_CLIENT_ID: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_ID }}
+ MB_BIGQUERY_TEST_CLIENT_SECRET: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_SECRET }}
+ MB_BIGQUERY_TEST_ACCESS_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_ACCESS_TOKEN }}
+ MB_BIGQUERY_TEST_REFRESH_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_REFRESH_TOKEN }}
+ MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON }}
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Google Related Classpath drivers
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-${{ matrix.driver }}-classpath-ee'
+ test-args: ':only "[metabase.query-processor-test.expressions-test metabase.driver.google-test metabase.driver.googleanalytics-test]"'
+
+ be-tests-mariadb-10-2-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mysql
+ MB_DB_TYPE: mysql
+ MB_DB_HOST: localhost
+ MB_DB_PORT: 3306
+ MB_DB_DBNAME: circle_test
+ MB_DB_USER: root
+ MB_MYSQL_TEST_USER: root
+ services:
+ mariadb:
+ image: circleci/mariadb:10.2.23
+ ports:
+ - "3306:3306"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test MariaDB driver (10.2)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mariadb-10-2-ee'
+
+ be-tests-mariadb-latest-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mysql
+ MB_DB_TYPE: mysql
+ MB_DB_HOST: localhost
+ MB_DB_PORT: 3306
+ MB_DB_DBNAME: circle_test
+ MB_DB_USER: root
+ MB_MYSQL_TEST_USER: root
+ services:
+ mariadb:
+ image: circleci/mariadb:latest
+ ports:
+ - "3306:3306"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test MariaDB driver (latest)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mariadb-latest-ee'
+
+ be-tests-mongo-4-0-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mongo
+ services:
+ mongodb:
+ image: metabase/qa-databases:mongo-sample-4.0
+ ports:
+ - "27017:27017"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test MongoDB driver (4.0)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mongo-4-0-ee'
+
+ be-tests-mongo-4-0-ssl-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mongo
+ MB_MONGO_TEST_USER: metabase
+ MB_MONGO_TEST_PASSWORD: metasample123
+ MB_TEST_MONGO_REQUIRES_SSL: true
+ steps:
+ - uses: actions/checkout@v3
+ - name: Spin up Mongo docker container
+ run: docker run -d -p 27017:27017 --name metamongo metabase/qa-databases:mongo-sample-4.0 mongod --dbpath /data/db2/ --sslMode requireSSL --sslPEMKeyFile /etc/mongo/metamongo.pem --sslCAFile /etc/mongo/metaca.crt
+ - name: Wait until the port 27017 is ready
+ run: while ! nc -z localhost 27017; do sleep 1; done
+ timeout-minutes: 5
+ - name: Make SSL certificates for Mongo available
+ run: |
+ curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metabase.crt \
+ -o ./test_resources/ssl/mongo/metabase.crt
+
+ curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metabase.key \
+ -o ./test_resources/ssl/mongo/metabase.key
+
+ curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metaca.crt \
+ -o ./test_resources/ssl/mongo/metaca.crt
+
+ - name: Test MongoDB SSL driver (4.0)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mongo-4-0-ee'
+
+ be-tests-mongo-5-0-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mongo
+ services:
+ mongodb:
+ image: metabase/qa-databases:mongo-sample-5.0
+ ports:
+ - "27017:27017"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test MongoDB driver (5.0)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mongo-5-0-ee'
+
+ be-tests-mongo-5-0-ssl-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mongo
+ MB_MONGO_TEST_USER: metabase
+ MB_MONGO_TEST_PASSWORD: metasample123
+ MB_TEST_MONGO_REQUIRES_SSL: true
+ steps:
+ - uses: actions/checkout@v3
+ - name: Spin up Mongo docker container
+ run: docker run -d -p 27017:27017 --name metamongo metabase/qa-databases:mongo-sample-5.0 mongod --dbpath /data/db2/ --tlsMode requireTLS --tlsCertificateKeyFile /etc/mongo/metamongo.pem --tlsCAFile /etc/mongo/metaca.crt
+ - name: Wait until the port 27017 is ready
+ run: while ! nc -z localhost 27017; do sleep 1; done
+ timeout-minutes: 5
+ - name: Make SSL certificates for Mongo available
+ run: |
+ curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metabase.crt \
+ -o ./test_resources/ssl/mongo/metabase.crt
+
+ curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metabase.key \
+ -o ./test_resources/ssl/mongo/metabase.key
+
+ curl https://raw.githubusercontent.com/metabase/metabase-qa/master/dbs/mongo/certificates/metaca.crt \
+ -o ./test_resources/ssl/mongo/metaca.crt
+
+ - name: Test MongoDB SSL driver (5.0)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mongo-5-0-ee'
+
+ be-tests-mongo-latest-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mongo
+ services:
+ mongodb:
+ image: circleci/mongo:latest
+ ports:
+ - "27017:27017"
+ env:
+ MONGO_INITDB_ROOT_USERNAME: metabase
+ MONGO_INITDB_ROOT_PASSWORD: metasample123
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test MongoDB driver (latest)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mongo-latest-ee'
+
+ be-tests-mysql-5-7-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mysql
+ MB_DB_TYPE: mysql
+ MB_DB_HOST: localhost
+ MB_DB_PORT: 3306
+ MB_DB_DBNAME: circle_test
+ MB_DB_USER: root
+ MB_MYSQL_TEST_USER: root
+ services:
+ mysql:
+ image: circleci/mysql:5.7.23
+ ports:
+ - "3306:3306"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test MySQL driver (5.7)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mysql-5-7-ee'
+
+ be-tests-mysql-latest-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: mysql
+ MB_DB_TYPE: mysql
+ MB_DB_HOST: localhost
+ MB_DB_PORT: 3306
+ MB_DB_DBNAME: circle_test
+ MB_DB_USER: root
+ MB_MYSQL_TEST_USER: root
+ # set up env vars for something named "MYSQL_SSL" to run MySQL SSL tests verifying connectivity with PEM cert
+ # they are deliberately given a different name to prevent them from affecting the regular test run against
+ # the configured MySQL instance, but there is one particular test (mysql-connect-with-ssl-and-pem-cert-test)
+ # that overrides the MB_MYSQL_TEST_* values with them
+ # the MYSQL_RDS_SSL_INSTANCE vars are defined as secrets and can be altered
+ MB_MYSQL_SSL_TEST_HOST: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_HOST }}
+ MB_MYSQL_SSL_TEST_SSL: true
+ MB_MYSQL_SSL_TEST_ADDITIONAL_OPTIONS: 'verifyServerCertificate=true'
+ # the contents of the ./resources/certificates/rds-combined-ca-bundle.pem file
+ MB_MYSQL_SSL_TEST_SSL_CERT: ${{ secrets.MB_MYSQL_SSL_TEST_SSL_CERT }}
+ MB_MYSQL_SSL_TEST_USER: metabase
+ MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
+ services:
+ mysql:
+ image: circleci/mysql:latest
+ ports:
+ - "3306:3306"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test MySQL driver (latest)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-mysql-latest-ee'
+
+ be-tests-oracle-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: oracle
+ MB_ORACLE_TEST_USER: metabase
+ MB_ORACLE_TEST_PASSWORD: ${{ secrets.MB_ORACLE_TEST_PASSWORD }}
+ MB_ORACLE_TEST_SID: ORCL
+ MB_ORACLE_TEST_HOST: ${{ secrets.MB_ORACLE_TEST_HOST }}
+ MB_ORACLE_SSL_TEST_SSL: true
+ MB_ORACLE_SSL_TEST_PORT: 2484
+ MB_ORACLE_SSL_TEST_SSL_USE_TRUSTSTORE: true
+ MB_ORACLE_SSL_TEST_SSL_TRUSTSTORE_PATH: './resources/certificates/rds_root_ca_truststore.jks'
+ MB_ORACLE_SSL_TEST_SSL_TRUSTSTORE_OPTIONS: local
+ MB_ORACLE_SSL_TEST_SSL_TRUSTSTORE_PASSWORD_VALUE: metabase
+ steps:
+ - uses: actions/checkout@v3
+ - name: Ensure truststore file
+ run: ls ${{ env.MB_ORACLE_SSL_TEST_SSL_TRUSTSTORE_PATH }}
+ - name: Make plugins directory
+ run: mkdir plugins
+ - name: Fetch JDBC driver
+ run: wget --output-document=plugins/ojdbc8.jar ${{ secrets.ORACLE_JDBC_JAR }}
+ - name: Test Oracle driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-oracle-ee'
+
+ be-tests-postgres-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: postgres
+ MB_DB_TYPE: postgres
+ MB_DB_PORT: 5432
+ MB_DB_HOST: localhost
+ MB_DB_DBNAME: circle_test
+ MB_DB_USER: circle_test
+ MB_POSTGRESQL_TEST_USER: circle_test
+ services:
+ postgres:
+ image: circleci/postgres:9.6-alpine
+ ports:
+ - "5432:5432"
+ env:
+ POSTGRES_USER: circle_test
+ POSTGRES_DB: circle_test
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Postgres driver (9.6)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-postgres-ee'
+
+ be-tests-postgres-latest-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: postgres
+ MB_DB_TYPE: postgres
+ MB_DB_PORT: 5432
+ MB_DB_HOST: localhost
+ MB_DB_DBNAME: circle_test
+ MB_DB_USER: circle_test
+ MB_POSTGRESQL_TEST_USER: circle_test
+ MB_POSTGRES_SSL_TEST_SSL: true
+ MB_POSTGRES_SSL_TEST_SSL_MODE: verify-full
+ MB_POSTGRES_SSL_TEST_SSL_ROOT_CERT_PATH: 'test-resources/certificates/us-east-2-bundle.pem'
+ services:
+ postgres:
+ image: circleci/postgres:latest
+ ports:
+ - "5432:5432"
+ env:
+ POSTGRES_USER: circle_test
+ POSTGRES_DB: circle_test
+ POSTGRES_HOST_AUTH_METHOD: trust
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Postgres driver (latest)
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-postgres-latest-ee'
+
+ be-tests-presto-186-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: presto
+ services:
+ presto:
+ image: metabase/presto-mb-ci:0.186
+ ports:
+ - "8080:8080"
+ env:
+ JAVA_TOOL_OPTIONS: "-Xmx2g"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Presto 0.186 driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-presto-186-ee'
+
+ be-tests-presto-jdbc-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: presto-jdbc
+ MB_PRESTO_JDBC_TEST_CATALOG: test_data
+ MB_PRESTO_JDBC_TEST_HOST: localhost
+ MB_PRESTO_JDBC_TEST_PORT: 8443
+ MB_PRESTO_JDBC_TEST_SSL: true
+ MB_PRESTO_JDBC_TEST_USER: metabase
+ MB_PRESTO_JDBC_TEST_PASSWORD: metabase
+ MB_ENABLE_PRESTO_JDBC_DRIVER: true
+ MB_PRESTO_JDBC_TEST_ADDITIONAL_OPTIONS: 'SSLTrustStorePath=/tmp/cacerts-with-presto-ssl.jks&SSLTrustStorePassword=changeit'
+ services:
+ presto:
+ image: metabase/presto-mb-ci:latest # version 0.254
+ ports:
+ - "8443:8443"
+ env:
+ JAVA_TOOL_OPTIONS: "-Xmx2g"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Wait for port ${{ env.MB_PRESTO_JDBC_TEST_PORT }} to be ready
+ run: while ! nc -z localhost ${{ env.MB_PRESTO_JDBC_TEST_PORT }}; do sleep 0.1; done
+ timeout-minutes: 15
+ - name: Create temp cacerts file based on bundled JDK one
+ run: cp $JAVA_HOME/lib/security/cacerts /tmp/cacerts-with-presto-ssl.jks
+ - name: Capture Presto server self signed CA
+ run: |
+ while [[ ! -s /tmp/presto-ssl-ca.pem ]];
+ do echo "Waiting to capture SSL CA" \
+ && openssl s_client -connect localhost:8443 2>/dev/null /tmp/presto-ssl-ca.pem \
+ && sleep 1; done
+ - name: Convert Presto CA from PEM to DER
+ run: openssl x509 -outform der -in /tmp/presto-ssl-ca.pem -out /tmp/presto-ssl-ca.der
+ - name: Add write permission on cacerts file
+ run: chmod u+w /tmp/cacerts-with-presto-ssl.jks
+ - name: Import Presto CA into temp cacerts file
+ run: |
+ keytool -noprompt -import -alias presto -keystore /tmp/cacerts-with-presto-ssl.jks \
+ -storepass changeit -file /tmp/presto-ssl-ca.der -trustcacerts
+ - name: Test Presto JDBC driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-presto-jdbc-ee'
+ - name: Capture max memory usage
+ run: cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
+
+ be-tests-redshift-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: redshift
+ MB_REDSHIFT_TEST_USER: metabase_ci
+ MB_REDSHIFT_TEST_DB: testdb
+ MB_REDSHIFT_TEST_HOST: ${{ secrets.MB_REDSHIFT_TEST_HOST }}
+ MB_REDSHIFT_TEST_PASSWORD: ${{ secrets.MB_REDSHIFT_TEST_PASSWORD }}
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Redshift driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-redshift-ee'
+
+ be-tests-snowflake-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ 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 }}
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Snowflake driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-snowflake-ee'
+
+ be-tests-sparksql-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: buildjet-2vcpu-ubuntu-2004
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: sparksql
+ services:
+ sparksql:
+ image: metabase/spark:3.2.1
+ ports:
+ - "10000:10000"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test Spark driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-sparksql-ee'
+
+ be-tests-sqlite-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: sqlite
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test SQLite driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-sqlite-ee'
+
+ be-tests-sqlserver-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: sqlserver
+ MB_SQLSERVER_TEST_HOST: localhost
+ MB_SQLSERVER_TEST_PASSWORD: 'P@ssw0rd'
+ MB_SQLSERVER_TEST_USER: SA
+ services:
+ sqlserver:
+ image: mcr.microsoft.com/mssql/server:2017-latest
+ ports:
+ - "1433:1433"
+ env:
+ ACCEPT_EULA: Y
+ SA_PASSWORD: 'P@ssw0rd'
+ MSSQL_MEMORY_LIMIT_MB: 1024
+ steps:
+ - uses: actions/checkout@v3
+ - name: Test MS SQL Server driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-sqlserver-ee'
+
+ be-tests-vertica-ee:
+ if: github.event.pull_request.draft == false
+ runs-on: ubuntu-20.04
+ timeout-minutes: 60
+ env:
+ CI: 'true'
+ DRIVERS: vertica
+ services:
+ vertica:
+ image: sumitchawla/vertica
+ ports:
+ - "5433:5433"
+ steps:
+ - uses: actions/checkout@v3
+ - name: Make plugins directory
+ run: mkdir plugins
+ - name: Fetch JDBC driver
+ run: wget --output-document=plugins/vertica-jdbc-7.1.2-0.jar ${{ secrets.VERTICA_JDBC_JAR }}
+ - name: Test Vertica driver
+ uses: ./.github/actions/test-driver
+ with:
+ junit-name: 'be-tests-vertica-ee'
diff --git a/.github/workflows/e2e-main.yml b/.github/workflows/e2e-main.yml
index 26c1b1005d44..3509d2b39230 100644
--- a/.github/workflows/e2e-main.yml
+++ b/.github/workflows/e2e-main.yml
@@ -10,6 +10,7 @@ on:
- "**.md"
- ".circleci/**"
- "**.unit.spec.*"
+ - "**_test.clj"
jobs:
@@ -43,7 +44,7 @@ jobs:
runs-on: ubuntu-20.04
timeout-minutes: 45
needs: build
- name: e2e-tests-${{ matrix.folder }}-${{ matrix.edition }}
+ name: e2e-tests-${{ matrix.folder }}${{ matrix.context }}-${{ matrix.edition }}
env:
MB_EDITION: ${{ matrix.edition }}
DISPLAY: ""
@@ -51,14 +52,12 @@ jobs:
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
MB_SNOWPLOW_AVAILABLE: true
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
- RECORDING_ENABLED: ${{ secrets.CURRENTS_KEY }}
- CYPRESS_DEPLOYSENTINEL_KEY: ${{ secrets.CYPRESS_DEPLOYSENTINEL_KEY }}
ELECTRON_EXTRA_LAUNCH_ARGS: '--remote-debugging-port=40500' # deploysentinel
strategy:
fail-fast: false
matrix:
java-version: [11]
- edition: [oss, ee]
+ edition: [ee]
folder:
- "admin"
- "binning"
@@ -79,6 +78,10 @@ jobs:
- "question"
- "sharing"
- "visualizations"
+ include:
+ - edition: oss
+ context: grep
+ java-version: 11
services:
maildev:
image: maildev/maildev:1.1.0
@@ -118,6 +121,11 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
+ # Test runs will be recorded using Deploysentinel on `master` only
+ - name: Set conditional ENVs
+ if: github.ref == 'refs/heads/master'
+ run: |
+ echo "CYPRESS_DEPLOYSENTINEL_KEY=${{ secrets.CYPRESS_DEPLOYSENTINEL_KEY }}" >> $GITHUB_ENV
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK ${{ matrix.java-version }}
@@ -139,19 +147,18 @@ jobs:
jar xf target/uberjar/metabase.jar version.properties
mv version.properties resources/
- - name: Run Cypress tests on ${{ matrix.folder }} - Master Branch
- if: github.ref == 'refs/heads/master' && env.RECORDING_ENABLED != null
+ - name: Run OSS-specific Cypress tests
+ if: matrix.edition == 'oss'
run: |
yarn run test-cypress-run \
- --folder ${{ matrix.folder }} \
- --record --key ${{ secrets.CURRENTS_KEY }} \
- --group ${{ matrix.folder }}-${{ matrix.edition }} \
- --ci-build-id "${{ github.run_id }}-${{ github.run_attempt }}"
+ --env grepTags=@OSS \
+ --spec './frontend/test/metabase/scenarios/**/*.cy.spec.js'
env:
TERM: xterm
- - name: Run Cypress tests on ${{ matrix.folder }}
- if: ${{ github.ref != 'refs/heads/master' }}
+ # These are EE-specific and version-agnostic tests
+ - name: Run Cypress E2E tests against EE uberjar
+ if: matrix.edition == 'ee'
run: |
yarn run test-cypress-run \
--folder ${{ matrix.folder }}
@@ -162,7 +169,7 @@ jobs:
uses: actions/upload-artifact@v2
if: failure()
with:
- name: cypress-artifacts-${{ matrix.folder }}-${{ matrix.edition }}
+ name: cypress-recording-${{ matrix.folder }}${{ matrix.context }}-${{ matrix.edition }}
path: |
./cypress
./logs/test.log
diff --git a/.github/workflows/e2e-tests-skipped-checks.yml b/.github/workflows/e2e-tests-skipped-checks.yml
index 3726003cb772..a3a0f96dbe4e 100644
--- a/.github/workflows/e2e-tests-skipped-checks.yml
+++ b/.github/workflows/e2e-tests-skipped-checks.yml
@@ -11,6 +11,7 @@ on:
- "**.md"
- ".circleci/**"
- "**.unit.spec.*"
+ - "**_test.clj"
jobs:
e2e-tests:
diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
index 681be2211865..c9ae0a65e059 100644
--- a/.github/workflows/e2e-tests.yml
+++ b/.github/workflows/e2e-tests.yml
@@ -8,6 +8,7 @@ on:
- "**.md"
- ".circleci/**"
- "**.unit.spec.*"
+ - "**_test.clj"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -54,6 +55,7 @@ jobs:
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
MB_SNOWPLOW_AVAILABLE: true
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
+ ELECTRON_EXTRA_LAUNCH_ARGS: '--remote-debugging-port=40500' # deploysentinel
strategy:
fail-fast: false
matrix:
@@ -122,6 +124,11 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
+ - name: Set conditional ENVs
+ run: |
+ if [[ ${{ github.event.pull_request.base.ref }} != release* ]]; then
+ echo "CYPRESS_DEPLOYSENTINEL_KEY=${{ secrets.CYPRESS_DEPLOYSENTINEL_KEY }}" >> $GITHUB_ENV
+ fi
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK ${{ matrix.java-version }}
@@ -147,7 +154,7 @@ jobs:
if: matrix.edition == 'oss'
run: |
yarn run test-cypress-run \
- --env grepTags=@OSS,grepFilterSpecs=true \
+ --env grepTags=@OSS \
--spec './frontend/test/metabase/scenarios/**/*.cy.spec.js'
env:
TERM: xterm
diff --git a/.github/workflows/frontend-skipped-checks.yml b/.github/workflows/frontend-skipped-checks.yml
index 86cf5c9cb9a3..76a40c5e32b9 100644
--- a/.github/workflows/frontend-skipped-checks.yml
+++ b/.github/workflows/frontend-skipped-checks.yml
@@ -1,11 +1,37 @@
+# Required checks with path filtering rules will block pull requests from merging if they change only the excluded files.
+# This is a workaround to allow the PR to be merged.
+# https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Frontend
on:
+ push:
+ branches:
+ - 'master'
+ - 'release-**'
+ paths:
+ # documentation
+ - "docs/**"
+ - "**.md"
+ # backend
+ - "enterprise/backend/**"
+ - "src/**"
+ - "test/**"
+ - "*modules/**" # modules/, test_modules/
+ # E2E
+ - "**.cy.*.js" # .cy.spec.js, .cy.snap.js
+ - "frontend/test/__support__/e2e/**"
+ - "frontend/test/__runner__/*cypress*"
pull_request:
paths:
+ # documentation
- "docs/**"
- "**.md"
- - "**_test.clj"
+ # backend
+ - "enterprise/backend/**"
+ - "src/**"
+ - "test/**"
+ - "*modules/**" # modules/, test_modules/
+ # E2E
- "**.cy.*.js" # .cy.spec.js, .cy.snap.js
- "frontend/test/__support__/e2e/**"
- "frontend/test/__runner__/*cypress*"
diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml
index 057c46e129da..85bccab7645d 100644
--- a/.github/workflows/frontend.yml
+++ b/.github/workflows/frontend.yml
@@ -5,11 +5,31 @@ on:
branches:
- 'master'
- 'release-**'
+ paths-ignore:
+ # documentation
+ - "docs/**"
+ - "**.md"
+ # backend
+ - "enterprise/backend/**"
+ - "src/**"
+ - "test/**"
+ - "*modules/**" # modules/, test_modules/
+ # E2E
+ - "**.cy.*.js" # .cy.spec.js, .cy.snap.js
+ - "frontend/test/__support__/e2e/**"
+ - "frontend/test/__runner__/*cypress*"
pull_request:
+ types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
+ # documentation
- "docs/**"
- "**.md"
- - "**_test.clj"
+ # backend
+ - "enterprise/backend/**"
+ - "src/**"
+ - "test/**"
+ - "*modules/**" # modules/, test_modules/
+ # E2E
- "**.cy.*.js" # .cy.spec.js, .cy.snap.js
- "frontend/test/__support__/e2e/**"
- "frontend/test/__runner__/*cypress*"
@@ -46,6 +66,7 @@ jobs:
name: Check types
fe-tests-unit:
+ if: github.event.pull_request.draft == false
runs-on: buildjet-2vcpu-ubuntu-2004
timeout-minutes: 20
steps:
@@ -61,6 +82,7 @@ jobs:
flags: front-end
fe-tests-timezones:
+ if: github.event.pull_request.draft == false
runs-on: ubuntu-20.04
timeout-minutes: 14
steps:
@@ -71,6 +93,7 @@ jobs:
name: Run frontend timezones tests
fe-chromatic:
+ if: github.event.pull_request.draft == false
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml
new file mode 100644
index 000000000000..2e09e6c6563f
--- /dev/null
+++ b/.github/workflows/pre-release.yml
@@ -0,0 +1,171 @@
+name: Pre-release [WIP]
+
+on:
+ workflow_dispatch:
+ inputs:
+ commit:
+ description: 'A full-length commit SHA-1 hash'
+ required: true
+
+env:
+ MAX_HASH_LENGTH: 8
+ CUSTOM_REPO: ${{ secrets.CUSTOM_RELEASE_REPO }}
+
+jobs:
+ build:
+ if: ${{ github.repository }} != 'metabase/metabase'
+ name: Build Metabase ${{ matrix.edition }} @${{ github.event.inputs.commit }}
+ runs-on: ubuntu-20.04
+ timeout-minutes: 40
+ strategy:
+ matrix:
+ edition: [oss, ee]
+ env:
+ MB_EDITION: ${{ matrix.edition }}
+ INTERACTIVE: false
+ steps:
+ - name: Fail early if custom docker relaese repo is missing
+ if: ${{ env.CUSTOM_REPO == null }}
+ run: exit 1
+ - name: Check out the code
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ github.event.inputs.commit }}
+ - name: Prepare front-end environment
+ uses: ./.github/actions/prepare-frontend
+ - name: Prepare back-end environment
+ uses: ./.github/actions/prepare-backend
+ - name: Build
+ run: ./bin/build
+ - name: Prepare uberjar artifact
+ uses: ./.github/actions/prepare-uberjar-artifact
+
+ check-uberjar-health:
+ runs-on: ubuntu-20.04
+ name: Is ${{ matrix.edition }} (java ${{ matrix.java-version }}) healthy?
+ needs: build
+ timeout-minutes: 10
+ strategy:
+ matrix:
+ edition: [oss, ee]
+ java-version: [11, 17]
+ steps:
+ - name: Prepare JRE (Java Run-time Environment)
+ uses: actions/setup-java@v3
+ with:
+ java-package: jre
+ java-version: ${{ matrix.java-version }}
+ distribution: 'temurin'
+ - run: java -version
+ - uses: actions/download-artifact@v2
+ name: Retrieve uberjar artifact
+ with:
+ name: metabase-${{ matrix.edition }}-uberjar
+ - name: Launch uberjar (and keep it running)
+ run: java -jar ./target/uberjar/metabase.jar &
+ - name: Wait for Metabase to start
+ run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
+
+ containerize:
+ runs-on: ubuntu-20.04
+ needs: check-uberjar-health
+ timeout-minutes: 15
+ strategy:
+ matrix:
+ edition: [oss, ee]
+ services:
+ registry:
+ image: registry:2
+ ports:
+ - 5000:5000
+ steps:
+ - name: Check out the code
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ github.event.inputs.commit }}
+ - name: Truncate commit hash
+ run: |
+ commit_id=${{ github.event.inputs.commit }}
+ truncated_hash=${commit_id:0:${{ env.MAX_HASH_LENGTH }}}
+
+ echo "COMMIT_IDENTIFIER=$truncated_hash" >> $GITHUB_ENV
+ shell: bash
+ - uses: actions/download-artifact@v3
+ name: Retrieve uberjar artifact
+ with:
+ name: metabase-${{ matrix.edition }}-uberjar
+ - name: Move the Uberjar to the context dir
+ run: mv ./target/uberjar/metabase.jar bin/docker/.
+ - name: Set up Docker Buildx
+ id: buildx
+ uses: docker/setup-buildx-action@v2
+ with:
+ driver-opts: network=host
+ - name: Build ${{ matrix.edition }} container
+ uses: docker/build-push-action@v3
+ with:
+ context: bin/docker/.
+ platforms: linux/amd64
+ network: host
+ tags: localhost:5000/local-metabase:${{ env.COMMIT_IDENTIFIER }}-${{ matrix.edition }}
+ no-cache: true
+ push: true
+
+ - name: Launch container
+ run: docker run --rm -dp 3000:3000 localhost:5000/local-metabase:${{ env.COMMIT_IDENTIFIER }}-${{ matrix.edition }}
+ timeout-minutes: 5
+ - name: Wait for Metabase to start
+ run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
+ timeout-minutes: 3
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
+ - name: Determine the target Docker Hub repository
+ run: |
+ echo "DOCKERHUB_REPO=${{ github.repository_owner }}/${{ env.CUSTOM_REPO }}" >> $GITHUB_ENV
+ echo "IMAGE_NAME=${{ env.COMMIT_IDENTIFIER }}-${{ matrix.edition }}" >> $GITHUB_ENV
+ - name: Retag and push container image to Docker Hub
+ run: |
+ echo "Pushing container image ${{ env.IMAGE_NAME}} to ${{ env.DOCKERHUB_REPO }} ..."
+ docker tag localhost:5000/local-metabase:${{ env.IMAGE_NAME }} ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_NAME }}
+ docker push ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_NAME }}
+ echo "Finished!"
+
+ verify-docker-pull:
+ runs-on: ubuntu-20.04
+ needs: containerize
+ timeout-minutes: 15
+ strategy:
+ matrix:
+ edition: [oss, ee]
+ steps:
+ - name: Truncate commit hash
+ run: |
+ commit_id=${{ github.event.inputs.commit }}
+ truncated_hash=${commit_id:0:${{ env.MAX_HASH_LENGTH }}}
+
+ echo "COMMIT_IDENTIFIER=$truncated_hash" >> $GITHUB_ENV
+ shell: bash
+ - name: Login to Docker Hub # authenticated, to avoid being rate-throttled
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
+ - name: Determine the container image to pull
+ run: |
+ echo "DOCKERHUB_REPO=${{ github.repository_owner }}/${{ env.CUSTOM_REPO }}" >> $GITHUB_ENV
+ echo "IMAGE_NAME=${{ env.COMMIT_IDENTIFIER }}-${{ matrix.edition }}" >> $GITHUB_ENV
+ - name: Pull the container image
+ run: |
+ echo "Pulling container image ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_NAME }} ..."
+ docker pull ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_NAME }}
+ echo "Successful!"
+ - name: Launch container
+ run: docker run --rm -dp 3000:3000 ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_NAME }}
+ timeout-minutes: 5
+ - name: Wait for Metabase to start
+ run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
+ timeout-minutes: 3
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bbfde2af77f9..3bf3c838253f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,51 +1,49 @@
name: Release [WIP]
on:
- workflow_dispatch:
- inputs:
- commit:
- description: 'A commit hash'
- required: true
- oss-tag:
- description: 'OSS version tag'
- required: true
- ee-tag:
- description: 'EE version tag'
- required: true
+ push:
+ tags:
+ - 'v*'
jobs:
- build:
- name: Build Metabase ${{ matrix.edition }} @${{ github.event.inputs.commit }}
+
+ download-uberjar:
runs-on: ubuntu-20.04
- timeout-minutes: 40
- strategy:
- matrix:
- edition: [oss, ee]
- env:
- MB_EDITION: ${{ matrix.edition }}
- INTERACTIVE: false
+ timeout-minutes: 10
steps:
- - name: Check out the code
- uses: actions/checkout@v3
+ - name: Download Uberjar for ${{ github.ref_name }}
+ run: |
+ JAR_DOWNLOAD_URL=https://downloads.metabase.com/${{ github.ref_name }}/metabase.jar
+ if [[ ${{ github.ref_name }} == v1* ]]; then
+ JAR_DOWNLOAD_URL=https://downloads.metabase.com/enterprise/${{ github.ref_name }}/metabase.jar
+ fi
+ echo $JAR_DOWNLOAD_URL > url.txt
+ echo "----- Downloading Uberjar from $JAR_DOWNLOAD_URL -----"
+ curl -OL $JAR_DOWNLOAD_URL
+ stat ./metabase.jar
+ date | tee timestamp
+ - name: Verify that this is a valid JAR file
+ run: file --mime-type ./metabase.jar | grep "application/zip"
+ - name: Reveal its version.properties
+ run: jar xf metabase.jar version.properties && cat version.properties
+ - name: Calculate SHA256 checksum
+ run: sha256sum ./metabase.jar | tee SHA256.sum
+ - name: Upload Uberjar as artifact
+ uses: actions/upload-artifact@v3
with:
- ref: ${{ github.event.inputs.commit }}
- - name: Prepare front-end environment
- uses: ./.github/actions/prepare-frontend
- - name: Prepare back-end environment
- uses: ./.github/actions/prepare-backend
- - name: Build
- run: ./bin/build
- - name: Prepare uberjar artifact
- uses: ./.github/actions/prepare-uberjar-artifact
+ name: metabase-uberjar-${{ github.ref_name }}
+ path: |
+ ./metabase.jar
+ ./url.txt
+ ./timestamp
+ ./SHA256.sum
- check-uberjar-health:
+ check-uberjar:
runs-on: ubuntu-20.04
- name: Is ${{ matrix.edition }} (java ${{ matrix.java-version }}) healthy?
- needs: build
+ needs: download-uberjar
timeout-minutes: 10
strategy:
matrix:
- edition: [oss, ee]
java-version: [11, 17]
steps:
- name: Prepare JRE (Java Run-time Environment)
@@ -54,62 +52,60 @@ jobs:
java-package: jre
java-version: ${{ matrix.java-version }}
distribution: 'temurin'
- - run: java -version
- - uses: actions/download-artifact@v2
- name: Retrieve uberjar artifact
+ - uses: actions/download-artifact@v3
+ name: Retrieve previously downloaded Uberjar
with:
- name: metabase-${{ matrix.edition }}-uberjar
- - name: Launch uberjar (and keep it running)
- run: java -jar ./target/uberjar/metabase.jar &
+ name: metabase-uberjar-${{ github.ref_name }}
+ - name: Reveal its version.properties
+ run: jar xf metabase.jar version.properties && cat version.properties
+ - name: Display when and where it was downloaded
+ run: |
+ cat timestamp
+ cat url.txt
+ - name: Show the checksum
+ run: cat SHA256.sum
+ - name: Launch Metabase Uberjar (and keep it running)
+ run: java -jar ./metabase.jar &
- name: Wait for Metabase to start
- run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
+ run: while ! curl -s localhost:3000/api/health; do sleep 1; done
+ timeout-minutes: 3
+ - name: Check API health
+ run: curl -s localhost:3000/api/health
containerize:
runs-on: ubuntu-20.04
- needs: check-uberjar-health
+ needs: check-uberjar
timeout-minutes: 15
- strategy:
- matrix:
- edition: [oss, ee]
services:
registry:
image: registry:2
ports:
- 5000:5000
steps:
- - name: Set the image tag based on the edition
- run: |
- if [[ ${{ matrix.edition }} == ee ]]; then
- echo "IMAGE_TAG=${{ github.event.inputs.ee-tag }}" >> $GITHUB_ENV
- else
- echo "IMAGE_TAG=${{ github.event.inputs.oss-tag }}" >> $GITHUB_ENV
- fi
- uses: actions/checkout@v3
+ - uses: actions/download-artifact@v3
+ name: Retrieve previously downloaded Uberjar
with:
- ref: ${{ github.event.inputs.commit }}
- - uses: actions/download-artifact@v2
- name: Retrieve uberjar artifact
- with:
- name: metabase-${{ matrix.edition }}-uberjar
+ name: metabase-uberjar-${{ github.ref_name }}
- name: Move the Uberjar to the context dir
- run: mv ./target/uberjar/metabase.jar bin/docker/.
+ run: mv ./metabase.jar bin/docker/.
- name: Set up Docker Buildx
id: buildx
- uses: docker/setup-buildx-action@v1
+ uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Build ${{ matrix.edition }} container
- uses: docker/build-push-action@v2
+ uses: docker/build-push-action@v3
with:
context: bin/docker/.
platforms: linux/amd64
network: host
- tags: localhost:5000/local-metabase:${{ env.IMAGE_TAG }}
+ tags: localhost:5000/local-metabase:${{ github.ref_name }}
no-cache: true
push: true
- name: Launch container
- run: docker run --rm -dp 3000:3000 localhost:5000/local-metabase:${{ env.IMAGE_TAG }}
+ run: docker run --rm -dp 3000:3000 localhost:5000/local-metabase:${{ github.ref_name }}
timeout-minutes: 5
- name: Wait for Metabase to start
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
@@ -117,25 +113,51 @@ jobs:
- name: Determine the target Docker Hub repository
run: |
- if [[ ${{ github.repository }} == 'metabase/metabase' ]]; then
- if [[ ${{ matrix.edition }} == ee ]]; then
- echo "Metabase EE: image ${{ env.IMAGE_TAG }} is going to be pushed to metabase/test-metabase-enterprise"
- echo "DOCKERHUB_REPO=metabase/test-metabase-enterprise" >> $GITHUB_ENV
- else
- echo "Metabase OSS: image ${{ env.IMAGE_TAG }} is going to be pushed to metabase/test-metabase"
- echo "DOCKERHUB_REPO=metabase/test-metabase" >> $GITHUB_ENV
- fi
+ if [[ ${{ github.ref_name }} == v1* ]]; then
+ echo "Metabase EE: image is going to be pushed to ${{ github.repository_owner }}/metabase-enterprise"
+ echo "DOCKERHUB_REPO=${{ github.repository_owner }}/metabase-enterprise" >> $GITHUB_ENV
else
- echo "DOCKERHUB_REPO=${{ github.repository_owner }}/${{ secrets.CUSTOM_RELEASE_REPO }}" >> $GITHUB_ENV
+ echo "Metabase OSS: image is going to be pushed to ${{ github.repository_owner }}/metabase"
+ echo "DOCKERHUB_REPO=${{ github.repository_owner }}/metabase" >> $GITHUB_ENV
fi
+
- name: Login to Docker Hub
- uses: docker/login-action@v1
+ uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- - name: Retag and push container image to Metabase Docker Hub
+ - name: Retag and push container image to Docker Hub
run: |
- echo "Pushing ${{ env.IMAGE_TAG }} to ${{ env.DOCKERHUB_REPO }} ..."
- docker tag localhost:5000/local-metabase:${{ env.IMAGE_TAG }} ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_TAG }}
- docker push ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_TAG }}
+ echo "Pushing ${{ github.ref_name }} to ${{ env.DOCKERHUB_REPO }} ..."
+ docker tag localhost:5000/local-metabase:${{ github.ref_name }} ${{ env.DOCKERHUB_REPO }}:${{ github.ref_name }}
+ docker push ${{ env.DOCKERHUB_REPO }}:${{ github.ref_name }}
echo "Finished!"
+
+ verify-docker-pull:
+ runs-on: ubuntu-20.04
+ needs: containerize
+ timeout-minutes: 15
+ steps:
+ - name: Login to Docker Hub # authenticated, to avoid being rate-throttled
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
+ - name: Determine the container image to pull
+ run: |
+ if [[ ${{ github.ref_name }} == v1* ]]; then
+ echo "DOCKERHUB_REPO=${{ github.repository_owner }}/metabase-enterprise" >> $GITHUB_ENV
+ else
+ echo "DOCKERHUB_REPO=${{ github.repository_owner }}/metabase" >> $GITHUB_ENV
+ fi
+ - name: Pull the container image
+ run: |
+ echo "Pulling container image ${{ env.DOCKERHUB_REPO }}:${{ github.ref_name }} ..."
+ docker pull ${{ env.DOCKERHUB_REPO }}:${{ github.ref_name }}
+ echo "Successful!"
+ - name: Launch container
+ run: docker run --rm -dp 3000:3000 ${{ env.DOCKERHUB_REPO }}:${{ github.ref_name }}
+ timeout-minutes: 5
+ - name: Wait for Metabase to start
+ run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
+ timeout-minutes: 3
diff --git a/.github/workflows/rerun-workflows.yml b/.github/workflows/rerun-workflows.yml
new file mode 100644
index 000000000000..1796bfbd1315
--- /dev/null
+++ b/.github/workflows/rerun-workflows.yml
@@ -0,0 +1,31 @@
+name: Rerun Flaky Workflows
+
+on:
+ workflow_run:
+ workflows: [Backend, Driver Tests, E2E Tests, Frontend]
+ types: [completed]
+ branches: [master, 'release-**']
+
+jobs:
+ rerun-on-failure:
+ runs-on: ubuntu-latest
+ if: ${{ github.event.workflow_run.conclusion }} == 'failure'
+ steps:
+ - uses: actions/github-script@v6
+ with:
+ script: |
+ const MAX_ATTEMPTS = 2;
+ const ATTEMPT = ${{ github.event.workflow_run.run_attempt }};
+
+ if (ATTEMPT <= MAX_ATTEMPTS) {
+ console.log("Rerruning...");
+
+ github.rest.actions.reRunWorkflowFailedJobs({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ run_id: ${{ github.event.workflow_run.id }},
+ });
+ } else {
+ console.log("Rerunning didn't help!");
+ console.log("Please check workflow " + ${{ github.event.workflow_run.id }});
+ }
diff --git a/.github/workflows/uberjar.yml b/.github/workflows/uberjar.yml
index c45ac6203341..a854abe17521 100644
--- a/.github/workflows/uberjar.yml
+++ b/.github/workflows/uberjar.yml
@@ -4,10 +4,11 @@ on:
push:
paths-ignore:
- 'docs/**'
- - 'frontend/test/**'
- - 'enterprise/frontend/test/**'
+ - "**.md"
+ - '**/frontend/test/**'
- ".**"
- "test*"
+ - "**_test.clj"
jobs:
build:
@@ -105,7 +106,7 @@ jobs:
run: docker ps
- name: Wait for Metabase to start and reach 100% health
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
- timeout-minutes: 1
+ timeout-minutes: 3
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
@@ -126,6 +127,8 @@ jobs:
- name: Run Trivy vulnerability scanner if master or main (ee)
if: ${{ (github.ref_name == 'master' || github.ref_name == 'main') && matrix.edition == 'ee' }}
uses: aquasecurity/trivy-action@master
+ env:
+ TRIVY_OFFLINE_SCAN: true
with:
image-ref: docker.io/metabase/metabase-enterprise-head:latest
format: sarif
@@ -134,6 +137,8 @@ jobs:
- name: Run Trivy vulnerability scanner if master or main (oss)
if: ${{ (github.ref_name == 'master' || github.ref_name == 'main') && matrix.edition == 'oss' }}
uses: aquasecurity/trivy-action@master
+ env:
+ TRIVY_OFFLINE_SCAN: true
with:
image-ref: docker.io/metabase/metabase-head:latest
format: sarif
@@ -142,6 +147,8 @@ jobs:
- name: Run Trivy vulnerability scanner if dev branch
if: ${{ !(startsWith(github.ref_name,'master') || startsWith(github.ref_name,'main') || startsWith(github.ref_name,'backport')) && matrix.edition == 'ee' }}
uses: aquasecurity/trivy-action@master
+ env:
+ TRIVY_OFFLINE_SCAN: true
with:
image-ref: docker.io/metabase/metabase-dev:${{ steps.extract_branch.outputs.branch }}
format: sarif
diff --git a/.mlc_config.json b/.mlc_config.json
index f7f9a643895d..6770ef90a187 100644
--- a/.mlc_config.json
+++ b/.mlc_config.json
@@ -2,14 +2,14 @@
"ignorePatterns": [
{
"pattern": "^https://downloads.metabase.com"
+ },
+ {
+ "pattern": "^https://docs.cypress.io/"
}
],
"timeout": "180s",
"retryOn429": true,
"retryCount": 5,
"fallbackRetryDelay": "30s",
- "aliveStatusCodes": [
- 200,
- 206
- ]
+ "aliveStatusCodes": [200, 206]
}
diff --git a/Dockerfile b/Dockerfile
index 78162c045ace..ff8a24cb6d9a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -38,6 +38,9 @@ RUN apk add -U bash ttf-dejavu fontconfig curl java-cacerts && \
COPY --from=builder /home/circleci/target/uberjar/metabase.jar /app/
COPY bin/docker/run_metabase.sh /app/
+RUN curl -L https://github.com/ClickHouse/metabase-clickhouse-driver/releases/download/1.0.1/clickhouse.metabase-driver.jar > /plugins/clickhouse.metabase-driver.jar
+RUN chmod 744 /plugins/clickhouse.metabase-driver.jar
+
# expose our default runtime port
EXPOSE 3000
diff --git a/bin/build-drivers/deps.edn b/bin/build-drivers/deps.edn
index 72ba9ef3edf1..aa816924fe21 100644
--- a/bin/build-drivers/deps.edn
+++ b/bin/build-drivers/deps.edn
@@ -18,6 +18,13 @@
metabase/metabase-core {:local/root "../.."}
metabase/driver-modules {:local/root "../../modules/drivers"}}
+ ;; These are needed for the Athena and Redshift drivers in order to build them. Maven repos from subprojects do not
+ ;; get copied over -- see
+ ;; https://ask.clojure.org/index.php/10726/deps-manifest-dependencies-respect-repos-dependent-project
+ :mvn/repos
+ {"athena" {:url "https://s3.amazonaws.com/maven-athena"}
+ "redshift" {:url "https://s3.amazonaws.com/redshift-maven-repository/release"}}
+
:jvm-opts
["-XX:-OmitStackTraceInFastThrow"]
diff --git a/bin/build-mb/deps.edn b/bin/build-mb/deps.edn
index a43859f42428..8b4f8b752a62 100644
--- a/bin/build-mb/deps.edn
+++ b/bin/build-mb/deps.edn
@@ -9,6 +9,13 @@
;; value currently used in tools.build but top level since we directly depend on it
org.apache.maven/maven-model {:mvn/version "3.8.4"}}
+ ;; These are needed for the Athena and Redshift drivers in order to build them. Maven repos from subprojects do not
+ ;; get copied over -- see
+ ;; https://ask.clojure.org/index.php/10726/deps-manifest-dependencies-respect-repos-dependent-project
+ :mvn/repos
+ {"athena" {:url "https://s3.amazonaws.com/maven-athena"}
+ "redshift" {:url "https://s3.amazonaws.com/redshift-maven-repository/release"}}
+
:aliases
{:test {:extra-paths ["test"]
:extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
diff --git a/bin/i18n/import-po-from-poeditor b/bin/i18n/import-po-from-poeditor
index c5d81da761b4..033208393e0c 100755
--- a/bin/i18n/import-po-from-poeditor
+++ b/bin/i18n/import-po-from-poeditor
@@ -61,43 +61,29 @@ async function main(args) {
}
}
-async function promXhr(uri, options) {
- const xhr = new XMLHttpRequest();
- return new Promise((resolve, reject) => {
- xhr.open(options.method, uri, true);
- for (const headerName in options.headers) {
- xhr.setRequestHeader(headerName, options.headers[headerName]);
- }
- xhr.send(options.body);
- xhr.onload = () => {
- const responseObject = {
- status: xhr.status,
- response: xhr.responseText,
- };
- xhr.status >= 200 && xhr.status <= 299
- ? resolve(responseObject)
- : reject(responseObject);
- };
- });
-}
-
// simple API client for poeditor
function poeditor(command, params = {}) {
- const uri = url.format({
- protocol: "https",
- hostname: "api.poeditor.com",
- pathname: `/v2/${command}`,
- });
const query = {
api_token: POEDITOR_API_TOKEN,
id: POEDITOR_PROJECT_ID,
...params,
};
- return promXhr(uri, {
- method: "POST",
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- body: url.format({ query }).replace(/^\?/, ""),
- }).then(res => res.json());
+ return new Promise((resolve, reject) => {
+ const req = https.request({
+ hostname: "api.poeditor.com",
+ path: `/v2/${command}`,
+ method: "POST",
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
+ }, res => {
+ const chunks = [];
+ res.on("data", chunk => { chunks.push(chunk); });
+
+ res.on("end", () => { resolve(JSON.parse(Buffer.concat(chunks))) });
+ })
+ req.on('error',reject);
+ req.write(url.format({ query }).replace(/^\?/, ""));
+ req.end();
+ })
}
async function getExistingLanguages() {
diff --git a/bin/lint-migrations-file/src/change_set/strict.clj b/bin/lint-migrations-file/src/change_set/strict.clj
index 84772d420f4f..8504e5d70c8e 100644
--- a/bin/lint-migrations-file/src/change_set/strict.clj
+++ b/bin/lint-migrations-file/src/change_set/strict.clj
@@ -28,7 +28,54 @@
(apply distinct? (mapcat #(str/split (-> % :sql :dbms) #",")
changes))))))
+(def change-types-supporting-rollback
+ ;; This set was generated with a little grep and awk from the docs here:
+ ;; https://docs.liquibase.com/workflows/liquibase-community/liquibase-auto-rollback.html
+ ;;
+ ;; If a new change type is introduced that supports automatic rollback, it should be added
+ ;; to this set.
+ #{:addCheckConstraint
+ :addColumn
+ :addDefaultValue
+ :addForeignKeyConstraint
+ :addLookupTable
+ :addNotNullConstraint
+ :addPrimaryKey
+ :addUniqueConstraint
+ :createIndex
+ :createSequence
+ :createSynonym
+ :createTable
+ :createView
+ :disableCheckConstraint
+ :disableTrigger
+ :dropNotNullConstraint
+ :enableCheckConstraint
+ :enableTrigger
+ :renameColumn
+ :renameSequence
+ :renameTable
+ :renameTrigger
+ :renameView})
+
+(defn- major-version
+ "Returns major version from id string, e.g. 44 from \"v44.00-034\""
+ [id-str]
+ (when (string? id-str)
+ (some-> (re-find #"\d+" id-str) Integer/parseInt)))
+
+(defn- rollback-present-when-required?
+ "Ensures rollback key is present when change type doesn't support auto rollback"
+ [{:keys [id changes] :as change-set}]
+ (or
+ (int? id)
+ (< (major-version id) 45)
+ (some change-types-supporting-rollback (mapcat keys changes))
+ (contains? change-set :rollback)))
+
(s/def ::change-set
- (s/merge
- :change-set.common/change-set
- (s/keys :req-un [::changes ::comment])))
+ (s/and
+ rollback-present-when-required?
+ (s/merge
+ :change-set.common/change-set
+ (s/keys :req-un [::changes ::comment]))))
diff --git a/bin/lint-migrations-file/test/lint_migrations_file_test.clj b/bin/lint-migrations-file/test/lint_migrations_file_test.clj
index 6734d5481783..02693229df31 100644
--- a/bin/lint-migrations-file/test/lint_migrations_file_test.clj
+++ b/bin/lint-migrations-file/test/lint_migrations_file_test.clj
@@ -191,3 +191,21 @@
(mock-change-set
:id "v42.00-001"
:changes [(mock-add-column-changes :columns [(mock-column :type problem-type)])]))))))))
+
+(deftest require-rollback-test
+ (testing "change types with no automatic rollback support"
+ (testing "missing rollback key fails"
+ (is (thrown-with-msg?
+ clojure.lang.ExceptionInfo
+ #"rollback-present-when-required"
+ (validate (mock-change-set :id "v45.12-345" :changes [{:sql {:sql "select 1"}}])))))
+ (testing "nil rollback is allowed"
+ (is (= :ok (validate (mock-change-set :id "v45.12-345"
+ :changes [{:sql {:sql "select 1"}}]
+ :rollback nil)))))
+ (testing "rollback values are allowed"
+ (is (= :ok (validate (mock-change-set :id "v45.12-345"
+ :changes [{:sql {:sql "select 1"}}]
+ :rollback {:sql {:sql "select 1"}}))))))
+ (testing "change types with automatic rollback support are allowed"
+ (is (= :ok (validate (mock-change-set :id "v45.12-345" :changes [(mock-add-column-changes)]))))))
diff --git a/bin/release/deps.edn b/bin/release/deps.edn
index cffa79de3fec..7e3ad1c5f7c2 100644
--- a/bin/release/deps.edn
+++ b/bin/release/deps.edn
@@ -9,6 +9,13 @@
org.flatland/ordered {:mvn/version "1.5.7"}
stencil/stencil {:mvn/version "0.5.0"}}
+ ;; These are needed for the Athena and Redshift drivers in order to build them. Maven repos from subprojects do not
+ ;; get copied over -- see
+ ;; https://ask.clojure.org/index.php/10726/deps-manifest-dependencies-respect-repos-dependent-project
+ :mvn/repos
+ {"athena" {:url "https://s3.amazonaws.com/maven-athena"}
+ "redshift" {:url "https://s3.amazonaws.com/redshift-maven-repository/release"}}
+
:aliases
{:test {:extra-paths ["test"]
:extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
diff --git a/bin/release/src/release.clj b/bin/release/src/release.clj
index 4e4d41459cbe..395524bd9c6a 100644
--- a/bin/release/src/release.clj
+++ b/bin/release/src/release.clj
@@ -6,7 +6,6 @@
[release
[check-prereqs :as check-prereqs]
[common :as c]
- [docker :as docker]
[draft-release :as draft-release]
[elastic-beanstalk :as eb]
[git-tags :as git-tags]
@@ -21,10 +20,8 @@
(def ^:private steps*
(ordered-map/ordered-map
:build-uberjar uberjar/build-uberjar!
- :build-docker docker/build-docker-image!
- :push-git-tags git-tags/push-tags!
:upload-uberjar uberjar/upload-uberjar!
- :push-docker-image docker/push-docker-image!
+ :push-git-tags git-tags/push-tags!
:publish-draft-release draft-release/create-draft-release!
:update-heroku-buildpack heroku/update-heroku-buildpack!
:publish-elastic-beanstalk-artifacts eb/publish-elastic-beanstalk-artifacts!
diff --git a/bin/release/src/release/docker.clj b/bin/release/src/release/docker.clj
deleted file mode 100644
index 408209fb823a..000000000000
--- a/bin/release/src/release/docker.clj
+++ /dev/null
@@ -1,41 +0,0 @@
-(ns release.docker
- "Code related to building, pushing, and validating new Docker images."
- (:require [metabuild-common.core :as u]
- [release.common :as c]))
-
-(defn build-docker-image! []
- (u/step "Build Docker image"
- (let [docker-dir (u/filename c/root-directory "bin" "docker")
- uberjar-path (u/filename docker-dir "metabase.jar")]
- (u/delete-file-if-exists! uberjar-path)
- (u/copy-file! (u/assert-file-exists c/uberjar-path) uberjar-path)
- (u/assert-file-exists uberjar-path)
- (u/sh "docker" "build" "--no-cache" "--pull" "-t" (c/docker-tag) docker-dir))))
-
-(defn- validate-docker-image []
- (u/step "Validate Docker image"
- (u/announce "TODO")
- ;; image="$1"
- ;; docker pull "$image" > /dev/null
- ;; docker_hash=$(docker run --rm "$image" version | grep -Eo 'hash [0-9a-f]+' | awk '{ print $2 }')
- ;; check-equals "docker: image tagged "$image" commit hash" "$tag_hash" "$docker_hash"
-
- ;; check-docker-image "$METABASE_DOCKER_REPO:v$VERSION"
- ))
-
-(defn push-docker-image! []
- (u/step "Push Docker image"
- (u/sh "docker" "push" (c/docker-tag))
- (let [latest-tag (str (c/docker-image-name) ":latest")]
- (cond
- (c/pre-release-version?)
- (u/announce "Pre release version -- not pushing %s" latest-tag)
-
- (not (c/latest-version?))
- (u/announce "Version is not latest -- not pushing %s" latest-tag)
-
- :else
- (u/step (format "Pushing tag %s" latest-tag)
- (u/sh "docker" "tag" (c/docker-tag) latest-tag)
- (u/sh "docker" "push" latest-tag)))))
- (validate-docker-image))
diff --git a/codecov.yml b/codecov.yml
index 623d717692ee..50f529dc2efd 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -1,6 +1,6 @@
codecov:
bot: "codecov-io"
- require_ci_to_pass: no
+ require_ci_to_pass: false
coverage:
status:
diff --git a/deps.edn b/deps.edn
index 00f28fa71a12..2a714f425072 100644
--- a/deps.edn
+++ b/deps.edn
@@ -32,7 +32,8 @@
com.draines/postal {:mvn/version "2.0.5"} ; SMTP library
com.google.guava/guava {:mvn/version "31.0.1-jre"} ; dep for BigQuery, Spark, and GA. Require here rather than letting different dep versions stomp on each other — see comments on #9697
com.fasterxml.jackson.core/jackson-databind
- {:mvn/version "2.13.2.2"} ; JSON processor used by snowplow-java-tracker (pinned version due to CVE-2020-36518)
+ {:mvn/version "2.13.4.2"} ; JSON processor used by snowplow-java-tracker (pinned version due to CVE-2020-36518)
+ com.fasterxml.woodstox/woodstox-core {:mvn/version "6.4.0"} ; trans dep of commons-codec (pinned version due to CVE-2022-40151)
com.h2database/h2 {:mvn/version "1.4.197"} ; embedded SQL database
com.snowplowanalytics/snowplow-java-tracker
{:mvn/version "0.12.0" ; Snowplow analytics
@@ -95,8 +96,8 @@
org.apache.poi/poi-ooxml {:mvn/version "5.2.2"
:exclusions [org.bouncycastle/bcpkix-jdk15on
org.bouncycastle/bcprov-jdk15on]}
- org.apache.sshd/sshd-core {:mvn/version "2.9.1"} ; ssh tunneling and test server
- org.apache.xmlgraphics/batik-all {:mvn/version "1.14"} ; SVG -> image
+ org.apache.sshd/sshd-core {:mvn/version "2.9.2"} ; ssh tunneling and test server
+ org.apache.xmlgraphics/batik-all {:mvn/version "1.16"} ; SVG -> image
org.clojars.pntblnk/clj-ldap {:mvn/version "0.0.17"} ; LDAP client
org.bouncycastle/bcpkix-jdk15on {:mvn/version "1.70"} ; Bouncy Castle crypto library -- explicit version of BC specified to resolve illegal reflective access errors
org.bouncycastle/bcprov-jdk15on {:mvn/version "1.70"}
@@ -107,12 +108,14 @@
org.clojure/core.match {:mvn/version "1.0.0"}
org.clojure/core.memoize {:mvn/version "1.0.257"} ; useful FIFO, LRU, etc. caching mechanisms
org.clojure/data.csv {:mvn/version "1.0.1"} ; CSV parsing / generation
+ org.clojure/data.xml {:mvn/version "0.0.8"} ; XML parsing / generation
org.clojure/java.classpath {:mvn/version "1.0.0"} ; examine the Java classpath from Clojure programs
org.clojure/java.jdbc {:mvn/version "0.7.12"} ; basic JDBC access from Clojure
org.clojure/java.jmx {:mvn/version "1.0.0"} ; JMX bean library, for exporting diagnostic info
org.clojure/math.combinatorics {:mvn/version "0.1.6"} ; combinatorics functions
org.clojure/math.numeric-tower {:mvn/version "0.0.5"} ; math functions like `ceil`
org.clojure/tools.logging {:mvn/version "1.2.4"} ; logging framework
+ org.clojure/tools.macro {:mvn/version "0.1.5"} ; local macros
org.clojure/tools.namespace {:mvn/version "1.3.0"}
org.clojure/tools.reader {:mvn/version "1.3.6"}
org.clojure/tools.trace {:mvn/version "0.7.11"} ; function tracing
@@ -128,7 +131,7 @@
org.slf4j/slf4j-api {:mvn/version "1.7.36"} ; 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.7.0"} ; extra Java 8 java.time classes like DayOfMonth and Quarter
- org.yaml/snakeyaml {:mvn/version "1.30"} ; YAML parser
+ org.yaml/snakeyaml {:mvn/version "1.33"} ; YAML parser
potemkin/potemkin {:mvn/version "0.4.5" ; utility macros & fns
:exclusions [riddley/riddley]}
pretty/pretty {:mvn/version "1.0.5"} ; protocol for defining how custom types should be pretty printed
@@ -158,6 +161,20 @@
:paths
["src" "shared/src" "resources"]
+ ;; These are needed for the Athena and Redshift drivers if you are developing against them locally. If those drivers'
+ ;; dependencies are not included (i.e., if we don't have the `:drivers` profile), these repos are effectively
+ ;; ignored.
+ ;;
+ ;; 1. Maven repos from subprojects do not get copied over -- see
+ ;; https://ask.clojure.org/index.php/10726/deps-manifest-dependencies-respect-repos-dependent-project
+ ;;
+ ;; 2. You cannot include `:mvn/repos` inside of an alias -- see
+ ;; https://ask.clojure.org/index.php/12367/support-mvn-repos-inside-an-alias -- if we could, this could go in the
+ ;; `:drivers` alias instead.
+ :mvn/repos
+ {"athena" {:url "https://s3.amazonaws.com/maven-athena"}
+ "redshift" {:url "https://s3.amazonaws.com/redshift-maven-repository/release"}}
+
:aliases
{
;;; Local Dev & test profiles
@@ -271,7 +288,7 @@
;; clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test (for EE)
:drivers-dev
{:extra-paths
- ["modules/drivers/bigquery/test"
+ ["modules/drivers/athena/test"
"modules/drivers/bigquery-cloud-sdk/test"
"modules/drivers/druid/test"
"modules/drivers/googleanalytics/test"
@@ -289,11 +306,33 @@
;;; Linters
- ;; clojure -M:dev:ee:ee-dev:drivers:drivers-dev:check
+ ;; clojure -M:ee:drivers:check
+ ;;
+ ;; checks that all the namespaces we actually ship can be compiled, without any dependencies that we don't
+ ;; ship (such as `:dev` dependencies). See #27009 for more context.
:check
{:extra-deps {athos/clj-check {:git/url "https://github.com/athos/clj-check.git"
:sha "518d5a1cbfcd7c952f548e6dbfcb9a4a5faf9062"}}
- :main-opts ["-m" "clj-check.check"]}
+ :main-opts ["-m" "clj-check.check"
+ "src"
+ "shared/src"
+ "enterprise/backend/src"
+ "modules/drivers/athena/src"
+ "modules/drivers/bigquery-cloud-sdk/src"
+ "modules/drivers/druid/src"
+ "modules/drivers/googleanalytics/src"
+ "modules/drivers/mongo/src"
+ "modules/drivers/oracle/src"
+ "modules/drivers/presto/src"
+ "modules/drivers/presto-common/src"
+ "modules/drivers/presto-jdbc/src"
+ "modules/drivers/redshift/src"
+ "modules/drivers/snowflake/src"
+ "modules/drivers/sparksql/src"
+ "modules/drivers/sqlite/src"
+ "modules/drivers/sqlserver/src"
+ "modules/drivers/vertica/src"]
+ :jvm-opts ["-Dclojure.main.report=stderr"]}
;; clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood
:eastwood
@@ -303,7 +342,7 @@
:source-paths ["src"
"shared/src"
"enterprise/backend/src"
- "modules/drivers/bigquery/src"
+ "modules/drivers/athena/src"
"modules/drivers/bigquery-cloud-sdk/src"
"modules/drivers/druid/src"
"modules/drivers/googleanalytics/src"
diff --git a/dev/src/dev/render_png.clj b/dev/src/dev/render_png.clj
index 730fc7c1468b..3a9871319617 100644
--- a/dev/src/dev/render_png.clj
+++ b/dev/src/dev/render_png.clj
@@ -72,9 +72,9 @@
(.write w html-str))
(.deleteOnExit tmp-file)
(open tmp-file)))
-
+
(comment
- (render-card-to-png 1)
+ (render-card-to-png 1)
;; open viz in your browser
(-> [["A" "B"]
[1 2]
diff --git a/docs/README.md b/docs/README.md
index 9b19ceb789e3..8f965a63baa6 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -32,6 +32,7 @@ Metabase is a deep product with a lot of tools to simplify business intelligence
## Documentation topics
Metabase's reference documentation.
+
### Installation
- [Installation overview](./installation-and-operation/start.md)
@@ -61,6 +62,7 @@ Metabase's reference documentation.
#### Query builder
- [Asking questions](./questions/query-builder/introduction.md)
+- [Visualizing data](./questions/sharing/visualizing-results.md)
- [Custom expressions](./questions/query-builder/expressions.md)
- [List of expressions](./questions/query-builder/expressions-list.md)
- [Joining data](./questions/query-builder/join.md)
@@ -68,7 +70,6 @@ Metabase's reference documentation.
#### SQL and native queries
- [The SQL editor](./questions/native-editor/writing-sql.md)
-- [Data reference](./questions/native-editor/data-model-reference.md)
- [SQL parameters](./questions/native-editor/sql-parameters.md)
- [Referencing models and saved questions](./questions/native-editor/referencing-saved-questions-in-queries.md)
- [SQL snippets](./questions/native-editor/sql-snippets.md)
@@ -77,9 +78,8 @@ Metabase's reference documentation.
#### Sharing
- [Sharing answers](./questions/sharing/answers.md)
-- [Visualizing data](./questions/sharing/visualizing-results.md)
- [Setting and getting alerts](./questions/sharing/alerts.md)
-- [Public links](./questions/sharing/public-links.md)
+- [Public sharing](./questions/sharing/public-links.md)
### Dashboards
@@ -104,7 +104,10 @@ Metabase's reference documentation.
- [Organization overview](./exploration-and-organization/start.md)
- [Basic exploration](./exploration-and-organization/exploration.md)
- [Collections](./exploration-and-organization/collections.md)
+- [History](./exploration-and-organization/history.md)
+- [Data reference](./exploration-and-organization/data-model-reference.md)
- [Events and timelines](./exploration-and-organization/events-and-timelines.md)
+- [X-rays](./exploration-and-organization/x-rays.md)
### People
@@ -139,9 +142,9 @@ Metabase's reference documentation.
- [Embedding overview](./embedding/start.md)
- [Embedding introduction](./embedding/introduction.md)
-- [Signed embedding](./embedding/signed-embedding.md)
- [Full-app embedding](./embedding/full-app-embedding.md)
-- [Embedding example apps](https://github.com/metabase/embedding-reference-apps)
+- [Signed embedding](./embedding/signed-embedding.md)
+- [Parameters for signed embeds](./embedding/signed-embedding-parameters.md)
### Configuration
@@ -151,7 +154,8 @@ Metabase's reference documentation.
- [Email](./configuring-metabase/email.md)
- [Slack](./configuring-metabase/slack.md)
- [Environment variables](./configuring-metabase/environment-variables.md)
-- [Metabase logs](./configuring-metabase/log-configuration.md)
+- [Configuration file](./configuring-metabase/config-file.md)
+- [Metabase log configuration](./configuring-metabase/log-configuration.md)
- [Timezones](./configuring-metabase/timezones.md)
- [Languages and localization](./configuring-metabase/localization.md)
- [Appearance](./configuring-metabase/appearance.md)
diff --git a/docs/api-documentation.md b/docs/api-documentation.md
index 23ed152f02c0..28572333d70e 100644
--- a/docs/api-documentation.md
+++ b/docs/api-documentation.md
@@ -40,7 +40,6 @@ _* indicates endpoints used for features available on [paid plans](https://www.m
- [Dataset](api/dataset.md)
- [Email](api/email.md)
- [Embed](api/embed.md)
-- [Emitter](api/emitter.md)
- [Field](api/field.md)
- [GeoJSON](api/geojson.md)
- [Google](api/google.md)
diff --git a/docs/api/action.md b/docs/api/action.md
index f01dd35dc4d6..d2a57a489792 100644
--- a/docs/api/action.md
+++ b/docs/api/action.md
@@ -53,28 +53,6 @@ Create a new HTTP action.
* **`action`**
-## `POST /api/action/:action-namespace/:action-name`
-
-Generic API endpoint for executing any sort of Action.
-
-### PARAMS:
-
-* **`action-namespace`**
-
-* **`action-name`**
-
-## `POST /api/action/:action-namespace/:action-name/:table-id`
-
-Generic API endpoint for executing any sort of Action with source Table ID specified as part of the route.
-
-### PARAMS:
-
-* **`action-namespace`**
-
-* **`action-name`**
-
-* **`table-id`**
-
## `PUT /api/action/:id`
### PARAMS:
diff --git a/docs/api/app.md b/docs/api/app.md
index 7014d70bb0fd..803465fe92fa 100644
--- a/docs/api/app.md
+++ b/docs/api/app.md
@@ -27,18 +27,6 @@ Fetch a specific App.
* **`id`**
-## `GET /api/app/global-graph`
-
-Fetch the global graph of all App Permissions.
-
-You must be a superuser to do this.
-
-## `GET /api/app/graph`
-
-Fetch the graph of all App Permissions.
-
-You must be a superuser to do this.
-
## `POST /api/app/`
Endpoint to create an app.
@@ -97,26 +85,6 @@ Endpoint to change an app.
* **`nav_items`** value may be nil, or if non-nil, value must be an array. Each value may be nil, or if non-nil, value must be a map.
-## `PUT /api/app/global-graph`
-
-Do a batch update of the global App Permissions by passing in a modified graph.
-
-You must be a superuser to do this.
-
-### PARAMS:
-
-* **`body`** value must be a map.
-
-## `PUT /api/app/graph`
-
-Do a batch update of the advanced App Permissions by passing in a modified graph.
-
-You must be a superuser to do this.
-
-### PARAMS:
-
-* **`body`** value must be a map.
-
---
[<< Back to API index](../api-documentation.md)
\ No newline at end of file
diff --git a/docs/api/card.md b/docs/api/card.md
index 8cd8918dd792..539ffaefe7e4 100644
--- a/docs/api/card.md
+++ b/docs/api/card.md
@@ -28,7 +28,7 @@ Delete a Card. (DEPRECATED -- don't delete a Card anymore -- archive it instead.
Get all the Cards. Option filter param `f` can be used to change the set of Cards that are returned; default is
`all`, but other options include `mine`, `bookmarked`, `database`, `table`, `recent`, `popular`, and `archived`. See
- corresponding implementation functions above for the specific behavior of each filter option. :card_index:.
+ corresponding implementation functions above for the specific behavior of each filter option. :card_index.
### PARAMS:
diff --git a/docs/api/dashboard.md b/docs/api/dashboard.md
index 9e464c1a373f..fdb67647f1d3 100644
--- a/docs/api/dashboard.md
+++ b/docs/api/dashboard.md
@@ -265,6 +265,8 @@ Copy a Dashboard.
* **`collection_position`** value may be nil, or if non-nil, value must be an integer greater than zero.
+* **`is_deep_copy`** value may be nil, or if non-nil, value must be a boolean.
+
* **`_dashboard`**
## `POST /api/dashboard/:id/cards`
diff --git a/docs/api/emitter.md b/docs/api/emitter.md
deleted file mode 100644
index bd4c7bdb2734..000000000000
--- a/docs/api/emitter.md
+++ /dev/null
@@ -1,57 +0,0 @@
----
-title: "Emitter"
-summary: |
- API endpoints for Emitter.
----
-
-# Emitter
-
-API endpoints for Emitter.
-
-## `DELETE /api/emitter/:emitter-id`
-
-Endpoint to delete an emitter.
-
-### PARAMS:
-
-* **`emitter-id`**
-
-## `POST /api/emitter/`
-
-Endpoint to create an emitter.
-
-### PARAMS:
-
-* **`action_id`** value must be an integer greater than zero.
-
-* **`card_id`** value may be nil, or if non-nil, value must be an integer greater than or equal to zero.
-
-* **`dashboard_id`** value may be nil, or if non-nil, value must be an integer greater than or equal to zero.
-
-* **`options`** value may be nil, or if non-nil, value must be a map.
-
-* **`parameter_mappings`** value may be nil, or if non-nil, value must be a map.
-
-## `POST /api/emitter/:id/execute`
-
-Execute a custom emitter.
-
-### PARAMS:
-
-* **`id`**
-
-* **`parameters`** value may be nil, or if non-nil, map of parameter name or ID -> map of parameter `:value` and `:type` of the value
-
-## `PUT /api/emitter/:emitter-id`
-
-Endpoint to update an emitter.
-
-### PARAMS:
-
-* **`emitter-id`**
-
-* **`emitter`**
-
----
-
-[<< Back to API index](../api-documentation.md)
\ No newline at end of file
diff --git a/docs/api/google.md b/docs/api/google.md
index 9299b08147cb..bc3b19f5e60c 100644
--- a/docs/api/google.md
+++ b/docs/api/google.md
@@ -10,7 +10,7 @@ summary: |
## `PUT /api/google/settings`
-Update Google Sign-In related settings. You must be a superuser to do this.
+Update Google Sign-In related settings. You must be a superuser or have `setting` permission to do this.
### PARAMS:
diff --git a/docs/api/ldap.md b/docs/api/ldap.md
index 79ba83d46d5e..7c531a4e0c09 100644
--- a/docs/api/ldap.md
+++ b/docs/api/ldap.md
@@ -10,7 +10,7 @@ summary: |
## `PUT /api/ldap/settings`
-Update LDAP related settings. You must be a superuser to do this.
+Update LDAP related settings. You must be a superuser or have `setting` permission to do this.
### PARAMS:
diff --git a/docs/api/permissions.md b/docs/api/permissions.md
index 1fde64ed13de..d2bc6ea9f0df 100644
--- a/docs/api/permissions.md
+++ b/docs/api/permissions.md
@@ -24,12 +24,6 @@ Remove a User from a PermissionsGroup (delete their membership).
* **`id`**
-## `GET /api/permissions/execution/graph`
-
-Fetch a graph of execution permissions.
-
-You must be a superuser to do this.
-
## `GET /api/permissions/graph`
Fetch a graph of all Permissions.
@@ -82,21 +76,6 @@ Add a `User` to a `PermissionsGroup`. Returns updated list of members belonging
* **`is_group_manager`** value may be nil, or if non-nil, value must be a boolean.
-## `PUT /api/permissions/execution/graph`
-
-Do a batch update of execution permissions by passing in a modified graph. The modified graph of the same
- form as returned by the corresponding GET endpoint.
-
- Revisions to the permissions graph are tracked. If you fetch the permissions graph and some other third-party
- modifies it before you can submit you revisions, the endpoint will instead make no changes and return a
- 409 (Conflict) response. In this case, you should fetch the updated graph and make desired changes to that.
-
-You must be a superuser to do this.
-
-### PARAMS:
-
-* **`body`** value must be a map.
-
## `PUT /api/permissions/graph`
Do a batch update of Permissions by passing in a modified graph. This should return the same graph, in the same
diff --git a/docs/configuring-metabase/appearance.md b/docs/configuring-metabase/appearance.md
index ac30f02a87dd..f48c4a53df34 100644
--- a/docs/configuring-metabase/appearance.md
+++ b/docs/configuring-metabase/appearance.md
@@ -13,7 +13,9 @@ redirect_from:
{% include plans-blockquote.html feature="Custom appearance" %}
-Appearance setttings give you the option to white label Metabase to match your company’s branding.
+Appearance settings give you the option to white label Metabase to match your company’s branding.
+
+If you're looking for date, time, number, or currency formatting, see [Formatting defaults](../data-modeling/formatting.md).
## Changing Metabase's appearance
diff --git a/docs/configuring-metabase/config-file.md b/docs/configuring-metabase/config-file.md
new file mode 100644
index 000000000000..cb8ffe125ec2
--- /dev/null
+++ b/docs/configuring-metabase/config-file.md
@@ -0,0 +1,125 @@
+---
+title: "Configuration file"
+---
+
+# Configuration file
+
+{% include plans-blockquote.html feature="Loading from a configuration file" self-hosted-only="true" %}
+
+On some paid, self-hosted plans, Metabase supports initialization on launch from a config file named `config.yml`. This file should either be in the current directory (the directory where the running Metabase JAR is located), or in a path specified by the `MB_CONFIG_FILE_PATH` [environment variable](./environment-variables.md). In this config file, you can specify:
+
+- Settings
+- Users
+- Databases
+
+The settings as defined in the config file work the same as if you set these settings in the Admin Settings in your Metabase. Settings defined in this configuration file will update any existing settings. If, for example, a database already exists (that is, you'd already added it via initial set up or **Admin settings** > **Databases**, Metabase will update the database entry based on the data in the config file).
+
+The config file settings are not treated as a hardcoded source of truth like [environment variables](./environment-variables.md) are.
+
+## Example `config.yml` file
+
+This file sets up a user account and two database connections.
+
+```
+version: 1
+config:
+ users:
+ - first_name: Cam
+ last_name: Era
+ password: 2cans3cans4cans
+ email: cam@example.com
+ databases:
+ - name: test-data (Postgres)
+ engine: postgres
+ details:
+ host: localhost
+ port: 5432
+ password: {% raw %} "{{ env POSTGRES_TEST_DATA_PASSWORD }}" {% endraw %}
+ dbname: test-data
+ - name: Sample Dataset (Copy)
+ engine: h2
+ details:
+ db: "/home/cam/metabase/resources/sample-database.db;USER=GUEST;PASSWORD={{env SAMPLE_DATASET_PASSWORD}}"
+```
+
+To determine which keys you can specify, simply look at the fields available in Metabase itself.
+
+## Referring to environment variables in the `config.yml`
+
+Environment variables can be specified with `{% raw %}{{ template-tags }}{% endraw %}` like `{% raw %}{{ env POSTGRES_TEST_DATA_PASSWORD }}{% endraw %}` or `{% raw %}[[options {{template-tags}}]]{% endraw %}`.
+
+Metabase doesn't support recursive expansion, so if one of your environment variables references _another_ environment variable, you're going to have a bad time.
+
+## Disable initial database sync
+
+When loading a data model from a serialized dump, you want to disable the scheduler so that the Metabase doesn't try to sync.
+
+To disable the initial database sync, you can add `config-from-file-sync-database` to the `settings` list and set the value to `false`. The setting `config-from-file-sync-database` must come _before_ the databases list, like so:
+
+```
+version: 1
+config:
+ settings:
+ config-from-file-sync-databases: false
+ databases:
+ - name: my-database
+ engine: h2
+ details: ...
+```
+
+## List of settings
+
+In general, the settings you can set in the `settings` section of this config file map to the [environment variables](./environment-variables.md).
+
+The actual key that you include in the config file differs slightly from the format used for environment variables. For environment variables, the form is in screaming snake case, prefixed by an `MB`:
+
+```
+MB_NAME_OF_VARIABLE
+```
+
+Whereas in the config file, you'd translate that to:
+
+```
+name-of-variable
+```
+
+So for example, if you wanted to specify the `MB_EMAIL_FROM_NAME` not in an environment variable, but instead in the `config.yml` file:
+
+```
+version: 1
+config:
+ settings:
+ config-from-file-sync-databases: false
+ email-from-name: Stampy von Mails-a-lot
+ databases:
+ - name: my-database
+ engine: h2
+ details: ...
+```
+
+## Users and Admins
+
+The first user created in a Metabase instance is an Admin. The first user listed in the config file may be designated an admin, but not necessarily, for example if someone has already spun up and logged into that Metabase for the first time, that user would be the automatically designated admin. Additionally, you can specify a user account as an admin by using the `is_superuser: true` key.
+
+In the following example:
+```
+version: 1
+config:
+ users:
+ - first_name: a
+ last_name: b
+ password: metabot1
+ email: a@b.com
+ - first_name: b
+ last_name: a
+ password: metabot1
+ email: b@a.com
+ - first_name: c
+ last_name: a
+ password: metabot1
+ is_superuser: true
+ email: c@a.com
+```
+both users a@b.com and c@a.com will be admins: a@b.com because it's the first one on the list (if the instance is not yet configured) and c@a.com because it has the is_superuser flag. If an instance has already been configured, then a@b.com will be loaded as a normal user.
+
+
diff --git a/docs/configuring-metabase/email.md b/docs/configuring-metabase/email.md
index c4734faf3a88..0ceb07d9b46a 100644
--- a/docs/configuring-metabase/email.md
+++ b/docs/configuring-metabase/email.md
@@ -27,6 +27,8 @@ You should see this form:

+You might also want to specify [approved domains for notifications](./settings.md#approved-domains-for-notifications).
+
## Google Apps
1. In the **SMTP host** field, enter smtp.gmail.com
@@ -59,4 +61,4 @@ Check if [email quotas](https://docs.aws.amazon.com/ses/latest/dg/quotas.html) a
## Recommended settings
* SSL is strongly recommended because it’s more secure and gives your account extra protection from threats.
-* If your email service has a whitelist of email addresses that are allowed to send email, be sure to whitelist the email address that you put in the **From Address** field to ensure you and your teammates receive all emails from Metabase.
\ No newline at end of file
+* If your email service has a whitelist of email addresses that are allowed to send email, be sure to whitelist the email address that you put in the **From Address** field to ensure you and your teammates receive all emails from Metabase.
diff --git a/docs/configuring-metabase/environment-variables.md b/docs/configuring-metabase/environment-variables.md
index 250dce97d11f..fe85187fc9dd 100644
--- a/docs/configuring-metabase/environment-variables.md
+++ b/docs/configuring-metabase/environment-variables.md
@@ -77,7 +77,32 @@ Only available in [some plans](https://www.metabase.com/pricing)
Type: string
Default: `"{}"`
-JSON object of primary colors used in charts and throughout Metabase.
+JSON object of primary colors used in charts and throughout Metabase. Examples:
+
+To change the user interface colors:
+
+```
+{
+ "brand":"#ff003b",
+ "filter":"#FF003B",
+ "summarize":"#FF003B"
+}
+```
+
+To change the chart colors:
+
+```
+{
+ "accent0":"#FF0005",
+ "accent1":"#E6C367",
+ "accent2":"#B9E68A",
+ "accent3":"#8AE69F",
+ "accent4":"#8AE6E4",
+ "accent5":"#8AA2E6",
+ "accent6":"#B68AE6",
+ "accent7":"#E68AD0"
+}
+```
### `MB_APPLICATION_DB_MAX_CONNECTION_POOL_SIZE`
@@ -188,6 +213,13 @@ Default: `true`
Color log lines. When set to `false` it will disable log line colors. This is disabled on Windows. Related to [MB_EMOJI_IN_LOGS](#mb_emoji_in_logs).
+### `MB_CONFIG_FILE_PATH`
+
+Type: string
+Default: `config.yml`
+
+This feature requires the `advanced-config` feature flag on your token.
+
### `MB_CUSTOM_FORMATTING`
Type: string
diff --git a/docs/configuring-metabase/localization.md b/docs/configuring-metabase/localization.md
index 606a43d0fc04..c89f37652066 100644
--- a/docs/configuring-metabase/localization.md
+++ b/docs/configuring-metabase/localization.md
@@ -6,13 +6,20 @@ redirect_from:
# Languages and localization
+The **Localization** settings allow you to set global defaults for your Metabase instance. You can find **Localization** under **Admin settings** > **Settings**.
+
+## Default language
+
+Here you can set the default language (also called the "instance language") across your Metabase UI, system [emails](./email.md), [dashboard subscriptions](../dashboards/subscriptions.md), and [alerts](../questions/sharing/alerts.md). People can pick a different language from their own [account settings](../people-and-groups/account-settings.md).
+
## Supported languages
-Thanks to our amazing user community, Metabase has been translated into many different languages. Due to [the way we collect translations](#policy-for-adding-and-removing-translations), languages may be added or removed during major releases depending on translation coverage.
+Thanks to our amazing user community, Metabase has been translated into many different languages. Due to [the way we collect translations](#translations), languages may be added or removed during major releases depending on translation coverage.
The languages you can currently pick from are:
- English (default)
+- Arabic
- Bulgarian
- Catalan
- Chinese (simplified)
@@ -37,52 +44,60 @@ The languages you can currently pick from are:
- Ukrainian
- Vietnamese
-## Policy for adding and removing translations
+## Translations
-Our community contributes to Metabase translations on our [POEditor project][metabase-poe]. If you'd like to help make Metabase available in a language you're fluent in, we'd love your help!
+Our community contributes to Metabase translations on our [POEditor project](https://poeditor.com/join/project/ynjQmwSsGh). If you'd like to help make Metabase available in a language you're fluent in, we'd love your help!
-For a new translation to be added to Metabase, it must reach 100%. Once it does, we add it in the next major or minor release of Metabase. All _existing_ translations in Metabase _must stay at 100%_ to continue being included in the next _major_ version of Metabase. This rule ensures that no one encounters a confusing mishmash of English and another language when using Metabase.
+For a new language to be added to Metabase, it must reach 100%. Once it does, we add it in the next major or minor release of Metabase. All _existing_ languages in Metabase _must stay at 100%_ to continue being included in the next _major_ version of Metabase. This rule ensures that no one encounters a confusing mishmash of English and another language when using Metabase.
We understand that this is a high bar, so we commit to making sure that before each major release, any additions or changes to text in the product are completed at least 10 calendar days before the release ships, at which point we notify all translators that a new release will be happening soon.
Note that while we only remove languages in major releases, we are happy to add them back for minor releases, so it's always a good time to jump in and start translating.
-[metabase-poe]: https://poeditor.com/join/project/ynjQmwSsGh
+## Report timezone
-## Localization
+Use **report timezone** to set a default display time zone for dates and times in Metabase. The report timezone setting is a display setting only, so changing the report timezone won't affect the time zone of any data in your database.
-The **Localization** settings allow you to set global defaults for your Metabase instance. Localization settings include options for:
+Report timezone doesn't apply to `timestamp without time zone` data types, including the output of [`convertTimezone`](../questions/query-builder/expressions/converttimezone.md) expressions. For example:
-- **Language**
-- **Date and time**
-- **Numbers**
-- **Currency**
+| Raw timestamp in your database | Data type | Report time zone | Displayed as |
+| ---------------------------------------- | ----------------------------- | ---------------- | ---------------------- |
+| `2022-12-28T12:00:00 AT TIME ZONE 'CST'` | `timestamp with time zone` | 'Canada/Eastern' | Dec 28, 2022, 7:00 AM |
+| `2022-12-28T12:00:00-06:00` | `timestamp with offset` | 'Canada/Eastern' | Dec 28, 2022, 7:00 AM |
+| `2022-12-28T12:00:00` | `timestamp without time zone` | 'Canada/Eastern' | Dec 28, 2022, 12:00 AM |
-The **Localization** settings can be found in the **Admin Panel** under the **Settings** tab.
+Report timezone is only supported for the following databases:
+ - BigQuery
+ - Druid
+ - MySQL
+ - Oracle
+ - PostgreSQL
+ - Presto
+ - Vertica
-### Instance language
+## First day of the week
-The default language for all users across the Metabase UI, system emails, pulses, and alerts. Users can pick a different language from their own account settings page.
+If you need to, you can change the first day of the week for your instance (the default is Sunday). Setting the first day of the week affects things like grouping by week and filtering in questions built using the [query builder](../questions/query-builder/introduction.md). This setting doesn't affect [SQL queries](../questions/native-editor/writing-sql.md).
-### First day of the week
+## Localization options
-If you need to, you can change the first day of the week for your instance (the default is Sunday). Setting the first day of the week affects things like grouping by week and filtering in questions built using the [query builder](../questions/query-builder/introduction.md). This setting doesn't affect [SQL queries](../questions/native-editor/writing-sql.md).
+**Localization options** allow you to set global default display formats for dates, times, numbers, and currencies.
-### Localization options
+You can also override these localization options for specific fields or questions. For more info, see [Formatting](../data-modeling/formatting.md).
-**Dates and Times**
+### Dates and times
-- `Date style:` the way dates should be displayed in tables, axis labels, and tooltips.
-- `Date separators:` you can choose between slashes, dashes, and dots here.
-- `Abbreviate names of days and months:` whenever a date is displayed with the day of the week and/or the month written out, turning this setting on will display e.g. `January` as `Jan` or `Monday` as `Mon`.
-- `Time style:` this lets you choose between a 12-hour or 24-hour clock to display the time by default where applicable.
+- **Date style:** the way dates should be displayed in tables, axis labels, and tooltips.
+- **Date separators:** you can choose between slashes (`2022/12/14`), dashes (`2022-12-14`), and dots (`2022.12.14.`).
+- **Abbreviate names of days and months:** whenever a date is displayed with the day of the week and/or the month written out, turning this setting on will display e.g. "January" as "Jan" or "Monday" as "Mon".
+- **Time style:** choose to display the time using either a 12 or 24-hour clock (e.g., 3:00 PM or 15:00).
-**Numbers**
+### Numbers
-- `Separator style:` some folks use commas to separate thousands places, and others use periods. Here's where you can indicate which camp you belong to.
+- **Separator style:** some people use commas to separate thousands places, and others use periods. Here's where you can indicate which camp you belong to.
-**Currency**
+### Currency
-- `Unit of currency:` if you do most of your business in a particular currency, you can specify that here.
-- `Currency label style:` whether you want to have your currencies labeled with a symbol, a code (like `USD`), or its full name.
-- `Where to display the unit of currency:` this pertains specifically to tables, and lets you choose if you want the currency labels to appear only in the column heading, or next to each value in the column.
+- **Unit of currency:** if you do most of your business in a particular currency, you can specify that here.
+- **Currency label style:** whether you want to have your currencies labeled with a symbol, a code (like "USD"), or its full name.
+- **Where to display the unit of currency:** this pertains specifically to tables, and lets you choose if you want the currency labels to appear only in the column heading, or next to each value in the column.
diff --git a/docs/configuring-metabase/settings.md b/docs/configuring-metabase/settings.md
index c07dcc57ea3c..db11f64194ef 100644
--- a/docs/configuring-metabase/settings.md
+++ b/docs/configuring-metabase/settings.md
@@ -4,54 +4,52 @@ redirect_from:
- /docs/latest/administration-guide/08-configuration-settings
---
-## General settings
+# General settings
This section contains settings for your whole instance, like its URL, the reporting timezone, and toggles for disabling or enabling some of Metabase's optional features.
-You can configure these settings in the **General** section of the **Settings** tab in the **Admin Panel**.
+You can configure these settings from **Settings** > **Admin Settings** > **General**.
-### Site Name
+## Site name
How you’d like to refer to this instance of Metabase.
-### Site URL
+## Site URL
-The base URL of this Metabase instance. The base URL is used in emails to allow users to click through to their specific instance. Make sure to include http:// or https:// to make sure it’s reachable.
+The site URL is the web address that people use to access your Metabase instance. Make sure to include `http://` or `https://` to make sure it’s reachable.
### Redirect to HTTPS
-Force all traffic to use HTTPS via redirect, if the site can serve over HTTPS.
+By default, Metabase is served over HTTP.
-_Default: disabled_.
+To force all traffic to use HTTPS via redirect, click `http://` (under the **Site URL** section) and select `https://` from the dropdown menu.
-For example, if you are serving your Metabase application at "example.com", and you enable HTTPS redirect, when a user enters an address like "example.com/data" in their browser's address bar, the user will be automatically redirected to a secure connection at `https://example.com/data`.
+For example, say you enable HTTPS redirect for a Metabase instance at the site URL "example.com". When someone enters an address like `example.com/data` in their browser's address bar, they'll get automatically redirected to a secure connection at `https://example.com/data`.
> Note: if you haven't set up HTTPS on your server, Metabase will not let you enable HTTPS redirect. Instead, you'll get a warning saying "It looks like HTTPS is not properly configured."
-### Email Address for Help Requests
+## Email address for help requests
This email address will be displayed in various messages throughout Metabase when users encounter a scenario where they need assistance from an admin, such as a password reset request.
-### Report Timezone
+## Approved domains for notifications
-The **report timezone** sets the default time zone for displaying times. The timezone is used when breaking out data by dates.
+Allowed email address domain(s) for new [dashboard subscriptions](../dashboards/subscriptions.md) and [alerts](../questions/sharing/alerts.md). To specify multiple domains, separate each domain with a comma, with no space in between (e.g., "domain1,domain2"). To allow all domains, leave the field empty. This setting doesn't affect existing subscriptions.
-_Setting the default timezone will not change the timezone of any data in your database_. If the underlying times in your database aren't assigned to a timezone, Metabase will use the report timezone as the default timezone.
+## Anonymous tracking
-### Anonymous Tracking
+This option turns determines whether or not you allow [anonymous data about your usage of Metabase](../installation-and-operation/information-collection.md) to be sent back to us to help us improve the product. [Your database’s data is never tracked or sent](https://www.metabase.com/security).
-This option turns determines whether or not you allow [anonymous data about your usage of Metabase](../installation-and-operation/information-collection.md) to be sent back to us to help us improve the product. _Your database’s data is never tracked or sent_.
+## Friendly table and field names
-### Enable X-rays
+By default, Metabase attempts to make field and table names more readable by changing things like `somehorriblename` to `Some Horrible Name`. This does not work well for languages other than English, or for fields that have lots of abbreviations or codes in them. If you'd like to turn this setting off, you can do so from the Admin Panel under **Settings** > **Admin settings** > **General**.
-[X-rays](../exploration-and-organization/x-rays.md) are a great way to allow your users to quickly explore your data or interesting parts of charts, or to see a comparison of different things. But if you're dealing with data sources where allowing users to run x-rays on them would incur burdonsome performance or monetary costs, you can turn them off here.
+To manually label field or table names in Metabase, check out the [Data Model](../data-modeling/metadata-editing.md) section in your admin settings.
-### Enabled Nested Queries
+## Enable nested queries
By default, Metabase allows your users to use a previously saved question as a source for queries. If you have a lot of slow running queries, you may want to switch this option off, as performance problem can occur.
-### Friendly Table and Field Names
+## Enable X-rays
-By default, Metabase attempts to make field and table names more readable by changing things like `somehorriblename` to `Some Horrible Name`. This does not work well for languages other than English, or for fields that have lots of abbreviations or codes in them. If you'd like to turn this setting off, you can do so from the Admin Panel under Settings > General > Friendly Table and Field Names.
-
-To manually fix field or table names if they still look wrong, you can go to the Metadata section of the Admin Panel, select the database that contains the table or field you want to edit, select the table, and then edit the name(s) in the input boxes that appear.
+[X-rays](../exploration-and-organization/x-rays.md) are a great way for people to get quick summary stats on your data. If these X-ray queries get too slow or expensive, you can turn them off here.
diff --git a/docs/configuring-metabase/start.md b/docs/configuring-metabase/start.md
index 7d1ad1109d32..3623a49dbe0e 100644
--- a/docs/configuring-metabase/start.md
+++ b/docs/configuring-metabase/start.md
@@ -20,7 +20,23 @@ Set up email for [Alerts](../questions/sharing/alerts.md) and [Dashboard subscri
Set up Slack for Alerts and Dashboard subscriptions.
-## [Localization](./localization.md)
+## [Environment variables](./environment-variables.md)
+
+Configure Metabase on launch via environment variables.
+
+## [Configuration file](./config-file.md)
+
+On self-hosted paid plans, you can configure Metabase via a configuration file.
+
+## [Metabase logs configuration](./log-configuration.md)
+
+Tell Metabase what to log.
+
+## [Timezones](./timezones.md)
+
+Guidance on timezone settings.
+
+## [Languages and localization](./localization.md)
Set language, datetime, and currency settings.
@@ -28,10 +44,14 @@ Set language, datetime, and currency settings.
Customize colors, fonts, and other visual elements.
-## [Caching](./caching.md)
+## [Caching query results](./caching.md)
Cache query results for faster loading times.
-## [Metabase logs configuration](./log-configuration.md)
+## [Custom maps](./custom-maps.md)
-Tell Metabase what to log.
+Upload custom maps to your Metabase.
+
+## [Customizing the Metabase Jetty webserver](./customizing-jetty-webserver.md)
+
+Set SSL and port settings for the Jetty webserver.
diff --git a/docs/configuring-metabase/timezones.md b/docs/configuring-metabase/timezones.md
index 859394f5c4ab..31081adf9646 100644
--- a/docs/configuring-metabase/timezones.md
+++ b/docs/configuring-metabase/timezones.md
@@ -8,6 +8,8 @@ redirect_from:
Metabase does its best to ensure proper and accurate reporting in whatever timezone you desire, but timezones are a complicated beast so it's important to abide by some recommendations listed below to ensure your reports come out as intended.
+## Time zone settings
+
The following places where timezones are set can all impact the data you see:
- `Database` - includes global database timezone settings, specific column type settings, and even individual data values.
@@ -15,14 +17,31 @@ The following places where timezones are set can all impact the data you see:
- `Metabase` - inside Metabase the reporting timezone setting (if set) will influence how your data is reported.
- `Metabase Cloud` - if you need to change the timezone on the server that hosts your Metabase Cloud instance, please [contact support](https://www.metabase.com/help-premium).
+## Recommended settings
+
To ensure proper reporting it's important that timezones be set consistently in all places. Metabase recommends the following settings:
-- Make sure all of your database columns are properly setup to include timezone awareness.
-- Unless you have a special need it's best to set your database reporting timezone to UTC and store all of your date/time related values in UTC.
+- Make sure all of your database columns are properly setup to include [time zone awareness](#data-types).
+- Unless you have a special need it's best to set your database reporting time zone to UTC and store all of your date/time related values in UTC.
- Configure your JVM to use the same timezone you want to use for reporting, which ideally should also match the timezone of your database.
- Set the Metabase `Report Timezone` to match the timezone you want to see your reports in, again, this should match the rest of the timezone settings you've made.
-Common Pitfalls:
+## Data types
+
+You can make your database columns time zone aware by storing them as specific data types, such as:
+
+| Data type | Description | Example |
+| ----------------------------- | ----------------------------------------- | ---------------------------------------------------- |
+| `timestamp with time zone` | Knows about location. | `2022-12-28T12:00:00 AT TIME ZONE 'America/Toronto'` |
+| `timestamp with offset` | Knows about the time difference from UTC. | `2022-12-28T12:00:00-04:00` |
+| `timestamp without time zone` | No time zone info. | `2022-12-28T12:00:00` |
+
+The exact data type will depend on your database. Some Metabase features only work with specific data types:
+
+- [Report timezone setting](../configuring-metabase/localization.md#report-timezone)
+- [`converttimezone` custom expression](../questions/query-builder/expressions/converttimezone.md)
+
+## Common pitfalls
1. Your database is using date/time columns without any timezone information. Typically when this happens your database will assume all the data is from whatever timezone the database is configured in or possible just default to UTC (check your database vendor to be sure).
2. Your JVM timezone is different from your Metabase `Report Timezone` choice. This is a very common issue and can be corrected by launching java with the `-Duser.timezone=` option properly set to match your Metabase report timezone.
diff --git a/docs/dashboards/filters.md b/docs/dashboards/filters.md
index 7aada0f79aa5..61fe97239518 100644
--- a/docs/dashboards/filters.md
+++ b/docs/dashboards/filters.md
@@ -94,9 +94,9 @@ Once you’ve added a filter to your dashboard, just click on the filter to sele
You can also set up a dashboard question to [update a filter on click](./interactive.md#use-a-chart-to-filter-a-dashboard).
-## Choosing between a dropdown or autocomplete for your filter
+## Default filter types
-If the column you're using for a filter has more than 100 unique values, you'll now automatically see a search box with autocomplete suggestions:
+If the column you're using for a filter has more than 100 unique values, you'll automatically see a search box with autocomplete suggestions.

@@ -108,9 +108,9 @@ In both cases, you can pick one or multiple selections for your filter.

-If Metabase somehow picked the wrong behavior for your field, admins can go to the [Data Model](../data-modeling/metadata-editing.md) section of the admin panel and click on the **gear** icon by the field in question to specify a list, search box, or plain input box.
+## Creating a dropdown filter
-
+To override the default text box or search box filter widget, ask your Metabase admin to help you [update the column's settings](../data-modeling/metadata-editing.md#changing-a-search-box-filter-to-a-dropdown-filter) on the Data Model page.
## Linking filters
diff --git a/docs/dashboards/interactive.md b/docs/dashboards/interactive.md
index 87883dd52573..80ae148e15c8 100644
--- a/docs/dashboards/interactive.md
+++ b/docs/dashboards/interactive.md
@@ -64,6 +64,8 @@ Possible destinations include:
- Saved questions
- URLs
+Internal Metabase destinations (dashboards or saved questions) will load in the same browser tab or window. External URLs will open in a new tab or window.
+
## Passing values to the destination
If you're linking to a dashboard or a SQL question that has filters, you can pass values from the current dashboard to filters in the destination.
diff --git a/docs/dashboards/introduction.md b/docs/dashboards/introduction.md
index 8c26891841ad..1ea9e99bc507 100644
--- a/docs/dashboards/introduction.md
+++ b/docs/dashboards/introduction.md
@@ -24,7 +24,17 @@ In the top right of the screen, click the **+** icon to open the **Create** menu

-If you don't want to build a dashboard from scratch, or want to experiment by making changes to an existing dashboard without affecting the original, you can **duplicate** an existing dashboard. From an existing dashboard, click on the **...** menu in the upper right, and select **Duplicate**.
+## Duplicating a dashboard
+
+If you don't want to build a dashboard from scratch, or want to experiment by making changes to an existing dashboard without affecting the original, you can **duplicate** an existing dashboard.
+
+To duplicate a dashboard, click on the **...** menu in the upper right of the dashboard, and select **Duplicate**.
+
+By default, Metabase will create a new dashboard, with copies of the questions in the original dashboard, and save everything to the collection you specify.
+
+If you don't wish to copy the dashboard's underlying questions, check the box that says **Only duplicate the dashboard**. Metabase will copy the dashboard and refer to the original questions.
+
+In both cases, duplication only includes the dashboard, its card layout, filters, and (optionally) the questions. What's not copied: dashboard subscriptions, or any sharing or embedding data settings. For example, if you copy a dashboard that has been made public, that copied dashboard will not be public by default.
## Adding saved questions to a dashboard
@@ -181,13 +191,11 @@ The part that says `refresh=60` sets the dashboard to automatically refresh ever
There is one important limitation with the `fullscreen` option: for security reasons, many browsers require user interaction to initiate fullscreen. In those browsers, using the `fullscreen` option will enable the fullscreen UI in Metabase, but it won't expand the browser content to fill the screen. To ensure the dashboard occupies the entire screen, either activate fullscreen by clicking the button in the UI, or use the `fullscreen` URL option and launch the browser in fullscreen or kiosk mode.
-## Archiving a dashboard
-
-Archiving a dashboard removes the dashboard from searches and collections. Archiving a dashboard does not archive the individual saved questions on it — it just archives the dashboard.
+## Dashboard version history
-To archive a dashboard, click the **pencil** icon to enter edit mode, then click the **...** menu, and select **Archive**.
+For [questions](../questions/start.md), dashboards, and [models](../data-modeling/models.md), Metabase keeps a version history for the previous fifteen versions of that item.
-To view all archived items, click the **menu** icon in the top-right of any collection page. You can **unarchive** a dashboard by clicking the icon of the box with the upward arrow next to that dashboard.
+See [History](../exploration-and-organization/history.md).
## Tips on creating helpful dashboards
diff --git a/docs/data-modeling/metadata-editing.md b/docs/data-modeling/metadata-editing.md
index 1b9d6a671be9..2a1f6220fe51 100644
--- a/docs/data-modeling/metadata-editing.md
+++ b/docs/data-modeling/metadata-editing.md
@@ -141,6 +141,16 @@ You can manually change the user interface for the filter to:

+## Changing a search box filter to a dropdown filter
+
+1. Go to **Settings** > **Admin settings** > **Data Model**.
+2. Select the database, schema, table, and field in question.
+3. Click the **gear** icon to view all the field’s settings.
+4. Set **Field Type** to “Category”.
+5. Set **Filtering on this field** to “A list of all values".
+
+This setting will run a query against your database to get the first 1,000 distinct values (ordered ascending) for that field and cache the first 100kB of text to display in the dropdown menu. If you have columns with more than 1,000 distinct values, or columns with text-heavy data, we recommend setting **Filtering on this field** to "Search box" instead. For more info, see [How database scans work](../databases/connecting.md#how-database-scans-work).
+
## Column order
Metabase will default to the column order native to the database.
diff --git a/docs/data-modeling/models.md b/docs/data-modeling/models.md
index 03b16d03b225..d1aece6352b5 100644
--- a/docs/data-modeling/models.md
+++ b/docs/data-modeling/models.md
@@ -98,7 +98,7 @@ You can refer to a model in a SQL query just like you can refer to a saved quest
```
{% raw %}
-SELECT * FROM {{#1}}
+SELECT * FROM {{#1-customer-model}}
{% endraw %}
```
@@ -106,15 +106,21 @@ Or as a [common table expression (CTE)][cte]:
```
{% raw %}
-WITH model AS {{#3807}}
+WITH model AS {{#3807-invoice-model}}
SELECT *
FROM model;
{% endraw %}
```
-## Model history
+Simply typing `{% raw %}{{#}} {% endraw %}` will allow you to search for models (for example, you could type in `{% raw %}{{#customer}}{% endraw %}` to search models, questions, and tables with the word "customer" in the title.
-Just like with saved questions, you can click on the model name with the down arrow to bring up the model's sidebar, then click on **History** at the bottom to bring up a list of the changes made to the model over time, and by whom.
+You can also use the data reference sidebar to browse the models available. To open the data reference sidebar, click on the **book** icon.
+
+## Model version history
+
+For [questions](../questions/start.md), [dashboards](../dashboards/start.md), and models, Metabase keeps a version history for the previous fifteen versions of that item. You can view changes, revert to previous versions, and archive outdated items.
+
+See [History](../exploration-and-organization/history.md).
## Verifying a model
diff --git a/docs/databases/connecting.md b/docs/databases/connecting.md
index 6a6cc72815f1..6e1792dace7b 100644
--- a/docs/databases/connecting.md
+++ b/docs/databases/connecting.md
@@ -2,6 +2,7 @@
title: Adding and managing databases
redirect_from:
- /docs/latest/administration-guide/01-managing-databases
+ - /docs/latest/databases/connections/sql-server
---
# Adding and managing databases
@@ -20,8 +21,7 @@ Although connection details differ database to database, in general you'll need
The databases listed below have official drivers maintained by the Metabase team. Customers on [paid plans](https://www.metabase.com/pricing) will get official support.
-If you don't see your database listed here, see [partner and community drivers](../developers-guide/partner-and-community-drivers.md#partner-drivers).
-
+- [Amazon Athena](./connections/athena.md)
- [BigQuery](./connections/bigquery.md) (Google Cloud Platform)
- Druid
- [Google Analytics](./connections/google-analytics.md)
@@ -34,10 +34,12 @@ If you don't see your database listed here, see [partner and community drivers](
- Redshift (Amazon Web Services)
- [Snowflake](./connections/snowflake.md)
- SparkSQL
-- [SQL Server](./connections/sql-server.md)
+- SQL Server
- SQLite
- [Vertica](./connections/vertica.md)
+If you don't see your database listed here, see [partner and community drivers](../developers-guide/partner-and-community-drivers.md#partner-drivers).
+
## Connecting to databases hosted by a cloud provider
For provider-specific connection details, like connecting to a PostgreSQL data warehouse on RDS:
@@ -105,19 +107,23 @@ A fingerprinting query examines the first 10,000 rows from each column and uses
## Syncing and scanning databases
-Metabase runs sync and scan queries in order to show tables and columns, populate dropdown menus with the right values, and suggest helpful visualizations. Metabase does _not_ copy any data from your database---it only maintains lists of the tables and columns.
+Metabase runs syncs and scans to stay up to date with your database. Syncs get updated schemas to display in the [Data Browser](https://www.metabase.com/learn/getting-started/data-browser). Scans take samples of column values to populate filter dropdown menus and suggest helpful visualizations. Metabase does not store _complete_ tables from your database.
### How database syncs work
-A Metabase **sync** query gets a list of updated table names, column names, and column data types from your database. The query runs against your database during setup, and again every hour by default. It's very fast with most relational databases, but can be slower with MongoDB and some [community-built database drivers](../developers-guide/partner-and-community-drivers.md). Syncing can't be turned off completely, otherwise Metabase wouldn't work.
+A Metabase **sync** is a query that gets a list of updated table and view names, column names, and column data types from your database. This query runs against your database during setup, and again every hour by default. This scanning query is fast with most relational databases, but can be slower with MongoDB and some [community-built database drivers](../developers-guide/partner-and-community-drivers.md). Syncing can't be turned off completely, otherwise Metabase wouldn't work.
### How database scans work
-A Metabase **scan** query caches the column _values_ for filter dropdowns by looking at the first 1,000 distinct records from each table. A scan is more intensive than a sync query, so it only runs once during setup, and again once a day by default. If you [disable scans](#scheduling-database-scans) entirely, you'll need to bring things up to date by running [manual scans](#manually-scanning-column-values).
+A Metabase **scan** is a query that caches the column _values_ for filter dropdowns by looking at the first 1,000 distinct records from each table, in ascending order. For each record, Metabase only stores the first 100 kilobytes of text, so if you have data with 1,000 characters each (like addresses), and your column has more than 200 unique addresses, Metabase will only cache the first 100 values from the scan query.
+
+Cached column values are displayed in filter dropdown menus. If people type in the filter search box for values that aren't in the first 1,000 distinct records or 100kB of text, Metabase will run a query against your database to look for those values on the fly.
+
+A scan is more intensive than a sync query, so it only runs once during setup, and again once a day by default. If you [disable scans](#scheduling-database-scans) entirely, you'll need to bring things up to date by running [manual scans](#manually-scanning-column-values).
### Getting tables, columns, and values for the first time
-When Metabase first connects to your database, it performs a **scan** to determine the metadata of the columns in your tables and automatically assign each column a [semantic type]().
+When Metabase first connects to your database, it performs a **scan** to determine the metadata of the columns in your tables and automatically assign each column a [semantic type](../data-modeling/field-types.md).
During the scan, Metabase also takes a sample of each table to look for URLs, JSON, encoded strings, etc. You can map table and column metadata to new values from **Admin settings** > **Data model**. For more on editing metadata, check out [the Data Model page: editing metadata](../data-modeling/metadata-editing.md).
@@ -154,6 +160,14 @@ To forget the data that Metabase has stored from previous [database scans](#sync

+### Syncing and scanning using the API
+
+Metabase syncs and scans regularly, but if the database administrator has just changed the database schema, or if a lot of data is added automatically at specific times, you may want to write a script that uses the [Metabase API](https://www.metabase.com/learn/administration/metabase-api) to force a sync or scan. [Our API](../api-documentation.md) provides two ways to initiate a sync or scan of a database:
+
+1. Using a a session token: the `/api/database/:id/sync_schema` or `api/database/:id/rescan_values` endpoints. These endpoints do the same things as going to the database in the Admin Panel and choosing **Sync database schema now** or **Re-scan field values now** respectively. To use these endpoints, you have to authenticate with a user ID and pass a session token in the header of your request.
+
+2. Using an API key: `/api/notify/db/:id`. We created this endpoint so that people could notify their Metabase to sync after an [ETL operation](https://www.metabase.com/learn/analytics/etl-landscape) finishes. To use this endpoint, you must pass an API key by defining the `MB_API_KEY` environment variable.
+
## Deleting databases
**Caution: Deleting a database is irreversible! All saved questions and dashboard cards based on the database will be deleted as well!**
diff --git a/docs/databases/connections/athena.md b/docs/databases/connections/athena.md
new file mode 100644
index 000000000000..4c8a6e9584d0
--- /dev/null
+++ b/docs/databases/connections/athena.md
@@ -0,0 +1,79 @@
+---
+title: Amazon Athena
+---
+
+# Amazon Athena
+
+Connecting Metabase to Athena depends on where Metabase is running.
+
+## Connecting to Athena
+
+To connect Metabase to Athena, you'll need to input your IAM credentials:
+
+- Access key
+- Secret Key
+
+Metabase will encrypt these credentials.
+
+If you use other AWS services, we recommend that you create a special AWS Service Account that only has the permissions required to run Athena, and input the IAM credentials from that account to connect Metabase to Athena.
+
+See [Identity and access management in Athena](https://docs.aws.amazon.com/athena/latest/ug/security-iam-athena.html).
+
+## Connecting using AWS Default Credentials Chain
+
+If you're running Metabase on AWS and want to use [AWS Default Credentials Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default), leave the Access and Secret keys blank.
+
+- For EC2, you can use [instance profiles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html).
+- For ECS, you can use [IAM roles for tasks](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html)
+
+In both cases, the Athena driver will automatically fetch session credentials based on which IAM role you've configured.
+
+## Connection settings
+
+### Display name
+
+How Metabase should refer to the data source in its user interface.
+
+### Region
+
+The region where your Athena database is hosted, e.g., `us-east-1`.
+
+### Workgroup
+
+For example: `primary`. See [documentation on workgroups](https://docs.aws.amazon.com/athena/latest/ug/user-created-workgroups.html)
+
+### S3 Staging directory
+
+This S3 staging directory must be in the same region you specify above.
+
+### Catalog
+
+You can use a different [catalog](https://docs.aws.amazon.com/athena/latest/ug/understanding-tables-databases-and-the-data-catalog.html) (for example if you're using federated queries).
+
+## Advanced options
+
+### Additional Athena connection string options
+
+You can append additional options to the connection string. For example, to disable result set streaming and enable TRACE-level debugging:
+
+```
+UseResultsetStreaming=0;LogLevel=6.
+```
+
+For more connection options, see Simba Athena JDBC Driver with SQL Connector's [Installation and Configuration Guide](https://s3.amazonaws.com/athena-downloads/drivers/JDBC/SimbaAthenaJDBC_2.0.13/docs/Simba+Athena+JDBC+Driver+Install+and+Configuration+Guide.pdf).
+
+### Rerun queries for simple explorations
+
+We execute the underlying query when you explore data using Summarize or Filter. This is on by default but you can turn it off if performance is slow.
+
+### Choose when syncs and scans happen
+
+By default, Metabase does a lightweight hourly sync and an intensive daily scan of field values. If you have a large database, turn this on to make changes.
+
+### Periodically refingerprint tables
+
+This enables Metabase to scan for additional field values during syncs allowing smarter behavior, like improved auto-binning on your bar charts.
+
+### Default result cache duration
+
+How long to keep question results. By default, Metabase will use the value you supply on the cache settings page, but if this database has other factors that influence the freshness of data, it could make sense to set a custom duration. You can also choose custom durations on individual questions or dashboards to help improve performance.
diff --git a/docs/databases/connections/mongodb.md b/docs/databases/connections/mongodb.md
index fd90a979f9bf..c9ff4a6e70e1 100644
--- a/docs/databases/connections/mongodb.md
+++ b/docs/databases/connections/mongodb.md
@@ -111,7 +111,7 @@ To make sure you are using the correct connection configuration:
## I added fields to my database but don't see them in Metabase
-Metabase may not sync all of your fields, as it only scans the first 200 documents in a collection to get a sample of the fields the collection contains. Since any document in a MongoDB collection can contain any number of fields, the only way to get 100% coverage of all fields would be to scan every single document in every single collection, which would put too much strain on your database (so we don't do that).
+Metabase may not sync all of your fields, as it only scans the first ten thousand documents in a collection to get a sample of the fields the collection contains. Since any document in a MongoDB collection can contain any number of fields, the only way to get 100% coverage of all fields would be to scan every single document in every single collection, which would put too much strain on your database (so we don't do that).
One workaround is to include all possible keys in the first document of the collection, and give those keys null values. That way, Metabase will be able to recognize the correct schema for the entire collection.
diff --git a/docs/databases/connections/sql-server.md b/docs/databases/connections/sql-server.md
deleted file mode 100644
index 715df7bc3a7c..000000000000
--- a/docs/databases/connections/sql-server.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-title: Microsoft SQL Server
-redirect_from:
- - /docs/latest/administration-guide/databases/sql-server
----
-
-# Microsoft SQL Server
-
-Also known as MSSQL.
-
-## Connecting to SQL Server with dynamic ports
-
-If your SQL Server uses dynamic ports, simply leave the port field blank.
diff --git a/docs/developers-guide/contributing.md b/docs/developers-guide/contributing.md
index 8ab8f3d187f2..479475ef4b6c 100644
--- a/docs/developers-guide/contributing.md
+++ b/docs/developers-guide/contributing.md
@@ -93,7 +93,9 @@ By our definition, "Bugs" are situations where the program doesn't do what it wa
### Help with Documentation
-There are a lot of docs. We often have difficulties keeping them up to date. If you are reading them and you notice inconsistencies, errors or outdated information, please help up keep them current!
+There are a lot of docs, which means keeping them up to date is hard. If you notice inconsistencies, errors, or outdated information, please help us keep them current!
+
+Note that **we cannot accept translations for documentation at this time**. We support [in-app translations](../configuring-metabase/localization.md), and only support languages that have 100% coverage. But 1) the in-app text is orders of magnitude shorter than our docs, 2) it changes at a slower pace, and 3) we have a lot of people help out. We may consider supporting docs in multiple languages in the future, but for now we need to focus our resources on improving our existing documentation (and expanding it to include all of the new features we're adding).
### Working on features
diff --git a/docs/developers-guide/e2e-tests.md b/docs/developers-guide/e2e-tests.md
index 96f7cda7a208..8dee153d907b 100644
--- a/docs/developers-guide/e2e-tests.md
+++ b/docs/developers-guide/e2e-tests.md
@@ -46,7 +46,7 @@ Try to avoid repeatedly testing pieces of the application incidentally. For exam
## Cypress Documentation
-* Introduction: https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Querying-by-Text-Content
+* Introduction: https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html
* Commands: https://docs.cypress.io/api/api/table-of-contents.html
* Assertions: https://docs.cypress.io/guides/references/assertions.html
diff --git a/docs/developers-guide/frontend.md b/docs/developers-guide/frontend.md
index d96e20e553ae..3ce494d802fd 100644
--- a/docs/developers-guide/frontend.md
+++ b/docs/developers-guide/frontend.md
@@ -113,148 +113,6 @@ Any additional selectors and actions defined in the entities' `objectSelectors`
You can also use the Redux actions and selectors directly, for example, `dispatch(Users.actions.loadList())` and `Users.selectors.getList(state)`.
-## Forms
-
-Metabase includes a comprehensive custom React and [`redux-form`](https://redux-form.com/5.2.3/) based form library. It also integrates with Metabase's [Entities](https://github.com/metabase/metabase/wiki/Frontend:-Entity-Loaders) system.
-
-The core React component of the system is [`metabase/containers/Form`](https://github.com/metabase/metabase/blob/master/frontend/src/metabase/containers/Form.jsx).
-
-### Form Definitions
-
-Form definitions can be provided in two different ways, with a JavaScript-based form definition object, or inline React `` elements.
-
-Pass a form definition to the `form` prop:
-
-```javascript
-