Compare commits

..

1 Commits

Author SHA1 Message Date
Denis Berezin
754dd7725f Remove a bit of dead code 2023-06-06 02:27:47 +03:00
5636 changed files with 209970 additions and 442230 deletions

View File

@@ -1,8 +1,5 @@
{:hooks
{:analyze-call
{next.jdbc/with-transaction
hooks.com.github.seancorfield.next-jdbc/with-transaction
next.jdbc/with-transaction+options
hooks.com.github.seancorfield.next-jdbc/with-transaction+options}}
:lint-as {next.jdbc/on-connection clojure.core/with-open
next.jdbc/on-connection+options clojure.core/with-open}}
hooks.com.github.seancorfield.next-jdbc/with-transaction}}
:lint-as {next.jdbc/on-connection clojure.core/with-open}}

View File

@@ -16,19 +16,3 @@
opts
body))]
{:node new-node})))
(defn with-transaction+options
"Expands (with-transaction+options [tx expr opts] body)
to (let [tx expr] opts body) per clj-kondo examples."
[{:keys [:node]}]
(let [[binding-vec & body] (rest (:children node))
[sym val opts] (:children binding-vec)]
(when-not (and sym val)
(throw (ex-info "No sym and val provided" {})))
(let [new-node (api/list-node
(list*
(api/token-node 'let)
(api/vector-node [sym val])
opts
body))]
{:node new-node})))

View File

@@ -1,46 +1,13 @@
{:config-paths ["macros"]
:linters
{:aliased-namespace-symbol {:level :warning}
:invalid-arity {:skip-args [metabase.mbql.util.match/match]} ; TODO (cam): can we fix these?
:keyword-binding {:level :warning}
:main-without-gen-class {:level :warning}
:misplaced-docstring {:level :warning}
:missing-body-in-when {:level :warning}
:missing-docstring {:level :warning}
:missing-else-branch {:level :warning}
:namespace-name-mismatch {:level :warning}
:non-arg-vec-return-type-hint {:level :warning}
:reduce-without-init {:level :warning}
:redundant-fn-wrapper {:level :warning}
:refer-all {:level :warning, :exclude [clojure.test]}
:single-key-in {:level :warning}
:unused-referred-var {:exclude {compojure.core [GET DELETE POST PUT]}}
:use {:level :warning}
:warn-on-reflection {:level :warning}
;;
;; disabled linters
;;
:unexpected-recur {:level :off} ; TODO (cam): I think we just need to tell it how to handle MBQL match and we can enable this?
:used-underscored-binding {:level :off} ; false positives: see https://github.com/clj-kondo/clj-kondo/issues/2152
;;
;; TODO (cam): here are some more linters we should experiment with enabling -- some might be useful.
;;
;; :docstring-leading-trailing-whitespace {:level :warning}
;; :docstring-no-summary {:level :warning}
;; :shadowed-var {:level :warning}
:unresolved-symbol
{:unresolved-symbol
{:exclude
[instaparse.core/transform
(cljs.test/is [=? malli=])
(cljs.test/is [=?])
(clojure.core.logic/fresh)
(clojure.core.logic/matcha)
(clojure.core.logic/run)
(clojure.test/is [query= sql= =? malli=])
(clojure.test/is [query= sql= =?])
(clojure.tools.macro/macrolet)
(metabase.async.streaming-response/streaming-response)
(metabase.driver.druid.query-processor-test/druid-query-returning-rows)
@@ -58,6 +25,33 @@
(taoensso.nippy/extend-freeze)
(taoensso.nippy/extend-thaw)]}
:refer-all {:level :warning
:exclude [clojure.test]}
;; TODO (cam): I think we just need to tell it how to handle MBQL match and we can enable this?
:unexpected-recur {:level :off}
;; TODO (cam): can we fix these?
:unused-referred-var {:exclude {compojure.core [GET DELETE POST PUT]}}
:missing-else-branch {:level :warning}
:misplaced-docstring {:level :warning}
:non-arg-vec-return-type-hint {:level :warning}
:missing-body-in-when {:level :warning}
:missing-docstring {:level :warning}
;; TODO (braden): This is useful, but it doesn't grok eg. enterprise/backend/src
:namespace-name-mismatch {:level :off}
:use {:level :warning}
:redundant-fn-wrapper {:level :warning}
:invalid-arity {:skip-args [metabase.mbql.util.match/match]}
:warn-on-reflection {:level :warning}
;; TODO (cam): here are some more linters we should experiment with enabling -- some might be useful.
;; :docstring-no-summary {:level :warning}
;; :docstring-leading-trailing-whitespace {:level :warning}
;; :reduce-without-init {:level :warning}
;; :used-underscored-binding {:level :warning}
;; :single-key-in {:level :warning}
;; :keyword-binding {:level :warning}
;; :main-without-gen-class {:level :warning}
;; :shadowed-var {:level :warning}
:deprecated-var
{:exclude
{metabase.cmd/dump {:namespaces ["metabase\\.cmd-test" "metabase-enterprise\\.serialization\\.cmd-test"]}
@@ -65,6 +59,12 @@
metabase.db.data-migrations/data-migrations {:namespaces ["metabase\\.db\\.data-migrations"]}
metabase.db.data-migrations/defmigration {:namespaces ["metabase\\.db\\.data-migrations"]}
metabase.db.data-migrations/run-migration-if-needed! {:namespaces ["metabase\\.db\\.data-migrations"]}
metabase.driver.common/ThreadSafeSimpleDateFormat {:namespaces ["metabase\\.driver.*"]}
metabase.driver.common/create-db-time-formatters {:namespaces ["metabase\\.driver.*"]}
metabase.driver.common/current-db-time {:namespaces ["metabase.*"]}
metabase.driver.common/current-db-time-date-formatters {:namespaces ["metabase\\.driver.*"]}
metabase.driver.common/current-db-time-native-query {:namespaces ["metabase\\.driver.*"]}
metabase.driver.common/first-successful-parse {:namespaces ["metabase\\.driver.*"]}
metabase.driver.sql-jdbc.execute.old-impl/set-timezone-sql {:namespaces ["metabase\\.driver.*"]}
metabase.driver.sql-jdbc.execute/set-time-zone-if-supported! {:namespaces ["metabase\\.driver.*"]}
metabase.driver/current-db-time {:namespaces ["metabase.*"]}
@@ -123,28 +123,47 @@
schema.core/either {:namespaces ["metabase.*"]}}}
:discouraged-var
{clojure.core/print {:message "Use metabase.util.log instead of clojure.core/print"}
clojure.core/println {:message "Use metabase.util.log instead of clojure.core/println"}
clojure.core/printf {:message "Use metabase.util.log instead of clojure.core/printf"}
clojure.core/prn {:message "Use metabase.util.log instead of clojure.core/prn"}
clojure.core/pr {:message "Use metabase.util.log instead of clojure.core/pr"}
clojure.core/tap> {:message "Don't use tap> is source code"}
clojure.core/time {:message "Don't use time in source code"}
clojure.java.jdbc/get-connection {:message "Use sql-jdbc.execute/do-with-connection-with-options (for drivers) or t2/with-connection (for the app DB) instead of jdbc/get-connection"}
clojure.java.jdbc/with-db-connection {:message "Use sql-jdbc.execute/do-with-connection-with-options (for drivers) or t2/with-connection (for the app DB) instead of jdbc/with-db-connection"}
clojure.java.jdbc/with-db-metadata {:message "Use sql-jdbc.execute/do-with-connection-with-options + .getMetaData instead of jdbc/with-db-metadata"}
clojure.pprint/pprint {:message "Use metabase.util.log instead of clojure.pprint/pprint"}
clojure.string/lower-case {:message "Use metabase.util/lower-case-en instead of clojure.string/lower-case"}
clojure.string/upper-case {:message "Use metabase.util/upper-case-en instead of clojure.string/upper-case"}
honeysql.core/call {:message "Use hx/call instead because it is Honey SQL 2 friendly"}
honeysql.core/raw {:message "Use hx/raw instead because it is Honey SQL 2 friendly"}
java-time/with-clock {:message "Use mt/with-clock"}
java.util.UUID/randomUUID {:message "Use clojure.core/random-uuid instead of java.util.UUID/randomUUID"}
malli.core/explainer {:message "Use metabase.util.malli.registry/explainer instead of malli.core/explainer"}
medley.core/random-uuid {:message "Use clojure.core/random-uuid instead of medley.core/random-uuid"}
metabase.lib.equality/find-column-indexes-for-refs {:message "Use lib.equality/closest-matches-in-metadata or lib.equality/find-closest-matches-for-refs instead of lib.equality/find-column-indexes-for-refs"}
metabase.test/with-temp* {:message "Use mt/with-temp instead of mt/with-temp*"}
toucan2.tools.with-temp {:message "Use mt/with-temp instead of t2.with-temp/with-temp"}}
{clojure.core/print {:message "Use clojure.tools.logging instead of clojure.core/print"}
clojure.core/println {:message "Use clojure.tools.logging instead of clojure.core/println"}
clojure.core/printf {:message "Use clojure.tools.logging instead of clojure.core/printf"}
clojure.core/prn {:message "Use clojure.tools.logging instead of clojure.core/prn"}
clojure.core/pr {:message "Use clojure.tools.logging instead of clojure.core/pr"}
clojure.pprint/pprint {:message "Use clojure.tools.logging instead of clojure.pprint/pprint"}
clojure.string/lower-case {:message "Use metabase.util/lower-case-en instead of clojure.string/lower-case"}
clojure.string/upper-case {:message "Use metabase.util/upper-case-en instead of clojure.string/upper-case"}
honeysql.core/call {:message "Use hx/call instead because it is Honey SQL 2 friendly"}
honeysql.core/raw {:message "Use hx/raw instead because it is Honey SQL 2 friendly"}
java-time/with-clock {:message "Use mt/with-clock"}
metabase.test/with-temp {:message "Use t2.with-temp/with-temp instead of metabase.test/with-temp"}
;; metabase.test/with-temp* {:message "Use t2.with-temp/with-temp instead of metabase.test/with-temp*"}
toucan.db/select {:message "Use t2/select instead of toucan.db/select"}
toucan.db/select-one {:message "Use t2/select-one instead of toucan.db/select-one"}
toucan.db/select-one-id {:message "Use t2/select-one-pk instead of toucan.db/select-one-id"}
toucan.db/select-one-field {:message "Use t2/select-one-fn instead of toucan.db/select-one-field"}
toucan.db/select-ids {:message "Use t2/select-pks-set instead of toucan.db/select-ids"}
toucan.db/select-field {:message "Use t2/select-fn instead of toucan.db/select-field"}
toucan.db/select-field->field {:message "Use t2/select-fn->fn instead of toucan.db/select-field->field"}
toucan.db/select-field->id {:message "Use t2/select-fn->pk instead of toucan.db/select-field->id"}
toucan.db/select-id->field {:message "Use t2/select-pk->field instead of toucan.db/select-id->field"}
toucan.db/simple-insert! {:message "Use t2/insert-returning-pks! with table name instead of toucan.db/simple-insert!"}
toucan.db/simple-insert-many! {:message "Use t2/insert-returning-pks! with table name instead of toucan.db/simple-insert-many!"}
toucan.db/simple-delete! {:message "Use t2/delete! with table name instead of toucan.db/simple-delete!"}
toucan.db/delete! {:message "Use t2/delete! instead of toucan.db/delete!"}
toucan.db/insert! {:message "Use t2/insert-returning-instances! instead of toucan.db/insert!"}
toucan.db/update! {:message "Use t2/update! instead of toucan.db/update!"}
toucan.db/update-where! {:message "Use t2/update! instead of toucan.db/update-where!"}
toucan.db/query {:message "Use mdb.query/query instead of toucan.db/query"}
toucan.db/count {:message "Use t2/count instead of toucan.db/count"}
toucan.db/exists? {:message "Use t2/exists? instead of toucan.db/exists?"}
toucan.db/transaction {:message "Use t2/with-transaction instead of toucan.db/transaction"}
toucan.db/with-call-counting {:message "Use t2/with-call-count instead of toucan.db/with-call-counting"}
toucan.db/execute! {:message "Use t2/query-one instead of toucan.db/execute!"}
toucan.db/reducible-query {:message "Use mdb.query/reducible-query instead of toucan.db/reducible-query"}
toucan.db/resolve-model {:message "Use metabase.db.util/resolve-model instead of toucan.db/resolve-model"}
toucan.models/primary-key {:message "Use metabase.db.util/primary-key instead of toucan.models/primary-key"}
toucan.models/model? {:message "Use mdb.u/toucan-model? instead of toucan.models/model?"}
toucan.util.test/with-temp {:message "Use t2.with-temp/with-temp instead of toucan.util.test/with-temp"}
toucan.util.test/with-temp* {:message "Use t2.with-temp/with-temp instead of toucan.util.test/with-temp*"}}
:discouraged-namespace
{camel-snake-kebab.core {:message "CSK is not Turkish-safe, use the versions in metabase.util instead."}
@@ -247,7 +266,6 @@
metabase.api.util api.util
metabase.async.streaming-response.thread-pool thread-pool
metabase.automagic-dashboards.core magic
metabase.automagic-dashboards.dashboard-templates dashboard-templates
metabase.automagic-dashboards.populate populate
metabase.cmd.copy.h2 copy.h2
metabase.config config
@@ -296,7 +314,6 @@
metabase.email-test et
metabase.email.messages messages
metabase.http-client client
metabase.lib.util lib.util
metabase.mbql.normalize mbql.normalize
metabase.mbql.schema mbql.s
metabase.mbql.util mbql.u
@@ -334,7 +351,6 @@
metabase.pulse.render.body body
metabase.pulse.render.common common
metabase.pulse.render.style style
metabase.pulse.parameters params
metabase.query-processor-test qp.test
metabase.query-processor.context qp.context
metabase.query-processor.error-type qp.error-type
@@ -361,9 +377,6 @@
metabase.query-processor.timezone qp.timezone
metabase.query-processor.util qp.util
metabase.related related
metabase.search.config search.config
metabase.search.filter search.filter
metabase.search.util search.util
metabase.search.scoring scoring
metabase.server.middleware.auth mw.auth
metabase.server.middleware.browser-cookie mw.browser-cookie
@@ -402,7 +415,6 @@
metabase.test.domain-entities test.de
metabase.test.mock.util mock.util
metabase.test.sync test.sync
metabase.test.redefs test.redefs
metabase.test.util.js test.js
metabase.test.util.timezone test.tz
metabase.timeseries-query-processor-test.util tqpt
@@ -441,11 +453,7 @@
ring.adapter.jetty9.servlet servlet
saml20-clj.core saml
toucan.db db
toucan.models models}}
:metabase/validate-deftest {:level :warning}
:metabase/missing-test-expr-requires-in-cljs {:level :warning}
:metabase/i-like-making-cams-eyes-bleed-with-horrifically-long-tests {:level :warning}}
toucan.models models}}}
:lint-as
{clojure.core.logic/defne clj-kondo.lint-as/def-catch-all
@@ -456,6 +464,7 @@
metabase-enterprise.serialization.test-util/with-random-dump-dir clojure.core/let
metabase.actions.test-util/with-actions clojure.core/let
metabase.api.common/let-404 clojure.core/let
metabase.api.dashboard-test/let-url clojure.core/let
metabase.api.search-test/do-test-users clojure.core/let
metabase.async.api-response-test/with-response clojure.core/let
metabase.dashboard-subscription-test/with-dashboard-sub-for-card clojure.core/let
@@ -489,20 +498,14 @@
metabase.test.data.interface/defdataset clojure.core/def
metabase.test.data.interface/defdataset-edn clojure.core/def
metabase.test/defdataset clojure.core/def
metabase.test/let-url clojure.core/let
metabase.test/with-actions clojure.core/let
metabase.test/with-open-channels clojure.core/let
metabase.test/with-single-admin-user clojure.core/fn
metabase.test/with-temp-dir clojure.core/let
metabase.test/with-temp-file clojure.core/let
metabase.test/with-user-in-groups clojure.core/let
metabase.util.files/with-open-path-to-resource clojure.core/let
metabase.util.malli.defn/defn schema.core/defn
metabase.util.malli.fn/fn schema.core/fn
metabase.util.malli/defmethod schema.core/defmethod
metabase.util.malli/defn schema.core/defn
metabase.util.malli/fn schema.core/fn
metabase.util.ssh/with-ssh-tunnel clojure.core/let
monger.operators/defoperator clojure.core/def
potemkin.types/defprotocol+ clojure.core/defprotocol
@@ -516,26 +519,20 @@
:hooks
{:analyze-call
{cljs.test/deftest hooks.clojure.test/deftest
cljs.test/is hooks.clojure.test/is
cljs.test/use-fixtures hooks.clojure.test/use-fixtures
clojure.test/deftest hooks.clojure.test/deftest
clojure.test/is hooks.clojure.test/is
clojure.test/use-fixtures hooks.clojure.test/use-fixtures
metabase-enterprise.advanced-config.caching-test/with-temp-persist-models hooks.common/with-seven-bindings
{clojure.test/deftest hooks.clojure.test/deftest
metabase-enterprise.advanced-permissions.models.permissions.application-permissions-test/with-new-group-and-current-graph hooks.common/with-two-top-level-bindings
metabase-enterprise.audit-app.pages-test/with-temp-objects hooks.common/with-one-binding
metabase-enterprise.serialization.test-util/with-temp-dpc hooks.toucan2.tools.with-temp/with-temp
metabase-enterprise.serialization.test-util/with-temp-dpc hooks.toucan.util.test/with-temp*
metabase.analytics.prometheus-test/with-prometheus-system hooks.common/with-two-bindings
metabase.api.automagic-dashboards-test/with-indexed-model hooks.metabase.api.automagic-dashboards-test/with-indexed-model
metabase.api.alert-test/with-alert-in-collection hooks.common/with-four-bindings
metabase.api.card-test/with-persistence-setup hooks.common/with-one-top-level-binding
metabase.api.card-test/with-temp-native-card! hooks.common/with-two-bindings
metabase.api.card-test/with-temp-native-card-with-params! hooks.common/with-two-bindings
metabase.api.card-test/with-temp-native-card hooks.common/with-two-bindings
metabase.api.card-test/with-temp-native-card-with-params hooks.common/with-two-bindings
metabase.api.collection-test/with-french-user-and-personal-collection hooks.common/with-two-top-level-bindings
metabase.api.common/defendpoint hooks.metabase.api.common/defendpoint
metabase.api.common/defendpoint-async hooks.metabase.api.common/defendpoint
metabase.api.common/defendpoint-async-schema hooks.metabase.api.common/defendpoint
metabase.api.common/defendpoint-schema hooks.metabase.api.common/defendpoint
metabase.api.dashboard-test/with-chain-filter-fixtures hooks.common/let-one-with-optional-value
metabase.api.dashboard-test/with-simple-dashboard-with-tabs hooks.common/with-one-binding
metabase.api.card-test/with-card-param-values-fixtures hooks.common/let-one-with-optional-value
@@ -555,9 +552,6 @@
metabase.db.schema-migrations-test.impl/test-migrations hooks.metabase.db.schema-migrations-test.impl/test-migrations
metabase.dashboard-subscription-test/with-link-card-fixture-for-dashboard hooks.common/let-second
metabase.driver.bigquery-cloud-sdk-test/calculate-bird-scarcity hooks.metabase.query-processor-test.expressions-test/calculate-bird-scarcity
metabase.lib.test-util.macros/$ids hooks.metabase.test.data/$ids
metabase.lib.test-util.macros/mbql-query hooks.metabase.test.data/mbql-query
metabase.lib.test-util.macros/with-testing-against-standard-queries hooks.metabase.lib.test-util.macros/with-testing-against-standard-queries
metabase.mbql.schema.macros/defclause hooks.metabase.mbql.schemas.macros/defclause
metabase.models.collection-test/with-collection-hierarchy hooks.common/let-one-with-optional-value
metabase.models.collection-test/with-personal-and-impersonal-collections hooks.common/with-two-bindings
@@ -600,10 +594,10 @@
metabase.test/with-log-level hooks.common/with-ignored-first-arg
metabase.test/with-log-messages-for-level hooks.common/with-ignored-first-arg
metabase.test/with-non-admin-groups-no-root-collection-perms hooks.common/do*
metabase.test/with-temp hooks.toucan2.tools.with-temp/with-temp
metabase.test/with-temp! hooks.toucan2.tools.with-temp/with-temp
metabase.test/with-temp* hooks.toucan.util.test/with-temp*
metabase.test/with-temp-file hooks.metabase.test.util/with-temp-file
metabase.test/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values}
metabase.test/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values
toucan.util.test/with-temp* hooks.toucan.util.test/with-temp*}
:macroexpand
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
@@ -687,13 +681,7 @@
;;
;; Please keep this form updated when you change the generated regex! <3
{:pattern "^metabase\\.(?!util\\.(?:(?:honeysql-extensions)|(?:honey-sql-1)))(?!query-processor-test)(?!(?:(?:driver)|(?:test\\.data))\\.(?:(?:sql(?:-jdbc)?)|(?:(?:sql(?:-jdbc)?))|(?:bigquery-cloud-sdk)|(?:presto(?:(?:(?:-common)|(?:-jdbc)))?)|(?:sqlserver))(?:-test)?(?:(?:\\.)|(?:$))).*"
:name honey-sql-2-namespaces}
;;
;; QP source namespaces: `metabase.query-processor.*`, `metabase-enterprise.sandbox.query-processor.*`, and
;; `metabase.driver.*`, excluding `-test` namespaces.
;;
{:pattern "metabase(?:(?:(?:-enterprise\\.sandbox)?\\.query-processor\\.)|(?:\\.driver\\.)).*(?<!-test)$"
:name qp-and-driver-source-namespaces}]
:name honey-sql-2-namespaces}]
:config-in-ns
{test-namespaces
@@ -706,8 +694,8 @@
source-namespaces
{:linters
{:discouraged-var
{clojure.core/with-redefs {:message "Don't use with-redefs outside of tests"}
clojure.core/eval {:message "Don't use eval outside of tests"}}}}
{clojure.core/with-redefs {:message "Don't use with-redefs"}
clojure.core/eval {:message "Don't use eval"}}}}
printable-namespaces
{:linters
@@ -730,7 +718,9 @@
metabase-lib
{:linters
{:docstring-leading-trailing-whitespace {:level :warning}
:reduce-without-init {:level :warning}
:used-underscored-binding {:level :warning}
:single-key-in {:level :warning}
:keyword-binding {:level :warning}
:shadowed-var {:level :warning}
:metabase/deftest-not-marked-parallel-or-synchronized {:level :warning}
@@ -738,24 +728,5 @@
;; eventually we should do this for the whole codebase, but the args are in the opposite order so switching them
;; all at once isn't trivial, at least we can stop using it in new code.
:discouraged-var
{medley.core/map-keys {:message "Use clojure.core/update-keys"}
medley.core/map-vals {:message "Use clojure.core/update-vals"}
metabase.test/with-temp {:message "Don't use application database inside MLv2 code"}}
:discouraged-namespace
{toucan.db {:message "Don't use application database inside MLv2 code"}
toucan.models {:message "Don't use application database inside MLv2 code"}
toucan.util.test {:message "Don't use application database inside MLv2 code"}
toucan2.core {:message "Don't use application database inside MLv2 code"}}}}
qp-and-driver-source-namespaces
{:linters
{:discouraged-namespace
{toucan.db {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan.models {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan.util.test {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
toucan2.core {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}}
:discouraged-var
{metabase.db.query/query {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
metabase.db.query/reducible-query {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}}}}}}
{medley.core/map-keys {:message "Use clojure.core/update-keys"}
medley.core/map-vals {:message "Use clojure.core/update-vals"}}}}}}

View File

@@ -1,154 +1,27 @@
(ns hooks.clojure.test
(:require [clj-kondo.hooks-api :as hooks]
[clojure.string :as str]))
(:require [clj-kondo.hooks-api :as hooks]))
(def ^:private disallowed-parallel-forms
"Things you should not be allowed to use inside parallel tests. Besides these, anything ending in `!` not whitelisted
in [[allowed-parallel-forms]] is not allowed."
'#{clojure.core/alter-var-root
(def disallowed-parallel-forms
'#{with-redefs
clojure.core/with-redefs
clojure.core/with-redefs-fn
metabase-enterprise.sandbox.test-util/with-gtaps
metabase-enterprise.sandbox.test-util/with-gtaps-for-user
metabase-enterprise.sandbox.test-util/with-user-attributes
metabase-enterprise.test/with-gtaps
metabase-enterprise.test/with-gtaps-for-user
metabase-enterprise.test/with-user-attributes
metabase.actions.test-util/with-actions
metabase.actions.test-util/with-actions-disabled
metabase.actions.test-util/with-actions-enabled
metabase.actions.test-util/with-actions-test-data
metabase.actions.test-util/with-actions-test-data-and-actions-enabled
metabase.actions.test-util/with-actions-test-data-tables
metabase.email-test/with-expected-messages
metabase.email-test/with-fake-inbox
metabase.test.data.users/with-group
metabase.test.data.users/with-group-for-user
metabase.test.persistence/with-persistence-enabled
metabase.test.util.log/with-log-level
metabase.test.util.log/with-log-messages-for-level
metabase.test.util.misc/with-single-admin-user
metabase.test.util.timezone/with-system-timezone-id
metabase.test.util/with-all-users-permission
metabase.test.util/with-column-remappings
metabase.test.util/with-discarded-collections-perms-changes
metabase.test.util/with-env-keys-renamed-by
metabase.test.util/with-locale
metabase.test.util/with-non-admin-groups-no-root-collection-for-namespace-perms
metabase.test.util/with-non-admin-groups-no-root-collection-perms
metabase.test.util/with-temp-env-var-value
metabase.test.util/with-temp-vals-in-db
metabase.test.util/with-temporary-raw-setting-values
metabase.test.util/with-temporary-setting-values
metabase.test.util/with-user-in-groups
metabase.test/with-actions
metabase.test/with-actions-disabled
metabase.test/with-actions-enabled
metabase.test/with-actions-test-data
metabase.test/with-actions-test-data-and-actions-enabled
metabase.test/with-actions-test-data-tables
metabase.test/with-all-users-permission
metabase.test/with-column-remappings
metabase.test/with-discarded-collections-perms-changes
metabase.test/with-ensure-with-temp-no-transaction!
metabase.test/with-env-keys-renamed-by
metabase.test/with-expected-messages
metabase.test/with-fake-inbox
metabase.test/with-group
metabase.test/with-group-for-user
metabase.test/with-locale
metabase.test/with-log-level
metabase.test/with-log-messages-for-level
metabase.test/with-non-admin-groups-no-root-collection-for-namespace-perms
metabase.test/with-non-admin-groups-no-root-collection-perms
metabase.test/with-persistence-enabled
metabase.test/with-single-admin-user
metabase.test/with-system-timezone-id
metabase.test/with-temp!
metabase.test/with-temp-env-var-value
metabase.test/with-temp-vals-in-db
metabase.test/with-temporary-raw-setting-values
metabase.test/with-temporary-setting-values
metabase.test/with-user-in-groups})
mt/with-temporary-setting-values})
;;; TODO -- we should disallow `metabase.test/user-http-request` with any method other than `:get`
(def ^:private allowed-parallel-forms
"These fns are destructive, but are probably fine inside ^:parallel tests because it usually means you're doing
something to an atom or something like that."
'#{clojure.core/assoc!
clojure.core/compare-and-set!
clojure.core/conj!
clojure.core/disj!
clojure.core/dissoc!
clojure.core/persistent!
clojure.core/pop!
clojure.core/reset!
clojure.core/reset-vals!
clojure.core/run!
clojure.core/swap!
clojure.core/swap-vals!
clojure.core/volatile!
clojure.core/vreset!
clojure.core/vswap!
clojure.core.async/<!
clojure.core.async/<!!
clojure.core.async/>!
clojure.core.async/>!!
clojure.core.async/alt!
clojure.core.async/alt!!
clojure.core.async/alts!
clojure.core.async/alts!!
clojure.core.async/close!
clojure.core.async/ioc-alts!
clojure.core.async/offer!
clojure.core.async/onto-chan!
clojure.core.async/onto-chan!!
clojure.core.async/poll!
clojure.core.async/put!
clojure.core.async/take!
clojure.core.async/to-chan!
clojure.core.async/to-chan!!
metabase.query-processor/process-query-and-save-execution!
metabase.query-processor/process-query-and-save-with-max-results-constraints!
metabase.query-processor.store/store-database!})
(defn- node->qualified-symbol [node]
(try
(when (hooks/token-node? node)
(let [sexpr (hooks/sexpr node)]
(when (symbol? sexpr)
(when-let [resolved (hooks/resolve {:name sexpr})]
(symbol (name (:ns resolved)) (name (:name resolved)))))))
;; some symbols like `*count/Integer` aren't resolvable.
(catch Exception _
nil)))
(defn- warn-about-disallowed-parallel-forms [form]
(letfn [(error! [form message]
(hooks/reg-finding! (assoc (meta form)
:message message
:type :metabase/validate-deftest)))
(f [form]
(when-let [qualified-symbol (node->qualified-symbol form)]
(cond
(disallowed-parallel-forms qualified-symbol)
(error! form (format "%s is not allowed inside a ^:parallel test or test fixture" qualified-symbol))
(and (not (allowed-parallel-forms qualified-symbol))
(str/ends-with? (name qualified-symbol) "!"))
(error! form (format "destructive functions like %s are not allowed inside a ^:parallel test or test fixture. If this should be allowed, add it to the whitelist in .clj-kondo/hooks/clojure/test.clj"
qualified-symbol)))))
(defn warn-about-disallowed-parallel-forms [form]
(letfn [(f [form]
(when-let [sexpr (and (hooks/token-node? form)
(hooks/sexpr form))]
(when (disallowed-parallel-forms sexpr)
(hooks/reg-finding! (assoc (meta form)
:message (format "%s is not allowed inside a ^:parallel test" sexpr)
:type :metabase/validate-deftest)))))
(walk [form]
(f form)
(doseq [child (:children form)]
(walk child)))]
(walk form)))
(defn- deftest-check-parallel
"1. Check if test is marked ^:parallel / ^:synchronized correctly
2. Make sure disallowed forms are not used in ^:parallel tests"
[{[_ test-name & body] :children, :as _node}]
(defn deftest [{{[_ test-name & body] :children, :as node} :node}]
(let [test-metadata (:meta test-name)
metadata-sexprs (map hooks/sexpr test-metadata)
combined-metadata (transduce
@@ -175,71 +48,5 @@
:type :metabase/deftest-not-marked-parallel-or-synchronized)))
(when parallel?
(doseq [form body]
(warn-about-disallowed-parallel-forms form)))))
(def ^:private number-of-lines-for-a-test-to-be-considered-horrifically-long
200)
(defn- deftest-check-not-horrifically-long
[node]
(let [{:keys [row end-row]} (meta node)]
(when (and row end-row)
(let [num-lines (- end-row row)]
(when (>= num-lines number-of-lines-for-a-test-to-be-considered-horrifically-long)
(hooks/reg-finding! (assoc (meta node)
:message (str (format "This test is horrifically long, it's %d lines! 😱 " num-lines)
"Do you really want to try to debug it if it fails? 💀 "
"Split it up into smaller tests! 🥰")
:type :metabase/i-like-making-cams-eyes-bleed-with-horrifically-long-tests)))))))
(defn deftest [{:keys [node cljc lang]}]
;; run [[deftest-check-parallel]] only once... if this is a `.cljc` file only run it for the `:clj` analysis, no point
;; in running it twice.
(when (or (not cljc)
(= lang :clj))
(deftest-check-parallel node))
(deftest-check-not-horrifically-long node)
{:node node})
;;; this is a hacky way to determine whether these namespaces are required in the `ns` form or not... basically `:ns`
;;; will come back as `nil` if they are not.
(defn- approximately-equal-ns-required? []
(= (:ns (hooks/resolve {:name 'metabase.test-runner.assert-exprs.approximately-equal/=?-report}))
'metabase.test-runner.assert-exprs.approximately-equal))
(defn- malli-equals-ns-required? []
(= (:ns (hooks/resolve {:name 'metabase.test-runner.assert-exprs.malli-equals/malli=-report}))
'metabase.test-runner.assert-exprs.malli-equals))
(defn- warn-about-missing-test-expr-requires-in-cljs [{:keys [children], :as _is-node}]
(let [[_is assertion-node] children]
(when (hooks/list-node? assertion-node)
(let [[assertion-symb-node] (:children assertion-node)]
(when (hooks/token-node? assertion-symb-node)
(let [assertion-symb-token (hooks/sexpr assertion-symb-node)
warn! (fn [ns-to-require]
(hooks/reg-finding!
(assoc (meta assertion-symb-node)
:message (format "You must require %s to use %s in ClojureScript"
ns-to-require
assertion-symb-token)
:type :metabase/missing-test-expr-requires-in-cljs)))]
(condp = assertion-symb-token
'=?
(when-not (approximately-equal-ns-required?)
(warn! 'metabase.test-runner.assert-exprs.approximately-equal))
'malli=
(when-not (malli-equals-ns-required?)
(warn! 'metabase.test-runner.assert-exprs.malli-equals))
nil)))))))
(defn is [{:keys [node lang]}]
(when (= lang :cljs)
(warn-about-missing-test-expr-requires-in-cljs node))
{:node node})
(defn use-fixtures [{:keys [node]}]
(warn-about-disallowed-parallel-forms node)
(warn-about-disallowed-parallel-forms form))))
{:node node})

View File

@@ -141,84 +141,6 @@
body))]
{:node node*}))
(defn with-five-bindings
"Helper for macros that have a shape like
(my-macro [a b c d e]
...)
=>
(let [a nil, b nil, c nil, d nil, e nil]
...)
All bindings are optional and `_` will be substituted if not supplied."
[{{[_ {[a b c d e] :children} & body] :children} :node}]
(let [node* (hooks/list-node
(list*
(hooks/token-node 'let)
(hooks/vector-node
[(or a (hooks/token-node '_)) (hooks/token-node 'nil)
(or b (hooks/token-node '_)) (hooks/token-node 'nil)
(or c (hooks/token-node '_)) (hooks/token-node 'nil)
(or d (hooks/token-node '_)) (hooks/token-node 'nil)
(or e (hooks/token-node '_)) (hooks/token-node 'nil)])
body))]
{:node node*}))
(defn with-six-bindings
"Helper for macros that have a shape like
(my-macro [a b c d e f]
...)
=>
(let [a nil, b nil, c nil, d nil, e nil, f nil]
...)
All bindings are optional and `_` will be substituted if not supplied."
[{{[_ {[a b c d e f] :children} & body] :children} :node}]
(let [node* (hooks/list-node
(list*
(hooks/token-node 'let)
(hooks/vector-node
[(or a (hooks/token-node '_)) (hooks/token-node 'nil)
(or b (hooks/token-node '_)) (hooks/token-node 'nil)
(or c (hooks/token-node '_)) (hooks/token-node 'nil)
(or d (hooks/token-node '_)) (hooks/token-node 'nil)
(or e (hooks/token-node '_)) (hooks/token-node 'nil)
(or f (hooks/token-node '_)) (hooks/token-node 'nil)])
body))]
{:node node*}))
(defn with-seven-bindings
"Helper for macros that have a shape like
(my-macro [a b c d e f g]
...)
=>
(let [a nil, b nil, c nil, d nil, e nil, f nil, g nil]
...)
All bindings are optional and `_` will be substituted if not supplied."
[{{[_ {[a b c d e f g] :children} & body] :children} :node}]
(let [node* (hooks/list-node
(list*
(hooks/token-node 'let)
(hooks/vector-node
[(or a (hooks/token-node '_)) (hooks/token-node 'nil)
(or b (hooks/token-node '_)) (hooks/token-node 'nil)
(or c (hooks/token-node '_)) (hooks/token-node 'nil)
(or d (hooks/token-node '_)) (hooks/token-node 'nil)
(or e (hooks/token-node '_)) (hooks/token-node 'nil)
(or f (hooks/token-node '_)) (hooks/token-node 'nil)
(or g (hooks/token-node '_)) (hooks/token-node 'nil)])
body))]
{:node node*}))
(defn with-one-top-level-binding
"Helper for macros that have a shape like

View File

@@ -1,28 +0,0 @@
(ns hooks.metabase.api.automagic-dashboards-test
(:require [clj-kondo.hooks-api :as hooks]))
(comment
(def input (hooks/parse-string
"(with-indexed-model [{:keys [model model-index model-index-value]}
{:query :some-query}]
:foo :bar :body)"))
(println (hooks/sexpr input))
(let [f (load-file ".clj-kondo/hooks/metabase/api/automagic_dashboards_test.clj")]
(f {:node input})))
(defn with-indexed-model
[{node :node}]
(let [[_macro-call binding-and-query-info & body] (:children node)
[binding query-info] (:children binding-and-query-info)
node* (hooks/list-node
[(hooks/token-node 'do-with-testing-model)
query-info
(hooks/list-node
(list* (hooks/token-node 'fn)
(hooks/vector-node [binding])
body))])]
#_(println (hooks/sexpr node*))
{:node node*}))

View File

@@ -1,12 +0,0 @@
(ns hooks.metabase.lib.test-util.macros
(:require [clj-kondo.hooks-api :as hooks]))
(defn with-testing-against-standard-queries [{{[_ & args] :children} :node}]
(let [[query-sym & body] args
node* (hooks/list-node
(list*
(hooks/token-node 'let)
(hooks/vector-node
[query-sym (hooks/token-node 'nil)])
body))]
{:node node*}))

View File

@@ -1,3 +0,0 @@
{:hooks
{:analyze-call {org.httpkit.server/with-channel httpkit.with-channel/with-channel}}}

View File

@@ -1,16 +0,0 @@
(ns httpkit.with-channel
(:require [clj-kondo.hooks-api :as api]))
(defn with-channel [{node :node}]
(let [[request channel & body] (rest (:children node))]
(when-not (and request channel) (throw (ex-info "No request or channel provided" {})))
(when-not (api/token-node? channel) (throw (ex-info "Missing channel argument" {})))
(let [new-node
(api/list-node
(list*
(api/token-node 'let)
(api/vector-node [channel (api/vector-node [])])
request
body))]
{:node new-node})))

View File

@@ -37,7 +37,6 @@
~(macros.common/ignore-unused 'card-id-nested) nil
~(macros.common/ignore-unused 'card-id-nested-query) nil
~(macros.common/ignore-unused 'card-id-native-query) nil
~(macros.common/ignore-unused 'card-id-pivot-table) nil
~(macros.common/ignore-unused 'dashcard-id) nil
~(macros.common/ignore-unused 'dashcard-top-level-click-id) nil
~(macros.common/ignore-unused 'dashcard-with-click-actions) nil

View File

@@ -6,7 +6,6 @@
;; prefer keeping source width about ~118, GitHub seems to cut off stuff at either 119 or 120 and it's nicer
;; to look at code in GH when you don't have to scroll back and forth
(fill-column . 118)
(whitespace-line-column . 118)
;; tell find-things-fast to always use this directory as project root regardless of presence of other
;; deps.edn files
(ftf-project-finders . (ftf-get-top-git-dir)))

View File

@@ -1,2 +1 @@
frontend/src/cljs
frontend/src/cljs_release

View File

@@ -13,25 +13,9 @@
}
],
"no-empty": [1, { "allowEmptyCatch": true }],
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "moment",
"message": "Moment is deprecated, please use dayjs"
},
{
"name": "moment-timezone",
"message": "Moment is deprecated, please use dayjs"
}
]
}
],
"curly": [1, "all"],
"eqeqeq": [1, "smart"],
"import/no-default-export": 2,
"import/no-named-as-default": 0,
"import/no-commonjs": 1,
"import/order": [
"error",
@@ -73,7 +57,7 @@
]
}
],
"no-console": [2, { "allow": ["warn", "error"] }],
"no-console": [2, {"allow": ["warn", "error"]}],
"react/no-is-mounted": 2,
"react/prefer-es6-class": 2,
"react/display-name": 1,
@@ -86,35 +70,14 @@
"react/no-unescaped-entities": 2,
"react/jsx-no-target-blank": 2,
"react/jsx-key": 2,
"react/forbid-component-props": [2, { "forbid": ["sx"] }],
"react/forbid-component-props": [2, { "forbid": ["w", "h"] }],
"react-hooks/exhaustive-deps": [
"warn",
{ "additionalHooks": "(useSyncedQueryString|useSafeAsyncFunction)" }
],
"prefer-const": [1, { "destructuring": "all" }],
"no-useless-escape": 0,
"no-only-tests/no-only-tests": [
"error",
{
"block": [
"describe",
"it",
"context",
"test",
"tape",
"fixture",
"serial",
"Feature",
"Scenario",
"Given",
"And",
"When",
"Then",
"describeWithSnowplow",
"describeEE"
]
}
],
"no-only-tests/no-only-tests": "error",
"complexity": ["error", { "max": 54 }]
},
"globals": {
@@ -137,16 +100,11 @@
"plugin:react/jsx-runtime",
"plugin:react-hooks/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript"
"plugin:import/warnings"
],
"settings": {
"import/internal-regex": "^metabase/|^metabase-lib/",
"import/resolver": {
"webpack": {
"typescript": true
}
},
"import/resolver": "webpack",
"import/ignore": ["\\.css$"],
"react": {
"version": "detect"
@@ -170,7 +128,6 @@
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-this-alias": "off",
"no-unused-vars": "off",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{

View File

@@ -1,24 +0,0 @@
---
name: Epic (Internal Use)
about: This issue is used to track a feature/project that may span several days
to weeks to implement. This is used by the core Metabase team to project plan
and manage work.
title: "[Epic] Title"
labels: ".Epic"
---
**Links**
- product doc: _link to product doc_
- eng doc: _link to technical design doc, if any_
- feature branch: `branch-name` _this should be the feature branch where this work will be done in. PRs will be delivered against this branch_
- issue links: _related issues if any_
**Implementation Plan**
***Milestone 1***
_insert tasklist here_
***Milestone 2***

View File

@@ -0,0 +1,38 @@
---
name: Feature or Project Implementation (Internal Use)
about: This issue is used to track a feature implementation for a solution to a user
problem that may span days to weeks to implement. This is used by the core Metabase
team to project plan and manage work.
title: X - Internal Use - Feature/Project X Implementation
labels: ".Epic"
assignees: ''
---
<Insert feature / project description here>: Notes - This demonstrates a workflow and implementation plan around feature X that we can use in features that go out in a release. There should be a paragraph description of the problem we're trying to solve and a high level what the solution will be.
**Links**
- product doc: _link to product doc_
- eng doc: _link to technical design doc_
- feature branch: `branch-name` _this should be the feature branch where this work will be done in. PRs will be delivered against this branch_
- Issue links: _list of issues this should close when this is delivered_
**Implementation Plan**
***Milestone 1*** )_(note, each milestone should correspond to a point at which product/design can jump in to apply polish, evaluate changes, etc)_
- [x] step 1: #somePRnumber
- [ ] step 2
- [ ] step 3
***Milestone 2***
- [ ] etc
***Mileston N*** _(we can put stuff like translations, docs, testing, etc)_
- [ ] String translations
- [ ] Write rough pass documents for handoff to customer success
**How to Test This Feature Out**
_(include steps on how to test this project feature out, for someone unfamiliar with this work. please try to use the sample dataset when possible. this will serve as the basis for automated e2e tests, as well as the acceptance test to showcase the essence of this feature for bug bashes, etc)_

View File

@@ -1,14 +0,0 @@
---
name: Task (Internal Use)
about: This issue is used to track a smaller feature/task that does not require
being broken down to milestones. This is used by the core Metabase team to
project plan and manage work.
labels: ".Task"
---
**Context**
- issue links: _related issues if any_

View File

@@ -1,6 +1,3 @@
> [!IMPORTANT]
> If you are merging into master, please add either a `backport` or a `no-backport` label to this PR. You will not be able to merge until you do this step. Refer to the section [Do I need to backport this PR?](https://www.notion.so/metabase/Metabase-Branching-Strategy-6eb577d5f61142aa960a626d6bbdfeb3?pvs=4#89f80d6f17714a0198aeb66c0efd1b71) in the Metabase Branching Strategy document for more details.
> **Warning**
>
> If that is your first contribution to Metabase, please sign the [Contributor License Agreement](https://docs.google.com/a/metabase.com/forms/d/1oV38o7b9ONFSwuzwmERRMi9SYrhYeOrkbmNaq9pOJ_E/viewform) (unless it's a tiny documentation change). Also, if you're attempting to fix a translation issue, please submit your changes to our [POEditor project](https://poeditor.com/join/project/ynjQmwSsGh) instead of opening a PR.

View File

@@ -1,64 +0,0 @@
name: Build E2E Matrix
description: Build and output a custom matrix for E2E tests.
outputs:
matrix:
description: Parameterized matrix in JSON format
value: ${{ steps.matrix.outputs.result }}
runs:
using: "composite"
steps:
- uses: actions/github-script@v6
id: matrix
with:
script: |
const java = 11;
const defaultRunner = "ubuntu-22.04";
const beefierRunner = "buildjet-2vcpu-ubuntu-2204";
const defaultOptions = {
"java-version": java,
runner: defaultRunner,
edition: "ee",
context: "folder"
};
const testSets = [
["actions", {} ],
["admin", {} ],
["admin-2", {} ],
["binning", {} ],
["collections", {} ],
["custom-column", {} ],
["dashboard", {} ],
["dashboard-cards", {} ],
["dashboard-filters", {} ],
["embedding", {} ],
["filters", {} ],
["joins", {} ],
["models", {} ],
["native", {} ],
["native-filters", {} ],
["onboarding", {} ],
["organization", {} ],
["permissions", {} ],
["question", {} ],
["sharing", {} ],
["visualizations", {} ],
["oss-subset", { edition: 'oss', context: "special" } ],
["slow", { runner: beefierRunner, context: "special" } ],
["mongo", { context: "qa-database" } ],
];
const config = testSets.map(([name, specialOptions]) => {
return {
...defaultOptions,
...specialOptions,
name,
}
});
const matrix = { include: config };
return matrix;

View File

@@ -1,77 +0,0 @@
name: Find and download uberjar for E2E tests
description: Download previously built artifact from one of the previous commits.
inputs:
edition:
description: Metabase edition.
required: true
runs:
using: "composite"
steps:
- name: Download previously stored uberjar
uses: actions/github-script@v6
with:
script: | # js
const fs = require('fs');
const execSync = require("child_process").execSync;
const getCommit = (ref = "HEAD") => {
const [commit] = execSync(`git rev-parse ${ref}`, { encoding: "utf8" }).split(
"\n",
);
return commit;
};
const baseConfig = {
owner: context.repo.owner,
repo: context.repo.repo,
};
async function getArtifact(commit, depth = 0) {
if (depth > 20) {
throw new Error("Couldn't find the artifact!");
}
const artifactName = `metabase-${{ inputs.edition }}-${commit}-uberjar`;
const {
data: { artifacts },
} = await github.rest.actions.listArtifactsForRepo({
...baseConfig,
name: artifactName,
per_page: 1,
});
const [artifact] = artifacts;
if (!artifact) {
const parentCommit = getCommit(`${commit}^`);
console.log(`Didn't find ${artifactName}.\n`);
console.log(`Switching to the parent commit: ${parentCommit}`);
return getArtifact(parentCommit, depth + 1);
} else {
return artifact;
}
}
const currentCommit = "${{ github.event.pull_request.head.sha || github.sha }}";
const { id } = await getArtifact(currentCommit);
const download = await github.rest.actions.downloadArtifact({
...baseConfig,
artifact_id: id,
archive_format: 'zip',
});
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
- name: Unzip Metabase artifact containing an uberjar
run: unzip mb.zip
shell: bash
- name: Get the version info
run: |
jar xf target/uberjar/metabase.jar version.properties
cat version.properties
mv version.properties resources/
shell: bash

View File

@@ -1,87 +0,0 @@
name: Prepare Docker containers
description: Prepare Docker containers for E2E tests.
inputs:
username:
description: DockerHub username
required: true
password:
description: DockerHub password/token
required: true
maildev:
description: Maildev
required: true
default: 'false'
openldap:
description: Maildev
required: true
default: 'false'
postgres:
description: Maildev
required: true
default: 'false'
mysql:
description: Maildev
required: true
default: 'false'
mongo:
description: Maildev
required: true
default: 'false'
runs:
using: "composite"
steps:
- name: Authenticate to prevent API rate limit
uses: docker/login-action@v3
with:
username: ${{ inputs.username }}
password: ${{ inputs.password }}
- name: Start Containers
run: |
Y='\033[33m'
G='\033[1;32m'
if ${{ inputs.maildev }}; then
echo -e "${Y}Starting maildev container..." &&
docker run -d -p 1080:1080 -p 1025:1025 maildev/maildev:2.0.5 &&
while ! nc -z localhost 1080; do sleep 1; done &&
while ! nc -z localhost 1025; do sleep 1; done &&
echo -e "${G}Maildev is up and running!"
fi
if ${{ inputs.openldap }}; then
echo -e "${Y}Starting openldap container..." &&
docker run -d -p 389:389 \
--env LDAP_ADMIN_PASSWORD=adminpass \
--env LDAP_USERS=user01@example.org,user02@example.org \
--env LDAP_PASSWORDS=123456,123465 \
--env LDAP_ROOT=dc=example,dc=org \
--env LDAP_PORT_NUMBER=389 \
bitnami/openldap:2.6.4 &&
while ! nc -z localhost 389; do sleep 1; done &&
echo -e "${G}Openldap is up and running!"
fi
if ${{ inputs.postgres }}; then
echo -e "${Y}Starting postgres container..." &&
docker run -d -p 5404:5432 metabase/qa-databases:postgres-sample-12 &&
while ! nc -z localhost 5404; do sleep 1; done &&
echo -e "${G}Postgres is up and running!"
fi
if ${{ inputs.mysql }}; then
echo -e "${Y}Starting mysql container..." &&
docker run -d -p 3304:3306 metabase/qa-databases:mysql-sample-8 &&
while ! nc -z localhost 3304; do sleep 1; done &&
echo -e "${G}MySQL is up and running!"
fi
if ${{ inputs.mongo }}; then
echo -e "${Y}Starting mongo container..." &&
docker run -d -p 27004:27017 metabase/qa-databases:mongo-sample-4.4 &&
while ! nc -z localhost 27004; do sleep 1; done &&
echo -e "${G}Mongo is up and running!"
fi
shell: bash

View File

@@ -1,38 +1,7 @@
name: Prepare cypress environment
description: Cypress preparation steps
inputs:
is-replay-browser:
description: Are we running E2E tests using Replay.io browser?
required: false
outputs:
chrome-path:
description: Custom Chrome install path
value: ${{ steps.setup-chrome.outputs.chrome-path }}
runs:
using: "composite"
steps:
# Browsers
- name: Install Chrome v111
if: ${{ inputs.is-replay-browser != 'true' }}
uses: browser-actions/setup-chrome@v1
with:
# https://chromium.cypress.io/linux/stable/111.0.5563.146
chrome-version: 1097615
id: setup-chrome
- name: Chrome install feedback
if: ${{ inputs.is-replay-browser != 'true' }}
run: |
echo Installed chromium version: ${{ steps.setup-chrome.outputs.chrome-version }}
${{ steps.setup-chrome.outputs.chrome-path }} --version
shell: bash
- name: Install Replay.io browser
if: ${{ inputs.is-replay-browser == 'true' }}
run: npx @replayio/cypress install
shell: bash
# Cypress-specific
- name: Check to see if dependencies should be cached
if: ${{ contains(github.event.head_commit.message, '[ci nocache]') }}
run: echo "Commit message includes [ci nocache]; dependencies will NOT be cached"

View File

@@ -5,7 +5,10 @@ runs:
- name: Prepare Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
# Please don't bump this version to anything higher.
# See https://github.com/facebook/jest/issues/11956 for more details,
# as a lot of people couldn't complete a jest run with any other versions.
node-version: 16.10.0
cache: 'yarn'
- name: Check to see if dependencies should be cached
if: ${{ contains(github.event.head_commit.message, '[ci nocache]') }}

View File

@@ -1,11 +1,4 @@
name: Prepare Uberjar Artifact
description: Prepare and upload Metabase JAR as a GitHub artifact.
inputs:
name:
required: true
description: Artifact name
runs:
using: "composite"
steps:
@@ -18,7 +11,7 @@ runs:
- name: Upload JARs as artifact
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.name }}
name: metabase-${{ matrix.edition }}-uberjar
path: |
./target/uberjar/metabase.jar
./COMMIT-ID

View File

@@ -3,7 +3,7 @@ default: &default
- "bin/**"
ci: &ci
- ".github/**/!(*.md|team.json)"
- ".github/**/!(*.md)"
shared_sources: &shared_sources
- "shared/src/**"
@@ -12,11 +12,6 @@ shared_sources: &shared_sources
shared_specs: &shared_specs
- "shared/test/**"
frontend_ci: &frontend_ci
- ".github/actions/prepare-frontend/**"
- ".github/actions/prepare-backend/**"
- ".github/workflows/frontend.yml"
frontend_sources: &frontend_sources
- *shared_sources
- "enterprise/frontend/!(test)**/**/{!(*.spec),}.{js,jsx,ts,tsx,css}"
@@ -32,23 +27,15 @@ frontend_sources: &frontend_sources
frontend_specs: &frontend_specs
- *shared_specs
- "frontend/test/**"
- "frontend/**/tests/**"
- "frontend/**/*.unit.*"
- "jest.config.json"
- "jest.unit.conf.json"
- "jest.tz.unit.conf.json"
frontend_all: &frontend_all
- *frontend_ci
- *ci
- *frontend_sources
- *frontend_specs
backend_ci: &backend_ci
- ".github/actions/prepare-frontend/**"
- ".github/actions/prepare-backend/**"
- ".github/actions/test-driver/**"
- ".github/workflows/backend.yml"
- ".github/workflows/drivers.yml"
backend_presto_kerberos:
- "**/presto_jdbc/**"
- "**/presto_jdbc.clj"
@@ -73,9 +60,10 @@ backend_specs: &backend_specs
backend_all: &backend_all
- *default
- *backend_ci
- *ci
- *backend_sources
- *backend_specs
- *frontend_sources # keep it here until we detect static viz changes
sources: &sources
- *shared_sources
@@ -91,8 +79,8 @@ e2e_specs: &e2e_specs
e2e_all:
- *default
- *ci
- *sources
- *e2e_specs
- *sources
snowplow:
- *ci

56
.github/team.json vendored
View File

@@ -1,68 +1,46 @@
{
"teams": [
{
"name": "Query",
"label": ".Team/Query",
"name": "Team 42",
"label": ".Team/42 :milky_way:",
"members": [
"ranquild",
"WiNloSt",
"deniskaber",
"oisincoveney",
"uladzimirdev",
"tsmacdonald",
"qnkhuat",
"calherries",
"kamilmielnik"
]
},
{
"name": "Embedding",
"label": ".Team/Embedding",
"members": [
"WiNloSt",
"deniskaber",
"oisincoveney",
"npretto"
]
},
{
"name": "BEC",
"label": ".Team/BackendComponents",
"members": [
"tsmacdonald",
"qnkhuat",
"calherries",
"piranha"
]
},
{
"name": "Query Processor",
"label": ".Team/QueryProcessor",
"label": ".Team/QueryProcessor :hammer_and_wrench:",
"members": [
"camsaul",
"kulyk",
"snoe",
"bshepherdson",
"metamben",
"lbrdnk"
"john-metabase",
"metamben"
]
},
{
"name": "Admin Webapp",
"label": ".Team/AdminWebapp",
"name": "Pixel Police",
"label": ".Team/PixelPolice :police_officer:",
"members": [
"noahmoss",
"escherize",
"adam-james-v",
"qwef",
"alxnddr",
"iethree",
"npfitz",
"shaunlebron",
"johnswanson"
]
},
{
"name": "DashViz",
"label": ".Team/DashViz",
"members": [
"alxnddr",
"EmmadUsmani",
"JesseSDevaney",
"kulyk",
"markbastian",
"adam-james-v"
"shaunlebron"
]
}
]

View File

@@ -29,10 +29,6 @@ jobs:
const prAuthor = context.payload.pull_request.user.login;
const prNumber = context.payload.pull_request.number;
if (prAuthor.endsWith('[bot]')) {
return false;
}
// Assign PR author
await github.rest.issues.addAssignees({
owner: context.repo.owner,

View File

@@ -54,11 +54,13 @@ jobs:
git checkout master
git push -u origin ${BACKPORT_BRANCH}
BACKPORT_PR_URL=$(gh pr create --base "${TARGET_BRANCH}" --head "${BACKPORT_BRANCH}" --label "was-backported" --assignee "${GITHUB_ACTOR}" --title "🤖 backported \"${ORIGINAL_TITLE}\"" --body "#${ORIGINAL_PULL_REQUEST_NUMBER}")
BACKPORT_PR_URL=$(hub pull-request -b "${TARGET_BRANCH}" -h "${BACKPORT_BRANCH}" -l "was-backported" -a "${GITHUB_ACTOR}" -F- <<<"🤖 backported \"${ORIGINAL_TITLE}\"
#${ORIGINAL_PULL_REQUEST_NUMBER}")
BACKPORT_PR_NUMBER=${BACKPORT_PR_URL##*/}
echo "backport_pr_number=$BACKPORT_PR_NUMBER" >> $GITHUB_OUTPUT
echo "New PR has been created"
env:
TARGET_BRANCH: ${{ steps.get_latest_release_branch.outputs.branch-name }}
ORIGINAL_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}

View File

@@ -3,8 +3,8 @@ name: Backend
on:
push:
branches:
- "master"
- "release-**"
- 'master'
- 'release-**'
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
@@ -13,13 +13,13 @@ concurrency:
cancel-in-progress: true
env:
CLJ_KONDO_VERSION: "2023.09.07"
CLJ_KONDO_VERSION: "2023.04.14"
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
timeout-minutes: 5
timeout-minutes: 3
outputs:
backend_all: ${{ steps.changes.outputs.backend_all }}
steps:
@@ -31,233 +31,181 @@ jobs:
token: ${{ github.token }}
filters: .github/file-paths.yaml
static-viz-files-changed:
needs: files-changed
if: needs.files-changed.outputs.backend_all != 'true'
name: Check whether static-viz files changed
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
static_viz: ${{ steps.static_viz.outputs.static_viz }}
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
with:
m2-cache-key: "files-changed"
- name: Build static-viz frontend
run: yarn build-static-viz
- name: Upload Static Viz Bundle Artifact
uses: actions/upload-artifact@v3
with:
name: static-viz-bundle-${{ github.sha }}
path: resources/frontend_client/app/dist
- name: Check for static viz changes
uses: dorny/paths-filter@v2.11.1
id: static_viz
with:
token: ${{ github.token }}
filters: .github/static-viz-sources.yaml
be-linter-cloverage:
needs: files-changed
if: github.ref_name == 'master' && needs.files-changed.outputs.backend_all == 'true'
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: "cloverage"
- name: Build static viz frontend
run: yarn build-static-viz
- name: Collect the test coverage
run: clojure -X:dev:ci:ee:ee-dev:test:cloverage
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v3
with:
files: ./target/coverage/codecov.json
flags: back-end
- uses: actions/checkout@v3
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: 'cloverage'
- name: Build static viz frontend
run: yarn build-static-viz
- name: Collect the test coverage
run: clojure -X:dev:ci:ee:ee-dev:test:cloverage
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v3
with:
files: ./target/coverage/codecov.json
flags: back-end
be-linter-clj-kondo:
needs: [files-changed, static-viz-files-changed]
if: |
!cancelled() &&
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
needs: files-changed
if: needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 10
env:
DOWNLOAD_URL: https://github.com/clj-kondo/clj-kondo/releases/download
steps:
- uses: actions/checkout@v3
- name: Install clj-kondo
run: |
curl -OL ${DOWNLOAD_URL}/v${CLJ_KONDO_VERSION}/clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip
curl -OL ${DOWNLOAD_URL}/v${CLJ_KONDO_VERSION}/clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip.sha256
cat clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip.sha256 >> SHA256sum.txt
echo " clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip" >> SHA256sum.txt
sha256sum -c SHA256sum.txt
unzip clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip
- run: ./clj-kondo --version
- name: Run clj-kondo
run: >-
./clj-kondo
--config ./.clj-kondo/config.edn
--config-dir ./.clj-kondo
--parallel
--lint
./src
./test
./enterprise/backend/src
./enterprise/backend/test
./shared/src
./shared/test
./modules/drivers/athena/src
./modules/drivers/athena/test
./modules/drivers/bigquery-cloud-sdk/src
./modules/drivers/bigquery-cloud-sdk/test
./modules/drivers/druid/src
./modules/drivers/druid/test
./modules/drivers/googleanalytics/src
./modules/drivers/googleanalytics/test
./modules/drivers/mongo/src
./modules/drivers/mongo/test
./modules/drivers/oracle/src
./modules/drivers/oracle/test
./modules/drivers/presto-jdbc/src
./modules/drivers/presto-jdbc/test
./modules/drivers/redshift/src
./modules/drivers/redshift/test
./modules/drivers/snowflake/src
./modules/drivers/snowflake/test
./modules/drivers/sparksql/src
./modules/drivers/sparksql/test
./modules/drivers/sqlite/src
./modules/drivers/sqlite/test
./modules/drivers/sqlserver/src
./modules/drivers/sqlserver/test
./modules/drivers/vertica/src
./modules/drivers/vertica/test
- uses: actions/checkout@v3
- name: Install clj-kondo
run: |
curl -OL ${DOWNLOAD_URL}/v${CLJ_KONDO_VERSION}/clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip
curl -OL ${DOWNLOAD_URL}/v${CLJ_KONDO_VERSION}/clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip.sha256
cat clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip.sha256 >> SHA256sum.txt
echo " clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip" >> SHA256sum.txt
sha256sum -c SHA256sum.txt
unzip clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip
- run: ./clj-kondo --version
- name: Run clj-kondo
run: >-
./clj-kondo
--config ./.clj-kondo/config.edn
--config-dir ./.clj-kondo
--parallel
--lint
./src
./test
./enterprise/backend/src
./enterprise/backend/test
./shared/src
./shared/test
./modules/drivers/athena/src
./modules/drivers/athena/test
./modules/drivers/bigquery-cloud-sdk/src
./modules/drivers/bigquery-cloud-sdk/test
./modules/drivers/druid/src
./modules/drivers/druid/test
./modules/drivers/googleanalytics/src
./modules/drivers/googleanalytics/test
./modules/drivers/mongo/src
./modules/drivers/mongo/test
./modules/drivers/oracle/src
./modules/drivers/oracle/test
./modules/drivers/presto-jdbc/src
./modules/drivers/presto-jdbc/test
./modules/drivers/redshift/src
./modules/drivers/redshift/test
./modules/drivers/snowflake/src
./modules/drivers/snowflake/test
./modules/drivers/sparksql/src
./modules/drivers/sparksql/test
./modules/drivers/sqlite/src
./modules/drivers/sqlite/test
./modules/drivers/sqlserver/src
./modules/drivers/sqlserver/test
./modules/drivers/vertica/src
./modules/drivers/vertica/test
be-linter-eastwood:
needs: [files-changed, static-viz-files-changed]
if: |
!cancelled() &&
github.event.pull_request.draft == false &&
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
needs: files-changed
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: "eastwood"
- run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood
name: Run Eastwood linter
- uses: actions/checkout@v3
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: 'eastwood'
- 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:
needs: [files-changed, static-viz-files-changed]
if: |
!cancelled() &&
github.event.pull_request.draft == true &&
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
needs: files-changed
if: github.event.pull_request.draft == true && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
name: be-tests-java-11-ee-pre-check
timeout-minutes: 60
timeout-minutes: 35
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
with:
m2-cache-key: java-11-ee-pre-check
- uses: actions/checkout@v3
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: java-11-ee-pre-check
- run: yarn install --frozen-lockfile --prefer-offline
- name: Build static viz frontend
run: yarn build-static-viz
- 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: Run tests
run: clojure -X:dev:ci:test:ee:ee-dev
- name: Publish Test Report (JUnit)
uses: dorny/test-reporter@v1
if: failure()
with:
path: "target/junit/**/*_test.xml"
name: JUnit Test Report be-tests-java-11-ee-pre-check
reporter: java-junit
list-suites: failed
list-tests: failed
fail-on-error: false
- name: Publish Test Report (JUnit)
uses: dorny/test-reporter@v1
if: failure()
with:
path: 'target/junit/**/*_test.xml'
name: JUnit Test Report be-tests-java-11-ee-pre-check
reporter: java-junit
list-suites: failed
list-tests: failed
fail-on-error: false
be-tests:
needs: [files-changed, static-viz-files-changed]
if: |
!cancelled() &&
github.event.pull_request.draft == false &&
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
needs: files-changed
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
name: be-tests-java-${{ matrix.java-version }}-${{ matrix.edition }}
timeout-minutes: 60
timeout-minutes: 35
strategy:
fail-fast: false
matrix:
edition: [oss, ee]
java-version: [11, 17, 21]
java-version: [11, 17]
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
with:
m2-cache-key: be-tests
# Depending on which files changed, we might either have to build static-viz
# from scratch or download the previously built artifact
- name: Build static-viz frontend
if: needs.files-changed.outputs.backend_all == 'true'
run: yarn build-static-viz
- name: Download Static Viz Bundle Artifact
if: needs.static-viz-files-changed.outputs.static_viz == 'true'
uses: actions/download-artifact@v3
with:
name: static-viz-bundle-${{ github.sha }}
path: resources/frontend_client/app/dist
- uses: actions/checkout@v3
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: be-tests
- name: Run tests
if: matrix.java-version != 21
run: clojure -X:dev:ci:test:${{ matrix.edition }}:${{ matrix.edition }}-dev
- run: yarn install --frozen-lockfile --prefer-offline
- name: Build static viz frontend
run: yarn build-static-viz
- name: Run tests using Java 21 on `master` only
if: matrix.java-version == 21 && github.ref_name == 'master'
run: clojure -X:dev:ci:test:${{ matrix.edition }}:${{ matrix.edition }}-dev
- name: Run tests
run: clojure -X:dev:ci:test:${{ matrix.edition }}:${{ matrix.edition }}-dev
- name: Publish Test Report (JUnit)
uses: dorny/test-reporter@v1
if: failure()
with:
path: "target/junit/**/*_test.xml"
name: JUnit Test Report be-tests-java-${{ matrix.java-version }}-${{ matrix.edition }}
reporter: java-junit
list-suites: failed
list-tests: failed
fail-on-error: false
- name: Publish Test Report (JUnit)
uses: dorny/test-reporter@v1
if: failure()
with:
path: 'target/junit/**/*_test.xml'
name: JUnit Test Report be-tests-java-${{ matrix.java-version }}-${{ matrix.edition }}
reporter: java-junit
list-suites: failed
list-tests: failed
fail-on-error: false
be-tests-stub:
needs: [files-changed, static-viz-files-changed]
needs: files-changed
if: |
always() &&
github.event.pull_request.draft == false &&
(needs.files-changed.outputs.backend_all == 'false' && needs.static-viz-files-changed.outputs.static_viz == 'false')
needs.files-changed.outputs.backend_all == 'false'
runs-on: ubuntu-22.04
name: be-tests-java-${{ matrix.java-version }}-${{ matrix.edition }}
timeout-minutes: 5
@@ -265,7 +213,7 @@ jobs:
fail-fast: false
matrix:
edition: [oss, ee]
java-version: [11, 17, 21]
java-version: [11, 17]
steps:
- run: |
echo "Didn't run due to conditional filtering"
@@ -273,17 +221,14 @@ jobs:
# 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:
needs: [files-changed, static-viz-files-changed]
if: |
!cancelled() &&
github.event.pull_request.draft == false &&
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
needs: files-changed
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
name: be-check-java-${{ matrix.java-version }}
timeout-minutes: 10
strategy:
matrix:
java-version: [11, 17, 21]
java-version: [11, 17, 19]
steps:
- uses: actions/checkout@v3
- name: Prepare backend
@@ -294,17 +239,17 @@ jobs:
run: clojure -M:ee:drivers:check
be-check-stub:
needs: [files-changed, static-viz-files-changed]
needs: files-changed
if: |
always() &&
github.event.pull_request.draft == false &&
(needs.files-changed.outputs.backend_all == 'false' && needs.static-viz-files-changed.outputs.static_viz == 'false')
needs.files-changed.outputs.backend_all == 'false'
runs-on: ubuntu-22.04
name: be-check-java-${{ matrix.java-version }}
timeout-minutes: 10
strategy:
matrix:
java-version: [11, 17, 21]
java-version: [11, 17, 19]
steps:
- run: |
echo "Didn't run due to conditional filtering"

View File

@@ -1,18 +0,0 @@
name: Backport Reminder
on:
pull_request:
types: [auto_merge_enabled, review_requested, labeled, unlabeled, synchronize]
branches:
- "master"
jobs:
backport-reminder:
name: Decide whether to backport or not
runs-on: ubuntu-22.04
steps:
- uses: jesusvasquez333/verify-pr-label-action@v1.4.0
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
valid-labels: 'backport, no-backport'
disable-reviews: true

View File

@@ -108,10 +108,13 @@ jobs:
git push origin "${BACKPORT_BRANCH}" --force-with-lease
if [[ $(gh pr list --base "${TARGET_BRANCH}" --head "${BACKPORT_BRANCH}" -s "open") ]]; then
if [[ $(hub pr list -b "${TARGET_BRANCH}" -h "${BACKPORT_BRANCH}" -s "open") ]]; then
echo "PR already exists"
else
BACKPORT_PR_URL=$(gh pr create --base "${TARGET_BRANCH}" --head "${BACKPORT_BRANCH}" --label "was-backported" --assignee "${GITHUB_ACTOR}" --title "🤖 backported \"${ORIGINAL_TITLE}\"" --body "#${ORIGINAL_PULL_REQUEST_NUMBER}")
BACKPORT_PR_URL=$(hub pull-request -b "${TARGET_BRANCH}" -h "${BACKPORT_BRANCH}" -l "was-backported" -a "${GITHUB_ACTOR}" -F- <<<"🤖 backported \"${ORIGINAL_TITLE}\"
#${ORIGINAL_PULL_REQUEST_NUMBER}")
BACKPORT_PR_NUMBER=${BACKPORT_PR_URL##*/}
echo "backport_pr_number=$BACKPORT_PR_NUMBER" >> $GITHUB_OUTPUT

View File

@@ -1,53 +0,0 @@
name: Bugs Slack Notification
on:
issues:
types:
- opened
jobs:
on-issue-opened:
runs-on: ubuntu-22.04
timeout-minutes: 5
if: |
contains(github.event.issue.labels.*.name, '.Needs Triage') &&
contains(github.event.issue.labels.*.name, 'Type:Bug')
steps:
- name: Setting title
uses: actions/github-script@v6
id: vars
with:
script: |
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
- name: Check for labels containing team name
id: check_labels
run: |
labels_json=$(sed 's/&/\$/g' <<< '${{ toJson(github.event.issue.labels) }}')
echo "has_team_label=false" >> "$GITHUB_OUTPUT"
for label in "$labels_json"
do
if [[ $label == *".Team"* ]]; then
echo "Label contains the team name."
echo "has_team_label=true" >> "$GITHUB_OUTPUT"
break
fi
done
- name: Send the issue ${{ github.event.issue.number }} to Slack
id: slack
if: steps.check_labels.outputs.has_team_label == 'false'
uses: slackapi/slack-github-action@v1.23.0
with:
payload: |
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${{ steps.vars.outputs.issue_title }}\n${{ github.event.issue.html_url }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_BUG_TRIAGE_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

View File

@@ -1,194 +0,0 @@
name: Release 1 - Build Release Artifact
run-name: Build ${{ inputs.version }}
on:
workflow_dispatch:
inputs:
version:
description: 'Metabase version (e.g. v0.46.3)'
type: string
required: true
commit:
description: 'A full-length commit SHA-1 hash'
required: true
jobs:
check-version:
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
ee: ${{ fromJson(steps.canonical_version.outputs.result).ee }}
oss: ${{ fromJson(steps.canonical_version.outputs.result).oss }}
steps:
- name: Fail early on the incorrect version format
if: ${{ !(startsWith(inputs.version,'v0.') || startsWith(inputs.version,'v1.')) }}
run: |
echo "The version format is invalid!"
echo "It must start with either 'v0.' or 'v1.'."
echo "Please, try again."
exit 1
- uses: actions/checkout@v3
with:
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Get Release Version
uses: actions/github-script@v6
id: canonical_version
with:
script: | # js
const { isValidVersionString, getCanonicalVersion, hasBeenReleased } = require('${{ github.workspace }}/release/dist/index.cjs');
const version = '${{ inputs.version }}';
if (!isValidVersionString(version)) {
throw new Error("The version format is invalid! It must start with either 'v0.' or 'v1.'.");
}
const versions = {
ee: getCanonicalVersion(version, 'ee'),
oss: getCanonicalVersion(version, 'oss'),
};
const ossReleased = await hasBeenReleased({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: versions.oss,
});
const eeReleased = await hasBeenReleased({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: versions.ee,
});
if (ossReleased || eeReleased) {
throw new Error("This version has already been released!", version);
}
return versions;
check-commit:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Check out the code to verify the release branch
uses: actions/checkout@v3
with:
fetch-depth: 0 # IMPORTANT! to get all the branches
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Get Release Branch
uses: actions/github-script@v6
id: release_branch
with:
result_encoding: string
script: | # js
const { getReleaseBranch } = require('${{ github.workspace }}/release/dist/index.cjs');
const version = '${{ inputs.version }}';
const releaseBranch = getReleaseBranch(version);
return releaseBranch;
- name: Ensure that the specified commit exists in the ${{ steps.release_branch.outputs.result }} release branch
run: |
RELEASE_BRANCH=${{ steps.release_branch.outputs.result }}
git checkout $RELEASE_BRANCH
git branch --contains ${{ inputs.commit }} | grep -q $RELEASE_BRANCH \
&& echo "Commit found in correct release branch" \
|| (echo "Commit not found in correct release branch" && exit 1)
get-build-requirements:
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
build_process: ${{ fromJson(steps.dependencies.outputs.result).build_process }}
steps:
- uses: actions/checkout@v3
with:
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Get build dependencies
uses: actions/github-script@v6
id: dependencies
with:
script: | # js
const { getBuildRequirements, getMajorVersion } = require('${{ github.workspace }}/release/dist/index.cjs');
const version = '${{ inputs.version }}';
const requirements = getBuildRequirements(version);
const build_process = getMajorVersion(version) < 46 ? 'legacy' : 'new';
console.log('Build process:', build_process);
return {
java_version: requirements.java,
node_version: requirements.node,
build_process,
};
build-uberjar-for-release:
needs: [check-version, check-commit, get-build-requirements]
runs-on: ubuntu-22.04
timeout-minutes: 50
strategy:
matrix:
edition: [oss, ee]
env:
INTERACTIVE: false
steps:
- name: Get correct version number
run: | # bash
if [ '${{matrix.edition}}' == 'ee' ]; then
version='${{ needs.check-version.outputs.ee }}'
else
version='${{ needs.check-version.outputs.oss }}'
fi
echo "VERSION=$version" >> $GITHUB_ENV
- name: Setup Java ${{ needs.get-build-requirements.outputs.java_version }}
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: ${{ needs.get-build-requirements.outputs.java_version }}
- name: Setup Node.js ${{ needs.get-build-requirements.outputs.node_version }}
uses: actions/setup-node@v3
with:
node-version: ${{ needs.get-build-requirements.outputs.node_version }}.x
- 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 Metabase
if: ${{ needs.get-build-requirements.outputs.build_process != 'legacy' }}
run: ./bin/build.sh :edition :${{ matrix.edition }} :version $VERSION
- name: Build Metabase (legacy)
if: ${{ needs.get-build-requirements.outputs.build_process == 'legacy' }}
run: cd ./bin/build-mb && clojure -X build/build! :edition :${{ matrix.edition }} :version $VERSION
- name: Store commit's SHA-1 hash
run: echo ${{ inputs.commit }} > COMMIT-ID
shell: bash
- run: mv ./target/uberjar/metabase.jar ./metabase.jar
- name: Calculate SHA256 checksum
run: sha256sum ./metabase.jar > SHA256.sum
shell: bash
- name: Upload JARs as artifact
uses: actions/upload-artifact@v3
with:
name: metabase-${{ matrix.edition }}-${{ inputs.commit }}-uberjar
path: |
./metabase.jar
./COMMIT-ID
./SHA256.sum

View File

@@ -1,54 +0,0 @@
name: Chromatic Storybook deployment
on:
push:
branches:
- "master"
pull_request:
types: [synchronize, labeled, unlabeled]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
timeout-minutes: 3
outputs:
frontend_all: ${{ steps.changes.outputs.frontend_all }}
e2e_specs: ${{ steps.changes.outputs.e2e_specs }}
steps:
- uses: actions/checkout@v3
- name: Test which files changed
uses: dorny/paths-filter@v2.11.1
id: changes
with:
token: ${{ github.token }}
filters: .github/file-paths.yaml
fe-chromatic:
needs: files-changed
if: contains(github.event.pull_request.labels.*.name, 'chromatic') || (github.ref_name == 'master' && needs.files-changed.outputs.frontend_all == 'true')
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: "cljs"
- name: Publish to Chromatic
uses: chromaui/action@v1
env:
PUBLISH_CHROMATIC: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
STORYBOOK_BUILD_TIMEOUT: 900000
if: env.PUBLISH_CHROMATIC != null
with:
token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

View File

@@ -24,7 +24,6 @@ jobs:
filters: .github/file-paths.yaml
analyze:
needs: files-changed
if: needs.files-changed.outputs.codeql == 'true'
runs-on: ubuntu-22.04
steps:
- name: Checkout repository

View File

@@ -7,9 +7,6 @@ on:
- 'release-**'
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
workflow_run:
workflows: [External Trigger Filter]
types: [completed]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
@@ -33,11 +30,9 @@ jobs:
be-tests-athena-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: athena
@@ -55,11 +50,9 @@ jobs:
be-tests-bigquery-cloud-sdk-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: bigquery-cloud-sdk
@@ -79,11 +72,9 @@ jobs:
be-tests-druid-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: druid
@@ -104,11 +95,9 @@ jobs:
be-tests-googleanalytics-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: googleanalytics
@@ -122,11 +111,9 @@ jobs:
be-google-related-drivers-classpath-test:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: 'googleanalytics,bigquery-cloud-sdk'
@@ -146,11 +133,9 @@ jobs:
be-tests-mariadb-10-2-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mysql
@@ -174,11 +159,9 @@ jobs:
be-tests-mariadb-latest-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mysql
@@ -200,13 +183,11 @@ jobs:
with:
junit-name: 'be-tests-mariadb-latest-ee'
be-tests-mongo-4-4-ee:
be-tests-mongo-4-2-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mongo
@@ -214,24 +195,22 @@ jobs:
MB_MONGO_TEST_PASSWORD: metasample123
services:
mongodb:
image: metabase/qa-databases:mongo-sample-4.4
image: metabase/qa-databases:mongo-sample-4.2
ports:
- "27017:27017"
steps:
- uses: actions/checkout@v3
- name: Test MongoDB driver (4.4)
- name: Test MongoDB driver (4.2)
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-mongo-4-4-ee'
junit-name: 'be-tests-mongo-4-2-ee'
test-args: ":exclude-tags '[:mb/once]'"
be-tests-mongo-4-4-ssl-ee:
be-tests-mongo-4-2-ssl-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mongo
@@ -241,7 +220,7 @@ jobs:
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.4 mongod --dbpath /data/db2/ --tlsMode requireTLS --tlsCertificateKeyFile /etc/mongo/metamongo.pem --tlsCAFile /etc/mongo/metaca.crt
run: docker run -d -p 27017:27017 --name metamongo metabase/qa-databases:mongo-sample-4.2 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
@@ -256,19 +235,17 @@ jobs:
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.4)
- name: Test MongoDB SSL driver (4.2)
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-mongo-4-4-ee'
junit-name: 'be-tests-mongo-4-2-ee'
test-args: ":exclude-tags '[:mb/once]'"
be-tests-mongo-5-0-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mongo
@@ -289,11 +266,9 @@ jobs:
be-tests-mongo-5-0-ssl-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mongo
@@ -326,11 +301,9 @@ jobs:
be-tests-mongo-latest-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mongo
@@ -352,13 +325,11 @@ jobs:
junit-name: 'be-tests-mongo-latest-ee'
test-args: ":exclude-tags '[:mb/once]'"
be-tests-mysql-8-0-ee:
be-tests-mysql-5-7-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mysql
@@ -370,23 +341,21 @@ jobs:
MB_MYSQL_TEST_USER: root
services:
mysql:
image: cimg/mysql:8.0
image: circleci/mysql:5.7.23
ports:
- "3306:3306"
steps:
- uses: actions/checkout@v3
- name: Test MySQL driver (8.0)
- name: Test MySQL driver (5.7)
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-mysql-8-0-ee'
junit-name: 'be-tests-mysql-5-7-ee'
be-tests-mysql-latest-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: mysql
@@ -422,11 +391,9 @@ jobs:
be-tests-oracle-18-4-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: oracle
@@ -451,11 +418,9 @@ jobs:
be-tests-oracle-21-3-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: oracle
@@ -489,43 +454,38 @@ jobs:
be-tests-postgres-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: postgres
MB_DB_TYPE: postgres
MB_DB_PORT: 5432
MB_DB_HOST: localhost
MB_DB_DBNAME: mb_test
MB_DB_USER: mb_test
MB_POSTGRESQL_TEST_USER: mb_test
MB_DB_DBNAME: circle_test
MB_DB_USER: circle_test
MB_POSTGRESQL_TEST_USER: circle_test
services:
postgres:
image: postgres:12-alpine
image: circleci/postgres:9.6-alpine
ports:
- "5432:5432"
env:
POSTGRES_USER: mb_test
POSTGRES_DB: mb_test
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_USER: circle_test
POSTGRES_DB: circle_test
steps:
- uses: actions/checkout@v3
- name: Test Postgres driver (12)
- name: Test Postgres driver (9.6)
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-postgres-ee'
be-tests-postgres-latest-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: postgres
@@ -556,11 +516,9 @@ jobs:
be-tests-presto-jdbc-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: presto-jdbc
@@ -608,11 +566,9 @@ jobs:
be-tests-redshift-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: redshift
@@ -630,11 +586,9 @@ jobs:
be-tests-snowflake-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: snowflake
@@ -654,11 +608,9 @@ jobs:
be-tests-sparksql-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: sparksql
@@ -677,11 +629,9 @@ jobs:
be-tests-sqlite-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: sqlite
@@ -693,13 +643,11 @@ jobs:
junit-name: 'be-tests-sqlite-ee'
test-args: ":exclude-tags '[:mb/once]'"
be-tests-sqlserver-2017-ee:
be-tests-sqlserver-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: sqlserver
@@ -717,49 +665,17 @@ jobs:
MSSQL_MEMORY_LIMIT_MB: 1024
steps:
- uses: actions/checkout@v3
- name: Test MS SQL Server 2017 driver
- name: Test MS SQL Server driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-sqlserver-ee'
test-args: ":exclude-tags '[:mb/once]'"
be-tests-sqlserver-2022-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
runs-on: ubuntu-22.04
timeout-minutes: 90
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:2022-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 2022 driver
uses: ./.github/actions/test-driver
with:
junit-name: 'be-tests-sqlserver-2022-ee'
test-args: ":exclude-tags '[:mb/once]'"
be-tests-vertica-ee:
needs: files-changed
if: |
(github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true') &&
(github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.conclusion == 'success')
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 90
timeout-minutes: 60
env:
CI: 'true'
DRIVERS: vertica

View File

@@ -28,6 +28,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set Metabase Enterprise License
run: |
echo "MB_PREMIUM_EMBEDDING_TOKEN=${{ secrets.ENTERPRISE_TOKEN }}" >> $GITHUB_ENV
if: startsWith(matrix.version.source, 'v1') || startsWith(matrix.version.target, 'v1')
- name: Set source Docker image to OSS
run: |
echo "DOCKER_SOURCE_IMAGE=metabase/metabase:${{ matrix.version.source }}" >> $GITHUB_ENV
@@ -43,8 +47,7 @@ jobs:
docker run -d \
-v $PWD/my-metabase:/metabase.db \
-p 3000:3000 \
-e CYPRESS_ALL_FEATURES_TOKEN=${{ secrets.ENTERPRISE_TOKEN }} \
-e CYPRESS_NO_FEATURES_TOKEN=${{ secrets.E2E_STARTER_TOKEN }} \
-e MB_PREMIUM_EMBEDDING_TOKEN \
--name metabase-${{ matrix.version.source }} \
${{ env.DOCKER_SOURCE_IMAGE }}
- name: Wait for Metabase to start on port 3000
@@ -73,8 +76,7 @@ jobs:
docker run -d \
-v $PWD/my-metabase:/metabase.db \
-p 3001:3000 \
-e CYPRESS_ALL_FEATURES_TOKEN=${{ secrets.ENTERPRISE_TOKEN }} \
-e CYPRESS_NO_FEATURES_TOKEN=${{ secrets.E2E_STARTER_TOKEN }} \
-e MB_PREMIUM_EMBEDDING_TOKEN \
--name metabase-${{ matrix.version.target }} \
${{ env.DOCKER_TARGET_IMAGE }}
- name: Wait for Metabase to start on port 3001

View File

@@ -3,6 +3,10 @@ name: E2E Stress Test Flake Fix
on:
workflow_dispatch:
inputs:
artifact:
description: 'Artifact ID'
type: string
required: true
spec:
description: 'Relative path of the target spec'
type: string
@@ -11,48 +15,71 @@ on:
description: 'Number of times to run the test (e.g. 20)'
type: string
required: true
grep:
description: 'Grep and filter tests to run in isolation'
type: string
required: false
jobs:
stress-test-flake-fix:
runs-on: ubuntu-22.04
timeout-minutes: 60
timeout-minutes: 20
name: Stress test E2E flake fix
env:
DISPLAY: ""
QA_DB_ENABLED: true
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
MB_SNOWPLOW_AVAILABLE: true
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
TERM: xterm
TZ: US/Pacific # to make node match the instance tz
steps:
- uses: actions/checkout@v3
with:
# Important because we need previous commits hashes to find and download the uberjar!
fetch-depth: 20
- name: Prepare Docker containers
uses: ./.github/actions/e2e-prepare-containers
with:
services:
maildev:
image: maildev/maildev:2.0.5
ports:
- "1080:1080"
- "1025:1025"
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
maildev: true
openldap: true
postgres: true
mysql: true
mongo: true
- name: Download Metabase ${{ matrix.edition }} uberjar
uses: ./.github/actions/e2e-download-uberjar
with:
edition: 'ee'
was-built: false
openldap:
image: osixia/openldap:1.5.0
ports:
- "389:389"
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
postgres-sample:
image: metabase/qa-databases:postgres-sample-12
ports:
- "5404:5432"
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
mongo-sample:
image: metabase/qa-databases:mongo-sample-4.4
ports:
- 27004:27017
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
mysql-sample:
image: metabase/qa-databases:mysql-sample-8
ports:
- 3304:3306
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- name: Download Metabase uberjar from a previously stored artifact
run: |
curl -L \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/metabase/metabase/actions/artifacts/$ARTIFACT_ID/zip \
-o mb.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARTIFACT_ID: ${{ github.event.inputs.artifact }}
- name: Unzip Metabase artifact containing an uberjar
run: unzip mb.zip
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK 11
@@ -61,21 +88,21 @@ jobs:
java-version: 11
distribution: "temurin"
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
- name: Run Snowplow micro
uses: ./.github/actions/run-snowplow-micro
# For all options and fine-grained control, take a look at the documentation
# https://github.com/cypress-io/cypress/tree/develop/npm/grep
- name: Get Metabase version info
run: |
jar xf target/uberjar/metabase.jar version.properties
mv version.properties resources/
- name: Stress-test ${{ github.event.inputs.spec }} ${{ github.event.inputs.burn_in }} times
run: |
yarn run test-cypress-run \
--spec '${{ github.event.inputs.spec }}' \
--env burn=${{ github.event.inputs.burn_in }},grep='${{ github.event.inputs.grep }}' \
--config-file e2e/support/cypress-stress-test.config.js \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
--env burn=${{ github.event.inputs.burn_in }} \
--config-file e2e/support/cypress-stress-test.config.js
env:
TERM: xterm
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v3
if: failure()

View File

@@ -1,141 +0,0 @@
name: E2E Tests using Replay.io browser
on:
# We'll record runs using Replay.io and their browser on a schedule as an experiment
schedule:
- cron: '0 22 * * *'
jobs:
test-run-id:
runs-on: ubuntu-22.04
outputs:
testRunId: ${{ steps.testRunId.outputs.testRunId }}
steps:
- id: testRunId
run: echo testRunId=$(npx uuid) >> "$GITHUB_OUTPUT"
e2e-matrix-builder:
runs-on: ubuntu-22.04
timeout-minutes: 5
outputs:
matrix: ${{ steps.e2e-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v3
- name: Generate matrix for E2E tests
id: e2e-matrix
uses: ./.github/actions/build-e2e-matrix
e2e-tests:
needs: [test-run-id, e2e-matrix-builder]
runs-on: ${{ matrix.runner }}
timeout-minutes: 90
name: e2e-tests-${{ matrix.name }}-${{ matrix.edition }}
env:
MB_EDITION: ${{ matrix.edition }}
DISPLAY: ""
QA_DB_ENABLED: true
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
MB_SNOWPLOW_AVAILABLE: true
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
TZ: US/Pacific # to make node match the instance tz
TERM: xterm
CYPRESS_REPLAYIO_ENABLED: 1
RECORD_REPLAY_METADATA_FILE: /tmp/replay-metadata.json
RECORD_REPLAY_METADATA_TEST_RUN_ID: ${{ needs.test-run-id.outputs.testRunId }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.e2e-matrix-builder.outputs.matrix) }}
steps:
- uses: actions/checkout@v3
with:
# Important in case we need to download uberjar from previous commits!
fetch-depth: 20
- name: Prepare Docker containers
uses: ./.github/actions/e2e-prepare-containers
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
maildev: true
openldap: ${{ startsWith(matrix.name, 'admin') }}
postgres: ${{ matrix.name != 'mongo'}}
mysql: ${{ matrix.name != 'mongo'}}
mongo: ${{ matrix.name == 'mongo'}}
- name: Download Metabase ${{ matrix.edition }} uberjar
uses: ./.github/actions/e2e-download-uberjar
with:
edition: ${{ matrix.edition }}
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK ${{ matrix.java-version }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java-version }}
distribution: "temurin"
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
with:
is-replay-browser: true
- name: Run Snowplow micro
uses: ./.github/actions/run-snowplow-micro
- name: Run OSS-specific Cypress tests
if: matrix.edition == 'oss' && github.event_name != 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags=@OSS,grepOmitFiltered=true \
--spec './e2e/test/scenarios/**/*.cy.spec.js' \
--browser "replay-chromium"
- name: Run slow and resource-intensive Cypress tests
if: matrix.name == 'slow' && github.event_name != 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags="@slow",grepOmitFiltered=true \
--spec './e2e/test/scenarios/**/*.cy.spec.js' \
--browser "replay-chromium"
- name: Run EE Cypress tests on ${{ matrix.name }}
if: matrix.context == 'folder' && github.event_name != 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags="-@slow+-@mongo --@quarantine",grepOmitFiltered=true \
--folder ${{ matrix.name }} \
--browser "replay-chromium"
- name: Run E2E tests that depend on Mongo
if: matrix.name == 'mongo'
run: |
yarn run test-cypress-run \
--env grepTags="@mongo --@quarantine",grepOmitFiltered=true \
--spec './e2e/test/scenarios/**/*.cy.spec.js' \
--browser "replay-chromium"
env:
CYPRESS_QA_DB_MONGO: true
- name: Upload Replay.io recordings
if: github.event_name == 'schedule' && !cancelled()
uses: replayio/action-upload@v0.5.1
with:
api-key: ${{ secrets.REPLAY_IO_TOKEN }}
public: false
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v3
if: failure()
with:
name: cypress-recording-${{ matrix.name }}-${{ matrix.edition }}
path: |
./cypress
./logs/test.log
if-no-files-found: ignore

View File

@@ -1,6 +1,9 @@
name: E2E Tests
on:
# We'll record runs using Replay.io and their browser on a schedule as an experiment
schedule:
- cron: '0 */8 * * *'
push:
branches:
- "master"
@@ -13,6 +16,13 @@ concurrency:
cancel-in-progress: true
jobs:
test-run-id:
runs-on: ubuntu-22.04
outputs:
testRunId: ${{ steps.testRunId.outputs.testRunId }}
steps:
- id: testRunId
run: echo testRunId=$(npx uuid) >> "$GITHUB_OUTPUT"
files-changed:
name: Check which files changed
runs-on: ubuntu-22.04
@@ -28,24 +38,9 @@ jobs:
token: ${{ github.token }}
filters: .github/file-paths.yaml
e2e-matrix-builder:
runs-on: ubuntu-22.04
timeout-minutes: 5
outputs:
matrix: ${{ steps.e2e-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v3
- name: Generate matrix for E2E tests
id: e2e-matrix
uses: ./.github/actions/build-e2e-matrix
build:
needs: [files-changed, e2e-matrix-builder]
if: |
!cancelled() &&
github.event.pull_request.draft == false &&
needs.e2e-matrix-builder.result == 'success' &&
needs.files-changed.outputs.e2e_all == 'true'
needs: files-changed
if: github.event.pull_request.draft == false && needs.files-changed.outputs.e2e_all == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 25
strategy:
@@ -68,175 +63,269 @@ jobs:
- name: Prepare uberjar artifact
uses: ./.github/actions/prepare-uberjar-artifact
with:
name: metabase-${{ matrix.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
e2e-tests:
needs: [build, e2e-matrix-builder]
needs: [build, files-changed, test-run-id]
if: |
!cancelled() && needs.build.result == 'success'
runs-on: ${{ matrix.runner }}
timeout-minutes: 90
name: e2e-tests-${{ matrix.name }}-${{ matrix.edition }}
always() &&
needs.files-changed.outputs.e2e_all == 'true' &&
needs.build.result == 'success'
runs-on: ubuntu-22.04
timeout-minutes: 60
name: e2e-tests-${{ matrix.folder }}${{ matrix.context }}-${{ matrix.edition }}
env:
MB_EDITION: ${{ matrix.edition }}
DISPLAY: ""
QA_DB_ENABLED: true
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
# Record runs using Deploysentinel except for the release branch
CYPRESS_DEPLOYSENTINEL_KEY: ${{ !(startsWith(github.event.pull_request.base.ref, 'release')) && secrets.CYPRESS_DEPLOYSENTINEL_KEY || '' }}
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
TERM: xterm
TZ: US/Pacific # to make node match the instance tz
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.e2e-matrix-builder.outputs.matrix) }}
steps:
- uses: actions/checkout@v3
- name: Prepare Docker containers
uses: ./.github/actions/e2e-prepare-containers
with:
matrix:
java-version: [11]
edition: [ee]
folder:
- "admin"
- "binning"
- "collections"
- "custom-column"
- "dashboard"
- "dashboard-filters"
- "downloads"
- "embedding"
- "filters"
- "joins"
- "models"
- "native"
- "native-filters"
- "onboarding"
- "organization"
- "permissions"
- "question"
- "sharing"
- "visualizations"
include:
- edition: oss
context: grep
java-version: 11
services:
maildev:
image: maildev/maildev:2.0.5
ports:
- "1080:1080"
- "1025:1025"
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
maildev: true
openldap: ${{ startsWith(matrix.name, 'admin') }}
postgres: ${{ matrix.name != 'mongo'}}
mysql: ${{ matrix.name != 'mongo'}}
mongo: ${{ matrix.name == 'mongo'}}
- name: Retrieve uberjar artifact for ${{ matrix.edition }}
uses: actions/download-artifact@v3
with:
name: metabase-${{ matrix.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
- name: Get the version info
openldap:
image: osixia/openldap:1.5.0
ports:
- "389:389"
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
postgres-sample:
image: metabase/qa-databases:postgres-sample-12
ports:
- "5404:5432"
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
mongo-sample:
image: metabase/qa-databases:mongo-sample-4.4
ports:
- 27004:27017
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
mysql-sample:
image: metabase/qa-databases:mysql-sample-8
ports:
- 3304:3306
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- name: Record runs using Deploysentinel except for the release branch
if: ${{ github.ref == 'refs/heads/master' || !(startsWith(github.event.pull_request.base.ref, 'release')) }}
run: |
jar xf target/uberjar/metabase.jar version.properties
mv version.properties resources/
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 }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java-version }}
distribution: "temurin"
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
- name: Run Snowplow micro
uses: ./.github/actions/run-snowplow-micro
- uses: actions/download-artifact@v3
name: Retrieve uberjar artifact for ${{ matrix.edition }}
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Get the version info
run: |
jar xf target/uberjar/metabase.jar version.properties
mv version.properties resources/
- name: Run OSS-specific Cypress tests
if: matrix.edition == 'oss' && github.event_name != 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags=@OSS,grepOmitFiltered=true \
--spec './e2e/test/scenarios/**/*.cy.spec.js' \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
- name: Run slow and resource-intensive Cypress tests
if: matrix.name == 'slow' && github.event_name != 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags="@slow",grepOmitFiltered=true \
--spec './e2e/test/scenarios/**/*.cy.spec.js' \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
- name: Run EE Cypress tests on ${{ matrix.name }}
if: matrix.context == 'folder' && github.event_name != 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags="-@slow+-@mongo --@quarantine",grepOmitFiltered=true \
--folder ${{ matrix.name }} \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
- name: Run E2E tests that depend on Mongo
if: matrix.name == 'mongo'
run: |
yarn run test-cypress-run \
--env grepTags="@mongo --@quarantine",grepOmitFiltered=true \
--spec './e2e/test/scenarios/**/*.cy.spec.js' \
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
--env grepTags=@OSS \
--spec './e2e/test/scenarios/**/*.cy.spec.js'
env:
CYPRESS_QA_DB_MONGO: true
TERM: xterm
- name: Run EE Cypress tests on ${{ matrix.folder }}
if: matrix.edition == 'ee' && github.event_name != 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags="-@quarantine" \
--folder ${{ matrix.folder }}
env:
TERM: xterm
# REPLAY.IO specific jobs
- name: Install Replay.io browser
if: ${{ github.event_name == 'schedule' }}
run: npx @replayio/cypress install
- name: Run OSS-specific Cypress tests using Replay.io browser
if: matrix.edition == 'oss' && github.event_name == 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags=@OSS \
--spec './e2e/test/scenarios/**/*.cy.spec.js' \
--browser "replay-chromium"
env:
TERM: xterm
CYPRESS_REPLAYIO_ENABLED: 1
RECORD_REPLAY_METADATA_FILE: /tmp/replay-metadata.json
RECORD_REPLAY_METADATA_TEST_RUN_ID: ${{ needs.test-run-id.outputs.testRunId }}
- name: Run EE Cypress tests on ${{ matrix.folder }} using Replay.io browser
if: matrix.edition == 'ee' && github.event_name == 'schedule'
run: |
yarn run test-cypress-run \
--env grepTags="-@quarantine" \
--folder ${{ matrix.folder }} \
--browser "replay-chromium"
env:
TERM: xterm
CYPRESS_REPLAYIO_ENABLED: 1
RECORD_REPLAY_METADATA_FILE: /tmp/replay-metadata.json
RECORD_REPLAY_METADATA_TEST_RUN_ID: ${{ needs.test-run-id.outputs.testRunId }}
- name: Upload Replay.io recordings
if: github.event_name == 'schedule' && always()
uses: replayio/action-upload@v0.4.7
with:
api-key: rwk_gXbvYctIcR6RZyEzUvby3gtkO4esrB2L321lkY8FSuQ
public: true
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v3
if: failure()
with:
name: cypress-recording-${{ matrix.name }}-${{ matrix.edition }}
name: cypress-recording-${{ matrix.folder }}${{ matrix.context }}-${{ matrix.edition }}
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
e2e-tests-skipped-stub:
needs: [e2e-tests, e2e-matrix-builder]
needs: [build, files-changed]
if: |
!cancelled() &&
needs.e2e-tests.result == 'skipped'
runs-on: ${{ matrix.runner }}
always() &&
needs.files-changed.outputs.e2e_all == 'false' &&
needs.build.result == 'skipped'
runs-on: ubuntu-22.04
timeout-minutes: 5
name: e2e-tests-${{ matrix.name }}-${{ matrix.edition }}
name: e2e-tests-${{ matrix.folder }}${{ matrix.context }}-${{ matrix.edition }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.e2e-matrix-builder.outputs.matrix) }}
matrix:
java-version: [11]
edition: [ee]
folder:
- "admin"
- "binning"
- "collections"
- "custom-column"
- "dashboard"
- "dashboard-filters"
- "downloads"
- "embedding"
- "filters"
- "joins"
- "models"
- "native"
- "native-filters"
- "onboarding"
- "organization"
- "permissions"
- "question"
- "sharing"
- "visualizations"
include:
- edition: oss
context: grep
java-version: 11
steps:
- run: |
echo "Didn't run due to conditional filtering"
visual-regression-tests:
runs-on: ubuntu-22.04
timeout-minutes: 60
needs: [build]
if: |
!cancelled() && needs.build.result == 'success'
timeout-minutes: 45
needs: build
if: ${{ github.event_name != 'schedule' }}
name: percy-visual-regression-tests
steps:
- uses: actions/checkout@v3
- name: Prepare Docker containers
uses: ./.github/actions/e2e-prepare-containers
with:
env:
MB_EDITION: ${{ matrix.edition }}
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
services:
maildev:
image: maildev/maildev:2.0.5
ports:
- "1080:1080"
- "1025:1025"
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
maildev: true
- name: Retrieve uberjar artifact for ${{ matrix.edition }}
uses: actions/download-artifact@v3
with:
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
- name: Get the version info
steps:
- uses: actions/checkout@v3
- name: Enable Percy recording on master only
if: github.ref == 'refs/heads/master'
run: |
jar xf target/uberjar/metabase.jar version.properties
mv version.properties resources/
echo "PERCY_TOKEN=${{ secrets.PERCY_TOKEN }}" >> $GITHUB_ENV
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK 11
uses: actions/setup-java@v3
with:
java-version: 11
distribution: 'temurin'
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
- uses: actions/download-artifact@v3
name: Retrieve uberjar artifact for ee
with:
name: metabase-ee-uberjar
- name: Get the version info
run: |
jar xf target/uberjar/metabase.jar version.properties
mv version.properties resources/
- name: Percy Test
run: yarn run test-visual-run --browser ${{ steps.cypress-prep.outputs.chrome-path }}
run: yarn run test-visual-run
env:
PERCY_TOKEN: ${{ github.ref == 'refs/heads/master' && secrets.PERCY_TOKEN || ''}}
PERCY_TOKEN: ${{ env.PERCY_TOKEN }}

View File

@@ -10,12 +10,6 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Format issue title (escape quotes)
uses: actions/github-script@v6
id: vars
with:
script: |
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
- name: Send escalated issue ${{ github.event.issue.number }} to Slack
id: slack
uses: slackapi/slack-github-action@v1.23.0
@@ -27,7 +21,7 @@ jobs:
"type": "section",
"text": {
"type": "mrkdwn",
"text": "🔥🔥🔥 ${{ steps.vars.outputs.issue_title }}\n${{ github.event.issue.html_url }}"
"text": "🔥🔥🔥 ${{ github.event.issue.title }}\n${{ github.event.issue.html_url }}"
}
}
]

View File

@@ -1,17 +0,0 @@
name: External Trigger Filter
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- 'master'
paths-ignore:
- '.github/**'
jobs:
trigger:
runs-on: ubuntu-22.04
timeout-minutes: 5
if: github.event.pull_request.head.repo.full_name != github.repository
steps:
- run: echo 'Thank you for your contribution to Metabase!'

View File

@@ -3,8 +3,8 @@ name: Frontend
on:
push:
branches:
- "master"
- "release-**"
- 'master'
- 'release-**'
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
@@ -29,25 +29,33 @@ jobs:
token: ${{ github.token }}
filters: .github/file-paths.yaml
fe-lint:
fe-linter-prettier:
needs: files-changed
if: needs.files-changed.outputs.frontend_all == 'true' || needs.files-changed.outputs.e2e_specs == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- run: yarn run lint-prettier
name: Run Prettier formatting linter
fe-linter-eslint:
needs: files-changed
if: needs.files-changed.outputs.frontend_all == 'true' || needs.files-changed.outputs.e2e_specs == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 20
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
with:
m2-cache-key: "cljs"
- name: Compile CLJS
run: yarn build-pure:cljs
- name: Run Prettier formatting linter
run: yarn run lint-prettier-pure
- name: Run ESLint linter
run: yarn run lint-eslint-pure
- uses: actions/checkout@v3
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: 'cljs'
- run: yarn run lint-eslint
name: Run ESLint linter
fe-type-check:
needs: files-changed
@@ -55,43 +63,38 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 10
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
with:
m2-cache-key: "cljs"
- run: yarn build-pure:cljs
name: Compile CLJS
- run: yarn type-check-pure
name: Check types
- uses: actions/checkout@v3
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: 'cljs'
- run: yarn build:cljs
name: Compile CLJS
- run: yarn type-check
name: Check types
fe-tests-unit:
needs: files-changed
if: github.event.pull_request.draft == false && needs.files-changed.outputs.frontend_all == 'true'
runs-on: buildjet-2vcpu-ubuntu-2204
timeout-minutes: 60
timeout-minutes: 30
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
with:
m2-cache-key: "cljs"
- name: Run frontend unit tests and collect the code coverage information
if: github.ref_name == 'master'
run: yarn run test-unit --coverage --silent
- name: Run frontend unit tests
if: github.ref_name != 'master'
run: yarn run test-unit --silent
- name: Upload coverage to codecov.io
if: github.ref_name == 'master'
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
flags: front-end
- uses: actions/checkout@v3
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: 'cljs'
- run: yarn run test-unit --coverage --silent
name: Run frontend unit tests
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
flags: front-end
fe-tests-timezones:
needs: files-changed
@@ -99,12 +102,37 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: 'cljs'
- run: yarn run test-timezones
name: Run frontend timezones tests
fe-chromatic:
needs: files-changed
if: github.event.pull_request.draft == false && needs.files-changed.outputs.frontend_all == 'true'
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: "cljs"
- name: Run frontend timezones tests
run: yarn run test-timezones
m2-cache-key: 'cljs'
- name: Publish to Chromatic
uses: chromaui/action@v1
env:
PUBLISH_CHROMATIC: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
STORYBOOK_BUILD_TIMEOUT: 900000
if: env.PUBLISH_CHROMATIC != null
with:
token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

View File

@@ -1,18 +1,16 @@
name: Fuzzing
on:
workflow_dispatch:
push:
branches:
- 'master'
- 'release-**'
paths:
- 'frontend/src/metabase-lib/expressions/**'
pull_request:
paths:
- 'frontend/src/metabase-lib/expressions/**'
paths-ignore:
- 'docs/**'
jobs:
fe-fuzz-tokenizer:
runs-on: ubuntu-22.04
timeout-minutes: 15

View File

@@ -14,7 +14,7 @@ concurrency:
jobs:
verify-i18n-files:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
@@ -24,13 +24,8 @@ jobs:
uses: ./.github/actions/prepare-backend
with:
m2-cache-key: 'i18n'
- name: Install xgettext if necessary
run: |
if ! command -v xgettext > /dev/null; then
sudo apt install gettext
fi
- name: Check xgettext version
run: xgettext --version
- run: sudo apt install gettext
- name: Check i18n tags/make sure template can be built
run: ./bin/i18n/update-translation-template
- name: Verify i18n translations (.po files)

View File

@@ -54,8 +54,7 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MB_EDITION: ee
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
steps:
- uses: actions/checkout@v3
with:

View File

@@ -14,8 +14,7 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MB_EDITION: ee
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
steps:
- uses: actions/checkout@v3
- name: Prepare front-end environment

View File

@@ -1,89 +1,132 @@
name: Release 2 - Pre-release testing
run-name: Pre-release Testing of ${{ github.event.workflow_run.name }}
name: Pre-release [WIP]
on:
workflow_run:
workflows: [Release 1 - Build Release Artifact]
types: [completed]
workflow_dispatch:
inputs:
commit:
description: 'A full-length commit SHA-1 hash'
required: true
version:
description: 'Intended version (e.g. v0.46.3)'
required: true
env:
MAX_HASH_LENGTH: 8
CUSTOM_REPO: ${{ secrets.CUSTOM_RELEASE_REPO }}
jobs:
release-artifact:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
check-commit:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Ensure that the intended version (${{ github.event.inputs.version }}) was never released before
run: |
if [[ "${{ github.event.inputs.version }}" = v* ]]; then
echo "Checking if ${{ github.event.inputs.version }} conflicts with a past release..."
else
echo "ERROR: the intended version must start with 'v', e.g. 'v0.46.3."
echo "ABORT!"
exit -1
fi
CANONICAL_REFS=https://github.com/metabase/metabase/archive/refs
URL=${CANONICAL_REFS}/tags/${{ github.event.inputs.version }}.zip
HTTP_CODE=$(curl -s -L -o /dev/null --head -w "%{HTTP_CODE}" ${URL})
if [[ $HTTP_CODE =~ "200" ]]; then
echo "ERROR: that version was already released in the past."
echo "ABORT!"
exit -1
else
if [[ $HTTP_CODE =~ "404" ]]; then
echo "That version has not been released yet."
echo "Proceeding to the next step..."
exit 0
else
echo "ERROR: Unhandled case of HTTP $HTTP_CODE while checking ${URL}"
echo "ABORT!"
exit -1
fi
fi
- name: Check out the code to verify the release branch
uses: actions/checkout@v3
with:
fetch-depth: 0 # IMPORTANT! to get all the branches
- name: Ensure that the specified commit exists in the latest release branch
run: |
echo "Checking if the specified commit is in a release branch..."
COMMIT=${{ github.event.inputs.commit }}
git branch -a --contains $COMMIT > branches.txt
if [[ $(grep -c master branches.txt) =~ 1 ]]; then
echo "Found in master branch. ABORT!"
exit -1
fi
if [[ $(grep -c 'release-x' branches.txt) =~ 1 ]]; then
echo "Found the commit $COMMIT in:"
git branch -a --contains $COMMIT
echo "Proceeding to the next step..."
exit 0
else
echo "Commit $COMMIT is not found in a single release branch"
echo "ABORT!."
exit -1
fi
build:
if: ${{ github.repository }} != 'metabase/metabase'
runs-on: ubuntu-22.04
needs: check-commit
name: Build Metabase ${{ matrix.edition }} @${{ github.event.inputs.commit }}
timeout-minutes: 40
strategy:
matrix:
edition: [oss, ee]
outputs:
ee_version: ${{ steps.version-properties.outputs.ee_version }}
oss_version: ${{ steps.version-properties.outputs.oss_version }}
commit: ${{ steps.version-properties.outputs.commit }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MB_EDITION: ${{ matrix.edition }}
INTERACTIVE: false
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: 'Download artifact'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
- name: Fail early if custom docker release 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
with:
m2-cache-key: pre-release-build
const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
- name: Determine the canonical version ## EE is always v1.x.y, OSS is always v0.x.y
uses: actions/github-script@v6
id: canonical_version
env:
INTENDED_VERSION: ${{ github.event.inputs.version }}
EDITION: ${{ matrix.edition }}
with:
result-encoding: string
script: |
var canonical_version;
const ver = process.env.INTENDED_VERSION;
if (process.env.EDITION === "ee") {
canonical_version = ver.replace(/^v0\./, "v1."); // always e.g. v1.47.2
} else {
canonical_version = ver.replace(/^v1\./, "v0."); // always e.g. v0.45.6
}
console.log("The canonical version of this", process.env.EDITION, "edition is", canonical_version);
return canonical_version;
const matchArtifact = allArtifacts.data.artifacts.find((artifact) => {
return artifact.name.includes('-${{ matrix.edition }}-');
});
- name: Build
run: ./bin/build.sh :edition :${{ matrix.edition }} :version ${{ steps.canonical_version.outputs.result }}
if (!matchArtifact) {
throw new Error('No artifact found');
}
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
- name: Unzip Metabase artifact containing an uberjar
run: unzip mb.zip
- name: Extract the version.properties file from the JAR
run: |
jar xf metabase.jar version.properties
mv version.properties resources/
- name: Reveal Metabase ${{ matrix.edition }} properties
id: version-properties
run: |
cat ./resources/version.properties
echo "commit=$(cat ./COMMIT-ID)" >> $GITHUB_OUTPUT
version=$(grep -o '^tag=.*' ./resources/version.properties | cut -d'=' -f2)
if [[ "${{ matrix.edition }}" == "ee" ]]; then
echo "ee_version=$version" >> $GITHUB_OUTPUT
else
echo "oss_version=$version" >> $GITHUB_OUTPUT
fi
shell: bash
- name: Upload Metabase ${{ matrix.edition }} JAR as artifact
uses: actions/upload-artifact@v3
with:
name: metabase-${{ matrix.edition }}-uberjar
path: |
./metabase.jar
./COMMIT-ID
./SHA256.sum
- name: Prepare uberjar artifact
uses: ./.github/actions/prepare-uberjar-artifact
check-uberjar-health:
needs: [release-artifact]
runs-on: ubuntu-22.04
name: Is ${{ matrix.edition }} (java ${{ matrix.java-version }}) healthy?
needs: build
timeout-minutes: 10
strategy:
matrix:
@@ -102,25 +145,23 @@ jobs:
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Launch uberjar (and keep it running)
run: java -jar ./metabase.jar &
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
run-sanity-check:
runs-on: ubuntu-22.04
timeout-minutes: 30
needs: [release-artifact, check-uberjar-health]
timeout-minutes: 15
needs: check-uberjar-health
strategy:
matrix:
edition: [oss, ee]
env:
MB_EDITION: ${{ matrix.edition }}
DISPLAY: ""
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
MB_SNOWPLOW_AVAILABLE: true
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
TERM: xterm
services:
maildev:
image: maildev/maildev:2.0.5
@@ -134,7 +175,7 @@ jobs:
- name: Check out the code
uses: actions/checkout@v3
with:
ref: ${{ needs.release-artifact.outputs.commit }}
ref: ${{ github.event.inputs.commit }}
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare JDK 11
@@ -152,13 +193,12 @@ jobs:
name: metabase-${{ matrix.edition }}-uberjar
- name: Get the version info
run: |
jar xf metabase.jar version.properties
jar xf target/uberjar/metabase.jar version.properties
mv version.properties resources/
- name: Run a few important Cypress tests as sanity check
run: |
mkdir -p ./target/uberjar && cp metabase.jar ./target/uberjar/metabase.jar
yarn test-cypress-run --folder onboarding
run: yarn test-cypress-run --folder onboarding
env:
TERM: xterm
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v3
if: failure()
@@ -170,8 +210,8 @@ jobs:
if-no-files-found: ignore
containerize:
needs: [release-artifact, run-sanity-check]
runs-on: ubuntu-22.04
needs: run-sanity-check
timeout-minutes: 15
strategy:
matrix:
@@ -181,26 +221,24 @@ jobs:
image: registry:2
ports:
- 5000:5000
outputs:
image_tag: ${{ steps.target.outputs.image_tag }}
steps:
- name: Get the version
run: |
if [[ "${{ matrix.edition }}" == "ee" ]]; then
echo "VERSION=${{ needs.release-artifact.outputs.ee_version }}" >> $GITHUB_ENV
else
echo "VERSION=${{ needs.release-artifact.outputs.oss_version }}" >> $GITHUB_ENV
fi
- name: Check out the code
uses: actions/checkout@v3
with:
ref: ${{ needs.release-artifact.outputs.commit }}
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 metabase.jar bin/docker/.
run: mv ./target/uberjar/metabase.jar bin/docker/.
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
@@ -212,12 +250,12 @@ jobs:
context: bin/docker/.
platforms: linux/amd64
network: host
tags: localhost:5000/local-metabase:${{ env.VERSION }}
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.VERSION }}
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
@@ -228,53 +266,48 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Retag and push container image to ${{ vars.DOCKERHUB_OWNER }} staging Docker Hub repo
id: target
- name: Determine the target Docker Hub repository
run: |
# SHA-1 truncated to 7 digits should be enough
SHORT_HASH=${COMMIT:0:7}
IMAGE_TAG=$VERSION-$SHORT_HASH
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
echo "Retag local image for staging"
docker tag localhost:5000/local-metabase:$VERSION $CONTAINER_IMAGE
echo "Pushing container image $CONTAINER_IMAGE ..."
docker push $CONTAINER_IMAGE
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!"
shell: bash
env:
COMMIT: ${{ needs.release-artifact.outputs.commit }}
verify-docker-pull:
runs-on: ubuntu-22.04
needs: [release-artifact, containerize]
needs: containerize
timeout-minutes: 15
strategy:
matrix:
edition: [oss, ee]
env:
IMAGE_TAG: ${{ needs.containerize.outputs.image_tag }}
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: |
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
echo "Pulling container image $CONTAINER_IMAGE ..."
docker pull $CONTAINER_IMAGE
echo "Pulling container image ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_NAME }} ..."
docker pull ${{ env.DOCKERHUB_REPO }}:${{ env.IMAGE_NAME }}
echo "Successful!"
- name: Launch container
run: |
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
docker run --rm -dp 3000:3000 $CONTAINER_IMAGE
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

View File

@@ -1,259 +1,98 @@
name: Release 3 - Publish Release
run-name: Publish Release ${{ inputs.version }}
name: Release [WIP]
on:
workflow_dispatch:
inputs:
version:
description: 'Metabase version (e.g. v0.46.3)'
type: string
required: true
commit:
description: 'A full-length commit SHA-1 hash'
required: true
push:
tags:
- 'v*'
jobs:
check-version:
trigger-docs-update:
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
ee: ${{ fromJson(steps.canonical_version.outputs.result).ee }}
oss: ${{ fromJson(steps.canonical_version.outputs.result).oss }}
steps:
- name: Fail early on the incorrect version format
if: ${{ !(startsWith(inputs.version,'v0.') || startsWith(inputs.version,'v1.')) }}
run: |
echo "The version format is invalid!"
echo "It must start with either 'v0.' or 'v1.'."
echo "Please, try again."
exit 1
- uses: actions/checkout@v3
with:
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Get Release Version
uses: actions/github-script@v6
id: canonical_version
with:
script: |
const { isValidVersionString, getCanonicalVersion, hasBeenReleased } = require('${{ github.workspace }}/release/dist/index.cjs');
const version = '${{ inputs.version }}';
if (!isValidVersionString(version)) {
throw new Error("The version format is invalid! It must start with either 'v0.' or 'v1.'.");
}
const versions = {
ee: getCanonicalVersion(version, 'ee'),
oss: getCanonicalVersion(version, 'oss'),
};
const ossReleased = await hasBeenReleased({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: versions.oss,
});
const eeReleased = await hasBeenReleased({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: versions.ee,
});
if (ossReleased || eeReleased) {
throw new Error("This version has already been released!", version);
}
return versions;
- uses: actions/github-script@v6
with:
github-token: ${{ secrets.DOCS_UPDATE_TOKEN }}
script: |
github.rest.repos.createDispatchEvent({
owner: '${{ github.repository_owner }}',
repo: '${{ secrets.DOCS_REPO }}',
event_type: 'trigger-docs-update',
client_payload: {
version: '${{ github.ref_name }}'
}
});
download-uberjar:
needs: check-version
runs-on: ubuntu-22.04
timeout-minutes: 10
strategy:
matrix:
edition: [oss, ee]
steps:
- name: find_release_artifact
id: find_release_artifact
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const fs = require('fs');
const artifacts = await github.rest.actions.listArtifactsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
name: `metabase-${{ matrix.edition }}-${{ inputs.commit }}-uberjar`,
per_page: 1,
});
if (!artifacts.data?.artifacts?.[0]?.id) {
throw new Error(`No artifacts found for ${{ inputs.commit }}`);
}
const artifact_id = artifacts.data.artifacts[0].id;
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact_id,
archive_format: 'zip',
});
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
- name: unzip uberjar artifact
run: unzip mb.zip
- 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: Check JAR version properties
run: |
# ensure actual jar checksum matches checksum file
grep -q $(sha256sum ./metabase.jar) SHA256.sum && echo "checksums match" || (echo "checksum mismatch" && exit 1)
# ensure stored commit-id file matches input commit
grep -q ${{ inputs.commit }} COMMIT-ID && echo "commit ids match" || (echo "commit-id mismatch" && exit 1)
# ensure version.properties partial commit matches commit input
grep -q $(cut -c 1-7 <<< ${{ inputs.commit }}) version.properties && echo "version.properties commit matches" || (echo "version.properties commit mismatch" && exit 1)
# ensure version.properties version number matches input version (without v0/v1 to make it ee/oss agnostic)
grep -q $(cut -c 3-99 <<< ${{ inputs.version }}) version.properties && echo "version.properties version number matches" || (echo "version.properties version number mismatch" && exit 1)
# ensure the build is not a snapshot build
grep -q "SNAPSHOT" version.properties && (echo "jar is a snapshot" && exit 1) || echo "jar is not a snapshot build"
- name: Calculate SHA256 checksum
run: sha256sum ./metabase.jar | tee SHA256.sum
- name: Upload Uberjar as artifact
uses: actions/upload-artifact@v3
with:
name: metabase-${{ matrix.edition }}-uberjar
name: metabase-uberjar-${{ github.ref_name }}
path: |
./metabase.jar
./COMMIT-ID
./url.txt
./timestamp
./SHA256.sum
./version.properties
upload-to-s3:
check-uberjar:
runs-on: ubuntu-22.04
needs: download-uberjar
timeout-minutes: 15
timeout-minutes: 10
strategy:
matrix:
edition: [oss, ee]
java-version: [11, 17]
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
- name: Prepare JRE (Java Run-time Environment)
uses: actions/setup-java@v3
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
- uses: actions/checkout@v3
with:
sparse-checkout: release
- name: prepare release scripts
run: cd release && yarn && yarn build
java-package: jre
java-version: ${{ matrix.java-version }}
distribution: 'temurin'
- uses: actions/download-artifact@v3
name: Retrieve uberjar artifact
name: Retrieve previously downloaded Uberjar
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Determine the upload path ## EE is always v1.x.y, OSS is always v0.x.y
uses: actions/github-script@v6
id: version_path
with:
result-encoding: string
script: |
const version = '${{ inputs.version }}';
const edition = '${{ matrix.edition }}';
const version_path = edition === 'ee'
? 'enterprise/' + version.replace(/^v0\./, "v1.") // always e.g. v1.47.2
: version.replace(/^v1\./, "v0."); // always e.g. v0.45.6;
console.log("The download path for this", edition, "edition is", version_path);
return version_path;
- name: Determine if this is a latest version
uses: actions/github-script@v6
id: latest_path
with:
result-encoding: string
script: |
const { isLatestRelease, getCanonicalVersion } = require('${{ github.workspace }}/release/dist/index.cjs');
const version = '${{ inputs.version }}';
const edition = '${{ matrix.edition }}';
const canonical_version = getCanonicalVersion(version, edition);
const isLatest = await isLatestRelease({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: canonical_version,
});
if (isLatest) {
return edition === 'ee' ? 'enterprise/latest' : 'latest';
}
return "false";
- name: Upload to S3
run: aws s3 cp ./metabase.jar s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.version_path.outputs.result }}/metabase.jar
- name: Upload to s3 latest path
if: ${{ steps.is_latest.outputs.result != 'false' }}
run: aws s3 cp ./metabase.jar s3://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}/${{ steps.latest_path.outputs.result }}/metabase.jar
- name: Create cloudfront invalidation
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: |
aws cloudfront create-invalidation \
--distribution-id ${{ vars.AWS_CLOUDFRONT_DOWNLOADS_ID }} \
--paths /${{ steps.version_path.outputs.result }}/metabase.jar
verify-s3-download:
runs-on: ubuntu-22.04
needs: upload-to-s3
timeout-minutes: 15
strategy:
matrix:
edition: [oss, ee]
steps:
- uses: actions/download-artifact@v3
name: Retrieve uberjar artifact
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Determine the download path ## EE is always v1.x.y, OSS is always v0.x.y
uses: actions/github-script@v6
id: version_path
with:
result-encoding: string
script: |
const version = '${{ inputs.version }}';
const edition = '${{ matrix.edition }}';
const version_path = edition === 'ee'
? 'enterprise/' + version.replace(/^v0\./, "v1.") // always e.g. v1.47.2
: version.replace(/^v1\./, "v0."); // always e.g. v0.45.6;
console.log("The download path for this", edition, "edition is", version_path);
return version_path;
- name: Upload to S3
run:
curl -L http://${{ vars.AWS_S3_DOWNLOADS_BUCKET }}.s3.${{ vars.AWS_REGION }}.amazonaws.com/${{ steps.version_path.outputs.result }}/metabase.jar -o metabase-downloaded.jar
- name: Verify Checksum
run: grep -q $(sha256sum ./metabase-downloaded.jar) SHA256.sum && echo "checksums match" || exit 1
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 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-22.04
needs: [check-version, download-uberjar]
needs: check-uberjar
timeout-minutes: 15
strategy:
matrix:
edition: [oss, ee]
services:
registry:
image: registry:2
@@ -263,45 +102,10 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # IMPORTANT! to get all the tags
- name: prepare release scripts
run: cd release && yarn && yarn build
- name: Determine the docker version tag
uses: actions/github-script@v6
id: canonical_version
with:
result-encoding: string
script: |
const version = '${{ inputs.version }}';
const edition = '${{ matrix.edition }}';
const canonical_version = edition === 'ee'
? '${{ needs.check-version.outputs.ee }}'
: '${{ needs.check-version.outputs.oss }}';
console.log("The canonical version of this Metabase", edition, "edition is", canonical_version);
return canonical_version;
- name: Check if the container image should be tagged as latest
uses: actions/github-script@v6
id: latest_version_check
with:
result-encoding: string
script: |
const { execSync } = require("child_process");
const { isLatestVersion } = require('${{ github.workspace }}/release/dist/index.cjs');
const currentTag = '${{ inputs.version }}';
const allTags = execSync("git tag -l").toString("utf-8").split("\n");
const isLatest = isLatestVersion(currentTag, allTags);
console.log("Latest version?", isLatest);
return isLatest ? "latest" : "not-latest";
- uses: actions/download-artifact@v3
name: Retrieve previously downloaded Uberjar
with:
name: metabase-${{ matrix.edition }}-uberjar
name: metabase-uberjar-${{ github.ref_name }}
- name: Move the Uberjar to the context dir
run: mv ./metabase.jar bin/docker/.
- name: Set up Docker Buildx
@@ -315,12 +119,12 @@ jobs:
context: bin/docker/.
platforms: linux/amd64
network: host
tags: localhost:5000/local-metabase:${{ steps.canonical_version.outputs.result }}
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:${{ steps.canonical_version.outputs.result }}
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
@@ -328,7 +132,7 @@ jobs:
- name: Determine the target Docker Hub repository
run: |
if [[ "${{ matrix.edition }}" == "ee" ]]; then
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
@@ -343,16 +147,94 @@ jobs:
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Retag and push container image to Docker Hub
run: |
echo "Pushing ${{ steps.canonical_version.outputs.result }} to ${{ env.DOCKERHUB_REPO }} ..."
docker tag localhost:5000/local-metabase:${{ steps.canonical_version.outputs.result }} ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }}
docker push ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }}
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!"
- name: Check if the container image should be tagged as latest
uses: actions/github-script@v6
id: latest_version_check
with:
result-encoding: string
script: |
const currentTag = context.payload.ref.replace("refs/tags/", "");
const { execSync } = require("child_process");
const isNumber = s => s.match(/\d+/) && s.match(/\d+/).shift() == s;
const parseVersion = version => {
const parts = version.split(".");
const prefix = parts[0];
const isOSS = prefix === "v0";
const isEE = prefix === "v1";
const feature = parseInt(parts[1], 10);
const maintenance = parseInt(parts[2], 10);
const build = parseInt(parts[3], 10);
const prerelease = parts[2] ? !isNumber(parts[2]) : false;
return { prefix, feature, maintenance, build, isOSS, isEE, prerelease };
}
const byVersions = (tag1, tag2) => {
const v1 = parseVersion(tag1);
const v2 = parseVersion(tag2);
const order1 = v1.feature * 1e6 + v1.maintenance * 1e3 + (v1.build || 0);
const order2 = v2.feature * 1e6 + v2.maintenance * 1e3 + (v2.build || 0);
return order2 - order1;
}
const currentVersion = parseVersion(currentTag);
console.log("Current version for", currentTag, "is", currentVersion);
if (currentVersion.prerelease) {
console.log("This is a pre-release!");
console.log("There is no need to tag the container image as latest.");
return "not-latest";
}
console.log();
console.log("Enumerating all git tag...");
const allTags = execSync("git tag -l").toString("utf-8").split("\n");
console.log("Found total", allTags.length, "tags");
console.log();
console.log("Filtering for", currentVersion.isOSS ? "OSS" : "EE");
const relevantTags = allTags.filter(tag => {
const { prefix } = parseVersion(tag);
return prefix === currentVersion.prefix;
});
console.log("Found total", relevantTags.length, "filtered tags");
if (relevantTags.length < 10) {
console.error("Something is NOT RIGHT! Please check all git tags!!!");
return "not-latest";
}
console.log();
console.log("Sorting tags to find the highest version numbers...");
const sortedTags = relevantTags.sort(byVersions)
console.log("Showing 20 tags with the highest versions...");
const topTags = sortedTags.slice(0, 20);
topTags.map(tag => {
const marker = tag === currentTag ? "--->" : " ";
console.log(marker, tag);
});
const latestTag = topTags[0];
const isLatest = latestTag === currentTag;
console.log();
if (isLatest) {
console.log("Thus, the container image for ", currentTag, "must be marked as latest.");
} else {
console.log("The latest container image stays as", latestTag);
console.log("There is no need to tag the", currentTag, "container image as latest.");
}
return isLatest ? "latest" : "not-latest";
- name: Tag the container image as latest
if: ${{ steps.latest_version_check.outputs.result == 'latest' }}
run: |
echo "Pushing ${{ env.DOCKERHUB_REPO }}:latest ..."
docker tag localhost:5000/local-metabase:${{ steps.canonical_version.outputs.result }} ${{ env.DOCKERHUB_REPO }}:latest
docker tag localhost:5000/local-metabase:${{ github.ref_name }} ${{ env.DOCKERHUB_REPO }}:latest
docker push ${{ env.DOCKERHUB_REPO }}:latest
echo "Finished!"
@@ -360,9 +242,6 @@ jobs:
runs-on: ubuntu-22.04
needs: containerize
timeout-minutes: 15
strategy:
matrix:
edition: [oss, ee]
steps:
- name: Login to Docker Hub # authenticated, to avoid being rate-throttled
uses: docker/login-action@v2
@@ -371,245 +250,38 @@ jobs:
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
- name: Determine the container image to pull
run: |
if [[ "${{ matrix.edition }}" = "ee" ]]; then
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: Determine the docker version tag
uses: actions/github-script@v6
id: canonical_version
with:
result-encoding: string
script: |
const version = '${{ inputs.version }}';
const edition = '${{ matrix.edition }}';
const canonical_version = edition === "ee"
? version.replace(/^v0\./, "v1.") // always e.g. v1.47.2
: version.replace(/^v1\./, "v0."); // always e.g. v0.47.2
console.log("The canonical version of this Metabase", edition, "edition is", canonical_version);
return canonical_version;
- name: Pull the container image
run: |
echo "Pulling container image ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }} ..."
docker pull ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }}
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 }}:${{ steps.canonical_version.outputs.result }}
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
push-tags:
permissions: write-all
needs: [verify-s3-download, verify-docker-pull, check-version]
publish-elastic-beanstalk-artifacts:
runs-on: ubuntu-22.04
timeout-minutes: 10
needs: containerize
timeout-minutes: 15
env:
NO_SLACK: 1
steps:
- uses: actions/checkout@v3
with:
sparse-checkout: release
- name: prepare release scripts
run: cd release && yarn && yarn build
- name: Tag Release
uses: actions/github-script@v6
with:
result-encoding: string
script: | # js
const { tagRelease } = require('${{ github.workspace }}/release/dist/index.cjs');
const OSSversion = '${{ needs.check-version.outputs.oss }}';
const EEversion = '${{ needs.check-version.outputs.ee }}';
// push EE tag
await tagRelease({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: EEversion,
commitHash: '${{ inputs.commit }}',
}).catch((err) => {
console.error('Failed to tag release', EEversion, err);
process.exit(1);
});
// push OSS tag
await tagRelease({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: OSSversion,
commitHash: '${{ inputs.commit }}',
}).catch((err) => {
console.error('Failed to tag release', OSSversion, err);
process.exit(1);
});
- name: Reorder release tags to put OSS first
run: |
git config user.name github-actions
git config user.email github-actions@github.com
$GITHUB_WORKSPACE/release/reorder-tags.sh ${{ needs.check-version.outputs.ee }} ${{ inputs.commit }}
trigger-docs-update:
needs: push-tags
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- uses: actions/github-script@v6
with:
github-token: ${{ secrets.DOCS_UPDATE_TOKEN }}
script: |
github.rest.repos.createDispatchEvent({
owner: '${{ github.repository_owner }}',
repo: '${{ secrets.DOCS_REPO }}',
event_type: 'trigger-docs-update',
client_payload: {
version: '${{ inputs.version }}'
}
});
draft-release-notes:
needs: push-tags
runs-on: ubuntu-22.04
timeout-minutes: 15
permissions: write-all
env:
AWS_S3_DOWNLOADS_BUCKET: ${{ vars.AWS_S3_DOWNLOADS_BUCKET }}
DOCKERHUB_OWNER: ${{ vars.DOCKERHUB_OWNER }}
DOCKERHUB_REPO: ${{ vars.DOCKERHUB_REPO }}
strategy:
matrix:
edition: [oss, ee]
steps:
- uses: actions/checkout@v3
with:
sparse-checkout: release
- name: prepare release scripts
run: cd release && yarn && yarn build
- uses: actions/download-artifact@v3
name: Retrieve uberjar artifact
with:
name: metabase-${{ matrix.edition }}-uberjar
- name: Read Checksum
run: |
CHECKSUM=$(cat SHA256.sum)
echo "CHECKSUM=$CHECKSUM" >> $GITHUB_ENV
- name: Publish draft release notes
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const { publishRelease, getCanonicalVersion } = require('${{ github.workspace }}/release/dist/index.cjs');
const version = '${{ inputs.version }}';
const edition = '${{ matrix.edition }}';
const canonical_version = getCanonicalVersion(version, edition);
await publishRelease({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: canonical_version,
checksum: '${{ env.CHECKSUM }}',
}).then(() => {
console.log('Successfully published release notes', '${{ inputs.version }}');
}).catch((err) => {
console.error('Failed to publish release notes', '${{ inputs.version }}', err);
process.exit(1);
});
publish-version-info:
runs-on: ubuntu-22.04
needs: [push-tags, check-version]
timeout-minutes: 15
strategy:
matrix:
edition: [oss, ee]
env:
AWS_S3_STATIC_BUCKET: ${{ vars.AWS_S3_STATIC_BUCKET }}
AWS_REGION: ${{ vars.AWS_REGION }}
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Configure AWS credentials for S3
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
- uses: actions/checkout@v3
with:
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Publish release notes
uses: actions/github-script@v6
id: new_version_info
with:
result-encoding: string
script: |
const { getVersionInfo } = require('${{ github.workspace }}/release/dist/index.cjs');
const fs = require('fs');
const edition = '${{ matrix.edition }}';
const canonical_version = edition === 'ee'
? '${{ needs.check-version.outputs.ee }}'
: '${{ needs.check-version.outputs.oss }}';
const newVersionInfo = await getVersionInfo({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version: canonical_version,
});
fs.writeFileSync('version-info.json', JSON.stringify(newVersionInfo));
- name: Upload new version-info.json to S3
run: |
if [[ "${{ matrix.edition }}" == "ee" ]]; then
aws s3 cp version-info.json s3://${{ vars.AWS_S3_STATIC_BUCKET }}/version-info-ee.json
else
aws s3 cp version-info.json s3://${{ vars.AWS_S3_STATIC_BUCKET }}/version-info.json
fi
- name: Create cloudfront invalidation for version-info.json and version-info-ee.json
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ vars.AWS_CLOUDFRONT_STATIC_ID }} \
--paths "/version-info.json" "/version-info-ee.json"
manage-issues:
permissions: write-all
runs-on: ubuntu-22.04
needs: [publish-version-info, draft-release-notes]
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
with:
sparse-checkout: release
- name: Prepare build scripts
run: cd ${{ github.workspace }}/release && yarn && yarn build
- name: Update milestones
uses: actions/github-script@v6
with:
script: | # js
const { closeMilestone, openNextMilestones } = require('${{ github.workspace }}/release/dist/index.cjs');
const version = '${{ inputs.version }}';
await closeMilestone({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version,
});
await openNextMilestones({
github,
owner: context.repo.owner,
repo: context.repo.repo,
version,
});
aws-region: us-east-1
- name: Publish Elastic Beanstalk artifacts
run: ./bin/ebs.sh :version ${{ github.ref_name }}

View File

@@ -4,17 +4,13 @@ on:
workflow_run:
workflows: [Backend, Driver Tests, E2E Tests, Frontend]
types: [completed]
branches: [master, 'release-x.**', 'backport-**']
branches: [master, 'release-**']
jobs:
rerun-on-failure:
name: 'Re-run ''${{ github.event.workflow_run.name }}'' workflow'
runs-on: ubuntu-22.04
# Do not re-run scheduled workflow runs. That's only Replay.io E2E tests for now.
if: github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.event != 'schedule'
env:
BRANCH_NAME: ${{ github.event.workflow_run.head_branch }}
AUTHOR_NAME: ${{ github.event.workflow_run.head_commit.author.name }}
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- name: Generate job summary
run: |
@@ -25,19 +21,19 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: lts/Hydrogen # 18.x.x
node-version: '16'
- run: npm install @slack/web-api
- name: Trigger a re-run
uses: actions/github-script@v6
with:
script: | # js
script: |
const MAX_ATTEMPTS = 2;
const ATTEMPT = "${{ github.event.workflow_run.run_attempt }}";
const BRANCH = "${{ github.event.workflow_run.head_branch }}";
const FAILED_RUN_URL = "${{ github.event.workflow_run.html_url }}";
const FAILED_RUN_NAME = "${{ github.event.workflow_run.name }}";
const AUTHOR = "${{ github.event.workflow_run.head_commit.author.name }}";
const BREAKING_COMMIT = "${{ github.event.workflow_run.head_sha }}";
const AUTHOR = process.env.AUTHOR_NAME;
const BRANCH = process.env.BRANCH_NAME;
if (ATTEMPT <= MAX_ATTEMPTS) {
github.rest.actions.reRunWorkflowFailedJobs({
@@ -52,27 +48,58 @@ jobs:
await slack.chat.postMessage({
channel: 'engineering-ci',
text: 'Failing tests',
blocks: [
{
"type": "header",
"text": {
"type": "plain_text",
"text": `:warning: CI is failing on ${BRANCH}`,
"text": `:alert: CI is failing on ${BRANCH} :alert:`,
"emoji": true,
}
},
],
attachments: [{
color: BRANCH === 'master' ? '#f85149' : "#ffce33",
color: '#f85149',
blocks: [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": `Commit <https://github.com/metabase/metabase/commit/${BREAKING_COMMIT}|${BREAKING_COMMIT.slice(0,7)}> by ${AUTHOR} has failing <${FAILED_RUN_URL}|${FAILED_RUN_NAME}> tests on the <https://github.com/metabase/metabase/commits/${BRANCH}|\`${BRANCH}\`> branch. :sad-panda:`
"text": `Commit <https://github.com/metabase/metabase/commit/${BREAKING_COMMIT}|${BREAKING_COMMIT.slice(0,7)}> by ${AUTHOR} has failing <${FAILED_RUN_URL}|${FAILED_RUN_NAME}> tests on the \`${BRANCH}\` branch . :sad-panda:\nPlease fix ASAP. \n\nEmoji this message when it's fixed.`
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Failing Commit",
"emoji": true
},
"url": `https://github.com/metabase/metabase/commit/${BREAKING_COMMIT}`
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": `Recent commits to ${BRANCH}`,
"emoji": true
},
"url": `https://github.com/metabase/metabase/commits/${BRANCH}`
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Failed CI Run",
"emoji": true
},
"url": `${FAILED_RUN_URL}`
}
]
}
]
}]
});

View File

@@ -1,61 +0,0 @@
name: Set Announcement URL
run-name: Set Announcement URL - ${{ inputs.announcement-url }}
on:
workflow_dispatch:
inputs:
announcement-url:
description: 'The URL of the announcement'
type: string
required: true
jobs:
set-announce-url:
runs-on: ubuntu-22.04
timeout-minutes: 15
steps:
- name: Validate input
if: ${{ !startsWith(inputs.announcement-url, 'http') }}
run: |
echo "announcement url must be a valid URL, got: ${{ inputs.announcement-url }}"
exit 1
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
- name: Update announcement url
uses: actions/github-script@v6
id: announce_url
with:
result-encoding: string
script: |
// TODO: move this to the release/ directory when it exists
const fs = require('fs');
const announcementUrl = '${{ inputs.announcement-url }}';
const OSSversionInfo = await fetch(`http://${{ vars.AWS_S3_STATIC_BUCKET }}.s3.${{ vars.AWS_REGION }}.amazonaws.com/version-info.json`).then(r => r.json());
const EEversionInfo = await fetch(`http://${{ vars.AWS_S3_STATIC_BUCKET }}.s3.${{ vars.AWS_REGION }}.amazonaws.com/version-info-ee.json`).then(r => r.json());
OSSversionInfo.latest.announcement_url = announcementUrl;
EEversionInfo.latest.announcement_url = announcementUrl;
if (!OSSversionInfo.older.length || !EEversionInfo.older.length) {
throw new Error('No older versions found in version-info.json');
}
fs.writeFileSync('version-info.json', JSON.stringify(OSSversionInfo));
fs.writeFileSync('version-info-ee.json', JSON.stringify(EEversionInfo));
- name: Upload new version-info.json to s3
run: |
aws s3 cp version-info.json s3://${{ vars.AWS_S3_STATIC_BUCKET }}/version-info.json
aws s3 cp version-info-ee.json s3://${{ vars.AWS_S3_STATIC_BUCKET }}/version-info-ee.json
- name: Create cloudfront invalidation for version-info.json and version-info-ee.json
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ vars.AWS_CLOUDFRONT_STATIC_ID }} \
--paths "/version-info.json" "/version-info-ee.json"

View File

@@ -45,8 +45,6 @@ jobs:
run: ./bin/build.sh
- name: Prepare uberjar artifact
uses: ./.github/actions/prepare-uberjar-artifact
with:
name: metabase-${{ matrix.edition }}-${{ github.sha }}-uberjar
check_jar_health:
runs-on: ubuntu-22.04
@@ -68,7 +66,7 @@ jobs:
- uses: actions/download-artifact@v3
name: Retrieve uberjar artifact
with:
name: metabase-${{ matrix.edition }}-${{ github.sha }}-uberjar
name: metabase-${{ matrix.edition }}-uberjar
- name: Launch uberjar
run: java -jar ./target/uberjar/metabase.jar &
- name: Wait for Metabase to start
@@ -100,7 +98,7 @@ jobs:
- name: Download uploaded artifacts to insert into container
uses: actions/download-artifact@v3
with:
name: metabase-${{ matrix.edition }}-${{ github.sha }}-uberjar
name: metabase-${{ matrix.edition }}-uberjar
path: bin/docker/
- name: Move the ${{ matrix.edition }} uberjar to the context dir
run: mv bin/docker/target/uberjar/metabase.jar bin/docker/.
@@ -218,7 +216,7 @@ jobs:
- name: Download uploaded artifacts to insert into container
uses: actions/download-artifact@v3
with:
name: metabase-${{ matrix.edition }}-${{ github.sha }}-uberjar
name: metabase-${{ matrix.edition }}-uberjar
path: bin/docker/
- name: Move the ${{ matrix.edition }} uberjar to the context dir
run: mv bin/docker/target/uberjar/metabase.jar bin/docker/.

15
.gitignore vendored
View File

@@ -12,7 +12,6 @@
.eastwood
.idea/
.nrepl-port
.portal
.vscode
/*.h2.db
/*.lock.db
@@ -26,7 +25,6 @@
/.lein-repl-history
/.sqlite.db
/backend-checksums.txt
/backend-docs/uberdoc.html
/build.xml
/checkouts
/classes
@@ -34,6 +32,7 @@
/crate-*
/cypress
/deploy/artifacts/*
/docs/uberdoc.html
/frontend/test/snapshots/*
/e2e/snapshots/*
/lein-plugins/*/target
@@ -75,25 +74,17 @@ xcuserdata
dev/src/dev/nocommit/
*~
**/cypress_sample_database.json
**/cypress_sample_instance_data.json
/frontend/src/cljs
/frontend/src/cljs_release
.shadow-cljs
.clerk
# lsp: ignore all but the config file
.lsp/*
!.lsp/config.edn
modules/drivers/*/.lsp/*
!modules/drivers/*/.lsp/config.edn
**/.clj-kondo/.cache
# Editor- or environment-specific local files
.clj-kondo/.cache
*.code-workspace
.java-version
.calva/
# malli + clj-kondo: ignore malli-types
.clj-kondo/metosin/malli-types-clj
.github/static-viz-sources.yaml

View File

@@ -1,9 +1,7 @@
{:keep-require-at-start? true
:show-docs-arity-on-same-line? true
:project-specs [{:project-path "deps.edn"
:classpath-cmd ["clojure"
"-A:dev:ee:ee-dev:drivers:drivers-dev:build:build/release:build/all"
"-Spath"]}]
:classpath-cmd ["clojure" "-A:dev:ee:ee-dev:drivers:drivers-dev" "-Spath"]}]
:clean {:ns-inner-blocks-indentation :keep}
:linters {:clojure-lsp/unused-public-var {:level :warning
:exclude-when-defined-by

1
.nvmrc
View File

@@ -1 +0,0 @@
v18.16.1

View File

@@ -1,4 +1,3 @@
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const appConfig = require("../webpack.config");
module.exports = {
@@ -16,7 +15,6 @@ module.exports = {
],
webpackFinal: storybookConfig => ({
...storybookConfig,
plugins: [...storybookConfig.plugins, new MiniCssExtractPlugin()],
module: {
...storybookConfig.module,
rules: [

View File

@@ -1,9 +1,5 @@
<style>
<style type="text/css">
:root {
--default-font-family: "Lato";
--color-brand: #509ee3;
--color-brand-alpha-04: rgba(80, 158, 227, 0.04);
--color-brand-alpha-88: rgba(80, 158, 227, 0.88);
--color-focus: #cbe2f7;
}
</style>

View File

@@ -1,7 +1,4 @@
import React from "react";
import "metabase/css/index.css";
import "metabase/lib/dayjs";
import { ThemeProvider } from "metabase/ui";
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
@@ -12,7 +9,3 @@ export const parameters = {
},
},
};
export const decorators = [
renderStory => <ThemeProvider>{renderStory()}</ThemeProvider>,
];

16
.vscode/settings.json vendored
View File

@@ -1,10 +1,10 @@
{
"typescript.tsc.autoDetect": "off",
"grunt.autoDetect": "off",
"jake.autoDetect": "off",
"gulp.autoDetect": "off",
"npm.autoDetect": "off",
"eslint.options": {
"rulePaths": ["frontend/lint/eslint-rules/"]
}
"typescript.tsc.autoDetect": "off",
"grunt.autoDetect": "off",
"jake.autoDetect": "off",
"gulp.autoDetect": "off",
"npm.autoDetect": "off",
"eslint.options": {
"rulePaths": ["frontend/lint/eslint-rules/"]
}
}

View File

@@ -2,7 +2,7 @@
# STAGE 1: builder
###################
FROM node:18-bullseye as builder
FROM node:16-slim as builder
ARG MB_EDITION=oss

View File

@@ -33,40 +33,7 @@ Metabase can be run just about anywhere. Check out our [Installation Guides](htt
## Contributing
## Quick Setup: Dev environment
In order to spin up a development environment, you need to start the front end and the backend as follows:
### Frontend quick setup
The following command will install the Javascript dependencies:
```
$ yarn install
```
To build and run without watching changes:
```
$ yarn build
```
To build and run with hot-reload:
```
$ yarn build-hot
```
### Backend quick setup
In order to run the backend, you'll need to build the drivers first, and then start the backend:
```
$ ./bin/build-drivers.sh
$ clojure -M:run
```
For a more detailed setup of a dev environment for Metabase, check out our [Developers Guide](./docs/developers-guide/start.md).
To get started with a development installation of the Metabase, check out our [Developers Guide](https://www.metabase.com/docs/latest/developers-guide/start).
## Internationalization

View File

@@ -4,7 +4,7 @@
"@babel/preset-env",
[
"@babel/preset-react",
{ "runtime": "automatic" }
{ "runtime": "automatic", "importSource": "@emotion/react" }
],
"@babel/preset-typescript"
],

View File

@@ -18,8 +18,7 @@
"xalan" {:resource "apache2_0.txt"}}
"amalloy" {"ring-gzip-middleware" {:resource "MIT.txt"}}
"buddy" {"buddy-core" {:resource "apache2_0.txt"}
"buddy-sign" {:resource "apache2_0.txt"}}
"buddy" {"buddy-core" {:resource "apache2_0.txt"}}
"colorize" {"colorize" {:resource "EPL.txt"}}
"com.github.jnr" {"jffi$native" {:resource "apache2_0.txt"}}
"com.google.api-client" {"google-api-client" {:resource "apache2_0.txt"}}

View File

@@ -3,7 +3,7 @@
[build-drivers :as build-drivers]
[build.licenses :as license]
[build.uberjar :as uberjar]
[build.version-properties :as version-properties]
[build.version-info :as version-info]
[clojure.edn :as edn]
[clojure.java.io :as io]
[clojure.string :as str]
@@ -87,7 +87,7 @@
(def all-steps
(ordered-map/ordered-map
:version (fn [{:keys [edition version]}]
(version-properties/generate-version-properties-file! edition version))
(version-info/generate-version-info-file! edition version))
:translations (fn [_]
(i18n/create-all-artifacts!))
:frontend (fn [{:keys [edition]}]
@@ -108,7 +108,7 @@
:or {edition (edition-from-env-var)
steps (keys all-steps)}}]
(let [version (or version
(version-properties/current-snapshot-version edition))]
(version-info/current-snapshot-version edition))]
(u/step (format "Running build steps for %s version %s: %s"
(case edition
:oss "Community (OSS) Edition"

View File

@@ -64,20 +64,14 @@
ns-decls))
(defn metabase-namespaces-in-topo-order [basis]
(let [ns-decls (into []
(comp (map io/file)
(mapcat ns.find/find-ns-decls-in-dir))
(all-paths basis))
ns-symbols (into #{} (map ns.parse/name-from-ns-decl) ns-decls)
sorted (->> (dependencies-graph ns-decls)
ns.deps/topo-sort
(filter ns-symbols))
orphans (remove (set sorted) ns-symbols)
all (concat orphans sorted)]
(assert (contains? (set all) 'metabase.bootstrap))
(when (contains? ns-symbols 'metabase-enterprise.core)
(assert (contains? (set all) 'metabase-enterprise.core)))
all))
(let [ns-decls (mapcat
(comp ns.find/find-ns-decls-in-dir io/file)
(all-paths basis))
ns-symbols (set (map ns.parse/name-from-ns-decl ns-decls))]
(->> (dependencies-graph ns-decls)
ns.deps/topo-sort
(filter ns-symbols)
(cons 'metabase.bootstrap))))
(defn compile-sources! [basis]
(u/step "Compile Clojure source files"
@@ -86,26 +80,27 @@
ns-decls (u/step "Determine compilation order for Metabase files"
(metabase-namespaces-in-topo-order basis))]
(with-duration-ms [duration-ms]
(b/compile-clj {:basis basis
:src-dirs paths
:class-dir class-dir
:ns-compile ns-decls})
(b/compile-clj {:basis basis
:src-dirs paths
:class-dir class-dir
:ns-compile ns-decls
:compile-opts {:direct-linking true}})
(u/announce "Finished compilation in %.1f seconds." (/ duration-ms 1000.0))))))
(defn copy-resources! [edition basis]
(u/step "Copy resources"
;; technically we don't NEED to copy the Clojure source files but it doesn't really hurt anything IMO.
(doseq [path (all-paths basis)]
(when (not (#{"src" "shared/src" "enterprise/backend/src"} path))
(u/step (format "Copy %s" path)
(b/copy-dir {:target-dir class-dir, :src-dirs [path]}))))))
(u/step (format "Copy %s" path)
(b/copy-dir {:target-dir class-dir, :src-dirs [path]})))))
(defn create-uberjar! [basis]
(u/step "Create uberjar"
(with-duration-ms [duration-ms]
(depstar/uber {:class-dir class-dir
:uber-file uberjar-filename
:basis basis
:exclude [".*metabase.*.clj[c|s]?$"]})
(depstar/uber {:class-dir class-dir
:uber-file uberjar-filename
:basis basis
:compile-opts {:direct-linking true}})
(u/announce "Created uberjar in %.1f seconds." (/ duration-ms 1000.0)))))
(def manifest-entries

View File

@@ -1,4 +1,4 @@
(ns build.version-properties
(ns build.version-info
(:require
[clojure.string :as str]
[metabuild-common.core :as u]))
@@ -32,6 +32,7 @@
(str \v version)
version)
:hash (git-hash)
:branch (git-branch)
:date (git-last-commit-date)}]
(str (name k) \= v))))
@@ -69,10 +70,10 @@
(format "v%d.%d.%d-SNAPSHOT" major minor patch))
"UNKNOWN")))
(defn generate-version-properties-file!
(defn generate-version-info-file!
"Generate version.properties file"
([edition]
(generate-version-properties-file! edition (current-snapshot-version edition)))
(generate-version-info-file! edition (current-snapshot-version edition)))
([edition version]
(u/delete-file-if-exists! version-properties-filename)

View File

@@ -61,7 +61,7 @@
'metabase.shared.util.i18n/trs})
(def ^:private plural-translation-macro-names
#{"trsn" "trun" "deferred-trsn" "deferred-trun"})
#{"trsn" "trsu" "deferred-trsn" "deferred-trun"})
(s/def ::translate (s/and
(complement vector?)
@@ -90,7 +90,7 @@
(apply str (rest i18n-string)))]
(merge {:message message}
(when (string? pl-i18n-string)
{:message-pl pl-i18n-string})))))
{:message-pl i18n-string})))))
(defn- analyze-translations
"Takes roots to grasp returning a map of :file, :line, :original (the original form), and :message. If identifying the

View File

@@ -40,6 +40,7 @@
assert-file-exists
copy-file!
create-directory-unless-exists!
delete-file!
delete-file-if-exists!
download-file!
file-exists?

View File

@@ -49,6 +49,12 @@
([file & more]
(dorun (map delete-file-if-exists! (cons file more)))))
(defn ^:deprecated delete-file!
"Alias for `delete-file-if-exists!`. Here for backwards compatibility. Prefer `delete-file-if-exists!` going
forward."
[& args]
(apply delete-file-if-exists! args))
(defn copy-file!
"Copy a `source` file (or directory, recursively) to `dest`."
[^String source ^String dest]

View File

@@ -7,8 +7,6 @@
(:import
(java.io BufferedReader File InputStreamReader)))
(set! *warn-on-reflection* true)
(defn- read-lines [^java.io.BufferedReader reader {:keys [quiet? err?]}]
(loop [lines []]
(if-let [line (.readLine reader)]

View File

@@ -8,6 +8,7 @@
[release.common :as c]
[release.common.slack :as slack]
[release.draft-release :as draft-release]
[release.elastic-beanstalk :as eb]
[release.git-tags :as git-tags]
[release.set-build-options :as set-build-options]
[release.uberjar :as uberjar]
@@ -21,6 +22,7 @@
:upload-uberjar uberjar/upload-uberjar!
:push-git-tags git-tags/push-tags!
:publish-draft-release draft-release/create-draft-release!
:publish-elastic-beanstalk-artifacts eb/publish-elastic-beanstalk-artifacts!
:update-version-info version-info/update-version-info!))
(defn- do-steps! [steps]
@@ -45,3 +47,12 @@
(let [steps (or (seq (map u/parse-as-keyword steps))
(keys steps*))]
(do-steps! steps))))
(defn publish-ebs [args]
(u/exit-when-finished-nonzero-on-exception
(let [version (:version args)]
(c/set-version! version)
(c/set-edition! (if (str/starts-with? (c/version) "0") :oss :ee))
(c/set-branch! "release-x.y.z") ;; FIXME: branch is irrelevant for CD run
(u/announce (format "Preparing Elastic Beanstalk artifacts for version %s" (c/version)))
(do-steps! [:publish-elastic-beanstalk-artifacts]))))

View File

@@ -11,7 +11,7 @@
(defn github-api-request-headers []
{"Content-Type" "application/json"
"Authorization" (format "Bearer %s" (u/env-or-throw :github-token))})
"Authorization" (format "token %s" (u/env-or-throw :github-token))})
(defn- GET [endpoint]
(-> (http/get (str (github-api-base) endpoint) {:headers (github-api-request-headers)})

View File

@@ -0,0 +1,115 @@
(ns release.elastic-beanstalk
"Code related to building and publishing Elastic Beanstalk artifacts."
(:require
[cheshire.core :as json]
[clojure.core.cache :as cache]
[metabuild-common.core :as u]
[release.common :as c]
[release.common.http :as common.http]
[release.common.upload :as common.upload]
[stencil.core :as stencil]
[stencil.loader]))
(set! *warn-on-reflection* true)
;; Disable caching of our template files for easier REPL debugging, we're only rendering them once anyways
(stencil.loader/set-cache (cache/ttl-cache-factory {} :ttl 0))
;; create a ZIP file with the contents
;; metabase-aws-eb.zip
;; ├─ Dockerrun.aws.json
;; ├─ .ebextensions/
;; ├─ 01_metabase.config
;; ├─ metabase_config/
;; ├─ (a bunch of other stuff)
(def ^:private eb-extensions-source
"Source location of the .ebextensions directory"
(u/assert-file-exists (u/filename c/root-directory "bin" "build" "src" "release" "elastic_beanstalk" ".ebextensions")))
(def ^:private eb-platform-source
"Source location of the .ebextensions directory"
(u/assert-file-exists (u/filename c/root-directory "bin" "build" "src" "release" "elastic_beanstalk" ".platform")))
(def ^:private archive-temp-dir
"Path where we'll put the contents of the ZIP file before we create it."
"/tmp/metabase-aws-eb")
(def ^:private archive-path "/tmp/metabase-aws-eb.zip")
(def ^:private html-file-path "/tmp/launch-aws-eb.html")
(defn- validate-json-docker-tag []
(u/step (format "Check that Dockerrun.aws.json Docker tag is %s" (c/docker-tag))
(u/step "Download archive"
(u/download-file! (c/artifact-download-url "metabase-aws-eb.zip") archive-path))
(u/step "Unzip archive"
(u/delete-file-if-exists! archive-temp-dir)
(u/sh "unzip" (u/assert-file-exists archive-path) "-d" archive-temp-dir))
(u/step "Validate JSON file"
(let [json (-> (u/assert-file-exists (u/filename (u/assert-file-exists archive-temp-dir) "Dockerrun.aws.json"))
slurp
(json/parse-string true))
tag (get-in json [:Image :Name])]
(u/announce "Docker tag is %s" tag)
(when-not (= tag (c/docker-tag))
(throw (ex-info "Incorrect Docker tag." {:expected (c/docker-tag)
:actual tag
:json json})))))))
(defn- validate-elastic-beanstalk-artifacts []
(u/step "Validate Elastic Beanstalk artifacts"
(common.http/check-url-exists (c/artifact-download-url "launch-aws-eb.html"))
(common.http/check-url-exists (c/artifact-download-url "metabase-aws-eb.zip"))
(validate-json-docker-tag)
(u/announce "Artifacts validated.")))
(defn- dockerrun-json-content []
{:AWSEBDockerrunVersion "1"
:Image {:Name (c/docker-tag)
:Update "true"}
:Ports [{:ContainerPort "3000"}]
:Logging "/var/log/metabase"})
(defn- create-archive! []
(u/step (format "Create metabase-aws-eb.zip for Docker image %s" (c/docker-tag))
(u/step "Create temp dir"
(u/delete-file-if-exists! archive-temp-dir)
(u/create-directory-unless-exists! archive-temp-dir))
(u/step "Generate Dockerrun.aws.json"
(spit (u/filename archive-temp-dir "Dockerrun.aws.json")
(json/generate-string (dockerrun-json-content) {:pretty true})))
(u/step "Copy .ebextensions"
(u/copy-file! eb-extensions-source (u/filename archive-temp-dir ".ebextensions")))
(u/step "Copy .platform"
(u/copy-file! eb-platform-source (u/filename archive-temp-dir ".platform")))
(u/step "Create metabase-aws-eb.zip"
(u/delete-file-if-exists! archive-path)
(u/sh {:dir archive-temp-dir} "zip" "--recurse-paths" archive-path ".")
(u/assert-file-exists archive-path))))
(def ^:private launch-template-filename
(u/assert-file-exists (u/filename c/root-directory "bin" "build" "src" "release" "elastic_beanstalk" "launch-aws-eb.html.template")))
(defn- create-html-file! []
(u/step (format "Create launch-aws-eb.html for Docker image %s" (c/docker-tag))
(u/delete-file-if-exists! html-file-path)
(spit html-file-path
(stencil/render (slurp launch-template-filename)
{:url (java.net.URLEncoder/encode (c/artifact-download-url "metabase-aws-eb.zip")
"UTF-8")}))
(u/assert-file-exists html-file-path)))
(defn- upload-artifacts! []
(u/step "Upload Elastic Beanstalk artifacts"
(u/step "Upload metabase-aws-eb.zip"
(common.upload/upload-artifact! archive-path "metabase-aws-eb.zip"))
(u/step "Upload launch-aws-eb.html"
(common.upload/upload-artifact! html-file-path "launch-aws-eb.html"))))
;; TODO -- we should merge the EB build logic into this script, it's still an ancient bash script
(defn publish-elastic-beanstalk-artifacts! []
(u/step "Create and publish Elastic Beanstalk artifacts"
(create-archive!)
(create-html-file!)
(upload-artifacts!))
(validate-elastic-beanstalk-artifacts))

View File

@@ -0,0 +1,37 @@
######
# Metabase Report server Elastic Beanstalk configuration
# Modify the environmental variables below to customize your installation
# Comment out a variable to disable a feature
#####
container_commands:
01-persist_env_vars:
command: ".ebextensions/metabase_config/metabase-setup.sh set_up_env_vars"
ignoreErrors: false
02_install_papertrail:
command: ".ebextensions/metabase_config/metabase-setup.sh install_papertrail"
test: test $PAPERTRAIL_HOST
ignoreErrors: true
03_try_papertrail:
command: "/sbin/service remote_syslog restart"
test: test -e /etc/log_files.yml
ignoreErrors: true
04_install_inspector:
command: yum -q list installed AwsAgent &>/dev/null && echo "Inspector installed, nothing to do here" || curl -O https://inspector-agent.amazonaws.com/linux/latest/install | sudo bash
ignoreErrors: true
05-install_cloudwatch_agent:
command: sudo yum update && sudo yum upgrade -y && yum -q list installed amazon-cloudwatch-agent &>/dev/null && echo "Cloudwatch agent installed, nothing to do here" || sudo yum -y install https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm
ignoreErrors: true
06-run_cloudwatch_agent:
command: sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:.ebextensions/metabase_config/cloudwatch/config.json -s
ignoreErrors: true
# NGINX will be disabled in future releases, this will make the trick
#option_settings:
# aws:elasticbeanstalk:environment:proxy:
# ProxyServer: "none"

View File

@@ -0,0 +1,134 @@
{
"agent": {
"metrics_collection_interval": 60
},
"metrics": {
"namespace": "Metabase",
"metrics_collected": {
"cpu": {
"measurement": [
"time_active",
"time_guest",
"time_guest_nice",
"time_idle",
"time_iowait",
"time_irq",
"time_nice",
"time_softirq",
"time_steal",
"time_system",
"time_user",
"usage_active",
"usage_guest",
"usage_guest_nice",
"usage_idle",
"usage_iowait",
"usage_irq",
"usage_nice",
"usage_softirq",
"usage_steal",
"usage_system",
"usage_user"
],
"totalcpu": false
},
"disk": {
"measurement": [
"free",
"total",
"used",
"used_percent",
"inodes_free",
"inodes_used",
"inodes_total"
],
"ignore_file_system_types": [
"sysfs", "devtmpfs"
] },
"diskio": {
"measurement": [
"reads",
"writes",
"read_bytes",
"write_bytes",
"read_time",
"write_time",
"io_time",
"iops_in_progress"
]
},
"swap": {
"measurement": [
"free",
"used",
"used_percent"
]
},
"mem": {
"measurement": [
"active",
"available",
"available_percent",
"buffered",
"cached",
"free",
"inactive",
"total",
"used",
"used_percent"
]
},
"net": {
"measurement": [
"bytes_sent",
"bytes_recv",
"drop_in",
"drop_out",
"err_in",
"err_out",
"packets_sent",
"packets_recv"
]
},
"netstat": {
"measurement": [
"tcp_close",
"tcp_close_wait",
"tcp_closing",
"tcp_established",
"tcp_fin_wait1",
"tcp_fin_wait2",
"tcp_last_ack",
"tcp_listen",
"tcp_none",
"tcp_syn_sent",
"tcp_syn_recv",
"tcp_time_wait",
"udp_socket"
]
},
"processes": {
"measurement": [
"blocked",
"dead",
"idle",
"paging",
"running",
"sleeping",
"stopped",
"total",
"total_threads",
"wait",
"zombies"
]
}
},
"append_dimensions": {
"ImageId": "${aws:ImageId}",
"InstanceId": "${aws:InstanceId}",
"InstanceType": "${aws:InstanceType}",
"AutoScalingGroupName": "${aws:AutoScalingGroupName}"
},
"aggregation_dimensions" : [["InstanceId"], ["InstanceType"], ["InstanceId","InstanceType"]]
}
}

View File

@@ -0,0 +1,104 @@
#!/bin/bash
####
# Metabase Report server Elastic Beanstalk metabase-setup.sh
# Modify the environmental variables to customize your installation
# Unset a variable to disable a feature
####
set_up_env_vars () {
# /opt/elasticbeanstalk/bin/get-config environment | jq -r 'to_entries | .[] | "export \(.key)=\"\(.value)\""' > /etc/profile.d/sh.local
if grep -q "Amazon Linux 2" /etc/os-release; then
if [ ! -z "$RDS_HOSTNAME" ]; then
# sed -i 's/RDS_HOSTNAME/MB_DB_HOST/' /etc/profile.d/sh.local
sed -i 's/RDS_HOSTNAME/MB_DB_HOST/' /opt/elasticbeanstalk/deployment/env.list
# sed -i 's/RDS_USERNAME/MB_DB_USER/' /etc/profile.d/sh.local
sed -i 's/RDS_USERNAME/MB_DB_USER/' /opt/elasticbeanstalk/deployment/env.list
# sed -i 's/RDS_PASSWORD/MB_DB_PASS/' /etc/profile.d/sh.local
sed -i 's/RDS_PASSWORD/MB_DB_PASS/' /opt/elasticbeanstalk/deployment/env.list
# sed -i 's/RDS_PORT/MB_DB_PORT/' /etc/profile.d/sh.local
sed -i 's/RDS_PORT/MB_DB_PORT/' /opt/elasticbeanstalk/deployment/env.list
# sed -i 's/RDS_DB_NAME/MB_DB_DBNAME/' /etc/profile.d/sh.local
sed -i 's/RDS_DB_NAME/MB_DB_DBNAME/' /opt/elasticbeanstalk/deployment/env.list
if [ "$RDS_PORT" == "3306" ]; then
# echo 'export MB_DB_TYPE="mysql"' >> /etc/profile.d/sh.local
echo 'MB_DB_TYPE=mysql' >> /opt/elasticbeanstalk/deployment/env.list
else
# echo 'export MB_DB_TYPE="postgres"' >> /etc/profile.d/sh.local
echo 'MB_DB_TYPE=postgres' >> /opt/elasticbeanstalk/deployment/env.list
fi
fi
else
if [ ! -z "$RDS_HOSTNAME" ]; then
# sed -i 's/RDS_HOSTNAME/MB_DB_HOST/' /etc/profile.d/sh.local
sed -i 's/RDS_HOSTNAME/MB_DB_HOST/' /opt/elasticbeanstalk/deploy/configuration/containerconfiguration
# sed -i 's/RDS_USERNAME/MB_DB_USER/' /etc/profile.d/sh.local
sed -i 's/RDS_USERNAME/MB_DB_USER/' /opt/elasticbeanstalk/deploy/configuration/containerconfiguration
# sed -i 's/RDS_PASSWORD/MB_DB_PASS/' /etc/profile.d/sh.local
sed -i 's/RDS_PASSWORD/MB_DB_PASS/' /opt/elasticbeanstalk/deploy/configuration/containerconfiguration
# sed -i 's/RDS_PORT/MB_DB_PORT/' /etc/profile.d/sh.local
sed -i 's/RDS_PORT/MB_DB_PORT/' /opt/elasticbeanstalk/deploy/configuration/containerconfiguration
# sed -i 's/RDS_DB_NAME/MB_DB_DBNAME/' /etc/profile.d/sh.local
sed -i 's/RDS_DB_NAME/MB_DB_DBNAME/' /opt/elasticbeanstalk/deploy/configuration/containerconfiguration
if [ "$RDS_PORT" == "3306" ]; then
# echo 'export MB_DB_TYPE="mysql"' >> /etc/profile.d/sh.local
sed -i 's/}}}}/,"MB_DB_TYPE":"mysql"}}}}/' /opt/elasticbeanstalk/deploy/configuration/containerconfiguration
else
# echo 'export MB_DB_TYPE="postgres"' >> /etc/profile.d/sh.local
sed -i 's/}}}}/,"MB_DB_TYPE":"postgres"}}}}/' /opt/elasticbeanstalk/deploy/configuration/containerconfiguration
fi
fi
fi
}
# add files to papertrail
pt_files () {
sed -i '/ - .*/d' /etc/log_files.yml
set -f
for file in $PAPERTRAIL_FILES; do
sed -i 's|files:|files:\n - '$file'|' /etc/log_files.yml
done
set +f
}
# papertail remote host
pt_remote_host () {
sed -i "s/.*host:.*/ host: $PAPERTRAIL_HOST/" /etc/log_files.yml
}
# papertail remote port
pt_port () {
sed -i "s/.*port:.*/ port: $PAPERTRAIL_PORT/" /etc/log_files.yml
}
# papertail local host
pt_local_host () {
eval export PAPERTRAIL_HOSTNAME=$PAPERTRAIL_HOSTNAME # expand vars like $HOSTNAME
sed -i "s/.*hostname:.*/hostname: $PAPERTRAIL_HOSTNAME/" /etc/log_files.yml
}
# download, install and configure papertrail
install_papertrail () {
cp .ebextensions/metabase_config/papertrail/log_files.yml /etc/log_files.yml && chmod 644 /etc/log_files.yml
cp .ebextensions/metabase_config/papertrail/remote_syslog /etc/init.d/remote_syslog && chmod 555 /etc/init.d/remote_syslog
cd /tmp/
wget -q "https://github.com/papertrail/remote_syslog2/releases/download/v0.20/remote_syslog_linux_amd64.tar.gz" &&
tar xzf remote_syslog_linux_amd64.tar.gz
/sbin/service remote_syslog stop
mv /tmp/remote_syslog/remote_syslog /usr/local/bin/
rm -rf remote_syslog_linux_amd64.tar.gz remote_syslog
# Setup Papertrail
[[ "$PAPERTRAIL_HOST" ]] && pt_remote_host
[[ "$PAPERTRAIL_PORT" ]] && pt_port
[[ "$PAPERTRAIL_FILES" ]] && pt_files
[[ "$PAPERTRAIL_HOSTNAME" ]] && pt_local_host
}
case $1 in
set_up_env_vars)
set_up_env_vars
;;
install_papertrail)
install_papertrail
;;
esac

View File

@@ -0,0 +1,12 @@
# /etc/log_files.yml - Papertrail
files:
- /var/log/nginx/access.log
- /var/log/nginx/error.log
- /var/log/eb-*
- /var/log/eb-docker/containers/*/*.log
hostname:
destination:
host:
port:
protocol: tls
new_file_check_interval: "10" # Check every 10 seconds

View File

@@ -0,0 +1,127 @@
#!/bin/bash
# /etc/init.d/remote_syslog
#
# remote_syslog This shell script takes care of starting and stopping
# remote_syslog daemon
#
# chkconfig: - 58 74
# description: papertrail/remote_syslog \
# https://github.com/papertrail/remote_syslog2/blob/master/examples/remote_syslog.init.d
### BEGIN INIT INFO
# Provides: remote_syslog
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $network $local_fs $remote_fs
# Should-Start: $syslog $named ntpdate
# Should-Stop: $syslog $named
# Short-Description: start and stop remote_errolog
# Description: papertrail/remote_syslog2
# https://github.com/papertrail/remote_syslog2/blob/master/examples/remote_syslog.init.d
### END INIT INFO
# Source function library.
. /etc/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
prog="/usr/local/bin/remote_syslog"
config="/etc/log_files.yml"
pid_dir="/var/run"
EXTRAOPTIONS=""
pid_file="$pid_dir/remote_syslog.pid"
PATH=/sbin:/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
RETVAL=0
is_running(){
# Do we have PID-file?
if [ -f "$pid_file" ]; then
# Check if proc is running
pid=`cat "$pid_file" 2> /dev/null`
if [[ $pid != "" ]]; then
exepath=`readlink /proc/"$pid"/exe 2> /dev/null`
exe=`basename "$exepath"`
if [[ $exe == "remote_syslog" ]]; then
# Process is running
return 0
fi
fi
fi
return 1
}
start(){
echo -n $"Starting $prog: "
unset HOME MAIL USER USERNAME
$prog -c $config --pid-file=$pid_file $EXTRAOPTIONS
RETVAL=$?
echo
return $RETVAL
}
stop(){
echo -n $"Stopping $prog: "
if (is_running); then
kill `cat $pid_file`
RETVAL=$?
echo
return $RETVAL
else
echo "$pid_file not found"
fi
}
status(){
echo -n $"Checking for $pid_file: "
if (is_running); then
echo "found"
else
echo "not found"
fi
}
reload(){
restart
}
restart(){
stop
start
}
condrestart(){
is_running && restart
return 0
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
reload)
reload
;;
condrestart)
condrestart
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
RETVAL=1
esac
exit $RETVAL

View File

@@ -0,0 +1,6 @@
#!/bin/bash
if [[ "x$NGINX_FORCE_SSL" == "x1" ]]; then
cp .platform/nginx/nginx-ssl.conf /etc/nginx/nginx.conf && nginx -t && /sbin/service nginx restart
else
cp .platform/nginx/nginx.conf /etc/nginx/nginx.conf && nginx -t && /sbin/service nginx restart
fi

View File

@@ -0,0 +1,6 @@
#!/bin/bash
if [[ "x$NGINX_FORCE_SSL" == "x1" ]]; then
cp .platform/nginx/nginx-ssl.conf /etc/nginx/nginx.conf && nginx -t && /sbin/service nginx restart
else
cp .platform/nginx/nginx.conf /etc/nginx/nginx.conf && nginx -t && /sbin/service nginx restart
fi

View File

@@ -0,0 +1,58 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
worker_rlimit_nofile 65936;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
include conf.d/*.conf;
map $http_upgrade $connection_upgrade {
default "upgrade";
}
server {
listen 80 default_server;
gzip on;
gzip_comp_level 4;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
access_log /var/log/nginx/access.log main;
location / {
if ($http_x_forwarded_proto != "https") {
rewrite ^ https://$host$request_uri? permanent;
}
proxy_pass http://docker;
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
}
# Include the Elastic Beanstalk generated locations
include conf.d/elasticbeanstalk/*.conf;
}
}

View File

@@ -0,0 +1,54 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
worker_rlimit_nofile 65936;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
include conf.d/*.conf;
map $http_upgrade $connection_upgrade {
default "upgrade";
}
server {
listen 80 default_server;
gzip on;
gzip_comp_level 4;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
access_log /var/log/nginx/access.log main;
location / {
proxy_pass http://docker;
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
}
# Include the Elastic Beanstalk generated locations
include conf.d/elasticbeanstalk/*.conf;
}
}

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>Redirecting...</title>
<link
rel="canonical"
href="https://console.aws.amazon.com/elasticbeanstalk/home?region=us-east-1#/newApplication?applicationName=Metabase&tierName=WebServer&platform=Docker&environmentType=LoadBalancing&sourceBundleUrl={{url}}&instanceType=t3a.small"
/>
<meta
http-equiv="refresh"
content="0; url=https://console.aws.amazon.com/elasticbeanstalk/home?region=us-east-1#/newApplication?applicationName=Metabase&tierName=WebServer&platform=Docker&environmentType=LoadBalancing&sourceBundleUrl={{url}}&instanceType=t3a.small"
/>
<h1>Redirecting...</h1>
<a
href="https://console.aws.amazon.com/elasticbeanstalk/home?region=us-east-1#/newApplication?applicationName=Metabase&tierName=WebServer&platform=Docker&environmentType=LoadBalancing&sourceBundleUrl={{url}}&instanceType=t3a.small"
>
Click here if you are not redirected.
</a>
<script>
location =
"https://console.aws.amazon.com/elasticbeanstalk/home?region=us-east-1#/newApplication?applicationName=Metabase&tierName=WebServer&platform=Docker&environmentType=LoadBalancing&sourceBundleUrl={{url}}&instanceType=t3a.small";
</script>

View File

@@ -1,6 +1,6 @@
(ns build.version-properties-test
(ns build.version-info-test
(:require
[build.version-properties :as version-properties]
[build.version-info :as version-info]
[clojure.test :refer :all]))
(deftest tag-parts-test
@@ -11,7 +11,7 @@
tag [tag (str \v tag)]]
(testing (str (pr-str (list 'tag-parts tag)) " => " (pr-str expected))
(is (= expected
(#'version-properties/tag-parts tag))))))
(#'version-info/tag-parts tag))))))
(deftest current-snapshot-version-test
(doseq [[branch edition->tag->expected] {"release-x.37.x" {:oss {nil "UNKNOWN"
@@ -34,4 +34,4 @@
[tag expected] tag->expected]
(testing (str (pr-str (list 'current-snapshot-version edition branch tag)) " => " (pr-str expected))
(is (= expected
(version-properties/current-snapshot-version edition branch tag))))))
(version-info/current-snapshot-version edition branch tag))))))

View File

@@ -8,6 +8,7 @@
["1.36.1" "1.36.0" true]
["1.36.0" "1.36.0" false]
["1.37.0" "1.36.9" true]
["1.37.0" "1.36.9" true]
["1.37.0.1" "1.37.0" true]
["1.37.1.1" "1.37.0.2" true]
["1.37.10" "1.37.2" true]

View File

@@ -0,0 +1,11 @@
(ns release.elastic-beanstalk-test
(:require
[clojure.test :refer :all]
[release.common :as c]
[release.elastic-beanstalk :as release.eb]))
(deftest create-html-file-test
(testing "Just make sure this doesn't barf."
(binding [c/*build-options* (atom {:version "0.46.0-test"
:edition :oss})]
(is (string? (#'release.eb/create-html-file!))))))

15
bin/ebs.sh Executable file
View File

@@ -0,0 +1,15 @@
#! /usr/bin/env bash
set -euo pipefail
# switch to project root directory if we're not already there
script_directory=`dirname "${BASH_SOURCE[0]}"`
cd "$script_directory/.."
source "./bin/check-clojure-cli.sh"
check_clojure_cli
source "./bin/clear-outdated-cpcaches.sh"
clear_outdated_cpcaches
clojure -X:build:build/publish-ebs $@

View File

@@ -24,7 +24,7 @@ POT_NAME="locales/metabase.pot"
POT_BACKEND_NAME="locales/metabase-backend.pot"
# NOTE: hardcoded in .babelrc
POT_FRONTEND_NAME="locales/metabase-frontend.pot"
# NOTE: hardcoded in src/metabase/automagic_dashboards/dashboard_templates.clj
# NOTE: hardcoded in src/metabase/automagic_dashboards/rules.clj
POT_AUTODASH_NAME="locales/metabase-automatic-dashboards.pot"
mkdir -p "locales"

View File

@@ -1,6 +1,6 @@
#! /usr/bin/env bash
set -euo pipefail
set -euxo pipefail
# Convenience for running clj-kondo against all the appropriate directories.

71
bin/update-version-info Executable file
View File

@@ -0,0 +1,71 @@
#!/usr/bin/env bash
# Generate the version-info.json file which provides basic information about the latest versions of Metabase available.
# This is used inside the Metabase application as a simple mechanism to be aware if an upgrade is available.
build() {
echo '{
"latest": {
"version": "v0.18.0",
"released": "2016-05-04T21:09:36.358Z",
"patch": false,
"highlights": [
"Notifications about available Metabase updates"
]
},
"older": [
{
"version": "v0.17.1",
"released": "2016-05-04T21:09:36.358Z",
"patch": true,
"highlights": [
""
]
},
{
"version": "v0.17.0",
"released": "2016-05-04T21:09:36.358Z",
"patch": false,
"highlights": [
"Tags + Search for Saved Questions",
"Calculated columns",
"Faster Syncing of Metadata",
"Lots of database driver improvements and bug fixes"
]
},
{
"version": "v0.16.1",
"released": "2016-05-04T21:09:36.358Z",
"patch": true,
"highlights": [
"Fixes for several time alignment issues (timezones)",
"Resolved problem with SQL Server db connections"
]
},
{
"version": "v0.16.0",
"released": "2016-05-04T21:09:36.358Z",
"patch": false,
"highlights": [
"Fullscreen (and fabulous) Dashboards",
"Say hello to Metabot in Slack"
]
}
]
}' > /tmp/version-info.json
}
upload() {
# send the current file to s3
aws s3 cp /tmp/version-info.json "s3://static.metabase.com/version-info.json"
}
publish() {
build && upload && rm /tmp/version-info.json 2> /dev/null
}
if [ "$1" ]; then
$1
else
publish
fi

175
deps.edn
View File

@@ -9,11 +9,11 @@
org.clojure/clojurescript]}
amalloy/ring-gzip-middleware {:mvn/version "0.1.4"} ; Ring middleware to GZIP responses if client can handle it
bigml/histogram {:mvn/version "4.1.4"} ; Histogram data structure
buddy/buddy-core {:mvn/version "1.11.423" ; various cryptographic functions
buddy/buddy-core {:mvn/version "1.10.413" ; various cryptographic functions
:exclusions [commons-codec/commons-codec
org.bouncycastle/bcpkix-jdk15on
org.bouncycastle/bcprov-jdk15on]}
buddy/buddy-sign {:mvn/version "3.5.351"} ; JSON Web Tokens; High-Level message signing library
buddy/buddy-sign {:mvn/version "3.4.333"} ; JSON Web Tokens; High-Level message signing library
camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.3"} ; util functions for converting between camel, snake, and kebob case
cheshire/cheshire {:mvn/version "5.11.0"} ; fast JSON encoding (used by Ring JSON middleware)
clj-bom/clj-bom {:mvn/version "0.1.2"} ; handle BOMs in imported CSVs
@@ -22,7 +22,7 @@
:exclusions [commons-codec/commons-codec
commons-io/commons-io
slingshot/slingshot]}
clojure.java-time/clojure.java-time {:mvn/version "1.3.0"} ; java.time utilities
clojure.java-time/clojure.java-time {:mvn/version "1.2.0"} ; java.time utilities
clojurewerkz/quartzite {:mvn/version "2.1.0" ; scheduling library
:exclusions [c3p0/c3p0
org.quartz-scheduler/quartz]}
@@ -32,26 +32,26 @@
:exclusions [it.unimi.dsi/fastutil
org.slf4j/slf4j-api]}
com.draines/postal {:mvn/version "2.0.5"} ; SMTP library
com.github.seancorfield/honeysql {:mvn/version "2.4.1066"} ; Honey SQL 2. SQL generation from Clojure data maps
com.github.seancorfield/honeysql {:mvn/version "2.4.1002"} ; Honey SQL 2. SQL generation from Clojure data maps
com.github.seancorfield/next.jdbc {:git/url "https://github.com/seancorfield/next-jdbc.git"
:sha "abd926cde3730087d3729dcea0ce0ab7ef1194f2"} ; for https://github.com/seancorfield/next-jdbc/issues/245; when this makes it to a release we can switch to that.
com.github.vertical-blank/sql-formatter {:mvn/version "2.0.4"} ; Java SQL formatting library https://github.com/vertical-blank/sql-formatter
com.google.guava/guava {:mvn/version "32.1.2-jre"} ; dep for BigQuery, Spark, and GA. Require here rather than letting different dep versions stomp on each other — see comments on #9697
:sha "8f372917a4b8b55c893ef6586f9ffe8dce4cbc7e"} ; for https://github.com/seancorfield/next-jdbc/issues/245; when this makes it to a release we can switch to that.
com.github.vertical-blank/sql-formatter {:mvn/version "2.0.3"} ; Java SQL formatting library https://github.com/vertical-blank/sql-formatter
com.google.guava/guava {:mvn/version "31.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.15.2"} ; JSON processor used by snowplow-java-tracker
com.fasterxml.woodstox/woodstox-core {:mvn/version "6.5.1"} ; trans dep of commons-codec (pinned version due to CVE-2022-40151)
{:mvn/version "2.14.2"} ; JSON processor used by snowplow-java-tracker
com.fasterxml.woodstox/woodstox-core {:mvn/version "6.5.0"} ; trans dep of commons-codec (pinned version due to CVE-2022-40151)
com.h2database/h2 {:mvn/version "2.1.214"} ; embedded SQL database
com.gfredericks/test.chuck {:mvn/version "0.2.14"} ; generating strings from regex
com.snowplowanalytics/snowplow-java-tracker
{:mvn/version "1.0.0" ; Snowplow analytics
:exclusions [com.fasterxml.jackson.core/jackson-databind]}
com.taoensso/nippy {:mvn/version "3.2.0"} ; Fast serialization (i.e., GZIP) library for Clojure
com.vladsch.flexmark/flexmark {:mvn/version "0.64.8"} ; Markdown parsing
com.vladsch.flexmark/flexmark {:mvn/version "0.64.0"} ; Markdown parsing
com.vladsch.flexmark/flexmark-ext-autolink
{:mvn/version "0.64.8"} ; Flexmark extension for auto-linking bare URLs
{:mvn/version "0.64.0"} ; Flexmark extension for auto-linking bare URLs
commons-fileupload/commons-fileupload {:mvn/version "1.5"} ; ring/ring-core 1.9.6 uses v1.4, but we want 1.5 due to a CVE. When we upgrade to the forthcoming ring/ring-core 1.10.0 we can remove this.
commons-codec/commons-codec {:mvn/version "1.16.0"} ; Apache Commons -- useful codec util fns
commons-io/commons-io {:mvn/version "2.13.0"} ; Apache Commons -- useful IO util fns
commons-codec/commons-codec {:mvn/version "1.15"} ; Apache Commons -- useful codec util fns
commons-io/commons-io {:mvn/version "2.11.0"} ; Apache Commons -- useful IO util fns
commons-net/commons-net {:mvn/version "3.9.0"} ; Apache Commons -- useful network utils. Transitive dep of Snowplow, pinned due to CVE-2021-37533
commons-validator/commons-validator {:mvn/version "1.7" ; Apache Commons -- useful validation util fns
:exclusions [commons-beanutils/commons-beanutils
@@ -67,36 +67,36 @@
hiccup/hiccup {:mvn/version "1.0.5"} ; HTML templating
honeysql/honeysql {:mvn/version "1.0.461" ; Transform Clojure data structures to SQL
:exclusions [org.clojure/clojurescript]}
inflections/inflections {:mvn/version "0.14.1"} ; Clojure/Script library used for prularizing words
info.sunng/ring-jetty9-adapter {:mvn/version "0.22.3"} ; Drop-in replacement for official Ring Jetty adapter. Supports Jetty 11 webserver.
info.sunng/ring-jetty9-adapter {:mvn/version "0.18.5"} ; Drop-in replacement for official Ring Jetty adapter. Supports Jetty 11 webserver.
instaparse/instaparse {:mvn/version "1.4.12"} ; Make your own parser
clj-commons/clj-yaml {:mvn/version "1.0.27"} ; Clojure wrapper for YAML library SnakeYAML
io.github.camsaul/toucan2 {:mvn/version "1.0.524"}
clj-commons/clj-yaml {:mvn/version "1.0.26"} ; Clojure wrapper for YAML library SnakeYAML
io.github.camsaul/toucan2 {:mvn/version "1.0.520"}
io.github.camsaul/toucan2-toucan1 {:mvn/version "1.0.520"}
;; The 2.X line of Resilience4j requires Java 17, so we cannot upgrade this dependency until that is our minimum JVM version
io.github.resilience4j/resilience4j-retry {:mvn/version "1.7.1"} ; Support for retrying operations
io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"} ; prometheus jvm collector
io.prometheus/simpleclient_jetty {:mvn/version "0.16.0"} ; prometheus jetty collector
javax.servlet/servlet-api {:mvn/version "2.5"} ; used by ring's multipart-params (file upload)
joda-time/joda-time {:mvn/version "2.12.5"}
joda-time/joda-time {:mvn/version "2.12.2"}
kixi/stats {:mvn/version "0.5.5" ; Various statistic measures implemented as transducers
:exclusions [org.clojure/data.avl]}
medley/medley {:mvn/version "1.4.0"} ; lightweight lib of useful functions
metabase/connection-pool {:mvn/version "1.2.0"} ; simple wrapper around C3P0. JDBC connection pools
metabase/saml20-clj {:mvn/version "2.2.4"} ; EE SAML integration.
metabase/saml20-clj {:mvn/version "2.2.1"} ; EE SAML integration.
metabase/throttle {:mvn/version "1.0.2"} ; Tools for throttling access to API endpoints and other code pathways
metosin/malli {:mvn/version "0.11.0"} ; Data-driven Schemas for Clojure/Script and babashka
metosin/malli {:mvn/version "0.10.2"} ; Data-driven Schemas for Clojure/Script and babashka
nano-id/nano-id {:mvn/version "1.0.0"} ; NanoID generator for generating entity_ids
net.cgrand/macrovich {:mvn/version "0.2.2"} ; utils for writing macros for both Clojure & ClojureScript
net.clojars.wkok/openai-clojure {:mvn/version "0.9.0"} ; OpenAI
net.cgrand/macrovich {:mvn/version "0.2.1"} ; utils for writing macros for both Clojure & ClojureScript
net.clojars.wkok/openai-clojure {:mvn/version "0.5.0"} ; OpenAI
net.i2p.crypto/eddsa {:mvn/version "0.3.0"} ; ED25519 key support (optional dependency for org.apache.sshd/sshd-core)
net.redhogs.cronparser/cron-parser-core {:mvn/version "3.5" ; describe Cron schedule in human-readable language
:exclusions [joda-time/joda-time ; exclude joda time 2.3 which has outdated timezone information
org.slf4j/slf4j-api]}
net.sf.cssbox/cssbox {:mvn/version "5.0.1" ; HTML / CSS rendering
net.sf.cssbox/cssbox {:mvn/version "5.0.0" ; HTML / CSS rendering
:exclusions [org.slf4j/slf4j-api]}
net.thisptr/jackson-jq {:mvn/version "1.0.0-preview.20230409"} ; Java implementation of the JQ json query language
org.apache.commons/commons-compress {:mvn/version "1.23.0"} ; compression utils
org.apache.commons/commons-lang3 {:mvn/version "3.13.0"} ; helper methods for working with java.lang stuff
net.thisptr/jackson-jq {:mvn/version "1.0.0-preview.20220705"} ; Java implementation of the JQ json query language
org.apache.commons/commons-compress {:mvn/version "1.22"} ; compression utils
org.apache.commons/commons-lang3 {:mvn/version "3.12.0"} ; helper methods for working with java.lang stuff
org.apache.logging.log4j/log4j-1.2-api {:mvn/version "2.20.0"} ; apache logging framework
org.apache.logging.log4j/log4j-api {:mvn/version "2.20.0"} ; add compatibility with log4j 1.2
org.apache.logging.log4j/log4j-core {:mvn/version "2.20.0"} ; apache logging framework
@@ -106,19 +106,17 @@
{:mvn/version "2.20.0"} ; allows the slf4j2 API to work with log4j 2
org.apache.logging.log4j/log4j-layout-template-json
{:mvn/version "2.20.0"} ; allows the custom json logging format
org.apache.poi/poi {:mvn/version "5.2.4"} ; Work with Office documents (e.g. Excel spreadsheets) -- newer version than one specified by Docjure
org.apache.poi/poi {:mvn/version "5.2.3"} ; Work with Office documents (e.g. Excel spreadsheets) -- newer version than one specified by Docjure
org.apache.poi/poi-ooxml {:mvn/version "5.2.3"
:exclusions [org.bouncycastle/bcpkix-jdk15on
org.bouncycastle/bcprov-jdk15on]}
org.apache.sshd/sshd-core {:mvn/version "2.10.0" ; ssh tunneling and test server
:exclusions [org.slf4j/slf4j-api
org.slf4j/jcl-over-slf4j]}
org.apache.xmlgraphics/batik-all {:mvn/version "1.17"} ; 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"}
org.clojure/clojure {:mvn/version "1.11.1"}
org.clojure/core.async {:mvn/version "1.6.681"
org.clojure/core.async {:mvn/version "1.6.673"
:exclusions [org.clojure/tools.reader]}
org.clojure/core.logic {:mvn/version "1.0.1"} ; optimized pattern matching library for Clojure
org.clojure/core.match {:mvn/version "1.0.1"}
@@ -128,25 +126,25 @@
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.2.0"} ; combinatorics functions
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.cli {:mvn/version "1.0.219"} ; command-line argument parsing
org.clojure/tools.cli {:mvn/version "1.0.214"} ; command-line argument parsing
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.4.4"}
org.clojure/tools.reader {:mvn/version "1.3.6"}
org.clojure/tools.trace {:mvn/version "0.7.11"} ; function tracing
org.eclipse.jetty/jetty-server {:mvn/version "11.0.17"} ; web server
org.flatland/ordered {:mvn/version "1.15.11"} ; ordered maps & sets
org.graalvm.js/js {:mvn/version "22.3.3"} ; JavaScript engine
org.liquibase/liquibase-core {:mvn/version "4.21.1" ; migration management (Java lib)
org.eclipse.jetty/jetty-server {:mvn/version "11.0.14"} ; web server
org.flatland/ordered {:mvn/version "1.15.10"} ; ordered maps & sets
org.graalvm.js/js {:mvn/version "22.3.1"} ; JavaScript engine
org.liquibase/liquibase-core {:mvn/version "4.11.0" ; migration management (Java lib)
:exclusions [ch.qos.logback/logback-classic]}
;; The 3.X line of development for mariadb-java-client only supports the jdbc:mariadb protocol, so use 2.X for now.
org.mariadb.jdbc/mariadb-java-client {:mvn/version "2.7.10"} ; MySQL/MariaDB driver
org.mariadb.jdbc/mariadb-java-client {:mvn/version "2.7.6"} ; MySQL/MariaDB driver
org.mindrot/jbcrypt {:mvn/version "0.4"} ; Crypto library
org.postgresql/postgresql {:mvn/version "42.6.0"} ; Postgres driver
org.postgresql/postgresql {:mvn/version "42.5.4"} ; Postgres driver
org.quartz-scheduler/quartz {:mvn/version "2.3.2"} ; Quartz job scheduler, provided by quartzite but this is a newer version.
org.slf4j/slf4j-api {:mvn/version "2.0.7"} ; abstraction for logging frameworks -- allows end user to plug in desired logging framework at deployment time
org.slf4j/slf4j-api {:mvn/version "2.0.6"} ; 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.2"} ; extra Java 8 java.time classes like DayOfMonth and Quarter
potemkin/potemkin {:mvn/version "0.4.6" ; utility macros & fns
@@ -155,7 +153,7 @@
prismatic/schema {:mvn/version "1.4.1"} ; Data schema declaration and validation library
redux/redux {:mvn/version "0.1.4"} ; Utility functions for building and composing transducers
riddley/riddley {:mvn/version "0.2.0"} ; code walking lib -- used interally by Potemkin, manifold, etc.
ring/ring-core {:mvn/version "1.10.0"} ; web server (Jetty wrapper)
ring/ring-core {:mvn/version "1.9.6"} ; web server (Jetty wrapper)
ring/ring-json {:mvn/version "0.5.1"} ; Ring middleware for reading/writing JSON automatically
slingshot/slingshot {:mvn/version "0.12.2"} ; enhanced throw/catch, used by other deps
stencil/stencil {:mvn/version "0.5.0"} ; Mustache templates for Clojure
@@ -203,18 +201,16 @@
;; (dev/start!)
:dev
{:extra-deps
{clj-http-fake/clj-http-fake {:mvn/version "1.0.4"
{clj-http-fake/clj-http-fake {:mvn/version "1.0.3"
:exclusions [slingshot/slingshot]}
clj-kondo/clj-kondo {:mvn/version "2023.09.07"} ; this is not for RUNNING kondo, but so we can hack on custom hooks code from the REPL.
clj-kondo/clj-kondo {:mvn/version "2023.02.17"} ; this is not for RUNNING kondo, but so we can hack on custom hooks code from the REPL.
cloverage/cloverage {:mvn/version "1.2.4"}
com.gfredericks/test.chuck {:mvn/version "0.2.14"} ; generating strings from regexes (useful with malli)
djblue/portal {:mvn/version "0.46.0"} ; ui for inspecting values
djblue/portal {:mvn/version "0.40.0"} ; ui for inspecting values
io.github.camsaul/humane-are {:mvn/version "1.0.2"}
io.github.metabase/hawk {:sha "9c97bcb6d4de116325e651b40973fd0a75b7ae21"}
jonase/eastwood {:mvn/version "1.4.0" ; inspects namespaces and reports possible problems using tools.analyzer
:exclusions
[org.ow2.asm/asm-all]}
lambdaisland/deep-diff2 {:mvn/version "2.10.211"} ; way better diffs
io.github.metabase/hawk {:sha "70611745e3cb8b95d5793a88581d5c330148715b"}
jonase/eastwood {:mvn/version "1.3.0"} ; inspects namespaces and reports possible problems using tools.analyzer
lambdaisland/deep-diff2 {:mvn/version "2.7.169"} ; way better diffs
methodical/methodical {:mvn/version "0.15.1"} ; drop-in replacements for Clojure multimethods and adds several advanced features
org.clojure/algo.generic {:mvn/version "0.1.3"}
pjstadig/humane-test-output {:mvn/version "0.11.0"}
@@ -283,8 +279,8 @@
{:extra-paths ["enterprise/backend/src"]}
;; Include EE tests.
;; for EE dev: `clojure -X:dev:ee:ee-dev`
;; for EE tests: `clojure -X:dev:ee:ee-dev:test`
;; for ee dev: :dev:ee:ee-dev
;; for ee tests: clojure -X:dev:ee:ee-dev:test
:ee-dev
{:extra-paths ["enterprise/backend/test"]}
@@ -295,15 +291,6 @@
:oss-dev
{}
;; Generate BE documentation with
;; clojure -M:marginalia
:marginalia
{:extra-deps
{com.github.tsmacdonald/marginalia {:mvn/version "0.9.2"}}
:main-opts ["-m" "marginalia.main" "-n" "Metabase" "-d" "backend-docs" "-D"
"The simplest, fastest way to get business intelligence and analytics to everyone in your company 😋"
"dev" "src" "shared/src" "enterprise/backend/src"]}
;; Find outdated versions of dependencies. Run with `clojure -M:outdated`
:outdated {;; Note that it is `:deps`, not `:extra-deps`
:deps {com.github.liquidz/antq {:mvn/version "RELEASE"}}
@@ -312,18 +299,17 @@
:cljs
{:extra-paths ["test" "shared/test"]
:extra-deps
{binaryage/devtools {:mvn/version "1.0.7"}
cider/cider-nrepl {:mvn/version "0.37.0"}
{binaryage/devtools {:mvn/version "1.0.6"}
cider/cider-nrepl {:mvn/version "0.30.0"}
cider/piggieback {:mvn/version "0.5.3"}
cljs-bean/cljs-bean {:mvn/version "1.9.0"}
com.lambdaisland/glogi {:mvn/version "1.3.169"}
io.github.metabase/hawk {:sha "9c97bcb6d4de116325e651b40973fd0a75b7ae21"}
org.clojars.mmb90/cljs-cache {:mvn/version "0.1.4"}
com.lambdaisland/glogi {:mvn/version "1.2.164"}
io.github.metabase/hawk {:sha "70611745e3cb8b95d5793a88581d5c330148715b"}
;; Forcibly targeting a newer release than ships by default with shadow-cljs 2.20.20.
;; It fixes an issue where TypeScript can't parse the `cljs_env.js` output file.
org.clojure/google-closure-library {:mvn/version "0.0-20230227-c7c0a541"}
refactor-nrepl/refactor-nrepl {:mvn/version "3.9.0"}
thheller/shadow-cljs {:mvn/version "2.25.7"}}}
refactor-nrepl/refactor-nrepl {:mvn/version "3.6.0"}
thheller/shadow-cljs {:mvn/version "2.22.2"}}}
;; for local dev -- include the drivers locally with :dev:drivers
:drivers
@@ -489,9 +475,7 @@
["metabase\\.driver\\.mysql.*"
"metabase\\.driver\\.postgres.*"]}
;; different port from `:test` so you can run it at the same time as `:test`.
:jvm-opts ["-Dmb.jetty.port=3002"
;; default heap size is 1GB, we were getting OOM on CI so we need to bump this
"-Xmx4g"]}
:jvm-opts ["-Dmb.jetty.port=3002"]}
;;; building Uberjar, build and release scripts
@@ -505,7 +489,7 @@
com.github.seancorfield/depstar {:mvn/version "2.1.303"}
expound/expound {:mvn/version "0.9.0"} ; better output of spec validation errors
io.github.borkdude/grasp {:mvn/version "0.1.4"}
io.github.clojure/tools.build {:mvn/version "0.9.5"}
io.github.clojure/tools.build {:git/tag "v0.9.4" :git/sha "76b78fe"}
org.clojure/data.xml {:mvn/version "0.2.0-alpha8"}
org.clojure/tools.deps.alpha {:mvn/version "0.15.1254"}
org.fedorahosted.tennera/jgettext {:mvn/version "0.15.1"}}
@@ -580,10 +564,18 @@
;; :upload-uberjar
;; :push-git-tags
;; :publish-draft-release
;; :publish-elastic-beanstalk-artifacts
;; :update-version-info
:build/release
{:exec-fn release/release}
;; Only in the CI for now: publish EBS artifact
;; FIXME: remove after full release automation on CI
;;
;; clojure -X:build:build/publish-ebs
:build/publish-ebs
{:exec-fn release/publish-ebs}
;; extra paths and deps for working on build scripts, or running the tests.
:build-dev
{:extra-paths ["bin/build/test"]
@@ -613,8 +605,9 @@
:exec-fn dev.h2-shell/shell
:java-opts ["-Dfile.encoding=UTF-8"]}
;; clojure -M:generate-automagic-dashboards-pot
:generate-automagic-dashboards-pot
{:main-opts ["-m" "metabase.automagic-dashboards.dashboard-templates"]}
{:main-opts ["-m" "metabase.automagic-dashboards.rules"]}
;; Start a Network REPL (nrepl) that you can connect your editor to.
;;
@@ -634,41 +627,9 @@
;;
;; clojure -M:liquibase dbDoc target/liquibase
:liquibase
{:extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.11"}}
{:extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.6"}}
:extra-paths ["dev/src"]
:main-opts ["-m" "dev.liquibase"]}
;; the following aliases are convenience for running tests against certain parts of the codebase.
;;
;; clj -X:dev:ee:ee-dev:test:test/mbql
;; run tests against MLv2. Very fast since this is almost all ^:parallel
:test/mlv2
{:exec-args {:only ["test/metabase/lib"]}}
;; test the Query Processor
;;
;; clj -X:dev:ee:ee-dev:test:test/qp
:test/qp
{:exec-args {:only ["test/metadata/driver"
"test/metabase/query_processor"
"test/metabase/query_processor_test"
metabase-enterprise.advanced-permissions.models.permissions.block-permissions-test
metabase-enterprise.sandbox.query-processor.middleware.row-level-restrictions-test]}}
;; test all MBQL related stuff: MLv2, the legacy shared `metabase.mbql` code, and the QP
;;
;; clj -X:dev:ee:ee-dev:test:test/mbql
:test/mbql
{:exec-args {:only ["shared/test/mbql"
"test/metabase/lib"
"test/metadata/driver"
"test/metabase/query_processor"
"test/metabase/query_processor_test"
metabase-enterprise.advanced-permissions.models.permissions.block-permissions-test
metabase-enterprise.sandbox.query-processor.middleware.row-level-restrictions-test]}}}}
:main-opts ["-m" "dev.liquibase"]}}}
;; TODO -- consider creating an alias that includes the `./bin` build-drivers & release code as well so we can work
;; on them all from a single REPL process.

View File

@@ -1,45 +1,9 @@
;; # Metabase Backend Developer Documentation
;;
;; Welcome to Metabase! Here are links to useful resources.
;;
;; ## Project Management
;;
;; - [Engineering and Product Playbook](https://www.notion.so/metabase/Engineering-and-Product-Playbook-cd4bc1c0b8744470bebc0b979f8f5268)
;; - [Weekly Tactical Board: how to](https://www.notion.so/metabase/Weekly-Tactical-Board-how-to-6e81f994a792493ba7ae430f2afa1673)
;; - [The Escalations Process](https://www.notion.so/Escalating-a-bug-b876f78c801345f3bda8504d4a63ba80)
;;
;; ## Dev Environment
;;
;; - [Getting started with backend development](https://github.com/metabase/metabase/blob/master/docs/developers-guide/devenv.md#backend-development)
;; - [Additional notes on using tools.deps](https://github.com/metabase/metabase/wiki/Migrating-from-Leiningen-to-tools.deps)
;; - [Other tips](https://github.com/metabase/metabase/wiki/Metabase-Backend-Dev-Secrets)
;;
;; ## Important Parts of the Codebase
;;
;; - [API Endpoints](#metabase.api.common)
;; - [Drivers](#metabase.driver)
;; - [Permissions](#metabase.models.permissions)
;; - [The Query Processor](#metabase.query-processor)
;; - [Application Settings](#metabase.models.setting)
;;
;; ## Important Libraries
;;
;; - [Toucan 2](https://github.com/camsaul/toucan2/) to work with models
;; - [Honey SQL](https://github.com/seancorfield/honeysql) (version 2) for SQL queries
;; - [Liquibase](https://docs.liquibase.com/concepts/changelogs/changeset.html) for database migrations
;; - [Compojure](https://github.com/weavejester/compojure) on top of [Ring](https://github.com/ring-clojure/ring) for our API
;;
;; <hr />
(ns dev
"Put everything needed for REPL development within easy reach"
(:require
[clojure.core.async :as a]
[clojure.string :as str]
[dev.debug-qp :as debug-qp]
[dev.model-tracking :as model-tracking]
[dev.explain :as dev.explain]
[dev.debug-qp :as debug-qp]
[honeysql.core :as hsql]
[malli.dev :as malli-dev]
[metabase.api.common :as api]
@@ -60,11 +24,11 @@
[metabase.test :as mt]
[metabase.test.data.impl :as data.impl]
[metabase.util :as u]
[metabase.util.log :as log]
[methodical.core :as methodical]
[potemkin :as p]
[toucan2.connection :as t2.connection]
[toucan.db :as db]
[toucan2.core :as t2]
[toucan2.connection :as t2.connection]
[toucan2.pipeline :as t2.pipeline]))
(set! *warn-on-reflection* true)
@@ -77,11 +41,7 @@
(doto x tap>))
(p/import-vars
[debug-qp
process-query-debug
pprint-sql]
[dev.explain
explain-query]
[debug-qp process-query-debug]
[model-tracking
track!
untrack!
@@ -97,43 +57,18 @@
(mbc/init!)
(reset! initialized? true))
(defn deleted-inmem-databases
"Finds in-memory Databases for which the underlying in-mem h2 db no longer exists."
[]
(let [h2-dbs (t2/select :model/Database :engine :h2)
in-memory? (fn [db] (some-> db :details :db (str/starts-with? "mem:")))
can-connect? (fn [db]
(binding [metabase.driver.h2/*allow-testing-h2-connections* true]
(try
(driver/can-connect? :h2 (:details db))
(catch org.h2.jdbc.JdbcSQLNonTransientConnectionException _
false)
(catch Exception e
(log/error e "Error checking in-memory database for deletion")
;; we don't want to delete these, so just pretend we could connect
true))))]
(remove can-connect? (filter in-memory? h2-dbs))))
(defn prune-deleted-inmem-databases!
"Delete any in-memory Databases to which we can't connect (in order to trigger cleanup of their related tasks, which
will otherwise spam logs)."
[]
(when-let [outdated-ids (seq (map :id (deleted-inmem-databases)))]
(t2/delete! :model/Database :id [:in outdated-ids])))
(defn start!
[]
(server/start-web-server! #'handler/app)
(when-not @initialized?
(init!))
(when config/is-dev?
(prune-deleted-inmem-databases!)
(with-out-str (malli-dev/start!))))
(with-out-str (malli-dev/start!)))
(when-not @initialized?
(init!)))
(defn stop!
[]
(malli-dev/stop!)
(server/stop-web-server!))
(metabase.server/stop-web-server!))
(defn restart!
[]
@@ -207,17 +142,12 @@
(try
(driver/with-driver driver
(letfn [(thunk []
(let [db (mt/db)]
(sql-jdbc.execute/do-with-connection-with-options
driver
db
{:session-timezone (qp.timezone/report-timezone-id-if-supported driver db)}
(fn [conn]
(with-open [stmt (sql-jdbc.execute/prepared-statement driver conn sql params)
rs (sql-jdbc.execute/execute-prepared-statement! driver stmt)]
(let [rsmeta (.getMetaData rs)]
{:cols (sql-jdbc.execute/column-metadata driver rsmeta)
:rows (reduce conj [] (sql-jdbc.execute/reducible-rows driver rs rsmeta canceled-chan))}))))))]
(with-open [conn (sql-jdbc.execute/connection-with-timezone driver (mt/db) (qp.timezone/report-timezone-id-if-supported driver (mt/db)))
stmt (sql-jdbc.execute/prepared-statement driver conn sql params)
rs (sql-jdbc.execute/execute-prepared-statement! driver stmt)]
(let [rsmeta (.getMetaData rs)]
{:cols (sql-jdbc.execute/column-metadata driver rsmeta)
:rows (reduce conj [] (sql-jdbc.execute/reducible-rows driver rs rsmeta canceled-chan))})))]
(if dataset
(data.impl/do-with-dataset (data.impl/resolve-dataset-definition *ns* dataset) thunk)
(thunk))))

View File

@@ -6,8 +6,6 @@
[clojure.walk :as walk]
[lambdaisland.deep-diff2 :as ddiff]
[medley.core :as m]
[metabase.db.query :as mdb.query]
[metabase.mbql.normalize :as mbql.normalize]
[metabase.mbql.schema :as mbql.s]
[metabase.mbql.util :as mbql.u]
[metabase.models.field :refer [Field]]
@@ -120,18 +118,18 @@
(field-id->name-form [field-id]
(list 'do (add-name-to-field-id field-id) field-id))]
(mbql.u/replace form
[:field (id :guard pos-int?) opts]
[:field (id :guard integer?) opts]
[:field id (add-name-to-field-id id) (cond-> opts
(pos-int? (:source-field opts))
(integer? (:source-field opts))
(update :source-field field-id->name-form))]
(m :guard (every-pred map? (comp pos-int? :source-table)))
(m :guard (every-pred map? (comp integer? :source-table)))
(add-names* (update m :source-table add-table-id-name))
(m :guard (every-pred map? (comp pos-int? :metabase.query-processor.util.add-alias-info/source-table)))
(m :guard (every-pred map? (comp integer? :metabase.query-processor.util.add-alias-info/source-table)))
(add-names* (update m :metabase.query-processor.util.add-alias-info/source-table add-table-id-name))
(m :guard (every-pred map? (comp pos-int? :fk-field-id)))
(m :guard (every-pred map? (comp integer? :fk-field-id)))
(-> m
(update :fk-field-id field-id->name-form)
add-names*)
@@ -516,7 +514,7 @@
(_ :guard string?)
(not (re-find #"\s+" x))
[:field (id :guard pos-int?) nil]
[:field (id :guard integer?) nil]
(every? can-symbolize? (field-and-table-name id))
[:field (field-name :guard string?) (opts :guard #(= (set (keys %)) #{:base-type}))]
@@ -541,7 +539,7 @@
(defn- expand [form table]
(try
(mbql.u/replace form
([:field (id :guard pos-int?) nil] :guard can-symbolize?)
([:field (id :guard integer?) nil] :guard can-symbolize?)
(let [[table-name field-name] (field-and-table-name id)
field-name (some-> field-name u/lower-case-en)
table-name (some-> table-name u/lower-case-en)]
@@ -569,19 +567,19 @@
expansion (expand without-join-alias table)]
[::& (:join-alias opts) expansion])
[:field (id :guard pos-int?) opts]
[:field (id :guard integer?) opts]
(let [without-opts [:field id nil]
expansion (expand without-opts table)]
(if (= expansion without-opts)
&match
[:field [::% (strip-$ expansion)] opts]))
(m :guard (every-pred map? (comp pos-int? :source-table)))
(m :guard (every-pred map? (comp integer? :source-table)))
(-> (update m :source-table (fn [table-id]
[::$$ (some-> (t2/select-one-fn :name Table :id table-id) u/lower-case-en)]))
(expand table))
(m :guard (every-pred map? (comp pos-int? :fk-field-id)))
(m :guard (every-pred map? (comp integer? :fk-field-id)))
(-> (update m :fk-field-id (fn [fk-field-id]
(let [[table-name field-name] (field-and-table-name fk-field-id)
field-name (some-> field-name u/lower-case-en)
@@ -615,36 +613,29 @@
[::$$ table-name]
(symbol (format "$$%s" table-name))))
(defn- query-table-name [{:keys [source-table source-query], :as inner-query}]
(defn- query-table-name [{:keys [source-table source-query]}]
(cond
(pos-int? source-table)
(u/lower-case-en (or (t2/select-one-fn :name Table :id source-table)
(throw (ex-info (format "Table %d does not exist!" source-table)
{:source-table source-table, :inner-query inner-query}))))
source-table
(do
(assert (integer? source-table))
(u/lower-case-en (t2/select-one-fn :name Table :id source-table)))
source-query
(recur source-query)))
(defn to-mbql-shorthand
([query]
(let [query (mbql.normalize/normalize query)]
(to-mbql-shorthand query (query-table-name (:query query)))))
(to-mbql-shorthand query (query-table-name (:query query))))
([query table-name]
(let [symbolized (-> query (expand table-name) symbolize ->sorted-mbql-query-map)
table-symb (some-> table-name symbol)]
(if (:query symbolized)
(list 'mt/mbql-query table-symb (cond-> (:query symbolized)
table-name (dissoc :source-table)))
(list 'mt/mbql-query table-symb (-> (:query symbolized)
(dissoc :source-table)))
(list 'mt/$ids table-symb symbolized)))))
(defn expand-symbolize [x]
(-> x (expand "orders") symbolize))
;; tests are in [[dev.debug-qp-test]] (in `./dev/test/dev` dir)
(defn pprint-sql
"Pretty print a SQL string."
[driver sql]
#_{:clj-kondo/ignore [:discouraged-var]}
(println (mdb.query/format-sql sql driver)))

View File

@@ -1,18 +0,0 @@
(ns dev.explain
(:require
[clojure.string :as str]
[honey.sql :as sql]
[toucan2.core :as t2]))
(defn explain-query
"Explain a sql query or a honeysql query with option to analyze the query."
([queryable]
(explain-query queryable false))
([queryable analyze?]
(->> (t2/query
(str/join
" "
(remove nil? ["EXPLAIN"
(when analyze? "ANALYZE")
"(" (if (map? queryable) (first (sql/format queryable {:inline true})) queryable) ")"])))
(map #(get % (keyword "query plan"))))))

View File

@@ -1,15 +0,0 @@
(ns dev.fe-helpers)
(defn redux-state []
(let [root (js/document.querySelector "#root")
store (.. root -_reactRootContainer -_internalRoot -current -child -memoizedProps -store)]
(.getState store)))
(defn current-card []
(.. (redux-state) -qb -card))
(defn current-legacy-query-js []
(.-dataset_query (current-card)))
(defn current-query []
(.-__MLv2_query js/window))

View File

@@ -1,17 +1,17 @@
(ns dev.render-png
"Improve feedback loop for dealing with png rendering code. Will create images using the rendering that underpins
pulses and subscriptions and open those images without needing to send them to slack or email."
(:require
[clojure.java.io :as io]
[clojure.java.shell :as sh]
[hiccup.core :as hiccup]
[metabase.models.card :as card]
[metabase.models.user :as user]
[metabase.pulse :as pulse]
[metabase.pulse.render :as render]
[metabase.pulse.render.test-util :as render.tu]
[metabase.query-processor :as qp]
[metabase.query-processor.middleware.permissions :as qp.perms]))
(:require [clojure.java.io :as io]
[clojure.java.shell :as sh]
[hiccup.core :as hiccup]
[metabase.models.card :as card]
[metabase.models.user :as user]
[metabase.pulse :as pulse]
[metabase.pulse.render :as render]
[metabase.pulse.render.test-util :as render.tu]
[metabase.query-processor :as qp]
[metabase.query-processor.middleware.permissions :as qp.perms]
[toucan.db :as db]))
;; taken from https://github.com/aysylu/loom/blob/master/src/loom/io.clj
(defn- os
@@ -56,9 +56,9 @@
:context :pulse
:card-id card-id}))
png-bytes (render/render-pulse-card-to-png (pulse/defaulted-timezone card)
card
query-results
1000)
card
query-results
1000)
tmp-file (java.io.File/createTempFile "card-png" ".png")]
(with-open [w (java.io.FileOutputStream. tmp-file)]
(.write w ^bytes png-bytes))
@@ -91,5 +91,5 @@
:custom-column-names {:names ["-A-" "-B-" "-C-" "-D-"]}
:hidden-columns {:hide [0 2]}})
:viz-tree
open-hiccup-as-html))
open-hiccup-as-html)
)

View File

@@ -1,20 +1,8 @@
(ns user
(:require
[environ.core :as env]
[humane-are.core :as humane-are]
[mb.hawk.assert-exprs]
[metabase.bootstrap]
[metabase.test-runner.assert-exprs]
[pjstadig.humane-test-output :as humane-test-output]))
;; Initialize Humane Test Output if it's not already initialized. Don't enable humane-test-output when running tests
;; from the CLI, it breaks diffs. This uses [[env/env]] rather than [[metabase.config]] so we don't load that namespace
;; before we load [[metabase.bootstrap]]
(when-not (= (env/env :mb-run-mode) "test")
(humane-test-output/activate!))
;;; Same for https://github.com/camsaul/humane-are
(humane-are/install!)
[metabase.test-runner.assert-exprs]))
(comment metabase.bootstrap/keep-me
;; make sure stuff like `schema=` and what not are loaded

View File

@@ -16,27 +16,27 @@
(model-tracking/track! 'Collection)
(testing "insert"
(t2/insert! Collection {:name "Test tracking" :description "My awesome collection"})
(t2/insert! Collection {:name "Test tracking" :color "#000000"})
(testing "should be tracked"
(is (=? [{:name "Test tracking"
:description "My awesome collection"}]
:color "#000000"}]
(get-in (model-tracking/changes) [:collection :insert]))))
(testing "should take affects"
(is (= 1 (t2/count Collection :name "Test tracking")))))
(testing "update"
(t2/update! Collection {:name "Test tracking"} {:description "Amazing collection"})
(t2/update! Collection {:name "Test tracking"} {:color "#ffffff"})
(testing "changes should be tracked"
(is (= [{:description "Amazing collection"}]
(is (= [{:color "#ffffff"}]
(get-in (model-tracking/changes) [:collection :update]))))
(testing "should take affects"
(is (= "Amazing collection" (t2/select-one-fn :description Collection :name "Test tracking")))))
(is (= "#ffffff" (t2/select-one-fn :color Collection :name "Test tracking")))))
(testing "delete"
(let [coll-id (t2/select-one-pk Collection :name "Test tracking")]
(t2/delete! Collection coll-id)
(testing "should be tracked"
(is (=? [{:description "Amazing collection"
(is (=? [{:color "#ffffff"
:name "Test tracking",
:id coll-id}]
(get-in (model-tracking/changes) [:collection :delete]))))
@@ -46,18 +46,18 @@
(testing "untrack should stop all tracking for"
(model-tracking/untrack-all!)
(testing "insert"
(t2/insert! Collection {:name "Test tracking" :description "My awesome collection"})
(t2/insert! Collection {:name "Test tracking" :color "#000000"})
(testing "changes not should be tracked"
(is (empty? (model-tracking/changes))))
(testing "should take affects"
(is (= 1 (t2/count Collection :name "Test tracking")))))
(testing "update"
(t2/update! Collection {:name "Test tracking"} {:description "Amazing collection"})
(t2/update! Collection {:name "Test tracking"} {:color "#ffffff"})
(testing "changes not should be tracked"
(is (empty? (model-tracking/changes))))
(testing "should take affects"
(is (= "Amazing collection" (t2/select-one-fn :description Collection :name "Test tracking")))))
(is (= "#ffffff" (t2/select-one-fn :color Collection :name "Test tracking")))))
(testing "delete"
(let [coll-id (t2/select-one-pk Collection :name "Test tracking")]

View File

@@ -58,7 +58,6 @@ Metabase's reference documentation.
- [Encrypting your database connection](./databases/encrypting-details-at-rest.md)
- [SSH tunneling](./databases/ssh-tunnel.md)
- [SSL certificate](./databases/ssl-certificates.md)
- [Uploading data](./databases/uploads.md)
### Questions
@@ -156,9 +155,9 @@ Metabase's reference documentation.
- [Embedding overview](./embedding/start.md)
- [Embedding introduction](./embedding/introduction.md)
- [Interactive embedding](./embedding/interactive-embedding.md)
- [Static embedding](./embedding/static-embedding.md)
- [Parameters for static embeds](./embedding/static-embedding-parameters.md)
- [Full-app embedding](./embedding/full-app-embedding.md)
- [Signed embedding](./embedding/signed-embedding.md)
- [Parameters for signed embeds](./embedding/signed-embedding-parameters.md)
### Configuration

View File

@@ -56,12 +56,6 @@ See [Actions in dashboards](../dashboards/actions.md).
Because basic actions are made of magic, you cannot archive them. You can just toggle them on or off. From the model detail page, next to the **New action** button, click on the **...** menu and click **Disable basic actions**.
## Basic actions from object detail view
If you have basic actions enabled for a model, you can click on an individual record to view its object detail. From that object detail model, you can click on the ellipses (**...**) and select update or delete to modify that specific record.
![Update record from object detail view](./images/update-record.png)
## Further reading
- [Introduction to actions](./introduction.md)

View File

@@ -118,6 +118,37 @@ VALUES (
{% endraw %}
```
### `INSERT` statement with optional fields
Though if you want to make a field optional in an `INSERT` statement, you need to get a little fancy. Metabase will only run an optional statement if a value is supplied to a variable in that optional statement, so the trick here is to use a SQL comment to sneak a variable into the optional column.
```
{% raw %}
[[,expected_invoice -- {{ expected_invoice}}]]
{% endraw %}
```
So a full `INSERT` statement with an optional variable would look like so (note that the optional clauses for the `{% raw %}{{ expected_invoice}}{{% endraw %}` variable are in both the column and values lists):
```
{% raw %}
INSERT INTO invoices (
account_id
,payment
[[,expected_invoice -- {{expected_invoice}}]]
,plan
,date_received
)
VALUES (
{{ account_id }}
,{{ payment }}
[[,{{ expected_invoice }}]]
,{{ plan }}
,({{ date_received }}
);
{% endraw %}
```
### Casting field values in actions
If you get a type error when you submit a form, you may need to `CAST` the data type in the query so it matches the data type of the target field in the database. Here we're casting a value to a `boolean`:

Some files were not shown because too many files have changed in this diff Show More