Compare commits
1 Commits
python-run
...
single-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f55dc0d3b5 |
@@ -1,7 +1,7 @@
|
||||
;; -*- comment-column: 100; -*-
|
||||
{:config-paths ["macros" "src" "config"]
|
||||
:linters
|
||||
{ ;;
|
||||
{;;
|
||||
;; enabled optional linters (these are disabled by default)
|
||||
;;
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
;; disabled linters
|
||||
;;
|
||||
|
||||
:redundant-ignore {:level :off} ; lots of false positives, see https://github.com/clj-kondo/clj-kondo/issues/2414
|
||||
:redundant-ignore {:level :off} ; lots of false positives, see https://github.com/clj-kondo/clj-kondo/issues/2414
|
||||
:unexpected-recur {:level :off} ; TODO (cam): I think we just need to tell it how to handle MBQL match and we can enable this?
|
||||
|
||||
;;
|
||||
@@ -68,7 +68,6 @@
|
||||
:metabase/validate-logging {:level :warning}
|
||||
:metabase/validate-mb-driver-tests {:level :warning}
|
||||
:metabase/validate-mb-once {:level :warning}
|
||||
:metabase/discourage-millis-duration {:level :warning}
|
||||
|
||||
:metabase/validate-deftest
|
||||
{:level :warning
|
||||
@@ -77,13 +76,8 @@
|
||||
;;
|
||||
;; TODO -- these should all be made thread safe if possible or renamed so they end in `!`. Then we can remove them
|
||||
;; from this list because by default anything ending in `!` is considered to be thread-unsafe
|
||||
;;
|
||||
;; TODO (Cam 6/19/25) -- move this into the `test-helpers/config.edn` file so they can live with their friends.
|
||||
:parallel/unsafe
|
||||
#{java.util.Locale/setDefault
|
||||
java.lang.System/setProperty
|
||||
java.lang.System/setProperties
|
||||
clojure.core/alter-var-root
|
||||
#{clojure.core/alter-var-root
|
||||
clojure.core/with-redefs
|
||||
clojure.core/with-redefs-fn
|
||||
metabase.actions.test-util/with-actions
|
||||
@@ -102,6 +96,7 @@
|
||||
metabase.test.util/with-all-users-permission
|
||||
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-model-cleanup
|
||||
metabase.test.util/with-non-admin-groups-no-root-collection-for-namespace-perms
|
||||
metabase.test.util/with-non-admin-groups-no-root-collection-perms
|
||||
@@ -124,6 +119,7 @@
|
||||
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-model-cleanup
|
||||
metabase.test/with-non-admin-groups-no-root-collection-for-namespace-perms
|
||||
@@ -173,7 +169,7 @@
|
||||
metabase.driver.sql-jdbc.execute/execute-prepared-statement!
|
||||
metabase.notification.models/create-notification!
|
||||
metabase.pulse.send/send-pulse!
|
||||
metabase-enterprise.transforms-python.python-runner-test/execute!
|
||||
metabase.query-processor.store/store-database!
|
||||
;; Safe in tests, where we typically use a private, temporary index per thread.
|
||||
metabase.search.core/reindex!
|
||||
metabase.util/run-count!
|
||||
@@ -187,7 +183,7 @@
|
||||
:metabase/disallow-hardcoded-driver-names-in-tests
|
||||
;; don't include `:h2` since that will probably lead to a mountain of tests where we are specifically testing
|
||||
;; middleware or compilation behavior with just H2 since it's the default.
|
||||
{:drivers #{ ;; core drivers
|
||||
{:drivers #{;; core drivers
|
||||
#_:h2
|
||||
:postgres
|
||||
:mysql
|
||||
@@ -215,7 +211,7 @@
|
||||
;; misc config for built-in linters
|
||||
;;
|
||||
|
||||
:invalid-arity {:skip-args [metabase.lib.util.match/match]} ; TODO (cam): can we fix these?
|
||||
:invalid-arity {:skip-args [metabase.lib.util.match/match]} ; TODO (cam): can we fix these?
|
||||
:refer-all {:level :warning, :exclude [clojure.test]}
|
||||
:unused-referred-var {:exclude {compojure.core [GET DELETE POST PUT]}}
|
||||
|
||||
@@ -229,15 +225,13 @@
|
||||
(clojure.core.logic/fresh)
|
||||
(clojure.core.logic/matcha)
|
||||
(clojure.core.logic/run)
|
||||
(clojure.test/is [=? malli= qv=])
|
||||
(clojure.test/is [query= =? malli= qv=])
|
||||
(clojure.tools.macro/macrolet)
|
||||
instaparse.core/transform
|
||||
(metabase.server.streaming-response/streaming-response)
|
||||
(metabase.driver.druid.query-processor-test/druid-query-returning-rows)
|
||||
(metabase.lib.util.match/match)
|
||||
(metabase.lib.util.match/match-one)
|
||||
(metabase.lib.util.match/match-lite)
|
||||
(metabase.lib.util.match/match-lite-recursive)
|
||||
(metabase.lib.util.match/replace)
|
||||
(metabase.lib.util.match/replace-in)
|
||||
(metabase.driver-api.core/match)
|
||||
@@ -250,15 +244,16 @@
|
||||
(taoensso.nippy/extend-freeze)
|
||||
(taoensso.nippy/extend-thaw)]}
|
||||
|
||||
;; TODO (Cam 8/22/25) -- what's the point of marking stuff deprecated if we don't have Kondo enforce it? We should
|
||||
;; remove all of these and add individual ignores as needed.
|
||||
:deprecated-var
|
||||
{:exclude
|
||||
{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.*"]}
|
||||
metabase.driver/supports? {:namespaces ["metabase.*"]}
|
||||
metabase.query-processor.util/normalize-token {:namespaces ["metabase.*"]}}}
|
||||
{metabase.cmd.core/dump {:namespaces ["metabase\\.cmd-test" "metabase-enterprise\\.serialization\\.cmd-test"]}
|
||||
metabase.cmd.core/load {:namespaces ["metabase\\.cmd-test" "metabase-enterprise\\.serialization\\.cmd-test"]}
|
||||
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.*"]}
|
||||
metabase.driver/supports? {:namespaces ["metabase.*"]}
|
||||
metabase.query-processor.middleware.resolve-joins/maybe-resolve-source-table {:namespaces ["metabase.*"]}
|
||||
metabase.query-processor.util/normalize-token {:namespaces ["metabase.*"]}}}
|
||||
|
||||
:discouraged-var
|
||||
{clojure.core/print {:message "Use metabase.util.log instead of clojure.core/print"}
|
||||
@@ -291,17 +286,12 @@
|
||||
me.flowthing.pp/pprint {:message "Use metabase.util.log instead of me.flowthing.pp/pprint"}
|
||||
medley.core/random-uuid {:message "Use clojure.core/random-uuid instead of medley.core/random-uuid"}
|
||||
metabase.driver/database-supports? {:message "Use metabase.driver.util/supports? instead of metabase.driver/database-supports?"}
|
||||
metabase.lib.convert/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
|
||||
metabase.lib.core/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
|
||||
metabase.lib.equality/find-column-indexes-for-refs {:message "Use lib.equality/closest-matches-in-metadata or lib.equality/find-closest-matches-for-refs instead of lib.equality/find-column-indexes-for-refs"}
|
||||
next.jdbc/active-tx? {:message "Use metabase.app-db.core/in-transaction? to see if we are in a transaction"}
|
||||
toucan2.core/debug {:message "Don't commit code using t2/debug"}
|
||||
toucan2.tools.debug/debug {:message "Don't commit code using t2/debug"}}
|
||||
next.jdbc/active-tx? {:message "Use metabase.app-db.core/in-transaction? to see if we are in a transaction"}}
|
||||
|
||||
:discouraged-namespace
|
||||
{camel-snake-kebab.core {:message "CSK is not Turkish-safe, use the versions in metabase.util instead."}
|
||||
cheshire.core {:message "Use metabase.util.json instead of cheshire.core directly."}
|
||||
clj-time.core {:message "Use java-time.api or metabase.util.date-2 instead of clj-time"}
|
||||
clojure.tools.logging {:message "Use metabase.util.log instead of clojure.tools.logging directly"}
|
||||
grouper.core {:message "Use metabase.util.grouper instead of grouper.core"}
|
||||
honeysql.core {:message "Use Honey SQL 2. Honey SQL 1 is removed as a dependency in Metabase 49+."}
|
||||
@@ -410,16 +400,10 @@
|
||||
clojure.test.check.clojure-test/defspec clojure.test/deftest
|
||||
clojure.test.check.properties/for-all clojure.core/let
|
||||
clojurewerkz.quartzite.jobs/defjob clojure.core/defn
|
||||
com.climate.claypoole/with-shutdown! clojure.core/let
|
||||
instaparse.core/defparser clojure.core/def
|
||||
metabase-enterprise.action-v2.test-util/with-test-tables! clojure.core/let
|
||||
metabase-enterprise.content-translation.routes-test/with-new-secret-key! clojure.core/fn
|
||||
metabase-enterprise.serialization.test-util/with-dbs clojure.core/fn
|
||||
metabase-enterprise.serialization.test-util/with-random-dump-dir clojure.core/let
|
||||
metabase-enterprise.transforms.api-test/with-transform-tags clojure.core/let
|
||||
metabase-enterprise.transforms.api-test/with-worker-runs clojure.core/let
|
||||
metabase-enterprise.transforms.test-util/with-transform-cleanup! clojure.core/let
|
||||
metabase-enterprise.transforms.execute/with-transform-lifecycle clojure.core/let
|
||||
metabase-enterprise.content-translation.routes-test/with-new-secret-key! clojure.core/fn
|
||||
metabase.actions.test-util/with-actions clojure.core/let
|
||||
metabase.api.common/let-404 clojure.core/let
|
||||
metabase.app-db.custom-migrations.util/with-temp-scheduler! clojure.core/fn
|
||||
@@ -478,7 +462,6 @@
|
||||
metabase.test/with-temp-file clojure.core/let
|
||||
metabase.test/with-user-in-groups clojure.core/let
|
||||
metabase.upload.impl-test/with-upload-table! clojure.core/let
|
||||
metabase.util/for-map clojure.core/for
|
||||
metabase.util.files/with-open-path-to-resource clojure.core/let
|
||||
metabase.util.malli.defn/defn schema.core/defn
|
||||
metabase.util.malli.defn/defn- schema.core/defn
|
||||
@@ -511,7 +494,6 @@
|
||||
clojure.core/ns hooks.clojure.core.ns/lint-ns
|
||||
clojure.core/require hooks.clojure.core.require/lint-require
|
||||
clojure.core/requiring-resolve hooks.clojure.core.require/lint-requiring-resolve
|
||||
clojure.core/- hooks.metabase.util.timing/discourage-millis-duration
|
||||
clojure.test/deftest hooks.clojure.test/deftest
|
||||
clojure.test/is hooks.clojure.test/is
|
||||
clojure.test/use-fixtures hooks.clojure.test/use-fixtures
|
||||
@@ -519,9 +501,7 @@
|
||||
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.cache.config-test/with-temp-persist-models hooks.common/with-seven-bindings
|
||||
metabase-enterprise.semantic-search.db.connection/with-migrate-tx hooks.common/let-one-with-optional-value
|
||||
metabase-enterprise.serialization.test-util/with-temp-dpc hooks.toucan2.tools.with-temp/with-temp
|
||||
metabase-enterprise.transforms-python.drivers-test/with-test-table hooks.common/with-two-bindings
|
||||
metabase.api.common.internal-test/with-jetty-server hooks.common/with-one-binding
|
||||
metabase.api.macros/defendpoint hooks.metabase.api.macros/defendpoint
|
||||
metabase.app-db.schema-migrations-test.impl/test-migrations hooks.metabase.app-db.schema-migrations-test.impl/test-migrations
|
||||
@@ -538,7 +518,6 @@
|
||||
metabase.dashboards.models.dashboard-tab-test/with-dashtab-in-personal-collection hooks.common/with-one-top-level-binding
|
||||
metabase.dashboards.models.dashboard-test/with-dash-in-collection! hooks.common/with-three-bindings
|
||||
metabase.driver.bigquery-cloud-sdk-test/calculate-bird-scarcity hooks.metabase.query-processor-test.expressions-test/calculate-bird-scarcity
|
||||
metabase.driver.mongo-test/with-describe-table-for-sample hooks.metabase.driver.mongo-test/with-describe-table-for-sample
|
||||
metabase.driver.oracle-test/with-temp-user hooks.common/let-one-with-optional-value
|
||||
metabase.driver/register! hooks.metabase.driver/register!
|
||||
metabase.driver-api/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
|
||||
@@ -552,7 +531,6 @@
|
||||
metabase.lib.schema.mbql-clause/define-tuple-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
|
||||
metabase.lib.test-util.macros/$ids hooks.metabase.test.data/$ids
|
||||
metabase.lib.test-util.macros/mbql-query hooks.metabase.test.data/mbql-query
|
||||
metabase.lib.test-util.macros/mbql-5-query hooks.metabase.test.data/mbql-query
|
||||
metabase.lib.test-util.macros/with-testing-against-standard-queries hooks.metabase.lib.test-util.macros/with-testing-against-standard-queries
|
||||
metabase.model-persistence.api-test/with-persistence-setup! hooks.common/with-one-top-level-binding
|
||||
metabase.model-persistence.api-test/with-setup! hooks.common/with-one-top-level-binding
|
||||
@@ -652,7 +630,6 @@
|
||||
|
||||
:macroexpand
|
||||
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
|
||||
clojurewerkz.quartzite.schedule.calendar-interval/build macros.quartz/build-job
|
||||
clojurewerkz.quartzite.schedule.cron/schedule macros.quartz/schedule
|
||||
clojurewerkz.quartzite.schedule.simple/schedule macros.quartz/simple-schedule
|
||||
clojurewerkz.quartzite.triggers/build macros.quartz/build-trigger
|
||||
@@ -713,30 +690,18 @@
|
||||
:name printable-namespaces}
|
||||
{:pattern "^metabase\\.(?:(?:lib)|(?:legacy-mbql))\\..*"
|
||||
:name metabase-lib}
|
||||
{:pattern "^metabase\\.lib\\..*"
|
||||
:name metabase-lib-excluding-legacy-mbql}
|
||||
;;
|
||||
;; QP and driver source namespaces: `metabase.query-processor.*`, `metabase-enterprise.sandbox.query-processor.*`,
|
||||
;; and `metabase.driver.*`, excluding `-test` 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}
|
||||
;;
|
||||
;; QP source namespaces
|
||||
;;
|
||||
{:pattern "metabase(?:(?:-enterprise\\.sandbox)?\\.query-processor\\.).*(?<!-test)$"
|
||||
:name qp-source-namespaces}
|
||||
;;
|
||||
;; driver namespaces, including tests
|
||||
;; driver namespaces
|
||||
;;
|
||||
{:pattern "metabase\\.driver.*$"
|
||||
:name driver-namespaces}
|
||||
;;
|
||||
;; Perf-sensitive namespaces are currently the union of metabase.lib.*, driver namespaces, and QP (without tests).
|
||||
;;
|
||||
{:pattern "^(?!.*-test(?:\\..*)?$)(?!.*test-util)(?!^metabase\\.test.*)^metabase\\.(lib|driver|legacy-mbql|query-processor).*"
|
||||
:name performance-sensitive}
|
||||
;;
|
||||
;; general QP tests are ones that run across multiple drivers. Anything `metabase.query-processor*-test`,
|
||||
;; `metabase.driver.sql*-test` (excluding SQLServer and SQLite), or
|
||||
;; `metabase-enterprise.sandbox.query-processor*-test`
|
||||
@@ -750,13 +715,9 @@
|
||||
{:inline-def {:level :off}
|
||||
:missing-docstring {:level :off}
|
||||
:private-call {:level :off}
|
||||
:discouraged-var {metabase.driver/database-supports? {:level :off}}
|
||||
:metabase/defsetting-must-specify-export {:level :off}
|
||||
:metabase/defsetting-namespace {:level :off}
|
||||
|
||||
:discouraged-var
|
||||
{metabase.driver/database-supports? {:level :off}
|
||||
metabase.lib.convert/->legacy-MBQL {:level :off}
|
||||
metabase.lib.core/->legacy-MBQL {:level :off}}}
|
||||
:metabase/defsetting-namespace {:level :off}}
|
||||
|
||||
:hooks
|
||||
{:analyze-call
|
||||
@@ -768,14 +729,9 @@
|
||||
{:linters
|
||||
{:metabase/validate-escaped-single-quotes-in-i18n {:level :warning}
|
||||
:metabase/validate-string-or-str-args-to-i18n {:level :warning}
|
||||
:metabase/discourage-millis-duration {:level :warning}
|
||||
|
||||
: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"}}
|
||||
|
||||
:discouraged-namespace
|
||||
{metabase.query-processor.store {:message "Please do not use the QP Store outside of legacy QP or driver code. Use MBQL 5/Lib or lib.metadata.jvm/applicaton-database-metadata-provider instead."}}}}
|
||||
clojure.core/eval {:message "Don't use eval outside of tests"}}}}
|
||||
|
||||
printable-namespaces
|
||||
{:linters
|
||||
@@ -787,7 +743,7 @@
|
||||
clojure.string/lower-case {:level :off}
|
||||
clojure.string/upper-case {:level :off}}}}
|
||||
|
||||
;; enable all the extra linters we haven't enabled in the core app yet in Lib. It's all new code so we can adhere
|
||||
;; enable all the extra linters we haven't enabled in the core app yet in MLv2. It's all new code so we can adhere
|
||||
;; to the new stricter rules from day 1
|
||||
metabase-lib
|
||||
{:linters
|
||||
@@ -800,89 +756,40 @@
|
||||
;; 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 metabase.util.performance/update-keys"}
|
||||
medley.core/map-vals {:message "Use clojure.core/update-vals"}
|
||||
metabase.lib.convert/->legacy-MBQL {:level :off}
|
||||
metabase.lib.core/->legacy-MBQL {:level :off}
|
||||
metabase.test/with-temp {:message "Don't use application database inside Lib code"}}
|
||||
{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
|
||||
{metabase.driver {:message "Lib code should be independent of driver code/methods"}
|
||||
metabase.test {:message "Lib tests should only use Lib test helpers"}
|
||||
metabase.test.data {:message "Lib tests should only use Lib test helpers"}
|
||||
metabase.util.malli.schema {:message "Prefer metabase.lib.schema schemas in Lib code"}
|
||||
toucan.db {:message "Don't use application database inside Lib code"}
|
||||
toucan.models {:message "Don't use application database inside Lib code"}
|
||||
toucan.util.test {:message "Don't use application database inside Lib code"}
|
||||
toucan2.core {:message "Don't use application database inside Lib code"}}}}
|
||||
|
||||
metabase-lib-excluding-legacy-mbql
|
||||
{:linters
|
||||
{:discouraged-var
|
||||
{metabase.legacy-mbql.util/unique-name-generator {:message "Use metabase.lib.util/unique-name-generator or non-truncating-unique-name-generator"}}}
|
||||
|
||||
:discouraged-namespace
|
||||
{metabase.legacy-mbql.js {:message "Avoid legacy MBQL utils in Lib; move things into Lib if needded."}
|
||||
metabase.legacy-mbql.jvm-util {:message "Avoid legacy MBQL utils in Lib; move things into Lib if needded."}
|
||||
metabase.legacy-mbql.predicates {:message "Avoid legacy MBQL utils in Lib; move things into Lib if needded."}
|
||||
metabase.legacy-mbql.util {:message "Avoid legacy MBQL utils in Lib; move things into Lib if needded."}}}
|
||||
{metabase.driver {:message "MLv2 code should be independent of driver code/methods"}
|
||||
metabase.test {:message "MLv2 tests should only use MLv2 test helpers"}
|
||||
metabase.test.data {:message "MLv2 tests should only use MLv2 test helpers"}
|
||||
metabase.util.malli.schema {:message "Prefer metabase.lib.schema schemas in MLv2 code"}
|
||||
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"}}}}
|
||||
|
||||
driver-namespaces
|
||||
{:linters
|
||||
{:discouraged-var
|
||||
{metabase.driver/database-supports? {:level :off}}}}
|
||||
|
||||
performance-sensitive
|
||||
{:linters
|
||||
{:discouraged-var
|
||||
{clojure.core/every? {:message "Use metabase.util.performance/every?"}
|
||||
clojure.core/mapv {:message "Use metabase.util.performance/mapv"}
|
||||
clojure.core/run! {:message "Use metabase.util.performance/run!"}
|
||||
clojure.core/select-keys {:message "Use metabase.util.performance/select-keys"}
|
||||
clojure.core/some {:message "Use metabase.util.performance/some"}
|
||||
clojure.core/update-keys {:message "Use metabase.util.performance/update-keys"}
|
||||
clojure.walk/keywordize-keys {:message "Use metabase.util.performance/keywordize-keys"}
|
||||
clojure.walk/postwalk {:message "Use metabase.util.performance/postwalk"}
|
||||
clojure.walk/prewalk {:message "Use metabase.util.performance/prewalk"}
|
||||
clojure.walk/walk {:message "Use metabase.util.performance/walk"}}}}
|
||||
|
||||
qp-and-driver-source-namespaces
|
||||
{:linters
|
||||
{:discouraged-namespace
|
||||
{metabase.query-processor.store {:level :off}
|
||||
metabase.util.malli.schema {:message "Prefer metabase.lib.schema or metabase.legacy-mbql.schema schemas in QP/driver code"}
|
||||
toucan.db {:message "Don't use application database directly in QP code; use the MetadataProvider"}
|
||||
toucan.models {:message "Don't use application database directly in QP code; use the MetadataProvider"}
|
||||
toucan.util.test {:message "Don't use application database directly in QP code; use the MetadataProvider"}
|
||||
toucan2.core {:message "Don't use application database directly in QP code; use the MetadataProvider"}}
|
||||
{metabase.util.malli.schema {:message "Prefer metabase.lib.schema or metabase.legacy-mbql.schema schemas in QP/driver code"}
|
||||
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
|
||||
{medley.core/map-keys {:message "Use metabase.util.performance/update-keys"}
|
||||
{medley.core/map-keys {:message "Use clojure.core/update-keys"}
|
||||
medley.core/map-vals {:message "Use clojure.core/update-vals"}
|
||||
metabase.app-db.core/query {:message "Don't use application database directly in QP code; use the MetadataProvider"}
|
||||
metabase.app-db.core/reducible-query {:message "Don't use application database directly in QP code; use the MetadataProvider"}
|
||||
metabase.lib.convert/->legacy-MBQL {:level :off}
|
||||
metabase.lib.core/->legacy-MBQL {:level :off}}}}
|
||||
|
||||
qp-source-namespaces
|
||||
{:linters
|
||||
{:discouraged-namespace
|
||||
{metabase.legacy-mbql.js {:message "Avoid legacy MBQL namespaces in the QP, use Lib instead"}
|
||||
metabase.legacy-mbql.jvm-util {:message "Avoid legacy MBQL namespaces in the QP, use Lib instead"}
|
||||
metabase.legacy-mbql.normalize {:message "Avoid legacy MBQL namespaces in the QP, use Lib instead"}
|
||||
metabase.legacy-mbql.predicates {:message "Avoid legacy MBQL namespaces in the QP, use Lib instead"}
|
||||
metabase.legacy-mbql.schema {:message "Avoid legacy MBQL namespaces in the QP, use Lib instead"}
|
||||
metabase.legacy-mbql.schema.helpers {:message "Avoid legacy MBQL namespaces in the QP, use Lib instead"}
|
||||
metabase.legacy-mbql.schema.macros {:message "Avoid legacy MBQL namespaces in the QP, use Lib instead"}
|
||||
metabase.legacy-mbql.util {:message "Avoid legacy MBQL namespaces in the QP, use Lib instead"}}}}
|
||||
metabase.app-db.core/query {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}
|
||||
metabase.app-db.core/reducible-query {:message "Don't use application database directly in QP code; use QP Store/metadata provider"}}}}
|
||||
|
||||
general-qp-and-driver-test-namespaces
|
||||
{:linters
|
||||
{:discouraged-var
|
||||
{metabase.test/with-column-remappings {:message "Use lib.tu/remap-metadata-provider instead of with-column-remappings in QP tests"}
|
||||
metabase.test.util/with-column-remappings {:message "Use lib.tu/remap-metadata-provider instead of with-column-remappings in QP tests"}
|
||||
metabase.test/with-temp {:message "Use lib.tu/mock-metadata-provider in QP tests. Exceptions: tests for code that doesn't use MPs or models not supported by MPs"}
|
||||
toucan2.tools.with-temp/with-temp {:message "Use lib.tu/mock-metadata-provider in QP tests. Exceptions: tests for code that doesn't use MPs or models not supported by MPs"}}
|
||||
|
||||
:metabase/disallow-hardcoded-driver-names-in-tests
|
||||
{:level :warning}}}}}
|
||||
{:metabase/disallow-hardcoded-driver-names-in-tests {:level :warning}}}}}
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
toucan2.core/insert-returning-pk!
|
||||
toucan2.core/insert-returning-pks!
|
||||
clojure.core.async/<!!
|
||||
clojure.core.async/<!
|
||||
clojure.core.async/>!!
|
||||
clojure.core.async/>!
|
||||
clojure.core.async/alts!
|
||||
clojure.core.async/alts!!
|
||||
clojure.core.async/close!
|
||||
clojure.core.async/poll!
|
||||
|
||||
@@ -52,13 +52,7 @@
|
||||
|
||||
(defn- lint-require* [node current-ns config]
|
||||
(let [[_require & args] (:children node)
|
||||
required-namespaces (->> (keep unwrap-require args)
|
||||
;; `require` runs through `cc/load-libs` which allows
|
||||
;; #{:as :reload :reload-all :require :use :verbose :refer :as-alias}
|
||||
;; as args. If you use a REPL tool that applies clj-kondo linting (e.g., Clojure MCP)
|
||||
;; you may wish to `:reload` or `:reload-all` a namespace. So we shouldn't assume all
|
||||
;; args are keywords:
|
||||
(remove keyword?))]
|
||||
required-namespaces (keep unwrap-require args)]
|
||||
(lint* node current-ns config required-namespaces)))
|
||||
|
||||
(defn- lint-requiring-resolve* [node current-ns config]
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
(ns hooks.metabase.driver.mongo-test
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[hooks.common]))
|
||||
|
||||
(defn with-describe-table-for-sample
|
||||
"Forward bindings for results of functions used in mongo's describe-table implementation."
|
||||
[{{[_ _ & body] :children} :node}]
|
||||
(let [node* (hooks/list-node
|
||||
(list*
|
||||
(hooks/token-node 'let)
|
||||
(hooks/vector-node
|
||||
[(hooks/token-node 'dbfields) (hooks/token-node '(delay nil))
|
||||
(hooks/token-node 'ftree) (hooks/token-node '(delay nil))
|
||||
(hooks/token-node 'nested-fields) (hooks/token-node '(delay nil))])
|
||||
body))]
|
||||
;; Avoid warnings when not all from the dbfields, ftree and nested-fields are used.
|
||||
{:node (hooks.common/update-ignored-linters node* conj :unused-binding)}))
|
||||
@@ -139,7 +139,6 @@
|
||||
saml-keystore-password
|
||||
saml-keystore-path
|
||||
saml-user-provisioning-enabled?
|
||||
sdk-iframe-embed-setup-settings
|
||||
send-email-on-first-login-from-new-device
|
||||
send-new-sso-user-admin-email?
|
||||
session-cookie-samesite
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
(ns hooks.metabase.util.timing
|
||||
"Custom CLJ Kondo hooks for timing-related linting rules."
|
||||
(:require [clj-kondo.hooks-api :as api]))
|
||||
|
||||
(defn discourage-millis-duration
|
||||
"Discourage slow and unreliable duration calculations using System/currentTimeMillis "
|
||||
[{:keys [node]}]
|
||||
(let [sexpr (api/sexpr node)]
|
||||
(when (and (list? sexpr) (= (first sexpr) '-) (>= (count sexpr) 2))
|
||||
(let [first-arg (second sexpr)]
|
||||
(when (= first-arg (list 'System/currentTimeMillis))
|
||||
(api/reg-finding!
|
||||
(assoc (meta node)
|
||||
:message "Avoid System/currentTimeMillis for calculating durations. Use u/start-timer and u/since-ms for more reliability."
|
||||
:type :metabase/discourage-millis-duration
|
||||
:level :warning)))))))
|
||||
@@ -120,10 +120,6 @@
|
||||
metabase.collections.models.collection-test/with-personal-and-impersonal-collections [[:block 1]]
|
||||
metabase.config/build-type-case [[:block 0]]
|
||||
metabase.dashboards.models.dashboard-test/with-dash-in-collection [[:inner 0]]
|
||||
metabase.driver-api.core/match [[:inner 0]]
|
||||
metabase.driver-api.core/match-one [[:inner 0]]
|
||||
metabase.driver-api.core/replace [[:inner 0]]
|
||||
metabase.driver-api.core/replace-in [[:inner 0]]
|
||||
metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]]
|
||||
metabase.driver.druid.client/DELETE [[:default]]
|
||||
metabase.driver.druid.client/GET [[:default]]
|
||||
@@ -152,12 +148,9 @@
|
||||
metabase.lib.join/with-join-alias [[:default]]
|
||||
metabase.lib.join/with-join-conditions [[:default]]
|
||||
metabase.lib.test-util.macros/$ids [[:inner 0]]
|
||||
metabase.lib.test-util.macros/mbql-5-query [[:inner 0]]
|
||||
metabase.lib.test-util.macros/mbql-query [[:inner 0]]
|
||||
metabase.lib.util.match/match [[:inner 0]]
|
||||
metabase.lib.util.match/match-one [[:inner 0]]
|
||||
metabase.lib.util.match/match-lite [[:inner 0]]
|
||||
metabase.lib.util.match/match-lite-recursive [[:inner 0]]
|
||||
metabase.lib.util.match/replace [[:inner 0]]
|
||||
metabase.lib.util.match/replace-in [[:inner 0]]
|
||||
metabase.models.interface/define-batched-hydration-method [[:inner 0]]
|
||||
@@ -285,7 +278,6 @@
|
||||
metabase.upload.impl-test/with-upload-table! [[:inner 0]]
|
||||
metabase.upload.types/with-currency [[:default]]
|
||||
metabase.upload.types/with-parens [[:default]]
|
||||
metabase.util/for-map [[:block 1]]
|
||||
metabase.util.async/cancelable-thread [[:block 0]]
|
||||
metabase.util.date-2/with-time-zone-same-instant [[:default]]
|
||||
metabase.util.encryption-test/with-secret-key [[:block 1]]
|
||||
|
||||
26
.eslintrc.js
@@ -1,8 +1,6 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
const path = require("path");
|
||||
|
||||
// `postcss-modules` lints css modules class names, but it currently crashes
|
||||
// eslint on vscode. If you use webstorm or want to run the lint for the cli, you
|
||||
// can use this flag to enable it. This is set to true in CI
|
||||
@@ -14,7 +12,6 @@ if (shouldLintCssModules) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ignorePatterns: ["!.storybook"],
|
||||
rules: {
|
||||
strict: [2, "never"],
|
||||
"no-undef": 2,
|
||||
@@ -49,7 +46,6 @@ module.exports = {
|
||||
],
|
||||
curly: [1, "all"],
|
||||
eqeqeq: [1, "smart"],
|
||||
"import/no-duplicates": ["warn", { considerQueryString: true }],
|
||||
"import/no-default-export": 2,
|
||||
"import/no-named-as-default": 0,
|
||||
"import/no-commonjs": 1,
|
||||
@@ -159,7 +155,6 @@ module.exports = {
|
||||
"import/internal-regex": "^metabase/|^metabase-lib/",
|
||||
"import/resolver": {
|
||||
webpack: {
|
||||
config: path.resolve(__dirname, "./rspack.main.config.js"),
|
||||
typescript: true,
|
||||
},
|
||||
},
|
||||
@@ -180,7 +175,6 @@ module.exports = {
|
||||
{
|
||||
files: ["*.js", "*.jsx", "*.ts", "*.tsx"],
|
||||
rules: {
|
||||
"jtag-missing-key": "error",
|
||||
"no-unconditional-metabase-links-render": "error",
|
||||
"no-color-literals": "error",
|
||||
"no-literal-metabase-strings": "error",
|
||||
@@ -205,15 +199,12 @@ module.exports = {
|
||||
"enterprise/frontend/src/metabase-enterprise/whitelabel/**/*",
|
||||
"frontend/lint/**/*",
|
||||
"*.stories.*",
|
||||
"**/.storybook/*",
|
||||
"stories-data.*",
|
||||
"e2e/**/*",
|
||||
"**/tests/*",
|
||||
"release/**/*",
|
||||
"webpack.config.js",
|
||||
"rspack.config.js",
|
||||
"rspack.main.config.js",
|
||||
"rspack.embedding-sdk-package.config.js",
|
||||
"rspack.embedding-sdk-bundle.config.js",
|
||||
],
|
||||
rules: {
|
||||
"no-color-literals": "off",
|
||||
@@ -230,7 +221,8 @@ module.exports = {
|
||||
"e2e/**/*",
|
||||
"**/tests/*",
|
||||
"release/**/*",
|
||||
"rspack.main.config.js",
|
||||
"webpack.config.js",
|
||||
"rspack.config.js",
|
||||
],
|
||||
rules: {
|
||||
"i18next/no-literal-string": "off",
|
||||
@@ -323,12 +315,6 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["frontend/src/metabase/query_builder/**/*"],
|
||||
rules: {
|
||||
"import/no-cycle": "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["docs/**/snippets/**/*.{ts,tsx,js,jsx}"],
|
||||
rules: {
|
||||
@@ -341,11 +327,5 @@ module.exports = {
|
||||
"no-color-literals": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["frontend/build/**/*.js"],
|
||||
rules: {
|
||||
"import/no-commonjs": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
50
.github/CODEOWNERS
vendored
@@ -1,13 +1,11 @@
|
||||
# Before changing this file, make sure you have read how we use it:
|
||||
# https://www.notion.so/metabase/Pull-Request-Code-Review-Guide-374f05df889748b69d67af753f9bc9b8?pvs=4#36550c04ceab40b5a8b8bee6264b090c
|
||||
|
||||
enterprise/frontend/src/embedding-sdk-package @metabase/embedding
|
||||
enterprise/frontend/src/embedding-sdk-bundle @metabase/embedding
|
||||
enterprise/frontend/src/embedding-sdk-shared @metabase/embedding
|
||||
enterprise/frontend/src/embedding-sdk @metabase/embedding
|
||||
frontend/src/metabase/embedding-sdk @metabase/embedding
|
||||
frontend/src/metabase/public/containers/PublicOrEmbeddedQuestion @metabase/embedding
|
||||
frontend/src/metabase/public/containers/PublicOrEmbeddedDashboard @metabase/embedding
|
||||
frontend/src/metabase/ui @metabase/core-frontend-ux-west
|
||||
frontend/src/metabase/ui @metabase/core-frontend-admin-webapp
|
||||
# snowplow/* @metabase/data
|
||||
src/metabase/analytics/prometheus.clj @metabase/cloud-ops
|
||||
.github @metabase/devexp
|
||||
@@ -22,8 +20,8 @@ docs/developers-guide # exclude from tech-writer ownership
|
||||
#
|
||||
|
||||
# modules/drivers @metabase/drivers
|
||||
src/metabase/driver/sql_jdbc/actions.clj @metabase/core-ux-east
|
||||
test/metabase/driver/sql_jdbc/actions_test.clj @metabase/core-ux-east
|
||||
src/metabase/driver/sql_jdbc/actions.clj @metabase/core-workflows
|
||||
test/metabase/driver/sql_jdbc/actions_test.clj @metabase/core-workflows
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/advanced_config @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/advanced_config @metabase/core-backend-admin-webapp
|
||||
@@ -55,8 +53,8 @@ test/metabase/driver/sql_jdbc/actions_test.clj @metabase/core-ux-east
|
||||
# enterprise/backend/src/metabase_enterprise/core @metabase/devexp
|
||||
# enterprise/backend/test/metabase_enterprise/core @metabase/devexp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/dashboard_subscription_filters @metabase/core-ux-east
|
||||
# enterprise/backend/test/metabase_enterprise/dashboard_subscription_filters @metabase/core-ux-east
|
||||
# enterprise/backend/src/metabase_enterprise/dashboard_subscription_filters @metabase/core-workflows
|
||||
# enterprise/backend/test/metabase_enterprise/dashboard_subscription_filters @metabase/core-workflows
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/enhancements @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/enhancements @metabase/core-backend-admin-webapp
|
||||
@@ -73,11 +71,11 @@ test/metabase/driver/sql_jdbc/actions_test.clj @metabase/core-ux-east
|
||||
# enterprise/backend/src/metabase_enterprise/scim @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/scim @metabase/core-backend-admin-webapp
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/search ...
|
||||
# enterprise/backend/test/metabase_enterprise/search ...
|
||||
# enterprise/backend/src/metabase_enterprise/search @metabase/core-workflows
|
||||
# enterprise/backend/test/metabase_enterprise/search @metabase/core-workflows
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/serialization @metabase/core-ux-east
|
||||
# enterprise/backend/test/metabase_enterprise/serialization @metabase/core-ux-east
|
||||
# enterprise/backend/src/metabase_enterprise/serialization @metabase/core-workflows
|
||||
# enterprise/backend/test/metabase_enterprise/serialization @metabase/core-workflows
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/snippet_collections @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/snippet_collections @metabase/core-backend-admin-webapp
|
||||
@@ -97,8 +95,8 @@ test/metabase/driver/sql_jdbc/actions_test.clj @metabase/core-ux-east
|
||||
# enterprise/backend/src/metabase_enterprise/upload_management @metabase/core-backend-admin-webapp
|
||||
# enterprise/backend/test/metabase_enterprise/upload_management @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/actions @metabase/core-ux-east
|
||||
test/metabase/actions @metabase/core-ux-east
|
||||
src/metabase/actions @metabase/core-backend-admin-webapp
|
||||
test/metabase/actions @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/activity_feed @metabase/core-backend-admin-webapp
|
||||
# test/metabase/activity_feed @metabase/core-backend-admin-webapp
|
||||
@@ -124,11 +122,11 @@ test/metabase/actions @metabase/core-ux-east
|
||||
# src/metabase/cmd @metabase/devexp
|
||||
# test/metabase/cmd @metabase/devexp
|
||||
|
||||
# src/metabase/channel @metabase/core-ux-east
|
||||
# test/metabase/channel @metabase/core-ux-east
|
||||
# src/metabase/channel @metabase/core-workflows
|
||||
# test/metabase/channel @metabase/core-workflows
|
||||
|
||||
# src/metabase/cloud_migration ...
|
||||
# test/metabase/cloud_migration ...
|
||||
# src/metabase/cloud_migration @metabase/core-workflows
|
||||
# test/metabase/cloud_migration @metabase/core-workflows
|
||||
|
||||
# src/metabase/cmd @metabase/devexp
|
||||
# test/metabase/cmd @metabase/devexp
|
||||
@@ -160,8 +158,8 @@ test/metabase/actions @metabase/core-ux-east
|
||||
# src/metabase/indexed_entities @metabase/core-backend-querying
|
||||
# test/metabase/indexed_entities @metabase/core-backend-querying
|
||||
|
||||
# src/metabase/integrations ...
|
||||
# test/metabase/integrations ...
|
||||
# src/metabase/integrations @metabase/core-workflows
|
||||
# test/metabase/integrations @metabase/core-workflows
|
||||
|
||||
# src/metabase/legacy_mbql @metabase/core-backend-querying
|
||||
# test/metabase/legacy_mbql @metabase/core-backend-querying
|
||||
@@ -184,8 +182,8 @@ test/metabase/actions @metabase/core-ux-east
|
||||
# src/metabase/moderation @metabase/core-backend-admin-webapp
|
||||
# test/metabase/moderation @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/notification @metabase/core-ux-east
|
||||
# test/metabase/notification @metabase/core-ux-east
|
||||
# src/metabase/notification @metabase/core-workflows
|
||||
# test/metabase/notification @metabase/core-workflows
|
||||
|
||||
# src/metabase/permissions @metabase/core-backend-admin-webapp
|
||||
# test/metabase/permissions @metabase/core-backend-admin-webapp
|
||||
@@ -202,8 +200,8 @@ test/metabase/actions @metabase/core-ux-east
|
||||
# src/metabase/public_sharing @metabase/core-backend-admin-webapp
|
||||
# test/metabase/public_sharing @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/pulse @metabase/core-ux-east
|
||||
# test/metabase/pulse @metabase/core-ux-east
|
||||
# src/metabase/pulse @metabase/core-workflows
|
||||
# test/metabase/pulse @metabase/core-workflows
|
||||
|
||||
# src/metabase/query_analysis @metabase/core-backend-querying
|
||||
# test/metabase/query_analysis @metabase/core-backend-querying
|
||||
@@ -220,8 +218,8 @@ test/metabase/actions @metabase/core-ux-east
|
||||
# src/metabase/sample_data @metabase/core-backend-admin-webapp
|
||||
# test/metabase/sample_data @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/search ...
|
||||
# test/metabase/search ...
|
||||
# src/metabase/search @metabase/core-workflows
|
||||
# test/metabase/search @metabase/core-workflows
|
||||
|
||||
# src/metabase/segments @metabase/core-backend-querying
|
||||
# test/metabase/segments @metabase/core-backend-querying
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -63,8 +63,7 @@ body:
|
||||
attributes:
|
||||
label: Information about your Metabase installation
|
||||
description: |
|
||||
For versions >= 56, copy and paste the Diagnostic info from `Admin -> Tools -> Help`.
|
||||
For versions < 56, copy and paste the Diagnostic info from `Admin -> Troubleshooting`.
|
||||
Copy and paste the JSON from `Admin -> Troubleshooting`.
|
||||
_This will be automatically formatted into code, so no need for backticks._
|
||||
placeholder: |
|
||||
{"browser-info": {
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -3,7 +3,7 @@
|
||||
|
||||
> **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). Also, if you're attempting to fix a translation issue, please submit your changes to our [Crowdin project](https://crowdin.com/project/metabase-i18n) instead of opening a PR.
|
||||
> 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 [Crowdin project](https://crowdin.com/project/metabase-i18n) instead of opening a PR.
|
||||
|
||||
Closes https://github.com/metabase/metabase/issues/[issue_number]
|
||||
|
||||
|
||||
3
.github/actions/await-port/action.yml
vendored
@@ -1,5 +1,4 @@
|
||||
name: await-port
|
||||
description: Wait for localhost port to be ready
|
||||
name: Wait for localhost port to be ready
|
||||
|
||||
inputs:
|
||||
port:
|
||||
|
||||
2
.github/actions/create-backport/action.yml
vendored
@@ -74,7 +74,7 @@ runs:
|
||||
|
||||
git push -u origin ${BACKPORT_BRANCH}
|
||||
|
||||
BACKPORT_PR_URL=$(gh pr create --base "${TARGET_BRANCH}" --head "${BACKPORT_BRANCH}" --label "was-backported" --assignee "${{ github.event.pull_request.user.login }}" --title "🤖 backported \"${ORIGINAL_TITLE}\"" --body "${PR_BODY}")
|
||||
BACKPORT_PR_URL=$(gh pr create --base "${TARGET_BRANCH}" --head "${BACKPORT_BRANCH}" --label "was-backported" --assignee "${GITHUB_ACTOR}" --title "🤖 backported \"${ORIGINAL_TITLE}\"" --body "${PR_BODY}")
|
||||
BACKPORT_PR_NUMBER=${BACKPORT_PR_URL##*/}
|
||||
|
||||
echo "backport_pr_number=$BACKPORT_PR_NUMBER" >> $GITHUB_OUTPUT
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
name: Cross-branch migration test
|
||||
|
||||
inputs:
|
||||
base-ref: ## Base branch PR came from
|
||||
required: true
|
||||
head-ref: ## Source branch of the pull request
|
||||
base-ref:
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
## First we deploy the base branch
|
||||
- name: Check out base branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -42,11 +39,10 @@ runs:
|
||||
run: kill $(cat metabase_pid)
|
||||
shell: bash
|
||||
|
||||
## Then we deploy the PR branch to ensure it migrates up successfully
|
||||
- name: Check out pull request branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.head-ref }}
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Run backend on pull request branch
|
||||
run: |
|
||||
@@ -69,38 +65,3 @@ runs:
|
||||
- name: Stop Metabase on pull request branch
|
||||
run: kill $(cat metabase_pid)
|
||||
shell: bash
|
||||
|
||||
## Testing that originally deploying this PR allows you to roll back to a previous release
|
||||
## Reset the checksums so when we go back to the base branch, it's as if the PR branch was what was originally deployed
|
||||
- name: Reset the checksums as if the PR branch was what was originally deployed
|
||||
run: |
|
||||
clojure -M:dev:ee:migrate reset-checksums
|
||||
shell: bash
|
||||
|
||||
- name: Rollback the last deployment
|
||||
run: |
|
||||
clojure -M:dev:ee:migrate rollback last-deployment
|
||||
shell: bash
|
||||
|
||||
- name: Re-check out base branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.base-ref }}
|
||||
|
||||
- name: Re-run backend on base branch
|
||||
run: |
|
||||
clojure -M:run:ee > metabase.log 2>&1 & echo $! > metabase_pid
|
||||
tail -F metabase.log &
|
||||
shell: bash
|
||||
|
||||
- name: Wait for Metabase to start
|
||||
run: |
|
||||
while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}' > /dev/null; do
|
||||
if ! kill -0 $(cat metabase_pid) 2>/dev/null; then
|
||||
cat metabase.log
|
||||
echo "Metabase process has exited."
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
shell: bash
|
||||
|
||||
@@ -8,7 +8,7 @@ inputs:
|
||||
description: DockerHub password/token
|
||||
required: true
|
||||
maildev:
|
||||
description: Maildev containers
|
||||
description: Maildev container
|
||||
required: true
|
||||
default: false
|
||||
openldap:
|
||||
@@ -31,10 +31,6 @@ inputs:
|
||||
description: Webhook tester container
|
||||
required: false
|
||||
default: false
|
||||
snowplow:
|
||||
description: Snowplow micro container
|
||||
required: false
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -46,68 +42,56 @@ runs:
|
||||
password: ${{ inputs.password }}
|
||||
|
||||
- name: Start Containers
|
||||
shell: bash
|
||||
run: | # bash
|
||||
run: |
|
||||
Y='\033[33m'
|
||||
G='\033[1;32m'
|
||||
|
||||
cd e2e/test/scenarios/
|
||||
|
||||
docker compose up \
|
||||
$([ ${{ inputs.postgres }} = true ] && echo "postgres-sample") \
|
||||
$([ ${{ inputs.mysql }} = true ] && echo "mysql-sample") \
|
||||
$([ ${{ inputs.mongo }} = true ] && echo "mongo-sample") \
|
||||
$([ ${{ inputs.maildev }} = true ] && echo "maildev") \
|
||||
$([ ${{ inputs.maildev }} = true ] && echo "maildev-ssl") \
|
||||
$([ ${{ inputs.openldap }} = true ] && echo "ldap") \
|
||||
$([ ${{ inputs.webhook }} = true ] && echo "webhook-tester") \
|
||||
$([ ${{ inputs.snowplow }} = true ] && echo "snowplow-micro") \
|
||||
-d
|
||||
|
||||
if ${{ inputs.maildev }}; then
|
||||
echo -e "${Y}checking maildev container..." &&
|
||||
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!"
|
||||
|
||||
echo -e "${Y}checking maildev-ssl container..." &&
|
||||
while ! nc -z localhost 1081; do sleep 1; done &&
|
||||
while ! nc -z localhost 465; do sleep 1; done &&
|
||||
echo -e "${G}Maildev-ssl is up and running!"
|
||||
fi
|
||||
|
||||
if ${{ inputs.openldap }}; then
|
||||
echo -e "${Y}checking openldap container..." &&
|
||||
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}checking postgres container..." &&
|
||||
echo -e "${Y}Starting postgres container..." &&
|
||||
docker run -d -p 5404:5432 -e TZ='US/Pacific' 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}checking mysql container..." &&
|
||||
echo -e "${Y}Starting mysql container..." &&
|
||||
docker run -d -p 3304:3306 -e TZ='US/Pacific' 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}checking mongo container..." &&
|
||||
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
|
||||
|
||||
if ${{ inputs.webhook }}; then
|
||||
echo -e "${Y}checking webhook test container..." &&
|
||||
echo -e "${Y}Starting webhook test container..." &&
|
||||
docker run -d -p 9080:8080/tcp tarampampam/webhook-tester:1.1.0 serve --create-session 00000000-0000-0000-0000-000000000000 &&
|
||||
while ! nc -z localhost 9080; do sleep 1; done &&
|
||||
echo -e "${G}Webhook tester is up and running!"
|
||||
fi
|
||||
|
||||
if ${{ inputs.snowplow }}; then
|
||||
echo -e "${Y}checking snowplow micro container..." &&
|
||||
while ! nc -z localhost 9090; do sleep 1; done &&
|
||||
echo -e "${G}Snowplow micro is up and running!"
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
68
.github/actions/fetch-artifact/action.yml
vendored
@@ -5,14 +5,6 @@ inputs:
|
||||
name:
|
||||
description: The name of the artifact to fetch
|
||||
required: true
|
||||
output-name:
|
||||
description: Optional custom name for the output jar file (defaults to metabase.jar)
|
||||
required: false
|
||||
default: "metabase.jar"
|
||||
extract-path:
|
||||
description: Directory path where the jar should be extracted (defaults to current directory)
|
||||
required: false
|
||||
default: "."
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -33,7 +25,7 @@ runs:
|
||||
});
|
||||
|
||||
if (!artifacts.data?.artifacts?.[0]?.id) {
|
||||
throw new Error(`No artifacts found for ${{ inputs.name }}`);
|
||||
throw new Error(`No artifacts found for ${{ inputs.commit }}`);
|
||||
}
|
||||
|
||||
const artifact_id = artifacts.data.artifacts[0].id;
|
||||
@@ -47,59 +39,25 @@ runs:
|
||||
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
|
||||
|
||||
- name: Extract and rename JAR
|
||||
- name: unzip uberjar artifact
|
||||
shell: bash
|
||||
run: unzip mb.zip
|
||||
- name: Move the Uberjar
|
||||
shell: bash
|
||||
run: | # bash
|
||||
OUTPUT_NAME="${{ inputs.output-name }}"
|
||||
EXTRACT_PATH="${{ inputs.extract-path }}"
|
||||
|
||||
# Create a temporary directory for extraction to avoid conflicts
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
|
||||
# Extract to temporary directory
|
||||
unzip mb.zip -d "$TEMP_DIR"
|
||||
|
||||
# Create target directory
|
||||
mkdir -p "$EXTRACT_PATH"
|
||||
|
||||
# Find the jar and move all files from its directory
|
||||
if [ -e "$TEMP_DIR/metabase.jar" ]; then
|
||||
# Move all files to extract path
|
||||
mv "$TEMP_DIR"/* "$EXTRACT_PATH/" 2>/dev/null || true
|
||||
# Rename the jar to custom name if different
|
||||
if [ "$OUTPUT_NAME" != "metabase.jar" ]; then
|
||||
mv "$EXTRACT_PATH/metabase.jar" "$EXTRACT_PATH/$OUTPUT_NAME"
|
||||
fi
|
||||
elif [ -e "$TEMP_DIR/target/uberjar/metabase.jar" ]; then
|
||||
# Move all files from uberjar directory to extract path
|
||||
mv "$TEMP_DIR/target/uberjar"/* "$EXTRACT_PATH/" 2>/dev/null || true
|
||||
# Rename the jar to custom name if different
|
||||
if [ "$OUTPUT_NAME" != "metabase.jar" ]; then
|
||||
mv "$EXTRACT_PATH/metabase.jar" "$EXTRACT_PATH/$OUTPUT_NAME"
|
||||
fi
|
||||
if [ -e "metabase.jar" ]; then
|
||||
echo "metabase.jar is already in the right place"
|
||||
elif [ -e "target/uberjar/metabase.jar" ]; then
|
||||
echo "moving metabase.jar to current directory"
|
||||
mv target/uberjar/metabase.jar metabase.jar
|
||||
else
|
||||
echo "Could not find metabase.jar in extracted files"
|
||||
echo "Could not find the metabase.jar file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up temporary directory
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
# Set the JAR_PATH environment variable
|
||||
if [ "$EXTRACT_PATH" == "." ]; then
|
||||
echo "JAR_PATH=$OUTPUT_NAME" >> $GITHUB_ENV
|
||||
else
|
||||
echo "JAR_PATH=$EXTRACT_PATH/$OUTPUT_NAME" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Verify that this is a valid JAR file
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Verifying JAR at: $JAR_PATH"
|
||||
file --mime-type "$JAR_PATH"
|
||||
|
||||
run: file --mime-type ./metabase.jar
|
||||
- name: Reveal its version.properties
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Extracting version.properties from: $JAR_PATH"
|
||||
jar xf "$JAR_PATH" version.properties && cat version.properties
|
||||
run: jar xf metabase.jar version.properties && cat version.properties
|
||||
|
||||
74
.github/actions/get-docker-info/action.yml
vendored
@@ -1,74 +0,0 @@
|
||||
name: Get Docker Info
|
||||
description: Determines Docker repository and tag based on branch and edition
|
||||
|
||||
inputs:
|
||||
branch:
|
||||
description: Branch name (defaults to current branch)
|
||||
required: false
|
||||
default: ${{ github.head_ref || github.ref_name }}
|
||||
edition:
|
||||
description: Edition (ee or oss)
|
||||
required: false
|
||||
default: ee
|
||||
owner:
|
||||
description: Repository owner (defaults to current repo owner)
|
||||
required: false
|
||||
default: ${{ github.repository_owner }}
|
||||
|
||||
outputs:
|
||||
repo:
|
||||
description: Docker repository name
|
||||
value: ${{ steps.docker-info.outputs.repo }}
|
||||
tag:
|
||||
description: Docker tag
|
||||
value: ${{ steps.docker-info.outputs.tag }}
|
||||
full_image:
|
||||
description: Full Docker image name (repo:tag)
|
||||
value: ${{ steps.docker-info.outputs.full_image }}
|
||||
editions:
|
||||
description: JSON array of editions to build
|
||||
value: ${{ steps.docker-info.outputs.editions }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Determine Docker info
|
||||
id: docker-info
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | # js
|
||||
const branch = '${{ inputs.branch }}';
|
||||
const edition = '${{ inputs.edition }}';
|
||||
const owner = '${{ inputs.owner }}';
|
||||
|
||||
console.log(`Determining Docker info for branch: ${branch}, edition: ${edition}`);
|
||||
|
||||
let repo, tag, editions;
|
||||
|
||||
if (branch === 'master') {
|
||||
// Master builds both editions with specific repos
|
||||
editions = ['ee', 'oss'];
|
||||
tag = 'latest';
|
||||
|
||||
if (edition === 'ee') {
|
||||
repo = `${owner}/metabase-enterprise-head`;
|
||||
} else {
|
||||
repo = `${owner}/metabase-head`;
|
||||
}
|
||||
} else {
|
||||
// All other branches only build EE to metabase-dev
|
||||
editions = ['ee'];
|
||||
tag = branch.replaceAll('/', '-'); // slashes make docker sad
|
||||
repo = `${owner}/metabase-dev`;
|
||||
}
|
||||
|
||||
const fullImage = `${repo}:${tag}`;
|
||||
|
||||
// Set outputs
|
||||
core.setOutput('repo', repo);
|
||||
core.setOutput('tag', tag);
|
||||
core.setOutput('full_image', fullImage);
|
||||
core.setOutput('editions', JSON.stringify(editions));
|
||||
|
||||
console.log(`Docker image: ${fullImage}`);
|
||||
console.log(`Editions: ${JSON.stringify(editions)}`);
|
||||
8
.github/actions/prepare-backend/action.yml
vendored
@@ -1,7 +1,4 @@
|
||||
name: Prepare back-end environment
|
||||
description: |
|
||||
Prepare the backend. Installs jdk, Clojure, and fetches the cache for m2. The CI config fetchs `ci-test-config.json`
|
||||
from master and lets us ignore tests at the var level if they are flaking.
|
||||
inputs:
|
||||
java-version:
|
||||
required: true
|
||||
@@ -24,14 +21,9 @@ runs:
|
||||
distribution: 'temurin'
|
||||
- name: Install Clojure CLI
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
curl -O https://download.clojure.org/install/linux-install-${{ inputs.clojure-version }}.sh &&
|
||||
sudo bash ./linux-install-${{ inputs.clojure-version }}.sh
|
||||
- name: Fetch CI config
|
||||
shell: bash
|
||||
run: |
|
||||
curl -o ci-test-config.json https://raw.githubusercontent.com/metabase/metabase/refs/heads/master/ci-test-config.json
|
||||
- name: 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"
|
||||
|
||||
13
.github/actions/run-snowplow-micro/action.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Run Snowplow micro
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Download Snowplow micro JAR
|
||||
run: curl -L -o snowplow-micro.jar https://github.com/snowplow-incubator/snowplow-micro/releases/download/micro-1.2.1/snowplow-micro-1.2.1.jar
|
||||
shell: bash
|
||||
- name: Launch Snowplow micro
|
||||
run: java -cp snowplow-micro.jar:snowplow com.snowplowanalytics.snowplow.micro.Main --collector-config snowplow/micro.conf --iglu snowplow/iglu.json &
|
||||
shell: bash
|
||||
- name: Wait until Snowplow micro is ready
|
||||
run: while ! curl -s 'http://localhost:9090' | grep '404'; do sleep 1; done
|
||||
shell: bash
|
||||
3
.github/actions/test-driver/action.yml
vendored
@@ -1,5 +1,4 @@
|
||||
name: Test database driver
|
||||
description: Test database driver
|
||||
inputs:
|
||||
junit-name:
|
||||
required: true
|
||||
@@ -40,7 +39,7 @@ runs:
|
||||
list-tests: failed
|
||||
fail-on-error: false
|
||||
- name: Upload Logs on Failure
|
||||
if: always() && !success()
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-logs-${{ github.job }}
|
||||
|
||||
@@ -11,7 +11,4 @@ runs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.github-token }}
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global user.email "github-automation@metabase.com"
|
||||
git config --global user.name "Metabase Automation"
|
||||
bash ${{ github.action_path }}/upgrade-deps.sh
|
||||
run: bash ${{ github.action_path }}/upgrade-deps.sh
|
||||
|
||||
@@ -59,11 +59,6 @@ for upgrade in $UPGRADES; do
|
||||
|
||||
# Commit the dependency update, and link to the diff
|
||||
git add deps.edn modules
|
||||
|
||||
# Use an email and name that will pass the CLA check:
|
||||
git config --global user.email "github-automation@metabase.com"
|
||||
git config --global user.name "Metabase Automation"
|
||||
|
||||
git commit -m ${TITLE} -m ${BODY} --no-verify
|
||||
git push -u origin $BRANCH_NAME
|
||||
|
||||
|
||||
17
.github/auto_request_review.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# The reviewers configuration for the auto_request_review workflow
|
||||
# See https://github.com/necojackarc/auto-request-review/tree/master?tab=readme-ov-file#reviewers-configuration
|
||||
# for the spec
|
||||
|
||||
files:
|
||||
# migrations related files
|
||||
'resources/migrations/**':
|
||||
- team:core-workflows
|
||||
'src/metabase/app_db/custom_migrations.clj':
|
||||
- team:core-workflows
|
||||
|
||||
options:
|
||||
ignore_draft: true
|
||||
ignored_keywords:
|
||||
- DO NOT REVIEW
|
||||
- WIP
|
||||
enable_group_assignment: false
|
||||
52
.github/copilot-instructions.md
vendored
@@ -1,52 +0,0 @@
|
||||
# Metabase Developer Assistant Instructions
|
||||
|
||||
## Frontend Development
|
||||
|
||||
### Repository Structure
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── metabase/ # Main application components and pages
|
||||
├── metabase-lib/ # Query building and data modeling utilities
|
||||
├── metabase-types/ # TypeScript type definitions
|
||||
└── metabase/ui/ # Design system components
|
||||
|
||||
e2e/ # Cypress end-to-end tests
|
||||
frontend/test/ # Jest unit tests
|
||||
```
|
||||
|
||||
### Technology Stack
|
||||
|
||||
- **Framework**: React 18 with TypeScript
|
||||
- **State**: Redux Toolkit
|
||||
- **Styling**: CSS Modules (preferred) > Emotion styled-components
|
||||
- **UI**: `metabase/ui` components built with Mantine v8
|
||||
- **Build**: Rspack (primary), Webpack (legacy)
|
||||
- **Testing**: Jest + React Testing Library, Cypress
|
||||
|
||||
### Coding Standards
|
||||
|
||||
#### Component Preferences
|
||||
|
||||
- Prefer `metabase/ui`components over `metabase/common/components`
|
||||
- Use `.tsx` for components, `.ts` for utilities
|
||||
|
||||
#### Styling
|
||||
|
||||
ALWAYS prefer Mantine style props,then CSS modules. DO NOT suggest styled components, they are deprecated.
|
||||
|
||||
#### TypeScript Migration
|
||||
|
||||
When heavily editing `.js`/`.jsx` files, create a separate PR to convert to TypeScript first, then implement changes.
|
||||
|
||||
#### Enterprise Features
|
||||
|
||||
Enterprise functionality MUST use the plugin system. It is very important to not expose enterprise code in the OSS version.
|
||||
|
||||
#### Testing Requirements
|
||||
|
||||
All PRs should include tests. Prefer Unit tests over E2E tests.
|
||||
|
||||
#### Localization
|
||||
|
||||
All user-facing strings MUST be localized using the ttag library. Localized strings should be complete phrases, do not concatenate a few separately localized strings. You should add context to strings where the meaning of the string might not be obvious in isolation: e.g. "Home" might have different words in some languages depending on whether you're talking about a dwelling or the landing page for a website.
|
||||
62
.github/file-paths.yaml
vendored
@@ -16,12 +16,10 @@ frontend_ci: &frontend_ci
|
||||
- ".github/actions/prepare-backend/**"
|
||||
- ".github/workflows/frontend.yml"
|
||||
|
||||
frontend_embedding_sdk_ci: &frontend_embedding_sdk_ci
|
||||
- ".github/actions/prepare-frontend/**"
|
||||
- ".github/actions/prepare-backend/**"
|
||||
- ".github/workflows/*embedding-sdk*.yml"
|
||||
|
||||
frontend_configs: &frontend_configs
|
||||
frontend_sources: &frontend_sources
|
||||
- *shared_sources
|
||||
- "enterprise/frontend/!(test)**/**/{!(*.spec),}.{js,jsx,ts,tsx,css}"
|
||||
- "frontend/!(test)**/**/{!(*.spec),}.{js,jsx,ts,tsx,css}"
|
||||
- "yarn.lock"
|
||||
- "**/tsconfig*.json"
|
||||
- "package.json"
|
||||
@@ -29,28 +27,13 @@ frontend_configs: &frontend_configs
|
||||
- ".eslintrc.js"
|
||||
- "postcss.config.js"
|
||||
- "rspack.config.js"
|
||||
- "rspack.main.config.js"
|
||||
- "rspack.embedding-sdk-package.config.js"
|
||||
- "rspack.embedding-sdk-bundle.config.js"
|
||||
- "rspack.static-viz.config.js"
|
||||
- "webpack.config.js"
|
||||
- "webpack.embedding-sdk.config.js"
|
||||
- "webpack.static-viz.config.js"
|
||||
- ".nvmrc"
|
||||
|
||||
frontend_sources: &frontend_sources
|
||||
- *shared_sources
|
||||
- *frontend_configs
|
||||
- "enterprise/frontend/!(test)**/**/{!(*.spec),}.{js,jsx,ts,tsx,css}"
|
||||
- "frontend/!(test)**/**/{!(*.spec),}.{js,jsx,ts,tsx,css}"
|
||||
|
||||
frontend_embedding_sources:
|
||||
- "enterprise/frontend/src/{embedding,embedding-*}/**/{!(*.spec),}.{js,jsx,ts,tsx,css}"
|
||||
- "enterprise/frontend/src/metabase-enterprise/{embedding,embedding-*}**/{!(*.spec),}.{js,jsx,ts,tsx,css}"
|
||||
|
||||
frontend_embedding_sdk_sources:
|
||||
- *frontend_configs
|
||||
- "enterprise/frontend/!(test)**/**/{!(*.spec),}.{js,jsx,ts,tsx,css}"
|
||||
|
||||
frontend_embedding_sdk_package_sources:
|
||||
- "enterprise/frontend/src/embedding-sdk-package/**/!(*.spec).{js,jsx,ts,tsx,css}"
|
||||
- *frontend_sources
|
||||
|
||||
frontend_specs: &frontend_specs
|
||||
- *shared_specs
|
||||
@@ -122,32 +105,25 @@ e2e_ci: &e2e_ci
|
||||
- ".github/actions/prepare-uberjar-artifact/**"
|
||||
- ".github/actions/e2e-prepare-containers/**"
|
||||
- ".github/actions/prepare-cypress/**"
|
||||
- ".github/actions/run-snowplow-micro/**"
|
||||
- ".github/workflows/e2e-*"
|
||||
|
||||
e2e_specs: &e2e_specs
|
||||
- e2e/!(test-host-app)**/*.cy.*.{js,ts}
|
||||
- "**/*.cy.*.js"
|
||||
- "**/*.cy.*.ts"
|
||||
|
||||
e2e_cross_version: &e2e_cross_version
|
||||
- ".github/workflows/e2e-cross-version.yml"
|
||||
- "e2e/test/scenarios/cross-version/**"
|
||||
|
||||
e2e_embedding_sdk:
|
||||
- *frontend_embedding_sdk_ci
|
||||
- *sources
|
||||
- "e2e/component/**/*.cy.*.{js,ts}"
|
||||
|
||||
e2e_embedding_sdk_compatibility:
|
||||
- *default
|
||||
- *frontend_embedding_sdk_ci
|
||||
- *e2e_ci
|
||||
- *frontend_sources
|
||||
- *backend_sources
|
||||
- "e2e/embedding-sdk-host-apps/**"
|
||||
- "e2e/runner/embedding-sdk/**"
|
||||
- "e2e/test-host-app/**/*.cy.*.{js,ts}"
|
||||
|
||||
snowplow: &snowplow
|
||||
- ".github/workflows/snowplow.yml"
|
||||
- "snowplow/**"
|
||||
- "e2e/runner/test-host-app/**/*.cy.*.js"
|
||||
- "e2e/runner/test-host-app/**/*.cy.*.ts"
|
||||
|
||||
e2e_all:
|
||||
- *default
|
||||
@@ -155,16 +131,16 @@ e2e_all:
|
||||
- *sources
|
||||
- *e2e_specs
|
||||
- "e2e/**"
|
||||
- *snowplow
|
||||
|
||||
snowplow:
|
||||
- ".github/workflows/snowplow.yml"
|
||||
- "snowplow/**"
|
||||
|
||||
documentation:
|
||||
- ".typedoc/**"
|
||||
- "docs/**"
|
||||
- "**/*.md"
|
||||
|
||||
embedding_documentation:
|
||||
- ".typedoc/**"
|
||||
- "docs/embedding/**"
|
||||
|
||||
yaml:
|
||||
- "**/*.yml"
|
||||
- "**/*.yaml"
|
||||
|
||||
172
.github/scripts/average-e2e-timings.js
vendored
@@ -1,172 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { execSync } = require("child_process");
|
||||
|
||||
const collectAndAverageTimings = async ({
|
||||
github,
|
||||
context,
|
||||
daysBack = 7,
|
||||
existingTimingsPath = "e2e/support/timings.json",
|
||||
}) => {
|
||||
try {
|
||||
const artifacts = await findMergedTimingArtifacts({
|
||||
github,
|
||||
context,
|
||||
daysBack,
|
||||
});
|
||||
|
||||
if (artifacts.length === 0) {
|
||||
throw new Error("No timing artifacts found in the specified time range");
|
||||
}
|
||||
|
||||
const timingData = await downloadAndExtractArtifacts(
|
||||
artifacts,
|
||||
token,
|
||||
github,
|
||||
context,
|
||||
);
|
||||
|
||||
if (timingData.length === 0) {
|
||||
throw new Error("No valid timing data extracted from artifacts");
|
||||
}
|
||||
|
||||
const averagedTimings = averageTimings(timingData);
|
||||
averagedTimings.durations.sort((a, b) => a.spec.localeCompare(b.spec));
|
||||
|
||||
fs.writeFileSync(
|
||||
existingTimingsPath,
|
||||
JSON.stringify(averagedTimings, null, 2),
|
||||
);
|
||||
console.log(`✅ Updated ${existingTimingsPath} with new timing data`);
|
||||
|
||||
return { artifactCount: timingData.length };
|
||||
} catch (error) {
|
||||
console.error("❌ Error in collectAndAverageTimings:", error.message);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const findMergedTimingArtifacts = async ({ github, context, daysBack }) => {
|
||||
const cutoffDate = new Date();
|
||||
cutoffDate.setDate(cutoffDate.getDate() - daysBack);
|
||||
|
||||
console.log(
|
||||
`Searching for merged timing artifacts created after ${cutoffDate.toISOString()}`,
|
||||
);
|
||||
|
||||
const artifacts = [];
|
||||
let page = 1;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const { data } = await github.rest.actions.listArtifactsForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: "merged-e2e-timings",
|
||||
per_page: 100,
|
||||
page,
|
||||
});
|
||||
|
||||
const validArtifacts = data.artifacts
|
||||
.filter((a) => new Date(a.created_at) > cutoffDate && !a.expired)
|
||||
.map(({ name, created_at, id }) => ({ name, created_at, id }));
|
||||
artifacts.push(...validArtifacts);
|
||||
|
||||
if (
|
||||
data.artifacts.some((a) => new Date(a.created_at) <= cutoffDate) ||
|
||||
data.artifacts.length < 100
|
||||
) {
|
||||
break;
|
||||
}
|
||||
page++;
|
||||
} catch (error) {
|
||||
console.log(
|
||||
`Error fetching artifacts page ${page - 1}: ${error.message}`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Found ${artifacts.length} merged timing artifacts total`);
|
||||
return artifacts;
|
||||
};
|
||||
|
||||
const downloadAndExtractArtifacts = async (artifacts, github, context) => {
|
||||
const tempDir = "timing-artifacts-temp";
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
|
||||
const timingData = [];
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
try {
|
||||
console.log(`Downloading artifact: ${artifact.name}`);
|
||||
|
||||
const zipPath = path.join(tempDir, `${artifact.name}.zip`);
|
||||
const extractPath = path.join(tempDir, artifact.name);
|
||||
|
||||
const { data } = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: artifact.id,
|
||||
archive_format: "zip",
|
||||
});
|
||||
|
||||
fs.writeFileSync(zipPath, Buffer.from(data));
|
||||
fs.mkdirSync(extractPath, { recursive: true });
|
||||
execSync(`unzip -q "${zipPath}" -d "${extractPath}"`);
|
||||
|
||||
const timingFilePath = path.join(extractPath, "runTimings.json");
|
||||
|
||||
if (fs.existsSync(timingFilePath)) {
|
||||
const content = JSON.parse(fs.readFileSync(timingFilePath, "utf8"));
|
||||
|
||||
if (content.durations?.length) {
|
||||
timingData.push(content);
|
||||
console.log(
|
||||
`✅ Extracted timing data from ${artifact.name} (${content.durations.length} specs)`,
|
||||
);
|
||||
} else {
|
||||
console.log(`⚠ Invalid timing data structure in ${artifact.name}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`⚠ No timing data found in ${artifact.name}`);
|
||||
}
|
||||
|
||||
fs.rmSync(extractPath, { recursive: true });
|
||||
} catch (error) {
|
||||
console.log(`❌ Error processing ${artifact.name}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
|
||||
console.log(
|
||||
`Successfully extracted timing data from ${timingData.length}/${artifacts.length} artifacts`,
|
||||
);
|
||||
return timingData;
|
||||
};
|
||||
|
||||
const averageTimings = (timingDataArray) => {
|
||||
if (!timingDataArray.length) {
|
||||
throw new Error("No timing data to process");
|
||||
}
|
||||
|
||||
const specTimings = timingDataArray
|
||||
.flatMap((data) => data.durations)
|
||||
.filter((item) => item.spec && typeof item.duration === "number")
|
||||
.reduce((acc, { spec, duration }) => {
|
||||
(acc[spec] = acc[spec] || []).push(duration);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const durations = Object.entries(specTimings).map(([spec, durations]) => ({
|
||||
spec,
|
||||
duration: Math.round(
|
||||
durations.reduce((sum, d) => sum + d, 0) / durations.length,
|
||||
),
|
||||
}));
|
||||
|
||||
return { durations };
|
||||
};
|
||||
|
||||
module.exports = { collectAndAverageTimings, averageTimings };
|
||||
132
.github/scripts/average-e2e-timings.unit.spec.js
vendored
@@ -1,132 +0,0 @@
|
||||
const { averageTimings } = require("./average-e2e-timings");
|
||||
|
||||
describe("averageTimings", () => {
|
||||
it("should throw error when given empty array", () => {
|
||||
expect(() => averageTimings([])).toThrow("No timing data to process");
|
||||
});
|
||||
|
||||
it("should work for n=1", () => {
|
||||
const input = [
|
||||
{
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = averageTimings(input);
|
||||
|
||||
expect(result.durations).toHaveLength(2);
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test1.cy.js",
|
||||
duration: 1000,
|
||||
});
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test2.cy.js",
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
|
||||
it("should calculate average for multiple specs", () => {
|
||||
const input = [
|
||||
{
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
},
|
||||
{
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 2000 },
|
||||
{ spec: "test2.cy.js", duration: 4000 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = averageTimings(input);
|
||||
|
||||
expect(result.durations).toHaveLength(2);
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test1.cy.js",
|
||||
duration: 1500,
|
||||
});
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test2.cy.js",
|
||||
duration: 3000,
|
||||
});
|
||||
});
|
||||
|
||||
it("should round averages to nearest integer", () => {
|
||||
const input = [
|
||||
{
|
||||
durations: [{ spec: "test1.cy.js", duration: 1001 }],
|
||||
},
|
||||
{
|
||||
durations: [{ spec: "test1.cy.js", duration: 1002 }],
|
||||
},
|
||||
];
|
||||
|
||||
const result = averageTimings(input);
|
||||
|
||||
expect(result.durations[0].duration).toBe(1002);
|
||||
});
|
||||
|
||||
it("should ignore invalid entries", () => {
|
||||
const input = [
|
||||
{
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: "invalid" },
|
||||
{ duration: 2000 }, // missing spec
|
||||
{ spec: "test3.cy.js", duration: 3000 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = averageTimings(input);
|
||||
|
||||
expect(result.durations).toHaveLength(2);
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test1.cy.js",
|
||||
duration: 1000,
|
||||
});
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test3.cy.js",
|
||||
duration: 3000,
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle merging different specs", () => {
|
||||
const input = [
|
||||
{
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
},
|
||||
{
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 3000 },
|
||||
{ spec: "test3.cy.js", duration: 1000 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = averageTimings(input);
|
||||
|
||||
expect(result.durations).toHaveLength(3);
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test1.cy.js",
|
||||
duration: 2000,
|
||||
});
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test2.cy.js",
|
||||
duration: 2000,
|
||||
});
|
||||
expect(result.durations).toContainEqual({
|
||||
spec: "test3.cy.js",
|
||||
duration: 1000,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,66 +1,24 @@
|
||||
const TYPES = new Set([
|
||||
"feat",
|
||||
"fix",
|
||||
"perf",
|
||||
"docs",
|
||||
"style",
|
||||
"refactor",
|
||||
"test",
|
||||
"build",
|
||||
"ci",
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param title {string}
|
||||
*/
|
||||
function isConventionalTitle(title) {
|
||||
const match = /^(?<type>\w+)\((?<rawScopes>[^)]+)\):/.exec(title);
|
||||
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [, type, rawScopes] = match;
|
||||
const scopes = rawScopes
|
||||
.split(",")
|
||||
.map((scope) => scope.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
return TYPES.has(type) && scopes.length > 0;
|
||||
}
|
||||
exports.updateComment = updateComment;
|
||||
|
||||
/** @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
|
||||
async function updateCommentForEmbedding({ github, context }) {
|
||||
async function updateComment({ github, context }) {
|
||||
const githubUsername = context.payload.sender.login;
|
||||
|
||||
const comment = `${CONVENTIONAL_COMMENT_IDENTIFIER}
|
||||
@${githubUsername} You have modified embedding code. Please make sure the PR title follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) style.
|
||||
Here are all the supported types:
|
||||
const comment = `${CONVENTIONAL_COMMIT_REMINDER_COMMENT_IDENTIFIER}
|
||||
@${githubUsername} You have modified embedding SDK code. Please make sure the PR title follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) style.
|
||||
Here are all the supported types that will be shown in the changelog:
|
||||
- \`feat\`
|
||||
- \`fix\`
|
||||
- \`perf\`
|
||||
- \`docs\`
|
||||
- \`style\`
|
||||
- \`refactor\`
|
||||
- \`test\`
|
||||
- \`build\`
|
||||
- \`ci\`
|
||||
|
||||
Please also make sure to include a scope.
|
||||
These types \`docs\`, \`style\`, \`refactor\`, \`test\`, \`build\`, and \`ci\` are also encouraged for non-changelog related tasks.
|
||||
|
||||
Please also make sure to include \`sdk\` scope, otherwise, the changelog will not include this task.
|
||||
|
||||
For example, these are valid PR titles:
|
||||
- \`feat(sdk-bundle): Add interactive dashboard component\`
|
||||
- \`fix(sdk-package): Fixed MetabaseProviderPropsStore logic\`
|
||||
- \`fix(sdk-package, embed-js): Fix API\``;
|
||||
- \`feat(sdk): Add interactive dashboard component\`
|
||||
- \`feat(sdk): Support theming pie chart\`
|
||||
- \`fix(sdk): Fix static dashboard crash\``;
|
||||
|
||||
return updateComment(comment, { github, context });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param comment {string}
|
||||
* @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments
|
||||
*/
|
||||
async function updateComment(comment, { github, context }) {
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const pullRequestNumber = context.issue.number;
|
||||
@@ -91,19 +49,20 @@ async function updateComment(comment, { github, context }) {
|
||||
}
|
||||
}
|
||||
|
||||
const CONVENTIONAL_COMMENT_IDENTIFIER =
|
||||
"<!---conventional comment identifier-->";
|
||||
|
||||
const CONVENTIONAL_COMMIT_REMINDER_COMMENT_IDENTIFIER =
|
||||
"<!---conventional commit reminder comment ID-->";
|
||||
/**
|
||||
* @param comments {{body: string}[]}
|
||||
* @typedef Comment
|
||||
* @property {string} body
|
||||
*
|
||||
* @param {Comment[]} comments
|
||||
*/
|
||||
function getExistingConventionalCommitReminderComment(comments) {
|
||||
for (const comment of comments) {
|
||||
if (comment.body.startsWith(CONVENTIONAL_COMMENT_IDENTIFIER)) {
|
||||
if (
|
||||
comment.body.startsWith(CONVENTIONAL_COMMIT_REMINDER_COMMENT_IDENTIFIER)
|
||||
) {
|
||||
return comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.isConventionalTitle = isConventionalTitle;
|
||||
exports.updateCommentForEmbedding = updateCommentForEmbedding;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
const { isConventionalTitle } = require("./utils");
|
||||
|
||||
describe("isConventionalTitle", () => {
|
||||
it("returns true for a simple conventional commit", () => {
|
||||
expect(isConventionalTitle("feat: add new feature")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true for a commit with a scope", () => {
|
||||
expect(isConventionalTitle("fix(api): correct API bug")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for a commit with multiple scopes, comma separated", () => {
|
||||
expect(isConventionalTitle("feat(build,deps): update dependencies")).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns true for a commit with multiple scopes, comma separated with space", () => {
|
||||
expect(isConventionalTitle("feat(build, deps): update dependencies")).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false for a commit with round brackets in a message", () => {
|
||||
expect(isConventionalTitle("feat: (something) update dependencies")).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false for missing colon", () => {
|
||||
expect(isConventionalTitle("feat add feature")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for invalid type", () => {
|
||||
expect(isConventionalTitle("invalidType: some message")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for empty message", () => {
|
||||
expect(isConventionalTitle("feat:")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for empty input", () => {
|
||||
expect(isConventionalTitle("")).toBe(false);
|
||||
});
|
||||
});
|
||||
39
.github/scripts/extract-changed-timings.js
vendored
@@ -1,39 +0,0 @@
|
||||
const extractChangedTimings = ({ oldTimings, newTimings }) => {
|
||||
const oldTimingMap =
|
||||
oldTimings.durations?.reduce((map, item) => {
|
||||
if (item.spec) {
|
||||
map[item.spec] = item.duration;
|
||||
}
|
||||
return map;
|
||||
}, {}) || {};
|
||||
|
||||
const changedDurations = newTimings.durations
|
||||
.filter((item) => {
|
||||
if (!item || !item.spec || typeof item.duration !== "number") {
|
||||
return false;
|
||||
}
|
||||
const newSpec = convertPathFormat(item.spec);
|
||||
const oldDuration = oldTimingMap[newSpec] || oldTimingMap[item.spec];
|
||||
// Include if it's a new spec or the duration has changed
|
||||
return oldDuration === undefined || oldDuration !== item.duration;
|
||||
})
|
||||
.map((item) => ({
|
||||
spec: convertPathFormat(item.spec),
|
||||
duration: item.duration,
|
||||
}));
|
||||
|
||||
return {
|
||||
changedTimings: { durations: changedDurations },
|
||||
hasChanges: changedDurations.length > 0,
|
||||
};
|
||||
};
|
||||
|
||||
// Convert cypress-split path format to timings.json path format
|
||||
// from "e2e/test/scenarios/..." to "../test/scenarios/..."
|
||||
function convertPathFormat(specPath) {
|
||||
return specPath.startsWith("e2e/test/")
|
||||
? specPath.replace("e2e/test/", "../test/")
|
||||
: specPath;
|
||||
}
|
||||
|
||||
module.exports = { extractChangedTimings };
|
||||
195
.github/scripts/extract-changed-timings.unit.spec.js
vendored
@@ -1,195 +0,0 @@
|
||||
const { extractChangedTimings } = require("./extract-changed-timings");
|
||||
|
||||
describe("extractChangedTimings", () => {
|
||||
it("should return empty durations when no changes", () => {
|
||||
const oldTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
};
|
||||
|
||||
const newTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
};
|
||||
|
||||
const result = extractChangedTimings({ oldTimings, newTimings });
|
||||
|
||||
expect(result.changedTimings.durations).toHaveLength(0);
|
||||
expect(result.hasChanges).toBe(false);
|
||||
});
|
||||
|
||||
it("should detect new specs", () => {
|
||||
const oldTimings = {
|
||||
durations: [{ spec: "test1.cy.js", duration: 1000 }],
|
||||
};
|
||||
|
||||
const newTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
};
|
||||
|
||||
const result = extractChangedTimings({ oldTimings, newTimings });
|
||||
|
||||
expect(result.changedTimings.durations).toHaveLength(1);
|
||||
expect(result.changedTimings.durations[0]).toEqual({
|
||||
spec: "test2.cy.js",
|
||||
duration: 2000,
|
||||
});
|
||||
expect(result.hasChanges).toBe(true);
|
||||
});
|
||||
|
||||
it("should detect duration changes", () => {
|
||||
const oldTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
};
|
||||
|
||||
const newTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2200 },
|
||||
],
|
||||
};
|
||||
|
||||
const result = extractChangedTimings({ oldTimings, newTimings });
|
||||
|
||||
expect(result.changedTimings.durations).toHaveLength(1);
|
||||
expect(result.changedTimings.durations[0]).toEqual({
|
||||
spec: "test2.cy.js",
|
||||
duration: 2200,
|
||||
});
|
||||
expect(result.hasChanges).toBe(true);
|
||||
});
|
||||
|
||||
it("should handle missing old timings", () => {
|
||||
const oldTimings = { durations: [] };
|
||||
|
||||
const newTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
};
|
||||
|
||||
const result = extractChangedTimings({ oldTimings, newTimings });
|
||||
|
||||
// All timings should be considered "changed" since old timings is empty
|
||||
expect(result.changedTimings.durations).toHaveLength(2);
|
||||
expect(result.changedTimings.durations).toContainEqual({
|
||||
spec: "test1.cy.js",
|
||||
duration: 1000,
|
||||
});
|
||||
expect(result.changedTimings.durations).toContainEqual({
|
||||
spec: "test2.cy.js",
|
||||
duration: 2000,
|
||||
});
|
||||
expect(result.hasChanges).toBe(true);
|
||||
});
|
||||
|
||||
it("should handle invalid timing entries", () => {
|
||||
const oldTimings = {
|
||||
durations: [{ spec: "test1.cy.js", duration: 1000 }],
|
||||
};
|
||||
|
||||
const newTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: "invalid" },
|
||||
{ duration: 2000 }, // missing spec
|
||||
{ spec: "test3.cy.js", duration: 3000 },
|
||||
],
|
||||
};
|
||||
|
||||
const result = extractChangedTimings({ oldTimings, newTimings });
|
||||
|
||||
// Only test3.cy.js should be included (new spec with valid data)
|
||||
expect(result.changedTimings.durations).toHaveLength(1);
|
||||
expect(result.changedTimings.durations[0]).toEqual({
|
||||
spec: "test3.cy.js",
|
||||
duration: 3000,
|
||||
});
|
||||
expect(result.hasChanges).toBe(true);
|
||||
});
|
||||
|
||||
it("should handle timing data with different specs", () => {
|
||||
const oldTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 1000 },
|
||||
{ spec: "test2.cy.js", duration: 2000 },
|
||||
],
|
||||
};
|
||||
|
||||
const newTimings = {
|
||||
durations: [
|
||||
{ spec: "test1.cy.js", duration: 3000 },
|
||||
{ spec: "test3.cy.js", duration: 1000 },
|
||||
],
|
||||
};
|
||||
|
||||
const result = extractChangedTimings({ oldTimings, newTimings });
|
||||
|
||||
expect(result.changedTimings.durations).toHaveLength(2);
|
||||
expect(result.changedTimings.durations).toContainEqual({
|
||||
spec: "test1.cy.js",
|
||||
duration: 3000,
|
||||
});
|
||||
expect(result.changedTimings.durations).toContainEqual({
|
||||
spec: "test3.cy.js",
|
||||
duration: 1000,
|
||||
});
|
||||
expect(result.hasChanges).toBe(true);
|
||||
});
|
||||
|
||||
it("should convert cypress-split paths to relative paths", () => {
|
||||
const oldTimings = {
|
||||
durations: [
|
||||
{
|
||||
spec: "../test/scenarios/dashboard/dashboard.cy.spec.js",
|
||||
duration: 1000,
|
||||
},
|
||||
{
|
||||
spec: "../test/scenarios/admin/databases.cy.spec.js",
|
||||
duration: 2000,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const newTimings = {
|
||||
durations: [
|
||||
{
|
||||
spec: "e2e/test/scenarios/dashboard/dashboard.cy.spec.js",
|
||||
duration: 1200,
|
||||
},
|
||||
{
|
||||
spec: "e2e/test/scenarios/admin/databases.cy.spec.js",
|
||||
duration: 2000,
|
||||
},
|
||||
{
|
||||
spec: "e2e/test/scenarios/collections/collections.cy.spec.js",
|
||||
duration: 1500,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = extractChangedTimings({ oldTimings, newTimings });
|
||||
|
||||
expect(result.changedTimings.durations).toHaveLength(2);
|
||||
expect(result.changedTimings.durations).toContainEqual({
|
||||
spec: "../test/scenarios/dashboard/dashboard.cy.spec.js",
|
||||
duration: 1200,
|
||||
});
|
||||
expect(result.changedTimings.durations).toContainEqual({
|
||||
spec: "../test/scenarios/collections/collections.cy.spec.js",
|
||||
duration: 1500,
|
||||
});
|
||||
expect(result.hasChanges).toBe(true);
|
||||
});
|
||||
});
|
||||
84
.github/scripts/get-issue-data.js
vendored
@@ -1,84 +0,0 @@
|
||||
const githubSlackMap = require("../../release/github-slack-map.json");
|
||||
|
||||
async function getIssueData({ context, github, manualIssueNumber }) {
|
||||
let issueNumber;
|
||||
let issueData;
|
||||
let prReviews = [];
|
||||
const triggerType = context.eventName;
|
||||
|
||||
if (context.eventName === "issues") {
|
||||
issueNumber = context.payload.issue.number;
|
||||
issueData = context.payload.issue;
|
||||
} else if (context.eventName === "workflow_dispatch") {
|
||||
issueNumber = parseInt(manualIssueNumber);
|
||||
const { data } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
issueData = data;
|
||||
} else if (context.eventName === "pull_request_review") {
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const { data: prData } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
// Extract issue number from branch name (e.g., "claude-fix/issue-12345-some-title")
|
||||
const branchMatch = prData.head.ref.match(/claude-fix\/issue-(\d+)/);
|
||||
if (!branchMatch) {
|
||||
throw new Error(
|
||||
`Could not extract issue number from branch name: ${prData.head.ref}`,
|
||||
);
|
||||
}
|
||||
|
||||
issueNumber = parseInt(branchMatch[1]);
|
||||
|
||||
const { data } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
issueData = data;
|
||||
|
||||
const { data: reviews } = await github.rest.pulls.listReviews({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
prReviews = reviews.filter(
|
||||
(review) =>
|
||||
review.state === "CHANGES_REQUESTED" &&
|
||||
isMetabaseMember(review.user.login),
|
||||
);
|
||||
}
|
||||
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
|
||||
const employeeComments = comments.filter((comment) =>
|
||||
isMetabaseMember(comment.user.login),
|
||||
);
|
||||
|
||||
return {
|
||||
issueNumber,
|
||||
issueData,
|
||||
employeeComments,
|
||||
prReviews,
|
||||
triggerType,
|
||||
isIssueAuthorEmployee: isMetabaseMember(issueData.user.login),
|
||||
};
|
||||
}
|
||||
|
||||
function isMetabaseMember(username) {
|
||||
return username in githubSlackMap;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getIssueData,
|
||||
};
|
||||
30
.github/scripts/setup-python-runner.sh
vendored
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Parse command line arguments
|
||||
SKIP_START=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--skip-start)
|
||||
SKIP_START=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Usage: $0 [--skip-start]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "Cloning python-runner repository..."
|
||||
rm -r python-runner
|
||||
git clone --depth 1 https://${METABASE_AUTOMATION_USER_TOKEN}@github.com/metabase/python-runner-container.git python-runner
|
||||
|
||||
if [ "$SKIP_START" = false ]; then
|
||||
echo "Starting python-runner..."
|
||||
cd python-runner
|
||||
make run-ci
|
||||
cd ..
|
||||
fi
|
||||
82
.github/team.json
vendored
@@ -8,11 +8,22 @@
|
||||
"romeovs",
|
||||
"bshepherdson",
|
||||
"metamben",
|
||||
"appleby",
|
||||
"uladzimirdev",
|
||||
"wzimrin",
|
||||
"ericnormand",
|
||||
"galdre"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Semantic Layer",
|
||||
"label": ".Team/SemanticLayer",
|
||||
"members": [
|
||||
"kamilmielnik",
|
||||
"lbrdnk",
|
||||
"bronsa"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Drivers",
|
||||
"label": ".Team/Drivers",
|
||||
@@ -26,10 +37,10 @@
|
||||
"label": ".Team/Embedding",
|
||||
"members": [
|
||||
"WiNloSt",
|
||||
"oisincoveney",
|
||||
"npretto",
|
||||
"heypoom",
|
||||
"sanex3339",
|
||||
"lorem--ipsum"
|
||||
"sanex3339"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -39,18 +50,35 @@
|
||||
"qnkhuat",
|
||||
"piranha",
|
||||
"crisptrutski",
|
||||
"deniskaber",
|
||||
"wotbrew",
|
||||
"spalt08",
|
||||
"stasgavrylov",
|
||||
"uladzimirdev",
|
||||
"kulyk",
|
||||
"kamilmielnik"
|
||||
"stasgavrylov"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Admin Webapp",
|
||||
"label": ".Team/AdminWebapp",
|
||||
"members": [
|
||||
"eliotmetabase"
|
||||
"npfitz",
|
||||
"johnswanson",
|
||||
"rafpaf",
|
||||
"iethree",
|
||||
"nvoxland",
|
||||
"fraserdrops",
|
||||
"edpaget"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DashViz",
|
||||
"label": ".Team/DashViz",
|
||||
"members": [
|
||||
"alxnddr",
|
||||
"kulyk",
|
||||
"tsplude",
|
||||
"lorem--ipsum",
|
||||
"noahmoss",
|
||||
"bpander"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -65,54 +93,20 @@
|
||||
"name": "Metabot",
|
||||
"label": ".Team/Metabot",
|
||||
"members": [
|
||||
"appleby",
|
||||
"noahmoss",
|
||||
"retro",
|
||||
"sloansparger",
|
||||
"somtom",
|
||||
"wotbrew",
|
||||
"tsplude",
|
||||
"lbrdnk",
|
||||
"bronsa"
|
||||
"somtom"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Product Growth",
|
||||
"label": ".Team/ProductGrowth",
|
||||
"members": [
|
||||
"nemanjaglumac",
|
||||
"chodorowicz",
|
||||
"andsalves"
|
||||
]
|
||||
"members": ["nemanjaglumac", "chodorowicz"]
|
||||
},
|
||||
{
|
||||
"name": "Cloud",
|
||||
"label": "cloud",
|
||||
"members": [
|
||||
"filipesilva"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DashViz",
|
||||
"label": ".Team/DashViz",
|
||||
"members": [
|
||||
"eliotmetabase"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "UX West",
|
||||
"label": ".Team/UXWest",
|
||||
"members": [
|
||||
"eliotmetabase",
|
||||
"npfitz",
|
||||
"johnswanson",
|
||||
"iethree",
|
||||
"nvoxland",
|
||||
"edpaget",
|
||||
"deniskaber",
|
||||
"alxnddr",
|
||||
"bpander"
|
||||
]
|
||||
"members": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
407
.github/workflows/app-db.yml
vendored
@@ -1,407 +0,0 @@
|
||||
name: App DB Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-app-db
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
be-tests-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MariaDB 10.2
|
||||
junit-name: be-tests-mariadb-10-2-ee
|
||||
image: circleci/mariadb:10.2.23
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MariaDB Latest
|
||||
junit-name: be-tests-mariadb-latest-ee
|
||||
image: circleci/mariadb:latest
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
job:
|
||||
- name: Enterprise Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only '"enterprise/backend/test"'
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
- name: EE App DB Tests (Part 1)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:partition/total 2
|
||||
:partition/index 0
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
- name: EE App DB Tests (Part 2)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:partition/total 2
|
||||
:partition/index 1
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: ${{ matrix.version.image }}
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
|
||||
# with the tag `mb/old-migrations-test`. `__ADDITIONAL_EXCLUDED_TAG__` is not used anywhere outside of splicing
|
||||
# it in to the command below.
|
||||
__ADDITIONAL_EXCLUDED_TAG__: >-
|
||||
${{
|
||||
(
|
||||
github.event_name == 'push' &&
|
||||
(
|
||||
github.ref == 'master' ||
|
||||
startsWith(github.ref, 'release-')
|
||||
) &&
|
||||
''
|
||||
) ||
|
||||
':mb/old-migrations-test'
|
||||
}}
|
||||
# actual serious env vars below
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
#
|
||||
# SSL config: only for mysql latest tests.
|
||||
#
|
||||
# set up env vars for something named "MYSQL_SSL" to run MySQL SSL tests verifying connectivity with PEM cert
|
||||
# they are deliberately given a different name to prevent them from affecting the regular test run against
|
||||
# the configured MySQL instance, but there is one particular test (mysql-connect-with-ssl-and-pem-cert-test)
|
||||
# that overrides the MB_MYSQL_TEST_* values with them
|
||||
# the MYSQL_RDS_SSL_INSTANCE vars are defined as secrets and can be altered
|
||||
MB_MYSQL_SSL_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_MYSQL_SSL_TEST_HOST: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_HOST }}
|
||||
MB_MYSQL_SSL_TEST_ADDITIONAL_OPTIONS: 'verifyServerCertificate=true'
|
||||
# the contents of the ./resources/certificates/rds-combined-ca-bundle.pem file
|
||||
MB_MYSQL_SSL_TEST_SSL_CERT: ${{ secrets.MB_MYSQL_SSL_TEST_SSL_CERT }}
|
||||
MB_MYSQL_SSL_TEST_USER: metabase
|
||||
MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mysql:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MySQL 8.0
|
||||
junit-name: be-tests-mysql-8-0-ee
|
||||
image: cimg/mysql:8.0
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MySQL Latest
|
||||
junit-name: be-tests-mysql-latest-ee
|
||||
image: mysql:latest
|
||||
env:
|
||||
enable-ssl-tests: 'true'
|
||||
job:
|
||||
- name: Enterprise Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only '"enterprise/backend/test"'
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
- name: EE App DB Tests (Part 1)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:partition/total 2
|
||||
:partition/index 0
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
- name: EE App DB Tests (Part 2)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:partition/total 2
|
||||
:partition/index 1
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: ${{ matrix.version.image }}
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
|
||||
# with the tag `mb/old-migrations-test`. `__ADDITIONAL_EXCLUDED_TAG__` is not used anywhere outside of splicing
|
||||
# it in to the command below.
|
||||
__ADDITIONAL_EXCLUDED_TAG__: >-
|
||||
${{
|
||||
(
|
||||
github.event_name == 'push' &&
|
||||
(
|
||||
github.ref == 'master' ||
|
||||
startsWith(github.ref, 'release-')
|
||||
) &&
|
||||
''
|
||||
) ||
|
||||
':mb/old-migrations-test'
|
||||
}}
|
||||
# actual serious env vars below
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
#
|
||||
# SSL config: only for mysql latest tests.
|
||||
#
|
||||
# set up env vars for something named "MYSQL_SSL" to run MySQL SSL tests verifying connectivity with PEM cert
|
||||
# they are deliberately given a different name to prevent them from affecting the regular test run against
|
||||
# the configured MySQL instance, but there is one particular test (mysql-connect-with-ssl-and-pem-cert-test)
|
||||
# that overrides the MB_MYSQL_TEST_* values with them
|
||||
# the MYSQL_RDS_SSL_INSTANCE vars are defined as secrets and can be altered
|
||||
MB_MYSQL_SSL_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_MYSQL_SSL_TEST_HOST: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_HOST }}
|
||||
MB_MYSQL_SSL_TEST_ADDITIONAL_OPTIONS: 'verifyServerCertificate=true'
|
||||
# the contents of the ./resources/certificates/rds-combined-ca-bundle.pem file
|
||||
MB_MYSQL_SSL_TEST_SSL_CERT: ${{ secrets.MB_MYSQL_SSL_TEST_SSL_CERT }}
|
||||
MB_MYSQL_SSL_TEST_USER: metabase
|
||||
MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-postgres:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Postgres 12.x
|
||||
junit-name: postgres-ee
|
||||
docker-image: postgres:12-alpine
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: Postgres Latest
|
||||
junit-name: postgres-latest-ee
|
||||
docker-image: postgres:latest
|
||||
env:
|
||||
enable-ssl-tests: 'true'
|
||||
job:
|
||||
- name: Driver Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
exclude-tag: ':mb/transforms-python-test'
|
||||
- name: Enterprise Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only '"enterprise/backend/test"'
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
- name: EE App DB Tests (Part 1)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:exclude-tags [:mb/driver-tests]
|
||||
:partition/total 2
|
||||
:partition/index 0
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
- name: EE App DB Tests (Part 2)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:partition/total 2
|
||||
:partition/index 1
|
||||
exclude-tag: ':mb/driver-tests :mb/transforms-python-test'
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
env:
|
||||
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
|
||||
# with the tag `mb/old-migrations-test`. `__ADDITIONAL_EXCLUDED_TAG__` is not used anywhere outside of splicing
|
||||
# it in to the command below.
|
||||
__ADDITIONAL_EXCLUDED_TAG__: >-
|
||||
${{
|
||||
(
|
||||
github.event_name == 'push' &&
|
||||
(
|
||||
github.ref == 'master' ||
|
||||
startsWith(github.ref, 'release-')
|
||||
) &&
|
||||
''
|
||||
) ||
|
||||
':mb/old-migrations-test'
|
||||
}}
|
||||
# actual serious env vars below
|
||||
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
|
||||
# SSL tests are only enabled for the postgres-latest job.
|
||||
MB_POSTGRES_SSL_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_POSTGRES_SSL_TEST_SSL_MODE: verify-full
|
||||
MB_POSTGRES_SSL_TEST_SSL_ROOT_CERT_PATH: 'test-resources/certificates/us-east-2-bundle.pem'
|
||||
services:
|
||||
postgres:
|
||||
image: ${{ matrix.version.docker-image }}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }} (${{ matrix.job.name }})
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: 'be-tests-${{ matrix.version.junit-name }}'
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
app-db-tests-result:
|
||||
needs:
|
||||
- be-tests-mariadb
|
||||
- be-tests-mysql
|
||||
- be-tests-postgres
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: app-db-tests-result
|
||||
if: always() && !cancelled()
|
||||
steps:
|
||||
- name: Check job status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
with:
|
||||
script: | # js
|
||||
const needs = JSON.parse(process.env.needs);
|
||||
|
||||
const jobs = Object.entries(needs).map(
|
||||
([jobName, jobValues]) => ({
|
||||
name: jobName,
|
||||
result: jobValues.result
|
||||
}));
|
||||
|
||||
// are all jobs skipped or successful?
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
||||
console.log("");
|
||||
console.log(" _------. ");
|
||||
console.log(" / , \_ ");
|
||||
console.log(" / / /{}\ |o\_ ");
|
||||
console.log(" / \ `--' /-' \ ");
|
||||
console.log(" | \ \ | ");
|
||||
console.log(" | |`-, | ");
|
||||
console.log(" / /__/)/ ");
|
||||
console.log(" | | ");
|
||||
console.log("");
|
||||
console.log("All app db tests have passed (or have been skipped). Cam is very proud of you.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// otherwise, something failed
|
||||
console.log("");
|
||||
console.log(" .::::::::::. .::::::::::. ");
|
||||
console.log(" .::::''''''::::. .::::''''''::::. ");
|
||||
console.log(" .:::' `::::.... ....::::' `:::. ");
|
||||
console.log(" .::' `:::::::| |:::::::' `::. ");
|
||||
console.log(" .::| |::::::|_ ___ __|::::::| |::. ");
|
||||
console.log(" `--' |::::::|_()__()_|::::::| `--' ");
|
||||
console.log(" ::: |::-o::| |::o-::| ::: ");
|
||||
console.log(" `::. .|::::::| |::::::|. .::' ");
|
||||
console.log(" `:::. .::\-----' `-----/::. .:::' ");
|
||||
console.log(" `::::......::::' `::::......::::' ");
|
||||
console.log(" `::::::::::' `::::::::::' ");
|
||||
console.log("");
|
||||
console.log("App db tests have failed. You have been sentenced to MetaJail.");
|
||||
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success') {
|
||||
console.log(`${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
|
||||
process.exit(1);
|
||||
16
.github/workflows/auto-backport.yml
vendored
@@ -6,19 +6,11 @@ on:
|
||||
|
||||
jobs:
|
||||
create-backport:
|
||||
if:
|
||||
| # the PR must have a backport label when it's merged, or be labeled with a backport label after merge
|
||||
if: | # the PR must be merged and have a backport label
|
||||
github.event.pull_request.merged == true && (
|
||||
github.event.action == 'closed' && (
|
||||
contains(github.event.pull_request.labels.*.name, 'backport') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'double-backport') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'triple-backport')
|
||||
) ||
|
||||
github.event.action == 'labeled' && (
|
||||
github.event.label.name == 'backport' ||
|
||||
github.event.label.name == 'double-backport' ||
|
||||
github.event.label.name == 'triple-backport'
|
||||
)
|
||||
contains(github.event.pull_request.labels.*.name, 'backport') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'double-backport') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'triple-backport')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
21
.github/workflows/auto-request-review.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Config is in .github/auto_request_review.yml
|
||||
name: Auto Request Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, ready_for_review, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
auto-request-review:
|
||||
# Don't run on forked repos
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
name: Auto Request Review
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Request review based on files changes and/or groups the author belongs to
|
||||
uses: necojackarc/auto-request-review@v0.13.0
|
||||
with:
|
||||
token: '${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}'
|
||||
2
.github/workflows/backend.yml
vendored
@@ -154,7 +154,7 @@ jobs:
|
||||
fail-on-error: false
|
||||
|
||||
- name: Upload Logs on Failure
|
||||
if: always() && !success()
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-logs-${{ matrix.java-version }}-${{ matrix.job.edition }}-${{ github.job }}
|
||||
|
||||
1
.github/workflows/backport-reminder.yml
vendored
@@ -5,7 +5,6 @@ on:
|
||||
types: [auto_merge_enabled, review_requested, labeled, unlabeled, synchronize]
|
||||
branches:
|
||||
- "master"
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
backport-reminder:
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
name: Block Master Branch PRs
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
check-source-branch:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Block master branch PRs
|
||||
if: github.head_ref == 'master'
|
||||
run: |
|
||||
echo "PRs from `master` branches are not allowed, please use a new branch name for your PR."
|
||||
exit 1
|
||||
1
.github/workflows/check-backport-script.yml
vendored
@@ -3,7 +3,6 @@ name: Prevent backport.sh from being merged
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
check-backport-script:
|
||||
|
||||
327
.github/workflows/claude-code-bug-fix.yml
vendored
@@ -1,327 +0,0 @@
|
||||
name: "Claude Code Bug Fix"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
pull_request_review:
|
||||
types:
|
||||
- submitted
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: "Issue number to fix"
|
||||
required: true
|
||||
type: string
|
||||
override_employee_check:
|
||||
description: "Override employee check for issue creator"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
auto-fix-bug:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
if: |
|
||||
(github.event_name == 'issues' && github.event.label.name == 'Claude Bug Fix' && github.event.issue.state == 'open') ||
|
||||
(github.event_name == 'workflow_dispatch') ||
|
||||
(github.event_name == 'pull_request_review' && github.event.review.state == 'changes_requested' && startsWith(github.event.pull_request.head.ref, 'claude-fix/'))
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get issue data and check employee status
|
||||
id: get_issue
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { getIssueData } = require('${{ github.workspace }}/.github/scripts/get-issue-data.js');
|
||||
|
||||
try {
|
||||
const result = await getIssueData({
|
||||
context,
|
||||
github,
|
||||
manualIssueNumber: '${{ github.event.inputs.issue_number }}'
|
||||
});
|
||||
|
||||
const { issueNumber, issueData, employeeComments, prReviews, triggerType, isIssueAuthorEmployee } = result;
|
||||
|
||||
// Check employee status and fail if not authorized
|
||||
if (!result.isIssueAuthorEmployee && '${{ github.event.inputs.override_employee_check }}' !== 'true') {
|
||||
core.setFailed(`User ${result.issueData.user.login} is not a Metabase employee. Skipping bug fix automation.`);
|
||||
}
|
||||
|
||||
core.setOutput('issue_number', issueNumber);
|
||||
core.setOutput('issue_data', JSON.stringify(issueData));
|
||||
core.setOutput('issue_comments', JSON.stringify(employeeComments));
|
||||
core.setOutput('pr_reviews', JSON.stringify(prReviews));
|
||||
core.setOutput('trigger_type', triggerType);
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
|
||||
- name: Set up git configuration
|
||||
run: |
|
||||
git config --global user.name "Claude Code[bot]"
|
||||
git config --global user.email "noreply@anthropic.com"
|
||||
|
||||
- name: Prepare issue context and bug fix prompt
|
||||
id: prepare_prompt
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
ISSUE_DATA: ${{ steps.get_issue.outputs.issue_data }}
|
||||
ISSUE_COMMENTS: ${{ steps.get_issue.outputs.issue_comments }}
|
||||
PR_REVIEWS: ${{ steps.get_issue.outputs.pr_reviews }}
|
||||
TRIGGER_TYPE: ${{ steps.get_issue.outputs.trigger_type }}
|
||||
with:
|
||||
script: |
|
||||
const issueData = JSON.parse(process.env.ISSUE_DATA);
|
||||
const issueComments = JSON.parse(process.env.ISSUE_COMMENTS);
|
||||
const prReviews = JSON.parse(process.env.PR_REVIEWS);
|
||||
const triggerType = process.env.TRIGGER_TYPE;
|
||||
const issueNumber = '${{ steps.get_issue.outputs.issue_number }}';
|
||||
|
||||
// Convert issue title to kebab case and create branch name
|
||||
const issueTitle = issueData.title || 'untitled';
|
||||
const kebabTitle = issueTitle
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/-+/g, '-')
|
||||
.substring(0, 50);
|
||||
const branchName = `claude-fix/issue-${issueNumber}-${kebabTitle}`;
|
||||
|
||||
// Format data for bash environment
|
||||
const formattedComments = issueComments.length > 0
|
||||
? issueComments.map(comment =>
|
||||
`**Comment by @${comment.user.login} on ${comment.created_at}:**\n${comment.body}`
|
||||
).join('\n\n---\n\n')
|
||||
: '';
|
||||
const formattedReviews = prReviews.length > 0
|
||||
? prReviews.map(review =>
|
||||
`**Review by @${review.user.login} on ${review.submitted_at} (Changes Requested):**\n${review.body || 'No review comment provided'}`
|
||||
).join('\n\n---\n\n')
|
||||
: '';
|
||||
|
||||
core.exportVariable('BRANCH_NAME', branchName);
|
||||
core.exportVariable('ISSUE_NUMBER', issueNumber);
|
||||
core.exportVariable('ISSUE_TITLE', issueData.title);
|
||||
core.exportVariable('ISSUE_AUTHOR', issueData.user.login);
|
||||
core.exportVariable('ISSUE_CREATED', issueData.created_at);
|
||||
core.exportVariable('ISSUE_BODY', issueData.body || 'No description provided');
|
||||
core.exportVariable('ISSUE_COMMENTS_FORMATTED', formattedComments);
|
||||
core.exportVariable('PR_REVIEWS_FORMATTED', formattedReviews);
|
||||
core.exportVariable('TRIGGER_TYPE', triggerType);
|
||||
|
||||
- name: Generate bug fix prompt
|
||||
run: |
|
||||
cat << 'EOF' >> $GITHUB_ENV
|
||||
BUG_FIX_PROMPT<<PROMPT_END
|
||||
You are helping to fix a bug reported in GitHub issue #${{ env.ISSUE_NUMBER }}.
|
||||
|
||||
## Bug Report Details
|
||||
**Title:** ${{ env.ISSUE_TITLE }}
|
||||
**Reporter:** ${{ env.ISSUE_AUTHOR }}
|
||||
**Created:** ${{ env.ISSUE_CREATED }}
|
||||
|
||||
**Description:**
|
||||
${{ env.ISSUE_BODY }}
|
||||
|
||||
**Comments:**
|
||||
${{ env.ISSUE_COMMENTS_FORMATTED }}
|
||||
EOF
|
||||
|
||||
# Add review feedback section only if this is a review trigger
|
||||
if [ "${{ env.TRIGGER_TYPE }}" = "pull_request_review" ]; then
|
||||
cat << 'EOF' >> $GITHUB_ENV
|
||||
|
||||
## Previous Fix Attempt - Review Feedback (MUST ADDRESS)
|
||||
Your previous automated fix attempt received the following feedback that needs to be addressed:
|
||||
|
||||
${{ env.PR_REVIEWS_FORMATTED }}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Add the rest of the prompt (always the same)
|
||||
cat << 'EOF' >> $GITHUB_ENV
|
||||
|
||||
## Instructions
|
||||
1. **Analyze the issue description and comments** - Understand what the issue is describing
|
||||
a. Pay particular attention to reproduction steps and if there's an agreed upon fix.
|
||||
2. **Search the codebase** - Use Grep and Glob tools to find relevant files
|
||||
3. **Implement a fix** - Follow existing code patterns and conventions
|
||||
4. **Test the fix** - Run appropriate tests using the commands in CLAUDE.md:
|
||||
- JavaScript/TypeScript: `yarn test-unit path/to/file.unit.spec.js`
|
||||
- Clojure: `clojure -X:dev:test :only namespace/test-name`
|
||||
- Lint: `yarn lint-eslint-pure` for JS/TS, `./bin/mage kondo <file>` for Clojure
|
||||
5. **Verify the fix** - Ensure tests pass and code follows conventions
|
||||
|
||||
## Important Guidelines
|
||||
- Follow the development patterns in CLAUDE.md
|
||||
- Write failing tests first, then fix them
|
||||
- Work in small, testable increments
|
||||
- Don't commit changes - leave that for the workflow
|
||||
- IMPORTANT: Focus only on fixing the reported bug, don't make unrelated changes
|
||||
|
||||
## Scratchpad Directory
|
||||
A `.claude-scratchpad/` directory has been created for your temporary files:
|
||||
- Use this directory for ALL temporary verification files. However, prefer using production test files rather than adhoc ones.
|
||||
- Only modify actual production code in the main repository directories
|
||||
- The scratchpad will be cleaned up automatically - nothing from it will be committed
|
||||
|
||||
Start by understanding the bug and then systematically work toward a solution.
|
||||
PROMPT_END
|
||||
EOF
|
||||
|
||||
- name: Determine branch strategy
|
||||
run: |
|
||||
BRANCH_NAME="${{ env.BRANCH_NAME }}"
|
||||
if git branch -r --list "origin/${BRANCH_NAME}" | grep -q .; then
|
||||
echo "Branch already exists: $BRANCH_NAME"
|
||||
echo "Will push to existing branch and skip PR creation"
|
||||
echo "BRANCH_EXISTS=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Creating new branch: $BRANCH_NAME"
|
||||
echo "Will create new PR"
|
||||
echo "BRANCH_EXISTS=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
# Export the branch name
|
||||
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Create scratchpad directory for Claude
|
||||
run: |
|
||||
# Create a scratchpad directory for Claude to use for temporary files
|
||||
mkdir -p .claude-scratchpad
|
||||
echo "Scratchpad directory created for Claude's temporary verification files"
|
||||
|
||||
- name: Generate GitHub App Token for Claude
|
||||
uses: actions/create-github-app-token@v1
|
||||
id: claude-app-token
|
||||
with:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Generate Bug Fix with Claude Code
|
||||
uses: anthropics/claude-code-action@beta
|
||||
with:
|
||||
mode: agent
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
direct_prompt: ${{ env.BUG_FIX_PROMPT }}
|
||||
timeout_minutes: "25"
|
||||
allowed_tools: "Bash(yarn test-unit*),Bash(yarn lint-eslint-pure),Bash(./bin/mage*),Bash(clojure*),Bash(find*),Bash(ls*),Bash(grep*),Write,Read,Glob,Grep,Edit,MultiEdit"
|
||||
disallowed_tools: "Bash(git commit*),Bash(git push*),Bash(gh*)"
|
||||
|
||||
- name: Clean up scratchpad directory
|
||||
run: |
|
||||
# Remove the scratchpad directory so its contents don't get committed
|
||||
rm -rf .claude-scratchpad/
|
||||
# Remove output file generated by Claude Code action
|
||||
rm -f output.txt
|
||||
echo "Scratchpad directory and action output files cleaned up"
|
||||
|
||||
- name: Check for changes and analysis
|
||||
id: check_changes
|
||||
run: |
|
||||
# Now check for actual code changes (excluding analysis file)
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
echo "Changes detected:"
|
||||
git status --short
|
||||
else
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
echo "No changes made by Claude."
|
||||
fi
|
||||
|
||||
- name: Get issue team labels and prepare reviewers
|
||||
if: steps.check_changes.outputs.has_changes == 'true'
|
||||
id: prepare_reviewers
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
ISSUE_DATA: ${{ steps.get_issue.outputs.issue_data }}
|
||||
with:
|
||||
script: |
|
||||
const issueData = JSON.parse(process.env.ISSUE_DATA);
|
||||
const issueLabels = issueData.labels.map(label => label.name);
|
||||
const teamLabels = issueLabels.filter(label => label.startsWith('.Team/'));
|
||||
const issueCreator = issueData.user.login;
|
||||
core.setOutput('team_labels', teamLabels.join(','));
|
||||
core.setOutput('reviewers', issueCreator);
|
||||
|
||||
- name: Create Pull Request
|
||||
if: steps.check_changes.outputs.has_changes == 'true'
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ env.BRANCH_NAME }}
|
||||
base: master
|
||||
commit-message: |
|
||||
Fix: ${{ env.ISSUE_TITLE }}
|
||||
|
||||
Fixes #${{ env.ISSUE_NUMBER }}
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
committer: "Claude Code[bot] <noreply@anthropic.com>"
|
||||
author: "Claude Code[bot] <noreply@anthropic.com>"
|
||||
title: "Fix: ${{ env.ISSUE_TITLE }}"
|
||||
body: |
|
||||
This PR automatically fixes the bug reported in #${{ env.ISSUE_NUMBER }}.
|
||||
|
||||
## Bug Report
|
||||
**Original Issue:** #${{ env.ISSUE_NUMBER }}
|
||||
**Reporter:** @${{ env.ISSUE_AUTHOR }}
|
||||
**Issue Title:** ${{ env.ISSUE_TITLE }}
|
||||
|
||||
## Automated Fix
|
||||
🤖 This fix was generated automatically by Claude Code based on the issue description.
|
||||
|
||||
**Please review carefully:**
|
||||
- [ ] The fix addresses the reported issue
|
||||
- [ ] Tests pass and code follows conventions
|
||||
- [ ] No unrelated changes were introduced
|
||||
- [ ] The solution is appropriate and maintainable
|
||||
|
||||
## Next Steps
|
||||
- Review the changes and test thoroughly
|
||||
- If the fix looks good, approve and merge
|
||||
- If issues are found, either fix them or close this PR
|
||||
|
||||
---
|
||||
Closes #${{ env.ISSUE_NUMBER }}
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
labels: |
|
||||
${{ steps.prepare_reviewers.outputs.team_labels }}
|
||||
automated-fix
|
||||
ci skip
|
||||
reviewers: ${{ steps.prepare_reviewers.outputs.reviewers }}
|
||||
|
||||
- name: Log existing branch update
|
||||
if: steps.check_changes.outputs.has_changes == 'true' && env.BRANCH_EXISTS == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
console.log('🔄 Updated existing branch: ${{ env.BRANCH_NAME }}');
|
||||
console.log('The changes have been pushed to the existing PR for this issue.');
|
||||
|
||||
- name: Log analysis for no changes
|
||||
if: steps.check_changes.outputs.has_changes == 'false'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
console.log('🤖 No code changes were made by Claude.');
|
||||
2
.github/workflows/cljs.yml
vendored
@@ -40,5 +40,5 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: 'cljs'
|
||||
- name: Run Cljs tests
|
||||
- name: Run Cljs tests for shared/ code
|
||||
run: yarn run test-cljs
|
||||
|
||||
126
.github/workflows/containerize-uberjar.yml
vendored
@@ -1,126 +0,0 @@
|
||||
name: Containerize Uberjar
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
get-container-info:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
name: Get container info
|
||||
outputs:
|
||||
editions: ${{ steps.ee-info.outputs.editions }}
|
||||
ee: ${{ toJson(fromJson(steps.build-matrix.outputs.result).ee) }}
|
||||
oss: ${{ toJson(fromJson(steps.build-matrix.outputs.result).oss) }}
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
- name: Get EE Docker info
|
||||
id: ee-info
|
||||
uses: ./.github/actions/get-docker-info
|
||||
with:
|
||||
edition: ee
|
||||
- name: Get OSS Docker info
|
||||
id: oss-info
|
||||
uses: ./.github/actions/get-docker-info
|
||||
with:
|
||||
edition: oss
|
||||
- name: Build matrix info
|
||||
id: build-matrix
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | # js
|
||||
const editions = JSON.parse('${{ steps.ee-info.outputs.editions }}');
|
||||
const result = {
|
||||
editions: editions,
|
||||
ee: {
|
||||
repo: '${{ steps.ee-info.outputs.repo }}',
|
||||
tag: '${{ steps.ee-info.outputs.tag }}'
|
||||
},
|
||||
oss: {
|
||||
repo: '${{ steps.oss-info.outputs.repo }}',
|
||||
tag: '${{ steps.oss-info.outputs.tag }}'
|
||||
}
|
||||
};
|
||||
console.log('Build matrix:', result);
|
||||
return result;
|
||||
|
||||
containerize:
|
||||
needs: [get-container-info]
|
||||
if: needs.get-container-info.result == 'success'
|
||||
strategy:
|
||||
matrix:
|
||||
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
|
||||
uses: ./.github/workflows/containerize-jar.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
artifact-name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
commit: ${{ github.event.inputs.commit || github.sha }}
|
||||
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
|
||||
tag: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).tag }}
|
||||
release: "false"
|
||||
build-args: |
|
||||
GIT_COMMIT_SHA=${{ github.event.inputs.commit || github.sha }}
|
||||
|
||||
run-trivy:
|
||||
needs: [get-container-info, containerize]
|
||||
strategy:
|
||||
matrix:
|
||||
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
|
||||
runs-on: ubuntu-22.04
|
||||
name: Run Trivy vulnerability scanner
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit || github.sha }}
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
env:
|
||||
TRIVY_OFFLINE_SCAN: true
|
||||
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
|
||||
tag: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).tag }}
|
||||
with:
|
||||
image-ref: docker.io/${{ env.repo }}:${{ env.tag }}
|
||||
format: sarif
|
||||
output: trivy-results.sarif
|
||||
version: "v0.57.1"
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: "trivy-results.sarif"
|
||||
|
||||
post-comment:
|
||||
needs: [get-container-info, containerize]
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-22.04
|
||||
name: Post PR comment
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
- name: Get Docker info for comment
|
||||
id: docker-info
|
||||
uses: ./.github/actions/get-docker-info
|
||||
with:
|
||||
branch: ${{ github.head_ref || github.ref_name }}
|
||||
edition: ee
|
||||
|
||||
- name: Post PR comment
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: docker-build-status
|
||||
message: |
|
||||
✅ Docker build completed for commit ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
The uberjar has been built and containerized successfully.
|
||||
|
||||
**Branch:** `${{ github.head_ref || github.ref_name }}`
|
||||
**Docker Image:** `${{ steps.docker-info.outputs.full_image }}`
|
||||
**Docker Hub:** [View on Docker Hub](https://hub.docker.com/r/${{ steps.docker-info.outputs.repo }}/tags?name=${{ steps.docker-info.outputs.tag }})
|
||||
**Triggered by:** @${{ github.actor }}
|
||||
|
||||
Pull the image:
|
||||
```bash
|
||||
docker pull ${{ steps.docker-info.outputs.full_image }}
|
||||
```
|
||||
@@ -4,53 +4,39 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- enterprise/frontend/src/embedding-sdk/**
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
frontend_embedding_sources: ${{ steps.changes.outputs.frontend_embedding_sources }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
pr-title-reminder:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
pull-requests: write
|
||||
needs: [ files-changed ]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.files-changed.outputs.frontend_embedding_sources == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Check if the PR title is already following Conventional Commits style
|
||||
uses: actions/github-script@v7
|
||||
id: is-conventional-commit
|
||||
with:
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const { isConventionalTitle } = require(".github/scripts/conventional-commit-pr-title-reminder/utils.js")
|
||||
const pullRequestTitle = context.payload.pull_request.title
|
||||
const conventionalCommitRegex = /^(?<type>\w+)\((?<scope>\w+)\):/
|
||||
const match = pullRequestTitle.match(conventionalCommitRegex)
|
||||
if (match) {
|
||||
const { type, scope } = match.groups
|
||||
return ['feat', 'fix', 'perf', 'docs', 'style', 'refactor', 'test', 'build', 'ci'].includes(type) && scope === 'sdk'
|
||||
}
|
||||
|
||||
return isConventionalTitle(pullRequestTitle)
|
||||
- name: Checkout repository so `gh` CLI works
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Remind PR authors to use conventional commit in the title
|
||||
if: ${{ steps.is-conventional-commit.outputs.result != 'true' }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const { updateCommentForEmbedding } = require(".github/scripts/conventional-commit-pr-title-reminder/utils.js")
|
||||
|
||||
await updateCommentForEmbedding({github, context})
|
||||
script: |
|
||||
const { updateComment } = require(".github/scripts/conventional-commit-pr-title-reminder/utils.js")
|
||||
await updateComment({github, context})
|
||||
|
||||
26
.github/workflows/coverage.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: Test coverage
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '40 1 * * *' # every day at 1:40
|
||||
|
||||
jobs:
|
||||
fe-test-coverage:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
- name: Run frontend unit tests
|
||||
run: yarn run test-unit --silent --coverage
|
||||
- name: Upload coverage to codecov.io
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./coverage/lcov.info
|
||||
flags: front-end
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
49
.github/workflows/cross-branch-migrations.yml
vendored
@@ -5,19 +5,6 @@ on:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- "resources/migrations/**"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
base-ref:
|
||||
description: 'Base reference for the migration test'
|
||||
required: true
|
||||
type: string
|
||||
head-ref:
|
||||
description: 'Head reference for the migration test'
|
||||
required: true
|
||||
type: string
|
||||
env:
|
||||
BASE_REF: ${{ inputs.base-ref || github.base_ref }}
|
||||
HEAD_REF: ${{ inputs.head-ref || github.head_ref }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
@@ -26,21 +13,19 @@ concurrency:
|
||||
jobs:
|
||||
migrations-h2-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: h2
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/cross-branch-migration-test
|
||||
with:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
base-ref: ${{ github.base_ref }}
|
||||
|
||||
migrations-postgres-12-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: postgres
|
||||
@@ -61,12 +46,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/cross-branch-migration-test
|
||||
with:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
base-ref: ${{ github.base_ref }}
|
||||
|
||||
migrations-postgres-latest-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: postgres
|
||||
@@ -87,12 +71,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/cross-branch-migration-test
|
||||
with:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
base-ref: ${{ github.base_ref }}
|
||||
|
||||
migrations-mysql-8-0-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: mysql
|
||||
@@ -109,12 +92,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/cross-branch-migration-test
|
||||
with:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
base-ref: ${{ github.base_ref }}
|
||||
|
||||
migrations-mysql-latest-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: mysql
|
||||
@@ -135,12 +117,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/cross-branch-migration-test
|
||||
with:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
base-ref: ${{ github.base_ref }}
|
||||
|
||||
migrations-mariadb-10-2-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: mysql
|
||||
@@ -157,12 +138,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/cross-branch-migration-test
|
||||
with:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
base-ref: ${{ github.base_ref }}
|
||||
|
||||
migrations-mariadb-latest-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
CI: 'true'
|
||||
MB_DB_TYPE: mysql
|
||||
@@ -179,5 +159,4 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/cross-branch-migration-test
|
||||
with:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
base-ref: ${{ github.base_ref }}
|
||||
|
||||
182
.github/workflows/docs-generate-base.yml
vendored
@@ -1,182 +0,0 @@
|
||||
# .github/workflows/reusable-doc-update.yml
|
||||
name: Reusable - Update Documentation
|
||||
|
||||
# This workflow is designed to be called by other workflows.
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target_branch:
|
||||
description: "The branch to check out and create a PR against"
|
||||
required: true
|
||||
type: string
|
||||
m2_cache_key_suffix:
|
||||
description: "A unique suffix for the Maven cache key"
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
automation_token:
|
||||
description: "A token with permissions to create branches and PRs"
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
update-docs:
|
||||
name: Update documentation for ${{ inputs.target_branch }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.automation_token }}
|
||||
ref: ${{ inputs.target_branch }}
|
||||
|
||||
- name: Prepare backend environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: "auto-update-docs-${{ inputs.m2_cache_key_suffix }}"
|
||||
|
||||
- name: Generate environment variables documentation
|
||||
id: generate_env_docs
|
||||
run: |
|
||||
set -e # Exit on any error
|
||||
# Ensure we're on the target branch for documentation generation
|
||||
git checkout ${{ inputs.target_branch }}
|
||||
echo "Generating environment variables documentation from branch: ${{ inputs.target_branch }}"
|
||||
clojure -M:ee:doc environment-variables-documentation
|
||||
continue-on-error: false
|
||||
|
||||
- name: Generate config template documentation
|
||||
id: generate_config_docs
|
||||
run: |
|
||||
set -e # Exit on any error
|
||||
echo "Generating config template documentation..."
|
||||
clojure -M:ee:doc config-template
|
||||
continue-on-error: false
|
||||
|
||||
- name: Generate API documentation
|
||||
id: generate_api_docs
|
||||
run: |
|
||||
set -e # Exit on any error
|
||||
echo "Generating API documentation..."
|
||||
clojure -M:ee:doc api-documentation
|
||||
continue-on-error: false
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
if git diff --quiet docs/; then
|
||||
echo "changes=false" >> $GITHUB_OUTPUT
|
||||
echo "No changes detected in docs directory"
|
||||
else
|
||||
echo "changes=true" >> $GITHUB_OUTPUT
|
||||
echo "Changes detected in docs directory"
|
||||
fi
|
||||
|
||||
- name: Create branch and commit changes
|
||||
id: commit_and_push
|
||||
if: steps.check_changes.outputs.changes == 'true'
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
|
||||
BRANCH_NAME="docs/auto-update-${{ inputs.target_branch }}-$(date +%Y-%m-%d-%H%M%S)"
|
||||
echo "Creating branch: $BRANCH_NAME"
|
||||
|
||||
# Create and switch to new branch from current target branch
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
|
||||
# Stage and commit changes
|
||||
git add docs/
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git commit -m "Update generated docs (${{ inputs.target_branch }})" --no-verify
|
||||
git push -u origin "$BRANCH_NAME"
|
||||
|
||||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
echo "Successfully created and pushed branch"
|
||||
|
||||
- name: Create pull request
|
||||
id: create_pr
|
||||
if: steps.check_changes.outputs.changes == 'true'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.automation_token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
run: |
|
||||
CHANGES_SUMMARY=$(git diff --name-only HEAD~1 docs/ 2>/dev/null | head -10 | sed 's/^/- /' || echo "- No specific files detected")
|
||||
TOTAL_CHANGES=$(git diff --name-only HEAD~1 docs/ 2>/dev/null | wc -l || echo "0")
|
||||
BRANCH_NAME="${{ steps.commit_and_push.outputs.branch_name }}"
|
||||
|
||||
echo "Creating PR with branch: $BRANCH_NAME"
|
||||
echo "Changes detected: $TOTAL_CHANGES files"
|
||||
|
||||
for i in {1..3}; do
|
||||
echo "Attempt $i of 3..."
|
||||
if gh pr create \
|
||||
--base "${{ inputs.target_branch }}" \
|
||||
--head "$BRANCH_NAME" \
|
||||
--title "Auto-update documentation for ${{ inputs.target_branch }}" \
|
||||
--body "This PR contains automatically generated documentation updates.
|
||||
|
||||
## Changes detected
|
||||
The following documentation files were updated:
|
||||
$CHANGES_SUMMARY
|
||||
$([ "$TOTAL_CHANGES" -gt 10 ] && echo "- ... and $((TOTAL_CHANGES - 10)) more files")
|
||||
|
||||
## Generation commands
|
||||
Documentation was generated using:
|
||||
- \`clojure -M:ee:doc environment-variables-documentation\`
|
||||
- \`clojure -M:ee:doc config-template\`
|
||||
- \`clojure -M:ee:doc api-documentation\`
|
||||
|
||||
Please review the changes and merge if they look correct." \
|
||||
--label "Type:Documentation" \
|
||||
--label "no-backport" \
|
||||
--reviewer "metabase/tech-writers"; then
|
||||
echo "PR created successfully on attempt $i"
|
||||
break
|
||||
else
|
||||
echo "PR creation failed on attempt $i"
|
||||
if [ $i -eq 3 ]; then
|
||||
echo "All attempts failed. Exiting with error."
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting 10 seconds before retry..."
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Skip PR creation
|
||||
if: steps.check_changes.outputs.changes == 'false'
|
||||
run: |
|
||||
echo "No documentation changes detected, skipping PR creation"
|
||||
|
||||
- name: Workflow summary
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check_changes.outputs.changes }}" == "true" ]; then
|
||||
if [ "${{ steps.commit_and_push.outcome }}" == "success" ]; then
|
||||
if [ "${{ steps.create_pr.outcome }}" == "success" ]; then
|
||||
echo "## ✅ Documentation Updated Successfully" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Branch: \`${{ steps.commit_and_push.outputs.branch_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- PR created targeting **${{ inputs.target_branch }}**" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "## ⚠️ Branch Created but PR Failed" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Branch: \`${{ steps.commit_and_push.outputs.branch_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Manual PR creation required" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
else
|
||||
echo "## ❌ Failed to Create Branch" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Check workflow logs for details" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
else
|
||||
echo "## ℹ️ No Changes Detected" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Documentation is up to date on **${{ inputs.target_branch }}**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
36
.github/workflows/docs-links.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: Check doc links
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
link-checker:
|
||||
name: Check documentation links
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
docs
|
||||
.lychee
|
||||
|
||||
- name: Cache lychee
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/lychee
|
||||
key: lychee-${{ runner.os }}-${{ hashFiles('.lychee/config.toml') }}
|
||||
|
||||
- name: Run link checker
|
||||
id: link-check
|
||||
uses: lycheeverse/lychee-action@v2
|
||||
with:
|
||||
fail: true
|
||||
args: docs --config ./.lychee/config.toml --offline
|
||||
format: markdown
|
||||
16
.github/workflows/docs-master-generate.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: Auto-update documentation (master)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 1 * * 1-5" # Every Monday through Friday at 1 AM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-master-docs:
|
||||
name: Call documentation update for master
|
||||
uses: ./.github/workflows/docs-generate-base.yml
|
||||
with:
|
||||
target_branch: "master"
|
||||
m2_cache_key_suffix: "master"
|
||||
secrets:
|
||||
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
16
.github/workflows/docs-release-generate.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: Auto-update documentation (release)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 2 * * 1-5" # Every Monday through Friday at 2 AM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-release-docs:
|
||||
name: Call documentation update for release branch
|
||||
uses: ./.github/workflows/docs-generate-base.yml
|
||||
with:
|
||||
target_branch: "release-x.56.x"
|
||||
m2_cache_key_suffix: "release"
|
||||
secrets:
|
||||
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
11
.github/workflows/docs_bump_detected.yml
vendored
@@ -18,15 +18,10 @@ jobs:
|
||||
env:
|
||||
SOURCE_BRANCH: ${{ github.head_ref || github.ref }}
|
||||
TARGET_BRANCH: ${{ github.event.pull_request.base.ref }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
steps:
|
||||
- name: Report changed docs files exist
|
||||
run: |
|
||||
echo "A PR with changes in the docs directory was detected."
|
||||
echo "Sending workflow trigger to create/update the corresponding PR in docs repo."
|
||||
echo "source branch: $SOURCE_BRANCH"
|
||||
echo "target branch: $TARGET_BRANCH"
|
||||
echo "pr number: $PR_NUMBER"
|
||||
echo "A change in the docs directory was detected."
|
||||
|
||||
- name: Trigger repo dispatch
|
||||
uses: actions/github-script@v7
|
||||
@@ -38,7 +33,5 @@ jobs:
|
||||
repo: "docs.metabase.github.io",
|
||||
event_type: "docs_update",
|
||||
client_payload: {
|
||||
dispatch_type: "docs_update",
|
||||
source_branch: process.env.SOURCE_BRANCH,
|
||||
target_branch: process.env.TARGET_BRANCH,
|
||||
pr_number: process.env.PR_NUMBER}});
|
||||
target_branch: process.env.TARGET_BRANCH}});
|
||||
|
||||
43
.github/workflows/docs_close_detected.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Docs Close Detected
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-x.[0-9]+.x'
|
||||
paths:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
push_and_merge_workflow_to_docs_metabase_github_io_job:
|
||||
if: github.event.pull_request.merged == false
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SOURCE_BRANCH: ${{ github.head_ref || github.ref }}
|
||||
TARGET_BRANCH: ${{ github.event.pull_request.base.ref }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
steps:
|
||||
- name: Report changed docs files exist
|
||||
run: |
|
||||
echo "A PR with changes to the docs directory was closed."
|
||||
echo "Sending workflow trigger to close corresponding PR in docs repo."
|
||||
echo "source branch: $SOURCE_BRANCH"
|
||||
echo "target branch: $TARGET_BRANCH"
|
||||
echo "pr number: $PR_NUMBER"
|
||||
|
||||
- name: Trigger repo dispatch
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
script: | #js
|
||||
github.rest.repos.createDispatchEvent({
|
||||
owner: "${{ github.repository_owner }}",
|
||||
repo: "docs.metabase.github.io",
|
||||
event_type: "docs_close",
|
||||
client_payload: {
|
||||
dispatch_type: "docs_close",
|
||||
source_branch: process.env.SOURCE_BRANCH,
|
||||
target_branch: process.env.TARGET_BRANCH,
|
||||
pr_number: process.env.PR_NUMBER}});
|
||||
9
.github/workflows/docs_merge_detected.yml
vendored
@@ -17,15 +17,10 @@ jobs:
|
||||
env:
|
||||
SOURCE_BRANCH: ${{ github.head_ref || github.ref }}
|
||||
TARGET_BRANCH: ${{ github.event.pull_request.base.ref }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
steps:
|
||||
- name: Report changed docs files exist
|
||||
run: |
|
||||
echo "A PR with changes to the docs directory was merged."
|
||||
echo "Sending workflow trigger to merge corresponding PR in docs repo."
|
||||
echo "source branch: $SOURCE_BRANCH"
|
||||
echo "target branch: $TARGET_BRANCH"
|
||||
echo "pr number: $PR_NUMBER"
|
||||
|
||||
- name: Trigger repo dispatch
|
||||
uses: actions/github-script@v7
|
||||
@@ -37,7 +32,5 @@ jobs:
|
||||
repo: "docs.metabase.github.io",
|
||||
event_type: "docs_merge",
|
||||
client_payload: {
|
||||
dispatch_type: "docs_merge",
|
||||
source_branch: process.env.SOURCE_BRANCH,
|
||||
target_branch: process.env.TARGET_BRANCH,
|
||||
pr_number: process.env.PR_NUMBER}});
|
||||
target_branch: process.env.TARGET_BRANCH}});
|
||||
|
||||
35
.github/workflows/drivers-stress-test.yml
vendored
@@ -689,51 +689,18 @@ jobs:
|
||||
junit-name: 'be-tests-starburst-ee'
|
||||
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
|
||||
|
||||
login-to-ecr:
|
||||
if: inputs.driver == 'Vertica'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.CI_IAM_ROLE }}
|
||||
role-session-name: GitHub_to_AWS_via_FederatedOIDC
|
||||
aws-region: us-east-1
|
||||
- name: Get ECR password
|
||||
id: login-ecr
|
||||
run: |
|
||||
PASSWORD=$(aws ecr get-login-password --region us-east-1)
|
||||
|
||||
REGISTRY="$(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com"
|
||||
|
||||
echo "docker_username=AWS" >> $GITHUB_OUTPUT
|
||||
echo "docker_password=$PASSWORD" >> $GITHUB_OUTPUT
|
||||
echo "registry=$REGISTRY" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
registry: ${{ steps.login-ecr.outputs.registry }}
|
||||
docker_username: ${{ steps.login-ecr.outputs.docker_username }}
|
||||
docker_password: ${{ steps.login-ecr.outputs.docker_password }}
|
||||
|
||||
be-tests-vertica-ee:
|
||||
if: inputs.driver == 'Vertica'
|
||||
runs-on: ubuntu-22.04
|
||||
needs: login-to-ecr
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: vertica
|
||||
services:
|
||||
vertica:
|
||||
image: ${{ needs.login-to-ecr.outputs.registry }}/vertica-ce:25.3.0-0
|
||||
image: vertica/vertica-ce:12.0.2-0
|
||||
ports:
|
||||
- "5433:5433"
|
||||
credentials:
|
||||
username: ${{ needs.login-to-ecr.outputs.docker_username }}
|
||||
password: ${{ needs.login-to-ecr.outputs.docker_password }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Make plugins directory
|
||||
|
||||
412
.github/workflows/drivers.yml
vendored
@@ -12,72 +12,17 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# This job will determine if the tier 3 driver tests should be skipped.
|
||||
# It will always run first.
|
||||
determine-driver-skips:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
skip: ${{ steps.check_team_and_branch.outputs.skip }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Fetch base branch
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.base.ref != ''
|
||||
run: |
|
||||
# Fetch the base branch to ensure its history is available for diffing
|
||||
git fetch origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }}
|
||||
# Only run this if it's a pull request and the base ref exists
|
||||
- name: Check if tier3 drivers should run
|
||||
id: check_team_and_branch
|
||||
run: |
|
||||
IS_MASTER_OR_RELEASE="${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') }}"
|
||||
HAS_TEAM_LABEL="${{ contains(github.event.pull_request.labels.*.name, '.Team/Drivers') || contains(github.event.pull_request.labels.*.name, '.Team/Querying') || contains(github.event.pull_request.labels.*.name, '.Team/SemanticLayer') || contains(github.event.pull_request.labels.*.name, '.Team/DevEx') }}"
|
||||
|
||||
|
||||
echo "Is master or release: ${IS_MASTER_OR_RELEASE}"
|
||||
echo "Has '.Team/Drivers' or '.Team/Querying' or '.Team/SemanticLayer' label: ${HAS_TEAM_LABEL}"
|
||||
if [[ "${IS_MASTER_OR_RELEASE}" == "true" ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "Merging into master or a release branch. Tier 3 driver tests will run."
|
||||
elif [[ "${HAS_TEAM_LABEL}" == "true" ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "A relevant team label is present. Tier 3 driver tests will run."
|
||||
else
|
||||
echo "Checking for changes in relevant folders..."
|
||||
CHANGED_FILES=$(git diff --name-only $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) ${{ github.sha }})
|
||||
|
||||
# Initialize an empty array to store folders with changes
|
||||
CHANGED_FOLDERS=()
|
||||
for folder in "src/metabase/lib" "src/metabase/query_processor" "modules/drivers" "src/metabase/driver"; do
|
||||
if echo "$CHANGED_FILES" | grep -q "$folder"; then
|
||||
CHANGED_FOLDERS+=("$folder")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#CHANGED_FOLDERS[@]} -gt 0 ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "Changes detected in the following folders: ${CHANGED_FOLDERS[*]}."
|
||||
echo "Tier 3 driver tests will run."
|
||||
else
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
echo "No relevant team label, not merging into master/release, and no changes in relevant folders. Tier 3 driver tests will be skipped."
|
||||
fi
|
||||
fi
|
||||
shell: bash
|
||||
be-tests-athena-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: athena
|
||||
MB_ATHENA_TEST_REGION: us-east-1
|
||||
MB_ATHENA_TEST_REGION_ROUTING: us-east-2
|
||||
MB_ATHENA_TEST_ACCESS_KEY: ${{ secrets.MB_ATHENA_TEST_ACCESS_KEY }}
|
||||
MB_ATHENA_TEST_SECRET_KEY: ${{ secrets.MB_ATHENA_TEST_SECRET_KEY }}
|
||||
MB_ATHENA_TEST_S3_STAGING_DIR: ${{ secrets.MB_ATHENA_TEST_S3_STAGING_DIR }}
|
||||
MB_ATHENA_TEST_S3_STAGING_DIR_ROUTING: ${{ secrets.MB_ATHENA_TEST_S3_STAGING_DIR_ROUTING }}
|
||||
# These credentials are used to test the driver when the user does not have the athena:GetTableMetadata permission
|
||||
MB_ATHENA_TEST_WITHOUT_GET_TABLE_METADATA_ACCESS_KEY: ${{ secrets.MB_ATHENA_TEST_WITHOUT_GET_TABLE_METADATA_ACCESS_KEY }}
|
||||
MB_ATHENA_TEST_WITHOUT_GET_TABLE_METADATA_SECRET_KEY: ${{ secrets.MB_ATHENA_TEST_WITHOUT_GET_TABLE_METADATA_SECRET_KEY }}
|
||||
@@ -90,7 +35,6 @@ jobs:
|
||||
junit-name: 'be-tests-athena-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -107,19 +51,6 @@ jobs:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
# DS 2025-09-26 temporarily disabled while we diagnose flakes
|
||||
# - name: Transforms Python Tests
|
||||
# test-args: >-
|
||||
# :only-tags [:mb/transforms-python-test]
|
||||
# needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: bigquery-cloud-sdk
|
||||
@@ -129,20 +60,15 @@ jobs:
|
||||
MB_BIGQUERY_TEST_ACCESS_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_ACCESS_TOKEN }}
|
||||
MB_BIGQUERY_TEST_REFRESH_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_REFRESH_TOKEN }}
|
||||
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON }}
|
||||
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING }}
|
||||
name: BigQuery ${{ matrix.job.name }}
|
||||
name: BigQuery
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
if: ${{ matrix.job.needs-python-runner == true }}
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Test BigQuery Cloud SDK driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-bigquery-cloud-sdk-ee'
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -159,18 +85,6 @@ jobs:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Transforms Python Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: clickhouse
|
||||
@@ -178,14 +92,9 @@ jobs:
|
||||
MB_CLICKHOUSE_TEST_PORT: 8123
|
||||
MB_CLICKHOUSE_TEST_OLD_PORT: 8124
|
||||
MB_CLICKHOUSE_TEST_NGINX_PORT: 8127
|
||||
name: Clickhouse ${{ matrix.job.name }}
|
||||
name: Clickhouse
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
if: ${{ matrix.job.needs-python-runner == true }}
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Add ClickHouse TLS instance to /etc/hosts
|
||||
run: |
|
||||
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
|
||||
@@ -205,7 +114,8 @@ jobs:
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-clickhouse-ee'
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -219,8 +129,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-druid-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -242,7 +151,6 @@ jobs:
|
||||
junit-name: 'be-tests-druid-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -256,8 +164,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-druid-jdbc-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -279,7 +186,6 @@ jobs:
|
||||
junit-name: 'be-tests-druid-jdbc-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -293,8 +199,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-databricks-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -304,7 +209,6 @@ jobs:
|
||||
MB_DATABRICKS_TEST_HTTP_PATH: ${{ secrets.MB_DATABRICKS_JDBC_TEST_HTTP_PATH }}
|
||||
MB_DATABRICKS_TEST_TOKEN: ${{ secrets.MB_DATABRICKS_JDBC_TEST_TOKEN }}
|
||||
MB_DATABRICKS_TEST_CATALOG: 'metabase_ci'
|
||||
MB_DATABRICKS_TEST_CATALOG_ROUTING: 'metabase_routing_catalog'
|
||||
MB_DATABRICKS_TEST_CLIENT_ID: ${{ secrets.MB_DATABRICKS_JDBC_TEST_CLIENT_ID }}
|
||||
MB_DATABRICKS_TEST_OAUTH_SECRET: ${{ secrets.MB_DATABRICKS_JDBC_TEST_OAUTH_SECRET }}
|
||||
name: Databricks
|
||||
@@ -316,7 +220,6 @@ jobs:
|
||||
junit-name: 'be-tests-databricks-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -330,8 +233,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-databricks-multi-catalog-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -342,7 +244,6 @@ jobs:
|
||||
MB_DATABRICKS_TEST_HTTP_PATH: ${{ secrets.MB_DATABRICKS_JDBC_TEST_HTTP_PATH }}
|
||||
MB_DATABRICKS_TEST_TOKEN: ${{ secrets.MB_DATABRICKS_JDBC_TEST_TOKEN }}
|
||||
MB_DATABRICKS_TEST_CATALOG: 'metabase_ci'
|
||||
MB_DATABRICKS_TEST_CATALOG_ROUTING: 'metabase_routing_catalog'
|
||||
MB_DATABRICKS_TEST_CLIENT_ID: ${{ secrets.MB_DATABRICKS_JDBC_TEST_CLIENT_ID }}
|
||||
MB_DATABRICKS_TEST_OAUTH_SECRET: ${{ secrets.MB_DATABRICKS_JDBC_TEST_OAUTH_SECRET }}
|
||||
name: Databricks (Multi-Catalog)
|
||||
@@ -354,7 +255,6 @@ jobs:
|
||||
junit-name: 'be-tests-databricks-multi-catalog-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -376,17 +276,12 @@ jobs:
|
||||
name: H2
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Test H2 driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-h2'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -419,7 +314,7 @@ jobs:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MySQL 8.0
|
||||
junit-name: be-tests-mysql-8-0-ee
|
||||
image: mysql:8.0
|
||||
image: cimg/mysql:8.0
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MySQL Latest
|
||||
@@ -429,13 +324,28 @@ jobs:
|
||||
enable-ssl-tests: 'true'
|
||||
job:
|
||||
- name: Driver Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Transforms Python Tests
|
||||
- name: Enterprise Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
:only '"enterprise/backend/test"'
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
- name: EE App DB Tests (Part 1)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:partition/total 2
|
||||
:partition/index 0
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
- name: EE App DB Tests (Part 2)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:partition/total 2
|
||||
:partition/index 1
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
@@ -490,14 +400,10 @@ jobs:
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
#if: ${{ matrix.job.needs-python-runner == true }}
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
@@ -515,8 +421,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mongo:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -529,15 +434,6 @@ jobs:
|
||||
- name: MongoDB Latest
|
||||
junit-name: be-tests-mongo-latest-ee
|
||||
image: mongo:latest
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
:exclude-tags '[:mongo-sharded-cluster-tests :mb/transforms-python-test]'
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Transforms Python Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mongo
|
||||
@@ -551,19 +447,16 @@ jobs:
|
||||
env:
|
||||
MONGO_INITDB_ROOT_USERNAME: metabase
|
||||
MONGO_INITDB_ROOT_PASSWORD: metasample123
|
||||
name: ${{ matrix.version.name }} ${{ matrix.job.name }}
|
||||
name: ${{ matrix.version.name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
if: ${{ matrix.job.needs-python-runner == true }}
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
test-args: >-
|
||||
:exclude-tags [:mongo-sharded-cluster-tests]
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -577,8 +470,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mongo-ssl:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -627,7 +519,7 @@ jobs:
|
||||
with:
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
:exclude-tags '[:mongo-sharded-cluster-tests :mb/transforms-python-test]'
|
||||
:exclude-tags [:mongo-sharded-cluster-tests]
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
@@ -642,8 +534,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mongo-sharded-cluster-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -676,8 +567,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-oracle:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -735,7 +625,6 @@ jobs:
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -768,13 +657,29 @@ jobs:
|
||||
enable-ssl-tests: 'true'
|
||||
job:
|
||||
- name: Driver Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Transforms Python Tests
|
||||
- name: Enterprise Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
:only '"enterprise/backend/test"'
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
- name: EE App DB Tests (Part 1)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:exclude-tags [:mb/driver-tests]
|
||||
:partition/total 2
|
||||
:partition/index 0
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
- name: EE App DB Tests (Part 2)
|
||||
build-static-viz: true
|
||||
test-args: >-
|
||||
:only '"test"'
|
||||
:partition/total 2
|
||||
:partition/index 1
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
env:
|
||||
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
|
||||
@@ -816,14 +721,10 @@ jobs:
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
#if: ${{ matrix.job.needs-python-runner == true }}
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Test ${{ matrix.version.name }} (${{ matrix.job.name }})
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: 'be-tests-${{ matrix.version.junit-name }}'
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
@@ -841,8 +742,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-presto-jdbc-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -897,7 +797,6 @@ jobs:
|
||||
junit-name: 'be-tests-presto-jdbc-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -927,31 +826,19 @@ jobs:
|
||||
skip: false
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags '[:mb/upload-tests :mb/transforms-python-test]'
|
||||
:exclude-tags [:mb/upload-tests]
|
||||
:fail-fast? true
|
||||
- name: Redshift Transforms Python Tests
|
||||
skip: false
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
:fail-fast? true
|
||||
needs-python-runner: true
|
||||
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: redshift
|
||||
MB_REDSHIFT_TEST_USER: metabase_ci
|
||||
MB_REDSHIFT_TEST_DB: testdb
|
||||
MB_REDSHIFT_TEST_DB_ROUTING: dev
|
||||
MB_REDSHIFT_TEST_HOST: ${{ secrets.MB_REDSHIFT_TEST_HOST }}
|
||||
MB_REDSHIFT_TEST_PASSWORD: ${{ secrets.MB_REDSHIFT_TEST_PASSWORD }}
|
||||
name: ${{ matrix.job.name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
if: ${{ matrix.job.needs-python-runner == true }}
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Test ${{ matrix.job.name }}
|
||||
if: ${{ !matrix.job.skip }}
|
||||
uses: ./.github/actions/test-driver
|
||||
@@ -975,19 +862,6 @@ jobs:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
# DS 2025-09-26 temporarily disabled while we diagnose flakes
|
||||
# - name: Transforms Python Tests
|
||||
# test-args: >-
|
||||
# :only-tags [:mb/transforms-python-test]
|
||||
# needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: snowflake
|
||||
@@ -997,28 +871,20 @@ jobs:
|
||||
MB_SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.MB_SNOWFLAKE_TEST_WAREHOUSE }}
|
||||
MB_SNOWFLAKE_TEST_PK_USER: METABASE PK
|
||||
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY }}
|
||||
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2 }}
|
||||
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY }}
|
||||
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2 }}
|
||||
|
||||
# RSA Role testing:
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DEFAULT_USER: RSA_ROLE_TEST_DEFAULT_USER
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_CUSTOM_USER: RSA_ROLE_TEST_CUSTOM_USER
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_ROLE: RSA_ROLE_TEST_ROLE
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DB: RSA_ROLE_TEST_DB
|
||||
name: Snowflake ${{ matrix.job.name }}
|
||||
name: Snowflake
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
if: ${{ matrix.job.needs-python-runner == true }}
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Test Snowflake driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-snowflake-ee'
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -1032,8 +898,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-sparksql-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -1053,7 +918,6 @@ jobs:
|
||||
junit-name: 'be-tests-sparksql-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -1067,8 +931,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-sqlite-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -1083,7 +946,6 @@ jobs:
|
||||
junit-name: 'be-tests-sqlite-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -1097,8 +959,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-sqlserver:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
@@ -1111,15 +972,6 @@ jobs:
|
||||
- name: SQL Server 2022
|
||||
junit-name: be-tests-sqlserver-2022-ee
|
||||
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Transforms Python Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: sqlserver
|
||||
@@ -1135,19 +987,15 @@ jobs:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: 'P@ssw0rd'
|
||||
MSSQL_MEMORY_LIMIT_MB: 1024
|
||||
name: ${{ matrix.version.name }} ${{ matrix.job.name }}
|
||||
name: ${{ matrix.version.name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python-runner
|
||||
if: ${{ matrix.job.needs-python-runner == true }}
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: ${{ matrix.job.test-args }}
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -1160,78 +1008,45 @@ jobs:
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
# TODO(dpsutton, 2025-08-20): Re-enable starburst when we figure out why it randomly dies in CI
|
||||
# be-tests-starburst-ee:
|
||||
# needs: determine-driver-skips
|
||||
# if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
# runs-on: ubuntu-22.04
|
||||
# timeout-minutes: 60
|
||||
# env:
|
||||
# CI: 'true'
|
||||
# DRIVERS: starburst
|
||||
# services:
|
||||
# starburst:
|
||||
# image: trinodb/trino:latest
|
||||
# env:
|
||||
# CATALOG_MANAGEMENT: dynamic
|
||||
# ports:
|
||||
# - "8080:8080"
|
||||
# name: Starburst
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - name: Make plugins directory
|
||||
# run: mkdir plugins
|
||||
# - name: Test Starburst driver
|
||||
# uses: ./.github/actions/test-driver
|
||||
# with:
|
||||
# junit-name: 'be-tests-starburst-ee'
|
||||
# test-args: >-
|
||||
# :only-tags [:mb/driver-tests]
|
||||
# - name: Upload Test Results
|
||||
# uses: ./.github/actions/upload-test-results
|
||||
# if: always()
|
||||
# with:
|
||||
# input-path: ./target/junit/
|
||||
# output-name: ${{ github.job }}
|
||||
# bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
# aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
# aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
# aws-region: ${{ vars.AWS_REGION }}
|
||||
# trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
login-to-ecr:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
be-tests-starburst-ee:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: starburst
|
||||
services:
|
||||
starburst:
|
||||
image: trinodb/trino:latest
|
||||
env:
|
||||
CATALOG_MANAGEMENT: dynamic
|
||||
ports:
|
||||
- "8080:8080"
|
||||
name: Starburst
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
- uses: actions/checkout@v4
|
||||
- name: Make plugins directory
|
||||
run: mkdir plugins
|
||||
- name: Test Starburst driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
role-to-assume: ${{ secrets.CI_IAM_ROLE }}
|
||||
role-session-name: GitHub_to_AWS_via_FederatedOIDC
|
||||
aws-region: us-east-1
|
||||
- name: Get ECR password
|
||||
id: login-ecr
|
||||
run: |
|
||||
PASSWORD=$(aws ecr get-login-password --region us-east-1)
|
||||
|
||||
REGISTRY="$(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com"
|
||||
|
||||
echo "docker_username=AWS" >> $GITHUB_OUTPUT
|
||||
echo "docker_password=$PASSWORD" >> $GITHUB_OUTPUT
|
||||
echo "registry=$REGISTRY" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
registry: ${{ steps.login-ecr.outputs.registry }}
|
||||
docker_username: ${{ steps.login-ecr.outputs.docker_username }}
|
||||
docker_password: ${{ steps.login-ecr.outputs.docker_password }}
|
||||
junit-name: 'be-tests-starburst-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-vertica-ee:
|
||||
needs: [determine-driver-skips, login-to-ecr]
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -1239,12 +1054,9 @@ jobs:
|
||||
DRIVERS: vertica
|
||||
services:
|
||||
vertica:
|
||||
image: ${{ needs.login-to-ecr.outputs.registry }}/vertica-ce:25.3.0-0
|
||||
image: vertica/vertica-ce:12.0.2-0
|
||||
ports:
|
||||
- "5433:5433"
|
||||
credentials:
|
||||
username: ${{ needs.login-to-ecr.outputs.docker_username }}
|
||||
password: ${{ needs.login-to-ecr.outputs.docker_password }}
|
||||
name: Vertica
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -1256,7 +1068,6 @@ jobs:
|
||||
junit-name: 'be-tests-vertica-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -1290,9 +1101,8 @@ jobs:
|
||||
- be-tests-sparksql-ee
|
||||
- be-tests-sqlite-ee
|
||||
- be-tests-sqlserver
|
||||
# - be-tests-starburst-ee
|
||||
# TODO(rileythomp, 2025-08-20): Re-enable vertica tests once the docker image is updated
|
||||
# - be-tests-vertica-ee
|
||||
- be-tests-starburst-ee
|
||||
- be-tests-vertica-ee
|
||||
- be-tests-clickhouse-ee
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
@@ -1314,7 +1124,7 @@ jobs:
|
||||
}));
|
||||
|
||||
// are all jobs skipped or successful?
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success' || job.name == 'be-tests-starburst-ee'))) {
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
||||
console.log("");
|
||||
console.log(" _------. ");
|
||||
console.log(" / , \_ ");
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
# Thi workflow does the backward compatibility tests
|
||||
# between the Embedding SDK package of version N and Embedding SDK Bundle of version N - 1
|
||||
# N - 1 is a target of a PR
|
||||
name: Embedding SDK backward-compatibility E2E Component Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cached-embedding-sdk-dist-artifact-name:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: "SDK backward-compatibility E2E > Check which files changed"
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
decide-target-ref:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
target-ref: ${{ steps.get-target-ref.outputs.value }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Decide ref to checkout
|
||||
id: get-target-ref
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "value=${{ github.base_ref }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "value=$(git rev-parse HEAD^1)" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# if this is a test on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
name: "SDK backward-compatibility E2E > [Backport only] Get build requirements"
|
||||
needs: [decide-target-ref]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.base_ref || github.ref, 'release-x')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
|
||||
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get build dependencies
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const {
|
||||
getBuildRequirements,
|
||||
getVersionFromReleaseBranch,
|
||||
} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const targetBranchName = '${{ github.base_ref || github.ref }}';
|
||||
|
||||
const version = getVersionFromReleaseBranch(targetBranchName);
|
||||
const requirements = getBuildRequirements(version);
|
||||
|
||||
return {
|
||||
java_version: requirements.java,
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
e2e-tests:
|
||||
name: "SDK backward-compatibility E2E > Run e2e tests"
|
||||
needs: [files-changed, decide-target-ref, get-build-requirements]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.files-changed.outputs.e2e_embedding_sdk == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: uberjar
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
|
||||
|
||||
- name: Build uberjar with ./bin/build.sh
|
||||
run: ./bin/build.sh
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.cached-embedding-sdk-dist-artifact-name }}
|
||||
path: ${{ github.workspace }}/resources/embedding-sdk
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
- name: Make app db snapshot
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js component \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }} \
|
||||
--env grepTags="-@skip-backward-compatibility",grepOmitFiltered=true
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-embedding-sdk
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-embedding-sdk-cross-version
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const {
|
||||
generateReport,
|
||||
parseReport,
|
||||
formatSummary
|
||||
} = require("./.github/scripts/handle-mochawesome-report.js");
|
||||
|
||||
const report = await generateReport();
|
||||
const results = parseReport(report);
|
||||
const summary = formatSummary(results);
|
||||
|
||||
await core.summary.addRaw(summary).write();
|
||||
243
.github/workflows/e2e-component-tests-embedding-sdk-cross-version.yml
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
name: E2E Cross-version Component Tests for Embedding SDK
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_all: ${{ steps.changes.outputs.e2e_all }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
# if this is a test on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
if: |
|
||||
!cancelled()
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
|
||||
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get build dependencies
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const {
|
||||
getBuildRequirements,
|
||||
getVersionFromReleaseBranch,
|
||||
} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const targetBranchName = '${{ github.base_ref || github.ref }}';
|
||||
|
||||
const version = getVersionFromReleaseBranch(targetBranchName);
|
||||
const requirements = getBuildRequirements(version);
|
||||
|
||||
return {
|
||||
java_version: requirements.java,
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
build:
|
||||
needs: [files-changed, get-build-requirements]
|
||||
if: |
|
||||
!cancelled()
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
INTERACTIVE: false
|
||||
# make sure that builds on release branches get licenses, because we use them for releases
|
||||
SKIP_LICENSES: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: uberjar
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 11 }}"
|
||||
|
||||
- name: Build uberjar with ./bin/build.sh
|
||||
run: ./bin/build.sh
|
||||
|
||||
- name: Prepare uberjar artifact
|
||||
uses: ./.github/actions/prepare-uberjar-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-cross-version
|
||||
|
||||
resolve-sdk-version:
|
||||
if: |
|
||||
!cancelled()
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
sdk_version: ${{ fromJson(steps.dependencies.outputs.result).sdk_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get latest SDK release for this branch
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const { getSdkVersionFromReleaseBranchName } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const branchName = '${{ github.base_ref || github.ref }}';
|
||||
|
||||
const sdk_version = await getSdkVersionFromReleaseBranchName({
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
branchName,
|
||||
});
|
||||
|
||||
return {
|
||||
sdk_version,
|
||||
};
|
||||
|
||||
e2e-tests:
|
||||
needs: [get-build-requirements, build, resolve-sdk-version]
|
||||
if: |
|
||||
!cancelled() && needs.build.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-tests-embedding-sdk
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
# 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 }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Docker containers
|
||||
uses: ./.github/actions/e2e-prepare-containers
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# Needed to omit newly added tests for functionality that is not yet released
|
||||
- name: Sparse checkout tests from SDK release commit
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: embedding-sdk-${{ needs.resolve-sdk-version.outputs.sdk_version }}
|
||||
path: sdk-release-e2e
|
||||
sparse-checkout: e2e
|
||||
|
||||
- name: Move tests from the release commit into the repo
|
||||
run: |
|
||||
rm -rf ./e2e/support/*
|
||||
mv ./sdk-release-e2e/e2e/support/* ./e2e/support
|
||||
|
||||
rm -rf ./e2e/test-component/*
|
||||
mv ./sdk-release-e2e/e2e/test-component/* ./e2e/test-component
|
||||
shell: bash
|
||||
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-cross-version
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare JDK ${{ needs.get-build-requirements.outputs.java_version || 11 }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ needs.get-build-requirements.outputs.java_version || 11 }}
|
||||
distribution: "temurin"
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Install Embedding SDK package v${{ needs.resolve-sdk-version.outputs.sdk_version }}
|
||||
run: yarn add @metabase/embedding-sdk-react@${{ needs.resolve-sdk-version.outputs.sdk_version }}
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
- name: Make app db snapshot
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
run: node e2e/runner/run_cypress_ci.js component --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-embedding-sdk
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-embedding-sdk-cross-version
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const {
|
||||
generateReport,
|
||||
parseReport,
|
||||
formatSummary
|
||||
} = require("./.github/scripts/handle-mochawesome-report.js");
|
||||
|
||||
const report = await generateReport();
|
||||
const results = parseReport(report);
|
||||
const summary = formatSummary(results);
|
||||
|
||||
await core.summary.addRaw(summary).write();
|
||||
|
||||
e2e-tests-skipped-stub:
|
||||
needs: [e2e-tests]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.e2e-tests.result == 'skipped'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
name: e2e-component-tests-embedding-sdk-cross-version
|
||||
steps:
|
||||
- run: |
|
||||
echo "Didn't run due to conditional filtering"
|
||||
@@ -9,11 +9,11 @@ on:
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: "SDK e2e > Check which files changed"
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
|
||||
e2e_all: ${{ steps.changes.outputs.e2e_all }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
@@ -25,7 +25,6 @@ jobs:
|
||||
|
||||
# if this is a test on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
name: "SDK e2e > [Backport only] Get build requirements"
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.base_ref || github.ref, 'release-x')
|
||||
@@ -60,11 +59,10 @@ jobs:
|
||||
};
|
||||
|
||||
build-uberjar:
|
||||
name: "SDK e2e > Build uberjar"
|
||||
needs: [files-changed, get-build-requirements]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.files-changed.outputs.e2e_embedding_sdk == 'true'
|
||||
needs.files-changed.outputs.e2e_all == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
env:
|
||||
@@ -92,10 +90,9 @@ jobs:
|
||||
- name: Prepare uberjar artifact
|
||||
uses: ./.github/actions/prepare-uberjar-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-sdk-tests
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
e2e-tests:
|
||||
name: "SDK e2e > Run e2e tests (React ${{ matrix.react-version }})"
|
||||
needs: [get-build-requirements, build-uberjar]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
@@ -106,17 +103,16 @@ jobs:
|
||||
matrix:
|
||||
react-version: [18, 19]
|
||||
fail-fast: false
|
||||
name: React ${{ matrix.react-version }}
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
|
||||
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
CYPRESS_IS_EMBEDDING_SDK: true
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
@@ -124,10 +120,16 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Docker containers
|
||||
uses: ./.github/actions/e2e-prepare-containers
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-sdk-tests
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
@@ -178,7 +180,7 @@ jobs:
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: "SDK e2e > Upload Cypress Artifacts upon failure"
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
@@ -188,7 +190,7 @@ jobs:
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: "SDK e2e > Publish Summary"
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
|
||||
155
.github/workflows/e2e-cross-version-for-breaking-changes.yml
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
name: E2E Cross-version Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
fe-ref:
|
||||
description: "Frontend git ref to use"
|
||||
type: string
|
||||
required: true
|
||||
default: "master"
|
||||
be-ref:
|
||||
description: "Backend git ref to use"
|
||||
type: string
|
||||
required: true
|
||||
default: "master"
|
||||
total-shards:
|
||||
description: "Total number of parallel test shards"
|
||||
type: number
|
||||
required: false
|
||||
default: 4
|
||||
workflow_call:
|
||||
inputs:
|
||||
fe-ref:
|
||||
description: "Frontend git ref to use"
|
||||
type: string
|
||||
required: true
|
||||
be-ref:
|
||||
description: "Backend git ref to use"
|
||||
type: string
|
||||
required: true
|
||||
total-shards:
|
||||
description: "Total number of parallel test shards"
|
||||
type: number
|
||||
required: false
|
||||
default: 4
|
||||
|
||||
jobs:
|
||||
generate-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix_json: ${{ steps.set_matrix.outputs.matrix_json }}
|
||||
shards_total: ${{ steps.set_matrix.outputs.shards_total }}
|
||||
steps:
|
||||
- name: Set up shard matrix
|
||||
id: set_matrix
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
# generate an output like `{"matrixJson": "{\"shard\": [1, 2, 3, 4]}", "shardsTotal": 4}` to be used in the other jobs
|
||||
script: |
|
||||
const totalShards = parseInt(process.env.TOTAL_SHARDS_INPUT, 10);
|
||||
|
||||
const matrix = { shard: Array.from({ length: totalShards }, (_, i) => i + 1) };
|
||||
|
||||
console.log("Generated matrix JSON", matrix);
|
||||
console.log(`Total shards: ${totalShards}`);
|
||||
|
||||
core.setOutput('matrix_json', JSON.stringify(matrix));
|
||||
core.setOutput('shards_total', totalShards);
|
||||
env:
|
||||
TOTAL_SHARDS_INPUT: ${{ inputs.total-shards }}
|
||||
|
||||
build-jar:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Node.js
|
||||
# We're not using `actions/prepare-frontend` because we don't want to install deps from the root repo
|
||||
# nor we want to touch the cache as we'll be potentially be installing different versions of packages
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: cross-version-uberjar
|
||||
|
||||
- name: Build jar with different frontend and backend version
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
CYPRESS_CI: true
|
||||
run: |
|
||||
node bin/backward-compatibility-test.js \
|
||||
${{ inputs.fe-ref }} ${{ inputs.be-ref }} \
|
||||
build
|
||||
|
||||
- name: Upload Uberjar
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: metabase-jar-cross-version
|
||||
path: .tmp/metabase-be/target/uberjar/metabase.jar
|
||||
if-no-files-found: error
|
||||
|
||||
run-breaking-changes-tests:
|
||||
needs: [build-jar, generate-matrix]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
TOTAL_SHARDS: ${{ needs.generate-matrix.outputs.shards_total }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix_json) }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: cross-version-uberjar
|
||||
|
||||
- name: Download Uberjar
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-jar-cross-version
|
||||
path: .tmp/metabase-be/target/uberjar/
|
||||
|
||||
- name: Download FE repo
|
||||
run: |
|
||||
git clone --depth 1 -b ${{ inputs.fe-ref }} https://github.com/metabase/metabase.git .tmp/metabase-fe
|
||||
|
||||
- name: Install FE dependencies
|
||||
working-directory: .tmp/metabase-fe
|
||||
run: |
|
||||
yarn install
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
with:
|
||||
working-directory: .tmp/metabase-fe
|
||||
|
||||
- name: Run the tests
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
|
||||
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
|
||||
CYPRESS_CI: true
|
||||
run: |
|
||||
node bin/backward-compatibility-test.js \
|
||||
${{ inputs.fe-ref }} ${{ inputs.be-ref }} \
|
||||
start > /dev/null & \
|
||||
node bin/backward-compatibility-test.js \
|
||||
${{ inputs.fe-ref }} ${{ inputs.be-ref }} \
|
||||
test ${{ matrix.shard}}/${{ env.TOTAL_SHARDS }}
|
||||
12
.github/workflows/e2e-cross-version.yml
vendored
@@ -66,10 +66,8 @@ jobs:
|
||||
docker run -d \
|
||||
-v $PWD/my-metabase:/metabase.db \
|
||||
-p 3000:3000 \
|
||||
-e CYPRESS_MB_ALL_FEATURES_TOKEN=${{ secrets.MB_ALL_FEATURES_TOKEN }} \
|
||||
-e CYPRESS_MB_STARTER_CLOUD_TOKEN=${{ secrets.MB_STARTER_CLOUD_TOKEN }} \
|
||||
-e CYPRESS_MB_PRO_CLOUD_TOKEN=${{ secrets.MB_PRO_CLOUD_TOKEN }} \
|
||||
-e CYPRESS_MB_PRO_SELF_HOSTED_TOKEN=${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }} \
|
||||
-e CYPRESS_ALL_FEATURES_TOKEN=${{ secrets.ENTERPRISE_TOKEN }} \
|
||||
-e CYPRESS_NO_FEATURES_TOKEN=${{ secrets.E2E_STARTER_TOKEN }} \
|
||||
--name metabase-${{ matrix.version.source }} \
|
||||
${{ env.DOCKER_SOURCE_IMAGE }}
|
||||
- name: Wait for Metabase to start on port 3000
|
||||
@@ -99,10 +97,8 @@ jobs:
|
||||
docker run -d \
|
||||
-v $PWD/my-metabase:/metabase.db \
|
||||
-p 3001:3000 \
|
||||
-e CYPRESS_MB_ALL_FEATURES_TOKEN=${{ secrets.MB_ALL_FEATURES_TOKEN }} \
|
||||
-e CYPRESS_MB_STARTER_CLOUD_TOKEN=${{ secrets.MB_STARTER_CLOUD_TOKEN }} \
|
||||
-e CYPRESS_MB_PRO_CLOUD_TOKEN=${{ secrets.MB_PRO_CLOUD_TOKEN }} \
|
||||
-e CYPRESS_MB_PRO_SELF_HOSTED_TOKEN=${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }} \
|
||||
-e CYPRESS_ALL_FEATURES_TOKEN=${{ secrets.ENTERPRISE_TOKEN }} \
|
||||
-e CYPRESS_NO_FEATURES_TOKEN=${{ secrets.E2E_STARTER_TOKEN }} \
|
||||
--name metabase-${{ matrix.version.target }} \
|
||||
${{ env.DOCKER_TARGET_IMAGE }}
|
||||
- name: Wait for Metabase to start on port 3001
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: "[NOT REQUIRED] SDK Compatibility tests"
|
||||
name: E2E Embedding SDK Compatibility tests
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -178,8 +178,12 @@ jobs:
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
shell: bash
|
||||
|
||||
- name: Build Embedding SDK package
|
||||
run: yarn build-embedding-sdk-package
|
||||
run: yarn build-embedding-sdk
|
||||
|
||||
- name: Prepare Embedding SDK artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -187,12 +191,6 @@ jobs:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: resources/embedding-sdk
|
||||
|
||||
# In order to check compatibility between Sample Apps and Embedding SDK, the job for each sample app:
|
||||
# - pulls a Sample App
|
||||
# - starts it
|
||||
# - runs its Cypress tests against the local metabase.jar and local @metabase/embedding-sdk-react package.
|
||||
# The job is not required, though if it's failing better to be sure that it's not caused by a breaking change.
|
||||
# For more details see `Sample Apps compatibility with Embedding SDK tests` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
|
||||
e2e-sample-apps-tests:
|
||||
needs: [get-build-requirements, get-sample-apps-data, build]
|
||||
strategy:
|
||||
@@ -210,8 +208,8 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
|
||||
HASH: ${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_attempt }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: true
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
SAMPLE_APP_BRANCH_NAME: ${{ needs.get-sample-apps-data.outputs.branch_name }}
|
||||
@@ -310,25 +308,17 @@ jobs:
|
||||
run: |
|
||||
exit 1
|
||||
|
||||
# The job checks integration of the Embedding SDK with consumer's apps that use different frameworks/bundlers,
|
||||
# or when we want to test some tricky integration cases like conflicting types.
|
||||
# The job
|
||||
# - starts an app
|
||||
# - runs its Cypress tests against the e2e test metabase instance and local @metabase/embedding-sdk-react package.
|
||||
# The job is not required, though if it's failing better to be sure that it's not caused by a breaking change.
|
||||
# For more details see `Embedding SDK integration tests with Host Apps` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
|
||||
e2e-host-apps-tests:
|
||||
needs: [get-build-requirements, build]
|
||||
needs: [ get-build-requirements, build ]
|
||||
strategy:
|
||||
matrix:
|
||||
test_suite:
|
||||
[
|
||||
"vite-6-host-app-e2e",
|
||||
"next-15-app-router-host-app-e2e",
|
||||
"next-15-pages-router-host-app-e2e",
|
||||
"angular-20-host-app-e2e",
|
||||
]
|
||||
environment: ["production", "development"]
|
||||
test_suite: [
|
||||
"vite-6-host-app-e2e",
|
||||
"next-15-app-router-host-app-e2e",
|
||||
"next-15-pages-router-host-app-e2e",
|
||||
"angular-20-host-app-e2e",
|
||||
]
|
||||
environment: [ "production", "development" ]
|
||||
if: |
|
||||
!cancelled() && needs.build.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -340,8 +330,8 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
|
||||
HASH: ${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_attempt }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "true"
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: true
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
HOST_APP_ENVIRONMENT: ${{ matrix.environment }}
|
||||
|
||||
@@ -63,10 +63,8 @@ jobs:
|
||||
DISPLAY: ""
|
||||
QA_DB_ENABLED: ${{ contains(fromJSON('["sql", "mongo"]'), inputs.qa_db) }}
|
||||
CYPRESS_QA_DB_MONGO: ${{ inputs.qa_db == 'mongo' }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
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
|
||||
MB_EDITION: ${{ inputs.mb_edition }}
|
||||
@@ -86,7 +84,6 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
maildev: true
|
||||
openldap: true
|
||||
snowplow: true
|
||||
postgres: ${{ inputs.qa_db == 'sql' }}
|
||||
mysql: ${{ inputs.qa_db == 'sql' }}
|
||||
mongo: ${{ inputs.qa_db == 'mongo' }}
|
||||
@@ -106,6 +103,8 @@ jobs:
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
- name: Run Snowplow micro
|
||||
uses: ./.github/actions/run-snowplow-micro
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
- name: Generate database snapshots
|
||||
|
||||
91
.github/workflows/e2e-test.yml
vendored
@@ -44,10 +44,8 @@ jobs:
|
||||
JOB_NAME: ${{ inputs.name }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
|
||||
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
MB_EXPERIMENTAL_SEARCH_BLOCK_ON_QUEUE: true
|
||||
@@ -57,17 +55,15 @@ jobs:
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
CYPRESS_QA_DB_MONGO: ${{ inputs.name == 'mongo' }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: "${{ inputs.name == 'embedding-sdk' }}"
|
||||
CYPRESS_IS_EMBEDDING_SDK: ${{ inputs.name == 'embedding-sdk' }}
|
||||
mongo: ${{ inputs.name == 'mongo' }}
|
||||
postgres: ${{ inputs.name != 'mongo' }}
|
||||
mysql: ${{ inputs.name != 'mongo' }}
|
||||
openldap: true
|
||||
maildev: true
|
||||
webhook: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
- name: Setup python-runner
|
||||
env:
|
||||
METABASE_AUTOMATION_USER_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: ./.github/scripts/setup-python-runner.sh
|
||||
|
||||
- name: Prepare Docker containers
|
||||
uses: ./.github/actions/e2e-prepare-containers
|
||||
@@ -75,23 +71,20 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
maildev: true
|
||||
openldap: true
|
||||
webhook: true
|
||||
snowplow: true
|
||||
postgres: ${{ inputs.name != 'mongo' }}
|
||||
mysql: ${{ inputs.name != 'mongo' }}
|
||||
mongo: ${{ inputs.name == 'mongo' }}
|
||||
openldap: ${{ env.openldap }}
|
||||
postgres: ${{ env.postgres }}
|
||||
mysql: ${{ env.mysql }}
|
||||
mongo: ${{ env.mongo }}
|
||||
webhook: ${{ env.webhook }}
|
||||
|
||||
- name: Run Embedding SDK Storybook
|
||||
if: inputs.name == 'embedding-sdk'
|
||||
uses: ./.github/actions/run-embedding-sdk-storybook
|
||||
|
||||
- name: Retrieve uberjar artifact for ${{ inputs.edition }}
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-${{ inputs.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
extract-path: "target/uberjar"
|
||||
output-name: "metabase.jar"
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
@@ -118,6 +111,9 @@ jobs:
|
||||
if: github.run_attempt != '1'
|
||||
run: echo "specs=$(cat failed-specs)" >> $GITHUB_ENV
|
||||
|
||||
- name: Run Snowplow micro
|
||||
uses: ./.github/actions/run-snowplow-micro
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
@@ -131,7 +127,7 @@ jobs:
|
||||
SPLIT_INDEX: ${{ inputs.split_index }}
|
||||
SPLIT_FILE: "./e2e/support/timings.json"
|
||||
SPLIT_OUTPUT_FILE: "newTimes.json"
|
||||
SPLIT_TIME_THRESHOLD: 0.01 # 1% threshold for changes
|
||||
SPLIT_TIME_THRESHOLD: 5.0
|
||||
# do not write cypress-split summary to github
|
||||
SPLIT_SUMMARY: false
|
||||
SPEC_PATH: ${{ env.specs || inputs.specs }}
|
||||
@@ -139,7 +135,7 @@ jobs:
|
||||
run: |
|
||||
echo "Running tests with specs: $SPEC_PATH"
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="-@mongo+-@flaky+-@OSS+-@skip",grepOmitFiltered=true \
|
||||
--env grepTags="-@mongo+-@flaky+-@OSS --@quarantine",grepOmitFiltered=true \
|
||||
--spec "$SPEC_PATH" \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
@@ -151,7 +147,7 @@ jobs:
|
||||
run: |
|
||||
echo "Running tests with specs: $SPEC_PATH"
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="${{inputs.tags && format('{0}+-@skip', inputs.tags) || '-@skip'}}",grepOmitFiltered=true \
|
||||
--env grepTags="${{inputs.tags}} --@quarantine",grepOmitFiltered=true \
|
||||
--spec "$SPEC_PATH" \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
@@ -185,51 +181,6 @@ jobs:
|
||||
name: failed-tests-${{ inputs.name }}-${{ inputs.edition }}
|
||||
path: ./cypress/test-results
|
||||
|
||||
- name: Extract chunk-specific changed timings
|
||||
id: extract-timings
|
||||
if: inputs.split_index >= 0 && github.ref == 'refs/heads/master'
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const oldTimingsPath = "./e2e/support/timings.json";
|
||||
const newTimingsPath = "./e2e/support/newTimes.json";
|
||||
|
||||
if (!fs.existsSync(newTimingsPath)) {
|
||||
throw new Error(`newTimes.json not found - cypress-split may not have generated it`);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(oldTimingsPath)) {
|
||||
throw new Error(`Original timings file ${oldTimingsPath} not found`);
|
||||
}
|
||||
|
||||
const oldTimings = fs.existsSync(oldTimingsPath)
|
||||
? JSON.parse(fs.readFileSync(oldTimingsPath, "utf8"))
|
||||
: { durations: [] };
|
||||
|
||||
const newTimings = fs.existsSync(newTimingsPath)
|
||||
? JSON.parse(fs.readFileSync(newTimingsPath, "utf8"))
|
||||
: { durations: [] };
|
||||
|
||||
const { extractChangedTimings } = require('${{ github.workspace }}/.github/scripts/extract-changed-timings.js');
|
||||
|
||||
const result = extractChangedTimings({ oldTimings, newTimings });
|
||||
|
||||
if (result.hasChanges) {
|
||||
core.setOutput('has-changes', 'true');
|
||||
fs.writeFileSync('chunkTimings.json', JSON.stringify(result.changedTimings, null, 2));
|
||||
console.log(`Extracted ${Object.keys(result.changedTimings).length} changed timings for chunk index ${{ inputs.split_index }}`);
|
||||
}
|
||||
|
||||
- name: Upload Timing Data
|
||||
uses: actions/upload-artifact@v4
|
||||
if: steps.extract-timings.outcome == 'success' && steps.extract-timings.outputs.has-changes == 'true'
|
||||
with:
|
||||
name: timing-data-${{ inputs.name }}-${{ inputs.split_index }}
|
||||
path: chunkTimings.json
|
||||
retention-days: 1 # gets merged immediately by parent workflow
|
||||
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
|
||||
141
.github/workflows/e2e-tests.yml
vendored
@@ -22,6 +22,72 @@ jobs:
|
||||
chunks: 50
|
||||
specs: ${{ inputs.specs }}
|
||||
|
||||
# if this is a test on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.ref, 'release-x')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
|
||||
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get build dependencies
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const { getBuildRequirements, getVersionFromReleaseBranch } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
|
||||
const version = getVersionFromReleaseBranch('${{ github.ref }}');
|
||||
const requirements = getBuildRequirements(version);
|
||||
|
||||
return {
|
||||
java_version: requirements.java,
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
build:
|
||||
needs: [get-build-requirements]
|
||||
if: ${{ !cancelled() && !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
edition: [oss, ee]
|
||||
env:
|
||||
MB_EDITION: ${{ matrix.edition }}
|
||||
INTERACTIVE: false
|
||||
# make sure that builds on release branches get licenses, because we use them for releases
|
||||
SKIP_LICENSES: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: uberjar
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
|
||||
|
||||
- name: Build uberjar with ./bin/build.sh
|
||||
timeout-minutes: 10
|
||||
run: ./bin/build.sh
|
||||
shell: bash
|
||||
|
||||
- name: Prepare uberjar artifact
|
||||
uses: ./.github/actions/prepare-uberjar-artifact
|
||||
with:
|
||||
name: metabase-${{ matrix.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
reset-e2e-test-comment:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
runs-on: ubuntu-latest
|
||||
@@ -58,8 +124,9 @@ jobs:
|
||||
uses: ./.github/workflows/e2e-test.yml
|
||||
needs:
|
||||
- e2e-matrix-builder
|
||||
- build
|
||||
if: |
|
||||
!cancelled() && !inputs.skip
|
||||
!cancelled() && needs.build.result == 'success'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.e2e-matrix-builder.outputs.matrix) }}
|
||||
@@ -78,76 +145,16 @@ jobs:
|
||||
name: Flaky e2e Tests
|
||||
uses: ./.github/workflows/e2e-test.yml
|
||||
needs:
|
||||
- build
|
||||
- e2e-matrix-builder
|
||||
# skip flaky test run if only e2e specs changed as an optimization
|
||||
if: |
|
||||
!cancelled() && !inputs.skip && needs.e2e-matrix-builder.outputs.isDefaultSpecPattern == 'true'
|
||||
!cancelled() && needs.build.result == 'success' && needs.e2e-matrix-builder.outputs.isDefaultSpecPattern == 'true'
|
||||
secrets: inherit
|
||||
with:
|
||||
name: Flaky
|
||||
tags: "@flaky"
|
||||
|
||||
collect-timings:
|
||||
name: Collect E2E Timings
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
needs: e2e-tests
|
||||
if: always() && !cancelled() && github.ref == 'refs/heads/master'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
.github
|
||||
e2e/support
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Download all timing artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: timing-data-*
|
||||
path: timing-files
|
||||
merge-multiple: false
|
||||
continue-on-error: true
|
||||
|
||||
- name: Merge split timings
|
||||
id: merge
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const chunkFiles = glob.sync('timing-files/*/chunkTimings.json');
|
||||
|
||||
const specs = new Map();
|
||||
chunkFiles.forEach(file => {
|
||||
const { durations = [] } = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
durations.forEach(({ spec, duration }) => specs.set(spec, duration));
|
||||
});
|
||||
|
||||
if (specs.size === 0) {
|
||||
throw new Error('No timing data found - no specs had timing changes');
|
||||
}
|
||||
|
||||
const outputPath = 'e2e/support/runTimings.json';
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
|
||||
const runTimings = {
|
||||
durations: [...specs].map(([spec, duration]) => ({ spec, duration }))
|
||||
};
|
||||
|
||||
fs.writeFileSync(outputPath, JSON.stringify(runTimings, null, 2));
|
||||
console.log(`Merged timing data for ${specs.size} specs`);
|
||||
|
||||
- name: Upload merged timing files
|
||||
if: steps.merge.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: merged-e2e-timings
|
||||
path: e2e/support/runTimings.json
|
||||
retention-days: 7
|
||||
|
||||
e2e-tests-result:
|
||||
needs:
|
||||
- e2e-tests
|
||||
@@ -174,3 +181,11 @@ jobs:
|
||||
echo "E2E Tests failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pr-env:
|
||||
needs: [build]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.event.pull_request.labels.*.name, 'PR-Env')
|
||||
uses: ./.github/workflows/pr-env.yml
|
||||
secrets: inherit
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
name: Embedding SDK Labels Reminder
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [auto_merge_enabled, review_requested, labeled, unlabeled, synchronize]
|
||||
branches:
|
||||
- "master"
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
frontend_embedding_sdk_package_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_package_sources }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
embedding-sdk-package-no-release-notes-reminder:
|
||||
needs: [ files-changed ]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
(needs.files-changed.outputs.frontend_embedding_sdk_package_sources == 'true')
|
||||
name: Embedding SDK package release-notes/no-release-notes label reminder
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: jesusvasquez333/verify-pr-label-action@v1.4.0
|
||||
id: verify-release-notes-labels
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
valid-labels: 'release-notes, no-release-notes'
|
||||
disable-reviews: true
|
||||
- name: Comment on PR if label should be added
|
||||
if: ${{ always() }}
|
||||
uses: ./.github/actions/notify-pull-request
|
||||
with:
|
||||
remove-previous-comment: true
|
||||
add-new-comment: ${{ steps.verify-release-notes-labels.outcome == 'failure' }}
|
||||
message: "⚠️ Changes were made to the <b>Embedding SDK Package</b>. Review this PR and apply one of the following labels.<br><br>- <code>release-notes</code> — select if this PR must be included in the main Metabase changelog release notes.<br>- <code>no-release-notes</code> — select if this PR should not be included in the main Metabase changelog release notes."
|
||||
include-log: true
|
||||
44
.github/workflows/embedding-sdk.yml
vendored
@@ -1,7 +1,12 @@
|
||||
name: SDK
|
||||
name: Embedding SDK
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
@@ -13,9 +18,8 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_embedding_sdk: ${{ steps.changes.outputs.e2e_embedding_sdk }}
|
||||
documentation: ${{ steps.changes.outputs.documentation }}
|
||||
frontend_embedding_sdk_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_sources }}
|
||||
embedding_documentation: ${{ steps.changes.outputs.embedding_documentation }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
@@ -29,9 +33,8 @@ jobs:
|
||||
needs: [files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
(needs.files-changed.outputs.e2e_embedding_sdk == 'true' ||
|
||||
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' ||
|
||||
needs.files-changed.outputs.embedding_documentation == 'true')
|
||||
(needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' ||
|
||||
needs.files-changed.outputs.documentation == 'true')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
@@ -43,7 +46,7 @@ jobs:
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
- name: Build Embedding SDK package
|
||||
run: yarn build-embedding-sdk-package
|
||||
run: yarn build-embedding-sdk
|
||||
- name: Prepare Embedding SDK artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -72,7 +75,7 @@ jobs:
|
||||
embedding-sdk-docs-snippets-type-check:
|
||||
needs: [files-changed, build]
|
||||
if: |
|
||||
needs.files-changed.outputs.embedding_documentation == 'true' &&
|
||||
needs.files-changed.outputs.documentation == 'true' &&
|
||||
needs.build.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
@@ -124,7 +127,7 @@ jobs:
|
||||
with:
|
||||
remove-previous-comment: true
|
||||
add-new-comment: ${{ steps.docs-generation.outcome == 'failure' && contains(steps.docs-generation.outputs.output, 'not included in the documentation') }}
|
||||
message: "📝 Some exported types from `@metabase/embedding-sdk-react` reference other types that are not exported, i.e. internal types.<br><br>The internal type reference breaks API documentation generation for the SDK and also may unexpectedly increase the output size of the type declaration file.<br><br>Please check the CI logs to find the name of such type.<br><br>- If a type should be publicly available in the SDK, add a description to it or a TypeDoc annotation and export it from SDK.<br>- If a type is internal and should _not_ be publicly exposed, refactor types to prevent referencing other types that are not exported from SDK.<br><br>You can run `yarn embedding-sdk:docs:generate` locally to verify that the issue has been resolved."
|
||||
message: "📝 Some exported from `@metabase/embedding-sdk-react` types reference other types that are not exported, i.e. internal types.<br><br>The internal type reference breaks API documentation generation for the SDK and also may unexpectedly increase the output size of the type declaration file.<br><br>Please check the CI logs to find the name of such type.<br><br>- If a type should be publicly available in the SDK, add a description to it or a TypeDoc annotation and export it from SDK.<br>- If a type is internal and should _not_ be publicly exposed, refactor types to prevent referencing other types that are not exported from SDK.<br><br>You can run `yarn embedding-sdk:docs:generate` locally to verify that the issue has been resolved."
|
||||
include-log: true
|
||||
- name: Comment on PR if 'Failed to resolve link' error happens
|
||||
uses: ./.github/actions/notify-pull-request
|
||||
@@ -137,22 +140,27 @@ jobs:
|
||||
if: ${{ steps.docs-generation.outcome == 'failure' }}
|
||||
run: exit 1
|
||||
|
||||
# See `Component e2e tests` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
|
||||
sdk-e2e-tests:
|
||||
name: "SDK e2e tests"
|
||||
uses: ./.github/workflows/e2e-component-tests-embedding-sdk.yml
|
||||
needs: [build]
|
||||
secrets: inherit
|
||||
with:
|
||||
cached-embedding-sdk-dist-artifact-name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
e2e-component-tests-embedding-sdk-backward-compatibility:
|
||||
name: "SDK e2e backward compatibility tests"
|
||||
uses: ./.github/workflows/e2e-component-tests-embedding-sdk-backward-compatibility.yml
|
||||
needs: [build]
|
||||
e2e-component-tests-embedding-sdk-cross-version:
|
||||
# run on: every push on release branches, and every pr that targets a release branch
|
||||
if: ${{ startsWith(github.ref, 'refs/heads/release-x.') || (github.event_name == 'pull_request' && startsWith(github.base_ref, 'release-x.')) }}
|
||||
uses: ./.github/workflows/e2e-component-tests-embedding-sdk-cross-version.yml
|
||||
secrets: inherit
|
||||
|
||||
cross-version-for-breaking-changes:
|
||||
# run on: every pr that targets a release branch
|
||||
if: ${{ github.event_name == 'pull_request' && startsWith(github.base_ref, 'release-x.') }}
|
||||
uses: ./.github/workflows/e2e-cross-version-for-breaking-changes.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
cached-embedding-sdk-dist-artifact-name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
fe-ref: ${{ github.base_ref }}
|
||||
be-ref: ${{ github.head_ref }}
|
||||
|
||||
embedding-sdk-tests-result:
|
||||
needs:
|
||||
@@ -160,7 +168,7 @@ jobs:
|
||||
- embedding-sdk-docs-snippets-type-check
|
||||
- embedding-sdk-api-documentation-generation-validation
|
||||
- sdk-e2e-tests
|
||||
- e2e-component-tests-embedding-sdk-backward-compatibility
|
||||
- e2e-component-tests-embedding-sdk-cross-version
|
||||
if: always() && !cancelled()
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
34
.github/workflows/frontend.yml
vendored
@@ -9,16 +9,6 @@ on:
|
||||
skip-lint:
|
||||
type: boolean
|
||||
default: false
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
skip:
|
||||
description: "Skip main frontend jobs (type-check, unit tests, timezone tests)"
|
||||
type: boolean
|
||||
default: false
|
||||
skip-lint:
|
||||
description: "Skip linting jobs (prettier, eslint, css variables check)"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-frontend
|
||||
@@ -52,8 +42,6 @@ jobs:
|
||||
run: yarn run lint-prettier-pure
|
||||
- name: Run ESLint linter
|
||||
run: yarn run lint-eslint-pure
|
||||
- name: Run CSS linter
|
||||
run: yarn run lint-css
|
||||
|
||||
fe-type-check:
|
||||
if: ${{ !inputs.skip }}
|
||||
@@ -75,13 +63,8 @@ jobs:
|
||||
fe-tests-unit:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2] # 2 shards for parallel execution
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
SHARDS: 2 # should match matrix above
|
||||
JEST_JUNIT_OUTPUT_DIR: ./target/junit
|
||||
JEST_JUNIT_OUTPUT_NAME: test-report-frontend-unit.xml
|
||||
steps:
|
||||
@@ -92,11 +75,22 @@ jobs:
|
||||
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
|
||||
run: yarn run test-unit --silent --shard=${{ matrix.shard }}/${{ env.SHARDS }}
|
||||
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@v4
|
||||
with:
|
||||
files: ./coverage/lcov.info
|
||||
flags: front-end
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always() && github.event_name != 'workflow_dispatch'
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: frontend-unit
|
||||
|
||||
10
.github/workflows/fuzzing.yml
vendored
@@ -7,10 +7,10 @@ on:
|
||||
- "master"
|
||||
- "release-**"
|
||||
paths:
|
||||
- "frontend/src/metabase/querying/expressions/**"
|
||||
- "frontend/src/metabase-lib/expressions/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "frontend/src/metabase/querying/expressions/**"
|
||||
- "frontend/src/metabase-lib/expressions/**"
|
||||
|
||||
jobs:
|
||||
fe-fuzz-lexifier:
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
- run: yarn test-unit frontend/src/metabase/querying/expressions/pratt/fuzz.lexifier.unit.spec.ts
|
||||
- run: yarn test-unit frontend/src/metabase-lib/v1/expressions/pratt/fuzz.lexifier.unit.spec.ts
|
||||
env:
|
||||
MB_FUZZ: 1
|
||||
name: Run fuzz testing on the lexifier
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
- run: yarn test-unit frontend/src/metabase/querying/expressions/fuzz.compiler.unit.spec.ts
|
||||
- run: yarn test-unit frontend/src/metabase-lib/v1/expressions/fuzz.compiler.unit.spec.ts
|
||||
env:
|
||||
MB_FUZZ: 1
|
||||
name: Run fuzz testing on the custom expression compiler
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
- run: yarn test-unit frontend/src/metabase/querying/expressions/fuzz.string.unit.spec.ts
|
||||
- run: yarn test-unit frontend/src/metabase-lib/v1/expressions/fuzz.string.unit.spec.ts
|
||||
env:
|
||||
MB_FUZZ: 1
|
||||
name: Run fuzz testing on the custom expression string quoter
|
||||
|
||||
32
.github/workflows/links.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Links
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
|
||||
jobs:
|
||||
linkChecker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write # required for peter-evans/create-issue-from-file
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Link Checker
|
||||
id: lychee
|
||||
uses: lycheeverse/lychee-action@v2
|
||||
with:
|
||||
fail: false
|
||||
args: docs --include-fragments --config ./.lychee/config.toml --verbose --no-progress
|
||||
format: markdown
|
||||
|
||||
- name: Create Issue From File
|
||||
if: steps.lychee.outputs.exit_code != 0
|
||||
uses: peter-evans/create-issue-from-file@v5
|
||||
with:
|
||||
title: Link Checker Report
|
||||
content-filepath: ./.lychee/out.md
|
||||
labels: "Type:Documentation"
|
||||
assignees: jeff-bruemmer
|
||||
20
.github/workflows/loki-update.yml
vendored
@@ -26,8 +26,26 @@ jobs:
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
|
||||
- name: Build Storybook
|
||||
# We won't be able to use Storybook play's `userEvent` with production build
|
||||
# see https://github.com/storybookjs/storybook/issues/19758
|
||||
run: NODE_ENV=development yarn build-storybook
|
||||
|
||||
- name: Serve Storybook
|
||||
run: yarn http-server storybook-static -p 6006 &
|
||||
shell: bash
|
||||
|
||||
- name: Wait for Storybook to become available
|
||||
run: |
|
||||
echo "Waiting for Storybook to be served..."
|
||||
while ! curl --silent --fail http://localhost:6006; do
|
||||
sleep 5
|
||||
echo "Retrying..."
|
||||
done
|
||||
echo "Storybook is being served!"
|
||||
|
||||
- name: Update Loki Snapshots
|
||||
run: yarn test-visual:loki:ci || yarn test-visual:loki-approve-diff && yarn test-visual:loki-prune
|
||||
run: yarn test-visual:loki || yarn test-visual:loki-approve-diff && yarn test-visual:loki-prune
|
||||
|
||||
- name: Check for changes
|
||||
id: check-changes
|
||||
|
||||
12
.github/workflows/loki.yml
vendored
@@ -1,8 +1,6 @@
|
||||
name: Loki Visual Regression Testing
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
@@ -53,8 +51,14 @@ jobs:
|
||||
- name: Compile CLJS
|
||||
run: NODE_ENV=development yarn build-pure:cljs
|
||||
|
||||
- name: Build Storybook
|
||||
# We won't be able to use Storybook play's `userEvent` with production build
|
||||
# see https://github.com/storybookjs/storybook/issues/19758
|
||||
run: NODE_ENV=development yarn build-storybook
|
||||
|
||||
- name: Run Loki Visual Tests
|
||||
run: yarn test-visual:loki:ci
|
||||
run: |
|
||||
yarn loki --requireReference --reactUri file:./storybook-static --chromeFlags='--headless --disable-gpu' --verboseRenderer
|
||||
|
||||
- name: Generate Visual Report on Failure
|
||||
if: failure()
|
||||
|
||||
133
.github/workflows/optimize-images.yml
vendored
@@ -1,133 +0,0 @@
|
||||
name: Auto-Optimize Docs PNG Images
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
optimize-images:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name,'Type:Documentation') }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install pngcrush and bc
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y pngcrush bc
|
||||
|
||||
- name: Get changed PNG files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46
|
||||
with:
|
||||
files: |
|
||||
docs/**/*.png
|
||||
|
||||
- name: Optimize changed PNG files
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
id: optimize
|
||||
run: |
|
||||
echo "Optimizing new PNG files with pngcrush..."
|
||||
echo "Branch: $(git_current_branch)"
|
||||
|
||||
# Initialize tracking variables
|
||||
total_files=0
|
||||
total_before=0
|
||||
total_after=0
|
||||
|
||||
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
|
||||
echo "Processing: $file"
|
||||
before_size=$(stat -c%s "$file")
|
||||
|
||||
# Use pngcrush with maximum lossless compression settings
|
||||
# -rem alla removes all ancillary chunks (metadata)
|
||||
# -brute tries all filter types and compression levels
|
||||
# -ow overwrites the original file
|
||||
pngcrush -rem alla -brute -ow "$file"
|
||||
|
||||
after_size=$(stat -c%s "$file")
|
||||
echo " > Before: ${before_size} bytes, After: ${after_size} bytes"
|
||||
|
||||
# Track totals
|
||||
total_files=$((total_files + 1))
|
||||
total_before=$((total_before + before_size))
|
||||
total_after=$((total_after + after_size))
|
||||
done
|
||||
|
||||
# Calculate savings
|
||||
total_saved=$((total_before - total_after))
|
||||
|
||||
# Output results for use in commit message
|
||||
echo "files_optimized=${total_files}" >> "$GITHUB_OUTPUT"
|
||||
echo "bytes_saved=${total_saved}" >> "$GITHUB_OUTPUT"
|
||||
echo "total_before=${total_before}" >> "$GITHUB_OUTPUT"
|
||||
echo "total_after=${total_after}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "Optimization complete"
|
||||
echo "Files processed: ${total_files}"
|
||||
echo "Total bytes saved: ${total_saved} (${total_before} -> ${total_after})"
|
||||
|
||||
- name: Check for changes after optimization
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
id: check_changes
|
||||
run: |
|
||||
git add ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
if git diff --quiet --cached; then
|
||||
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||||
echo "No changes detected after optimization"
|
||||
else
|
||||
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Changes detected after optimization"
|
||||
git diff --cached --stat
|
||||
fi
|
||||
|
||||
- name: Commit optimized images
|
||||
if: steps.changed-files.outputs.any_changed == 'true' && steps.check_changes.outputs.has_changes == 'true'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
run: |
|
||||
git config --global user.email "github-automation@metabase.com"
|
||||
git config --global user.name "Metabase Automation"
|
||||
git add docs/**/*.png
|
||||
|
||||
# Format bytes saved for human readability
|
||||
bytes_saved="${{ steps.optimize.outputs.bytes_saved }}"
|
||||
bytes_before="${{ steps.optimize.outputs.total_before }}"
|
||||
if [ "$bytes_saved" -gt 1048576 ]; then
|
||||
# Convert to MB
|
||||
size_saved=$(echo "scale=1; $bytes_saved / 1048576" | bc)
|
||||
size_unit="MB"
|
||||
elif [ "$bytes_saved" -gt 1024 ]; then
|
||||
# Convert to KB
|
||||
size_saved=$(echo "scale=1; $bytes_saved / 1024" | bc)
|
||||
size_unit="KB"
|
||||
else
|
||||
size_saved="$bytes_saved"
|
||||
size_unit="bytes"
|
||||
fi
|
||||
|
||||
# Calculate percentage reduction
|
||||
if [ "$bytes_before" -gt 0 ]; then
|
||||
percentage=$(echo "scale=1; $bytes_saved * 100 / $bytes_before" | bc)
|
||||
git commit -m "Optimized ${{ steps.optimize.outputs.files_optimized }} images, saved ${size_saved} ${size_unit} (${percentage}% smaller)"
|
||||
else
|
||||
git commit -m "Optimized ${{ steps.optimize.outputs.files_optimized }} images, saved ${size_saved} ${size_unit}"
|
||||
fi
|
||||
git push
|
||||
echo 'Files optimized:'
|
||||
echo ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
echo 'Applied: pngcrush -rem alla -brute -ow'
|
||||
|
||||
- name: Output summary
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: |
|
||||
if [ "${{ steps.check_changes.outputs.has_changes }}" = "false" ]; then
|
||||
echo "PNG files were already optimized - no changes needed"
|
||||
else
|
||||
echo "PNG files optimized and committed successfully"
|
||||
fi
|
||||
8
.github/workflows/perf-test-labeled.yml
vendored
@@ -9,13 +9,9 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
uberjar:
|
||||
if: ${{ github.event.label.name == 'Run-perf' }}
|
||||
uses: ./.github/workflows/uberjar.yml
|
||||
secrets: inherit
|
||||
|
||||
run-perf-test:
|
||||
needs: [uberjar]
|
||||
if: ${{ github.event.label.name == 'Run-perf' }}
|
||||
uses: ./.github/workflows/perf-test.yml
|
||||
with:
|
||||
wait_for_uberjar: true
|
||||
secrets: inherit
|
||||
|
||||
68
.github/workflows/perf-test.yml
vendored
@@ -3,6 +3,11 @@ run-name: Run perf test for ${{ github.ref_name }} by @${{ github.actor }}
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
wait_for_uberjar:
|
||||
description: "Wait for uberjar build"
|
||||
required: false
|
||||
type: boolean
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -26,12 +31,69 @@ jobs:
|
||||
uses: aws-actions/amazon-ecr-login@v2
|
||||
with:
|
||||
registries: "${{ secrets.PR_ENV_AWS_ACCOUNT_ID }}"
|
||||
- name: Wait for uberjar
|
||||
id: wait_for_uberjar
|
||||
if: ${{ inputs.wait_for_uberjar == true }}
|
||||
run: |
|
||||
## Get workflow run id for uberjar build
|
||||
curl -Ls --output e2e-tests.json \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/actions/workflows/run-tests.yml/runs?head_sha=${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ID=$(jq -r '.workflow_runs[0].id' e2e-tests.json)
|
||||
echo "Run ID: ${ID}"
|
||||
|
||||
## Get workflow run id for uberjar build
|
||||
while [ -z "$JOB_ID" ]; do
|
||||
NEXT_URL="https://api.github.com/repos/${{ github.repository }}/actions/runs/${ID}/jobs?filter=latest"
|
||||
|
||||
# Search for build job
|
||||
echo "Searching for build job..."
|
||||
while [ -z "$JOB_ID" ] && [ -n "$NEXT_URL" ]; do
|
||||
# Get one page of jobs
|
||||
RESPONSE=$(curl -s -i \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"$NEXT_URL")
|
||||
|
||||
# Extract headers and body
|
||||
HEADERS=$(echo "$RESPONSE" | sed -n '1,/^\r$/p')
|
||||
BODY=$(echo "$RESPONSE" | sed '1,/^\r$/d')
|
||||
LINK_HEADER=$(echo "$HEADERS" | grep -i '^link:' | sed 's/^link: //')
|
||||
|
||||
# Check for next page in Link header
|
||||
if echo "$LINK_HEADER" | grep -q 'rel="next"'; then
|
||||
NEXT_URL=$(echo "$LINK_HEADER" | sed -E 's/.*<([^>]+)>; rel="next".*/\1/')
|
||||
else
|
||||
NEXT_URL=""
|
||||
fi
|
||||
|
||||
# Check for build job
|
||||
JOB_ID=$(echo "$BODY" | jq -r '.jobs[] | select(.name | contains("build (ee)")) | .id')
|
||||
done
|
||||
done
|
||||
echo "Job ID: ${JOB_ID}"
|
||||
|
||||
## Wait for uberjar build to complete
|
||||
while [ true ]; do
|
||||
curl -Ls --output uberjar.json \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/actions/jobs/${JOB_ID}
|
||||
jq -r '.steps[] | select(.name == "Prepare uberjar artifact") | .status' uberjar.json | grep -q "completed" && break
|
||||
echo "Waiting for uberjar build..."
|
||||
sleep 10
|
||||
done
|
||||
echo "run_id=$(jq -r '.workflow_runs[0].id' e2e-tests.json)" >> $GITHUB_OUTPUT
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
extract-path: "target/uberjar"
|
||||
output-name: "metabase.jar"
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ inputs.wait_for_uberjar && steps.wait_for_uberjar.outputs.run_id || github.run_id }}
|
||||
- name: Move uberjar to bin/docker
|
||||
run: |
|
||||
jar xf target/uberjar/metabase.jar
|
||||
|
||||
8
.github/workflows/pr-env-labeled.yml
vendored
@@ -9,13 +9,9 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
uberjar:
|
||||
if: ${{ github.event.label.name == 'PR-Env' }}
|
||||
uses: ./.github/workflows/uberjar.yml
|
||||
secrets: inherit
|
||||
|
||||
pr-env:
|
||||
needs: [uberjar]
|
||||
if: ${{ github.event.label.name == 'PR-Env' }}
|
||||
uses: ./.github/workflows/pr-env.yml
|
||||
with:
|
||||
wait_for_uberjar: true
|
||||
secrets: inherit
|
||||
|
||||
72
.github/workflows/pr-env.yml
vendored
@@ -3,6 +3,11 @@ run-name: Building Dynamic PR Environment for ${{ github.ref_name }} by @${{ git
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
wait_for_uberjar:
|
||||
description: "Wait for uberjar build"
|
||||
required: false
|
||||
type: boolean
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -26,12 +31,69 @@ jobs:
|
||||
uses: aws-actions/amazon-ecr-login@v2
|
||||
with:
|
||||
registries: "${{ secrets.PR_ENV_AWS_ACCOUNT_ID }}"
|
||||
- name: Wait for uberjar
|
||||
id: wait_for_uberjar
|
||||
if: ${{ inputs.wait_for_uberjar == true }}
|
||||
run: |
|
||||
## Get workflow run id for uberjar build
|
||||
curl -Ls --output e2e-tests.json \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/actions/workflows/run-tests.yml/runs?head_sha=${{ github.event.pull_request.head.sha || github.sha }}
|
||||
ID=$(jq -r '.workflow_runs[0].id' e2e-tests.json)
|
||||
echo "Run ID: ${ID}"
|
||||
|
||||
## Get workflow run id for uberjar build
|
||||
while [ -z "$JOB_ID" ]; do
|
||||
NEXT_URL="https://api.github.com/repos/${{ github.repository }}/actions/runs/${ID}/jobs?filter=latest"
|
||||
|
||||
# Search for build job
|
||||
echo "Searching for build job..."
|
||||
while [ -z "$JOB_ID" ] && [ -n "$NEXT_URL" ]; do
|
||||
# Get one page of jobs
|
||||
RESPONSE=$(curl -s -i \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"$NEXT_URL")
|
||||
|
||||
# Extract headers and body
|
||||
HEADERS=$(echo "$RESPONSE" | sed -n '1,/^\r$/p')
|
||||
BODY=$(echo "$RESPONSE" | sed '1,/^\r$/d')
|
||||
LINK_HEADER=$(echo "$HEADERS" | grep -i '^link:' | sed 's/^link: //')
|
||||
|
||||
# Check for next page in Link header
|
||||
if echo "$LINK_HEADER" | grep -q 'rel="next"'; then
|
||||
NEXT_URL=$(echo "$LINK_HEADER" | sed -E 's/.*<([^>]+)>; rel="next".*/\1/')
|
||||
else
|
||||
NEXT_URL=""
|
||||
fi
|
||||
|
||||
# Check for build job
|
||||
JOB_ID=$(echo "$BODY" | jq -r '.jobs[] | select(.name | contains("build (ee)")) | .id')
|
||||
done
|
||||
done
|
||||
echo "Job ID: ${JOB_ID}"
|
||||
|
||||
## Wait for uberjar build to complete
|
||||
while [ true ]; do
|
||||
curl -Ls --output uberjar.json \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/actions/jobs/${JOB_ID}
|
||||
jq -r '.steps[] | select(.name == "Prepare uberjar artifact") | .status' uberjar.json | grep -q "completed" && break
|
||||
echo "Waiting for uberjar build..."
|
||||
sleep 10
|
||||
done
|
||||
echo "run_id=$(jq -r '.workflow_runs[0].id' e2e-tests.json)" >> $GITHUB_OUTPUT
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
extract-path: "target/uberjar"
|
||||
output-name: "metabase.jar"
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ inputs.wait_for_uberjar && steps.wait_for_uberjar.outputs.run_id || github.run_id }}
|
||||
- name: Move uberjar to bin/docker
|
||||
run: |
|
||||
jar xf target/uberjar/metabase.jar
|
||||
@@ -45,7 +107,7 @@ jobs:
|
||||
tags: ${{ secrets.PR_ENV_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/metabase-enterprise:pr${{ github.event.number }}
|
||||
push: true
|
||||
deploy_pr:
|
||||
needs: [build]
|
||||
needs: [ build ]
|
||||
runs-on: ubuntu-latest
|
||||
name: PR Review ENV
|
||||
permissions:
|
||||
@@ -129,7 +191,7 @@ jobs:
|
||||
kubectl apply -f ./metabase.yml
|
||||
preview_links:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy_pr]
|
||||
needs: [ deploy_pr ]
|
||||
steps:
|
||||
- uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
|
||||
13
.github/workflows/pre-release.yml
vendored
@@ -103,14 +103,8 @@ jobs:
|
||||
env:
|
||||
MB_EDITION: ${{ matrix.edition }}
|
||||
DISPLAY: ""
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.MB_PRO_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.MB_PRO_SELF_HOSTED_TOKEN }}
|
||||
# these are needed for backwards compatibility with release branches earlier than v55, since
|
||||
# this workflow always runs from master, but we run tests from the release branch
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
|
||||
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
|
||||
# disabled because of out of memory issues
|
||||
# probably related to https://github.com/cypress-io/cypress/issues/27415
|
||||
CYPRESS_NO_COMMAND_LOG: 1
|
||||
@@ -128,7 +122,6 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
maildev: true
|
||||
snowplow: true
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare JDK 21
|
||||
@@ -139,6 +132,8 @@ jobs:
|
||||
- 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@v4
|
||||
name: Retrieve uberjar artifact for ${{ matrix.edition }}
|
||||
with:
|
||||
|
||||
118
.github/workflows/release-embedding-sdk.yml
vendored
@@ -5,12 +5,18 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: "Branch we want to release the SDK from (to release from other branches use the workflow from those branches)"
|
||||
description: "Branch we want to release the SDK from"
|
||||
type: choice
|
||||
required: true
|
||||
default: "master"
|
||||
options:
|
||||
- master
|
||||
- release-x.51.x
|
||||
- release-x.52.x
|
||||
- release-x.53.x
|
||||
- release-x.54.x
|
||||
- release-x.55.x
|
||||
- esbuild-main
|
||||
|
||||
concurrency:
|
||||
# We want to ensure only one job is running at a time per branch because
|
||||
@@ -171,39 +177,41 @@ jobs:
|
||||
outputs:
|
||||
sdk_version: ${{ fromJson(steps.new-sdk-version.outputs.result).version }}
|
||||
major_version: ${{ fromJson(steps.new-sdk-version.outputs.result).majorVersion }}
|
||||
pre_release_label: ${{ fromJson(steps.new-sdk-version.outputs.result).preReleaseLabel }}
|
||||
steps:
|
||||
- name: Check out the code using the provided branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
|
||||
- name: Prepare build scripts
|
||||
run: yarn --cwd release --frozen-lockfile && yarn --cwd release build
|
||||
|
||||
- name: Get current SDK version
|
||||
id: current-sdk-version
|
||||
run: |
|
||||
VERSION=$(jq -r .version ./enterprise/frontend/src/embedding-sdk-package/package.template.json)
|
||||
VERSION=$(jq -r .version ./enterprise/frontend/src/embedding-sdk/package.template.json)
|
||||
echo "sdk_current_version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get next SDK patch or pre-release version
|
||||
- name: Get next SDK patch version
|
||||
id: new-sdk-version
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const { getNextSdkVersion } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
|
||||
const branch = "${{ inputs.branch }}";
|
||||
script: |
|
||||
const currentVersion = "${{ steps.current-sdk-version.outputs.sdk_current_version }}";
|
||||
const [currentVersionWithoutSuffix, suffix] = currentVersion.split("-");
|
||||
const versionParts = currentVersionWithoutSuffix.split(".");
|
||||
versionParts[versionParts.length - 1] = parseInt(versionParts.at(-1)) + 1;
|
||||
const newVersion = versionParts.join(".");
|
||||
const [_, majorVersion] = versionParts
|
||||
|
||||
const result = getNextSdkVersion(branch, currentVersion);
|
||||
if (!suffix) {
|
||||
return {
|
||||
version: newVersion,
|
||||
majorVersion
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
version: result.version,
|
||||
preReleaseLabel: result.preReleaseLabel,
|
||||
majorVersion: result.majorVersion
|
||||
};
|
||||
version: `${newVersion}-${suffix}`,
|
||||
majorVersion
|
||||
}
|
||||
|
||||
- name: Append workflow summary
|
||||
run: |
|
||||
@@ -285,8 +293,12 @@ jobs:
|
||||
run: |
|
||||
./bin/embedding-sdk/release_utils.bash update_package_json_template ${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
|
||||
- name: Build SDK package
|
||||
run: yarn run build-embedding-sdk-package
|
||||
- name: Update changelog
|
||||
run: |
|
||||
yarn embedding-sdk:generate-changelog -o changelog-diff
|
||||
|
||||
- name: Build SDK bundle
|
||||
run: yarn run build-embedding-sdk
|
||||
|
||||
- name: Generate SDK package.json in the build directory
|
||||
run: yarn run embedding-sdk:generate-package
|
||||
@@ -297,6 +309,12 @@ jobs:
|
||||
name: metabase-sdk
|
||||
path: ./resources/embedding-sdk
|
||||
|
||||
- name: Upload changelog diff
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sdk-changelog-diff
|
||||
path: ./changelog-diff
|
||||
|
||||
publish-npm:
|
||||
needs: [build-sdk, determine-sdk-version]
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -317,7 +335,6 @@ jobs:
|
||||
git config --global user.name "Metabase Automation"
|
||||
|
||||
- name: Update readme
|
||||
if: inputs.branch != 'master'
|
||||
run: |
|
||||
bash ./bin/embedding-sdk/release_utils.bash update_readme ${{ env.sdk_version }}
|
||||
|
||||
@@ -325,26 +342,25 @@ jobs:
|
||||
run: |
|
||||
bash ./bin/embedding-sdk/release_utils.bash update_package_json_template ${{ env.sdk_version }}
|
||||
|
||||
- name: Create a PR updating readme + published version (using GitHub Metabase Automation account)
|
||||
- name: Retrieve SDK changelog diff
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: sdk-changelog-diff
|
||||
|
||||
- name: Update changelog
|
||||
run: |
|
||||
cat changelog-diff enterprise/frontend/src/embedding-sdk/CHANGELOG.md > new-changelog
|
||||
mv new-changelog enterprise/frontend/src/embedding-sdk/CHANGELOG.md
|
||||
|
||||
- name: Create a PR updating readme + published version, and changelog (using GitHub Metabase Automation account)
|
||||
run: |
|
||||
git checkout -b update-sdk-version-${{ env.sdk_version }}
|
||||
|
||||
if [ "${{ inputs.branch }}" = "master" ]; then
|
||||
COMMIT_MSG="chore(sdk): Update published npm version to ${{ env.sdk_version }}"
|
||||
PR_TITLE="chore(sdk): Update SDK version to ${{ env.sdk_version }}"
|
||||
PR_BODY="Update published npm package version to ${{ env.sdk_version }}"
|
||||
else
|
||||
COMMIT_MSG="docs(sdk): Update Readme version references and published npm version to ${{ env.sdk_version }}"
|
||||
PR_TITLE="docs(sdk): Update SDK version to ${{ env.sdk_version }}"
|
||||
PR_BODY="Update Readme version references and published npm package version to ${{ env.sdk_version }}"
|
||||
fi
|
||||
|
||||
git commit -a -m "$COMMIT_MSG"
|
||||
git commit -a -m 'Update Readme version references and published npm version to ${{ env.sdk_version }}'
|
||||
git push origin HEAD
|
||||
gh pr create --base "${{ inputs.branch }}" \
|
||||
--assignee "${GITHUB_ACTOR}" \
|
||||
--title "$PR_TITLE" \
|
||||
--body "$PR_BODY"
|
||||
gh pr create --base ${{ inputs.branch }}\
|
||||
--assignee "${GITHUB_ACTOR}"\
|
||||
--title "docs(sdk): Update SDK version to ${{ env.sdk_version }}"\
|
||||
--body "Update Readme version references and published npm package version to ${{ env.sdk_version }}"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
@@ -372,39 +388,15 @@ jobs:
|
||||
name: metabase-sdk
|
||||
path: sdk
|
||||
|
||||
- name: Compute master tag
|
||||
id: compute-tag
|
||||
run: |
|
||||
if [ -n "${{ needs.determine-sdk-version.outputs.pre_release_label }}" ]; then
|
||||
echo "masterTag=${{ needs.determine-sdk-version.outputs.pre_release_label }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "masterTag=nightly" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Publish to NPM
|
||||
working-directory: sdk
|
||||
run: |
|
||||
echo //registry.npmjs.org/:_authToken=${{ secrets.NPM_RELEASE_TOKEN }} > .npmrc
|
||||
|
||||
TAG=$(jq -r --null-input \
|
||||
--arg branch "${{ inputs.branch }}" \
|
||||
--arg masterTag "${{ steps.compute-tag.outputs.masterTag }}" '
|
||||
{
|
||||
"master": $masterTag,
|
||||
"release-x.51.x": "51-stable",
|
||||
"release-x.52.x": "52-stable",
|
||||
"release-x.53.x": "53-stable",
|
||||
"release-x.54.x": "54-stable",
|
||||
"release-x.55.x": "55-stable",
|
||||
"release-x.56.x": "56-stable"
|
||||
}[$branch]'
|
||||
)
|
||||
|
||||
echo "Resolved npm tag: $TAG"
|
||||
npm publish --tag "$TAG"
|
||||
# Please keep the value in sync with `inputs.branch`'s release branch
|
||||
npm publish --tag ${{fromJson('{"master": "nightly", "release-x.51.x": "51-stable", "release-x.52.x": "52-stable", "release-x.53.x": "53-stable", "release-x.54.x": "54-stable", "release-x.55.x": "55-stable", "esbuild-main": "56-esbuild"}')[inputs.branch]}}
|
||||
|
||||
- name: Add `latest` tag to the latest release branch (`release-x.55.x`) deployment
|
||||
if: ${{ inputs.branch == 'release-x.56.x' }}
|
||||
if: ${{ inputs.branch == 'release-x.55.x' }}
|
||||
working-directory: sdk
|
||||
run: |
|
||||
npm dist-tag add @metabase/embedding-sdk-react@${{ env.sdk_version }} latest
|
||||
|
||||
2
.github/workflows/release-log.yml
vendored
@@ -7,7 +7,9 @@ on:
|
||||
- release-x.*
|
||||
tags:
|
||||
- v*
|
||||
- nightly-*
|
||||
- latest-*
|
||||
- beta-*
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
|
||||
9
.github/workflows/release-notes-preview.yml
vendored
@@ -34,23 +34,18 @@ jobs:
|
||||
AWS_S3_DOWNLOADS_BUCKET: test_bucket
|
||||
with:
|
||||
script: | # js
|
||||
const R = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const { getChangelog } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const fs = require('fs');
|
||||
|
||||
const version = '${{ inputs.version }}';
|
||||
|
||||
const issues = await R.getMilestoneIssues({
|
||||
const notes = await getChangelog({
|
||||
version,
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
|
||||
const notes = R.getWebsiteChangelog({
|
||||
version,
|
||||
issues,
|
||||
});
|
||||
|
||||
fs.writeFileSync(`release-notes-preview-${version}.md`, notes);
|
||||
- name: Upload Release Notes
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
22
.github/workflows/release-tag.yml
vendored
@@ -12,6 +12,8 @@ on:
|
||||
description: Tag name to apply to this release
|
||||
type: choice
|
||||
options:
|
||||
- nightly
|
||||
- beta
|
||||
- latest
|
||||
required: true
|
||||
tag_rollout:
|
||||
@@ -34,6 +36,8 @@ on:
|
||||
tag_name:
|
||||
type: string
|
||||
# options:
|
||||
# - nightly
|
||||
# - beta
|
||||
# - latest
|
||||
# - none (skips everything)
|
||||
required: true
|
||||
@@ -246,9 +250,19 @@ jobs:
|
||||
|
||||
- name: Tag the docker image
|
||||
run: | # sh
|
||||
docker buildx imagetools create --tag \
|
||||
$DOCKERHUB_REPO\:$TAG_NAME \
|
||||
$DOCKERHUB_REPO\:$DOCKERHUB_VERSION
|
||||
docker pull $DOCKERHUB_REPO\:$DOCKERHUB_VERSION
|
||||
docker tag $DOCKERHUB_REPO\:$DOCKERHUB_VERSION $DOCKERHUB_REPO\:$TAG_NAME
|
||||
docker image ls $DOCKERHUB_REPO
|
||||
|
||||
SOURCE_ID=$(docker image ls $DOCKERHUB_REPO\:$DOCKERHUB_VERSION -q)
|
||||
TAGGED_ID=$(docker image ls $DOCKERHUB_REPO\:$TAG_NAME -q)
|
||||
|
||||
if [[ $SOURCE_ID != $TAGGED_ID ]]; then
|
||||
echo "Error tagging docker image"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker push $DOCKERHUB_REPO\:$TAG_NAME
|
||||
|
||||
push-git-tags:
|
||||
permissions: write-all
|
||||
@@ -266,7 +280,7 @@ jobs:
|
||||
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
fetch-depth: 0 # we want all branches and tags
|
||||
fetch-tags: true
|
||||
- name: Add and push git tag for latest
|
||||
- name: Add and push git tag for nightly/beta/latest
|
||||
if: ${{ !inputs.dot-x-tag }}
|
||||
run: | # sh
|
||||
if [[ "${{ matrix.edition }}" == "ee" ]]; then
|
||||
|
||||
8
.github/workflows/release.yml
vendored
@@ -17,6 +17,8 @@ on:
|
||||
options:
|
||||
- none
|
||||
- latest
|
||||
- beta
|
||||
- nightly
|
||||
required: true
|
||||
default: 'none'
|
||||
auto:
|
||||
@@ -39,6 +41,8 @@ on:
|
||||
# options:
|
||||
# - none
|
||||
# - latest
|
||||
# - beta
|
||||
# - nightly
|
||||
required: true
|
||||
default: 'none'
|
||||
auto:
|
||||
@@ -399,9 +403,7 @@ jobs:
|
||||
});
|
||||
|
||||
trigger-ee-extra-pr:
|
||||
needs:
|
||||
- containerize-ee
|
||||
- verify-s3-download
|
||||
needs: verify-s3-download
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
|
||||
55
.github/workflows/run-tests.yml
vendored
@@ -7,12 +7,9 @@ on:
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') }}
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
@@ -24,7 +21,6 @@ jobs:
|
||||
backend_all: ${{ steps.changes.outputs.backend_all }}
|
||||
frontend_all: ${{ steps.changes.outputs.frontend_all }}
|
||||
frontend_sources: ${{ steps.changes.outputs.frontend_sources }}
|
||||
documentation: ${{ steps.changes.outputs.documentation }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
@@ -38,7 +34,6 @@ jobs:
|
||||
|
||||
static-viz-files-changed:
|
||||
name: Check whether static-viz files changed
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
@@ -68,34 +63,14 @@ jobs:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/static-viz-sources.yaml
|
||||
|
||||
uberjar:
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
if: ${{ !cancelled() && !(needs.files-changed.outputs.documentation == 'true' && needs.files-changed.outputs.backend_all == 'false' && needs.files-changed.outputs.frontend_all == 'false' && needs.files-changed.outputs.e2e_all == 'false') }}
|
||||
uses: ./.github/workflows/uberjar.yml
|
||||
secrets: inherit
|
||||
|
||||
backend-tests:
|
||||
if: ${{ !cancelled() && needs.files-changed.result == 'success' }}
|
||||
if: ${{ !cancelled() }}
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
uses: ./.github/workflows/backend.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.backend_all != 'true' && needs.static-viz-files-changed.outputs.static_viz != 'true' }}
|
||||
|
||||
semantic-search-tests:
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/semantic-search.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.backend_all != 'true' }}
|
||||
|
||||
app-db-tests:
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/app-db.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.backend_all != 'true' }}
|
||||
|
||||
driver-tests:
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/drivers.yml
|
||||
@@ -112,8 +87,7 @@ jobs:
|
||||
skip-lint: ${{ needs.files-changed.outputs.frontend_all != 'true' && needs.files-changed.outputs.e2e_specs != 'true' }}
|
||||
|
||||
e2e-tests:
|
||||
needs: [files-changed, uberjar]
|
||||
if: always() && !cancelled() && needs.files-changed.result == 'success'
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/e2e-tests.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -123,28 +97,3 @@ jobs:
|
||||
# e2e_all_files includes all changes which trigger all e2e tests, if the list of changes files is included in the
|
||||
# list of changed specs files, then only specs are changed and we can run only those specs
|
||||
specs: ${{ needs.files-changed.outputs.e2e_all_files == needs.files-changed.outputs.e2e_specs_files && needs.files-changed.outputs.e2e_specs_files || '' }}
|
||||
|
||||
sdk-tests:
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/embedding-sdk.yml
|
||||
secrets: inherit
|
||||
|
||||
pr-env:
|
||||
needs: [uberjar]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.event.pull_request.labels.*.name, 'PR-Env')
|
||||
uses: ./.github/workflows/pr-env.yml
|
||||
secrets: inherit
|
||||
|
||||
containerize:
|
||||
needs: [uberjar]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.uberjar.result == 'success' &&
|
||||
(
|
||||
(github.event_name == 'push' && contains(fromJson('["master", "metabot-v3-main", "internal-tools"]'), github.ref_name)) ||
|
||||
(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'build-docker-uberjar'))
|
||||
)
|
||||
uses: ./.github/workflows/containerize-uberjar.yml
|
||||
secrets: inherit
|
||||
|
||||
348
.github/workflows/semantic-search.yml
vendored
@@ -1,348 +0,0 @@
|
||||
name: Semantic Search Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-semantic-search
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
semantic-search-tests-h2:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: H2 with pgvector pg17
|
||||
junit-name: semantic-search-tests-h2-pgvector-17
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
job:
|
||||
- name: Semantic Search Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only '"enterprise/backend/test/metabase_enterprise/semantic_search"'
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
|
||||
services:
|
||||
pgvector:
|
||||
image: ${{ matrix.version.pgvector-image }}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_DB: mb_semantic_search
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
env:
|
||||
MB_PGVECTOR_DB_URL: 'jdbc:postgres://localhost:5432/mb_semantic_search?user=postgres&password=postgres'
|
||||
CI: 'true'
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[${{ matrix.job.exclude-tag }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
semantic-search-tests-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MariaDB 10.2 with pgvector pg17
|
||||
junit-name: semantic-search-tests-mariadb-10-2
|
||||
mariadb-image: circleci/mariadb:10.2.23
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
- name: MariaDB Latest with pgvector pg17
|
||||
junit-name: semantic-search-tests-mariadb-latest
|
||||
mariadb-image: circleci/mariadb:latest
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
job:
|
||||
- name: Semantic Search Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only '"enterprise/backend/test/metabase_enterprise/semantic_search"'
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
|
||||
services:
|
||||
pgvector:
|
||||
image: ${{ matrix.version.pgvector-image }}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_DB: mb_semantic_search
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
mysql:
|
||||
image: ${{ matrix.version.mariadb-image }}
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
MB_PGVECTOR_DB_URL: 'jdbc:postgres://localhost:5432/mb_semantic_search?user=postgres&password=postgres'
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[${{ matrix.job.exclude-tag }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
semantic-search-tests-mysql:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MySQL 8.0 with pgvector pg17
|
||||
junit-name: semantic-search-tests-mysql-8-0
|
||||
mysql-image: cimg/mysql:8.0
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
- name: MySQL Latest with pgvector pg17
|
||||
junit-name: semantic-search-tests-mysql-latest
|
||||
mysql-image: mysql:latest
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
job:
|
||||
- name: Semantic Search Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only '"enterprise/backend/test/metabase_enterprise/semantic_search"'
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
|
||||
services:
|
||||
pgvector:
|
||||
image: ${{ matrix.version.pgvector-image }}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_DB: mb_semantic_search
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
mysql:
|
||||
image: ${{ matrix.version.mysql-image }}
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
MB_PGVECTOR_DB_URL: 'jdbc:postgres://localhost:5432/mb_semantic_search?user=postgres&password=postgres'
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[${{ matrix.job.exclude-tag }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
semantic-search-tests-postgres:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Postgres 12.x with pgvector pg17
|
||||
junit-name: semantic-search-tests-postgres-12
|
||||
postgres-image: postgres:12-alpine
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
- name: Postgres Latest with pgvector pg17
|
||||
junit-name: semantic-search-tests-postgres-latest
|
||||
postgres-image: postgres:latest
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
job:
|
||||
- name: Semantic Search Tests
|
||||
build-static-viz: false
|
||||
test-args: >-
|
||||
:only '"enterprise/backend/test/metabase_enterprise/semantic_search"'
|
||||
exclude-tag: ':mb/driver-tests'
|
||||
|
||||
services:
|
||||
pgvector:
|
||||
image: ${{ matrix.version.pgvector-image }}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_DB: mb_semantic_search
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
postgres:
|
||||
image: ${{ matrix.version.postgres-image }}
|
||||
ports:
|
||||
- "5433:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: postgres
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5433
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_USER: mb_test
|
||||
MB_POSTGRESQL_TEST_USER: mb_test
|
||||
MB_PGVECTOR_DB_URL: 'jdbc:postgres://localhost:5432/mb_semantic_search?user=postgres&password=postgres'
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[${{ matrix.job.exclude-tag }}]'
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
semantic-search-tests-result:
|
||||
needs:
|
||||
- semantic-search-tests-h2
|
||||
- semantic-search-tests-mariadb
|
||||
- semantic-search-tests-mysql
|
||||
- semantic-search-tests-postgres
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: semantic-search-tests-result
|
||||
if: always() && !cancelled()
|
||||
steps:
|
||||
- name: Check job status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
with:
|
||||
script: | # js
|
||||
const needs = JSON.parse(process.env.needs);
|
||||
|
||||
const jobs = Object.entries(needs).map(
|
||||
([jobName, jobValues]) => ({
|
||||
name: jobName,
|
||||
result: jobValues.result
|
||||
}));
|
||||
|
||||
// are all jobs skipped or successful?
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
||||
console.log("");
|
||||
console.log(" _------. ");
|
||||
console.log(" / , \_ ");
|
||||
console.log(" / / /{}\ |o\_ ");
|
||||
console.log(" / \ `--' /-' \ ");
|
||||
console.log(" | \ \ | ");
|
||||
console.log(" | |`-, | ");
|
||||
console.log(" / /__/)/ ");
|
||||
console.log(" | | ");
|
||||
console.log("");
|
||||
console.log("All semantic search tests have passed (or have been skipped). Cam is very proud of you.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// otherwise, something failed
|
||||
console.log("");
|
||||
console.log(" .::::::::::. .::::::::::. ");
|
||||
console.log(" .::::''''''::::. .::::''''''::::. ");
|
||||
console.log(" .:::' `::::.... ....::::' `:::. ");
|
||||
console.log(" .::' `:::::::| |:::::::' `::. ");
|
||||
console.log(" .::| |::::::|_ ___ __|::::::| |::. ");
|
||||
console.log(" `--' |::::::|_()__()_|::::::| `--' ");
|
||||
console.log(" ::: |::-o::| |::o-::| ::: ");
|
||||
console.log(" `::. .|::::::| |::::::|. .::' ");
|
||||
console.log(" `:::. .::\-----' `-----/::. .:::' ");
|
||||
console.log(" `::::......::::' `::::......::::' ");
|
||||
console.log(" `::::::::::' `::::::::::' ");
|
||||
console.log("");
|
||||
console.log("Semantic search tests have failed. You have been sentenced to MetaJail.");
|
||||
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success') {
|
||||
console.log(`${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
|
||||
process.exit(1);
|
||||
147
.github/workflows/snyk.yml
vendored
@@ -13,11 +13,12 @@ on:
|
||||
schedule:
|
||||
- cron: '0 5 * * *'
|
||||
|
||||
|
||||
jobs:
|
||||
# Generate pom.xml files for all Maven projects
|
||||
generate-poms:
|
||||
monitor:
|
||||
# don't run this workflow on a cron for forks
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
name: Generate pom.xml files
|
||||
name: Generate Snyk report
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -25,144 +26,26 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: 'snyk'
|
||||
- uses: snyk/actions/setup@0.4.0
|
||||
- name: Generate all pom.xml
|
||||
run: .github/scripts/write-poms.sh
|
||||
- name: Upload pom files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pom-files
|
||||
path: |
|
||||
pom.xml
|
||||
modules/drivers/*/pom.xml
|
||||
|
||||
# Yarn/NPM projects that don't need pom files
|
||||
frontend-projects:
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
name: ${{ matrix.project.name }}
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
project:
|
||||
- name: Main Frontend (yarn)
|
||||
file: yarn.lock
|
||||
package-manager: yarn
|
||||
category: main-frontend-yarn
|
||||
- name: Embedding SDK Template
|
||||
file: bin/embedding-sdk/templates/yarn.lock
|
||||
package-manager: yarn
|
||||
category: embedding-sdk-template
|
||||
- name: E2E Angular 20 Host App
|
||||
file: e2e/embedding-sdk-host-apps/angular-20-host-app/package-lock.json
|
||||
package-manager: npm
|
||||
category: e2e-angular-host
|
||||
- name: E2E Next 15 App Router
|
||||
file: e2e/embedding-sdk-host-apps/next-15-app-router-host-app/package-lock.json
|
||||
package-manager: npm
|
||||
category: e2e-next-app-router
|
||||
- name: E2E Next 15 Pages Router
|
||||
file: e2e/embedding-sdk-host-apps/next-15-pages-router-host-app/package-lock.json
|
||||
package-manager: npm
|
||||
category: e2e-next-pages-router
|
||||
- name: E2E Vite 6 Host App
|
||||
file: e2e/embedding-sdk-host-apps/vite-6-host-app/package-lock.json
|
||||
package-manager: npm
|
||||
category: e2e-vite-host
|
||||
- name: Release Helper
|
||||
file: release/yarn.lock
|
||||
package-manager: yarn
|
||||
category: release-helper
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: snyk/actions/setup@0.4.0
|
||||
- name: Run snyk test
|
||||
- name: Run snyk main project
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
continue-on-error: true
|
||||
run: snyk test --org="metabase-AnD9iS4Wfu4eBYMsG2DZ5N" --remote-repo-url="https://github.com/metabase/metabase" --file=${{ matrix.project.file }} --package-manager=${{ matrix.project.package-manager }} --sarif-file-output=snyk-${{ matrix.project.category }}.sarif
|
||||
- name: Upload results
|
||||
run: snyk test --all-projects --exclude="modules" --sarif-file-output=snyk-main.sarif
|
||||
- name: Upload result to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: snyk-${{ matrix.project.category }}.sarif
|
||||
category: ${{ matrix.project.category }}
|
||||
|
||||
# Maven projects that need pom files
|
||||
maven-projects:
|
||||
needs: generate-poms
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
name: ${{ matrix.project.name }}
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
project:
|
||||
- name: Main Backend (maven)
|
||||
file: pom.xml
|
||||
category: main-backend-maven
|
||||
- name: Driver - Athena
|
||||
file: modules/drivers/athena/pom.xml
|
||||
category: driver-athena
|
||||
- name: Driver - BigQuery Cloud SDK
|
||||
file: modules/drivers/bigquery-cloud-sdk/pom.xml
|
||||
category: driver-bigquery-cloud-sdk
|
||||
- name: Driver - ClickHouse
|
||||
file: modules/drivers/clickhouse/pom.xml
|
||||
category: driver-clickhouse
|
||||
- name: Driver - Databricks
|
||||
file: modules/drivers/databricks/pom.xml
|
||||
category: driver-databricks
|
||||
- name: Driver - Druid
|
||||
file: modules/drivers/druid/pom.xml
|
||||
category: driver-druid
|
||||
- name: Driver - Druid JDBC
|
||||
file: modules/drivers/druid-jdbc/pom.xml
|
||||
category: driver-druid-jdbc
|
||||
- name: Driver - Hive-like
|
||||
file: modules/drivers/hive-like/pom.xml
|
||||
category: driver-hive-like
|
||||
- name: Driver - MongoDB
|
||||
file: modules/drivers/mongo/pom.xml
|
||||
category: driver-mongo
|
||||
- name: Driver - Oracle
|
||||
file: modules/drivers/oracle/pom.xml
|
||||
category: driver-oracle
|
||||
- name: Driver - Presto JDBC
|
||||
file: modules/drivers/presto-jdbc/pom.xml
|
||||
category: driver-presto-jdbc
|
||||
- name: Driver - Redshift
|
||||
file: modules/drivers/redshift/pom.xml
|
||||
category: driver-redshift
|
||||
- name: Driver - Snowflake
|
||||
file: modules/drivers/snowflake/pom.xml
|
||||
category: driver-snowflake
|
||||
- name: Driver - SparkSQL
|
||||
file: modules/drivers/sparksql/pom.xml
|
||||
category: driver-sparksql
|
||||
- name: Driver - SQLite
|
||||
file: modules/drivers/sqlite/pom.xml
|
||||
category: driver-sqlite
|
||||
- name: Driver - SQL Server
|
||||
file: modules/drivers/sqlserver/pom.xml
|
||||
category: driver-sqlserver
|
||||
- name: Driver - Starburst
|
||||
file: modules/drivers/starburst/pom.xml
|
||||
category: driver-starburst
|
||||
- name: Driver - Vertica
|
||||
file: modules/drivers/vertica/pom.xml
|
||||
category: driver-vertica
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: snyk/actions/setup@0.4.0
|
||||
- name: Download pom files
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: pom-files
|
||||
- name: Run snyk test
|
||||
sarif_file: snyk-main.sarif
|
||||
category: main-project
|
||||
- name: Run snyk drivers
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
continue-on-error: true
|
||||
run: snyk test --org="metabase-AnD9iS4Wfu4eBYMsG2DZ5N" --remote-repo-url="https://github.com/metabase/metabase" --file=${{ matrix.project.file }} --package-manager=maven --sarif-file-output=snyk-${{ matrix.project.category }}.sarif
|
||||
|
||||
- name: Upload results
|
||||
run: cd modules && snyk test --all-projects --sarif-file-output=snyk-drivers.sarif
|
||||
- name: Upload result to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: snyk-${{ matrix.project.category }}.sarif
|
||||
category: ${{ matrix.project.category }}
|
||||
sarif_file: modules/snyk-drivers.sarif
|
||||
category: drivers
|
||||
|
||||
1
.github/workflows/translation-update.yml
vendored
@@ -43,7 +43,6 @@ jobs:
|
||||
command_args: |
|
||||
--config locales/crowdin.yml
|
||||
-e=el
|
||||
-e=et
|
||||
-e=es-MX
|
||||
-e=gu-IN
|
||||
-e=hr
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
name: Uberjar Containerize labeled
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [ labeled, opened, synchronize, reopened ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
uberjar:
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'build-docker-uberjar') || github.event.label.name == 'build-docker-uberjar' }}
|
||||
uses: ./.github/workflows/uberjar.yml
|
||||
secrets: inherit
|
||||
|
||||
containerize:
|
||||
needs: [uberjar]
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'build-docker-uberjar') || github.event.label.name == 'build-docker-uberjar' }}
|
||||
uses: ./.github/workflows/containerize-uberjar.yml
|
||||
secrets: inherit
|
||||
180
.github/workflows/uberjar.yml
vendored
@@ -1,16 +1,14 @@
|
||||
name: Build Uberjar
|
||||
name: Build + Docker Uberjar
|
||||
|
||||
# This workflow builds an uberjar and pushes it to dockerhub for any branch, either on-demand,
|
||||
# or automatically for the listed branches
|
||||
|
||||
concurrency:
|
||||
group: uberjar-${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}
|
||||
cancel-in-progress: false
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- metabot-v3-main
|
||||
- feature-visualizer
|
||||
- internal-tools
|
||||
paths-ignore:
|
||||
# config files
|
||||
@@ -26,102 +24,65 @@ on:
|
||||
inputs:
|
||||
commit:
|
||||
description: "Optional full-length commit SHA-1 hash"
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
check-existing-artifacts:
|
||||
get-container-info:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
name: Get container info
|
||||
outputs:
|
||||
ee_exists: ${{ steps.check-ee.outcome == 'success' }}
|
||||
oss_exists: ${{ steps.check-oss.outcome == 'success' }}
|
||||
should_build: ${{ steps.decide.outputs.should_build }}
|
||||
editions: ${{ toJson(fromJson(steps.info.outputs.result).editions) }}
|
||||
ee: ${{ toJson(fromJson(steps.info.outputs.result).ee) }}
|
||||
oss: ${{ toJson(fromJson(steps.info.outputs.result).oss) }}
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
- name: Try to fetch existing EE artifact
|
||||
id: check-ee
|
||||
continue-on-error: true
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
output-name: metabase-ee.jar
|
||||
|
||||
- name: Try to fetch existing OSS artifact
|
||||
id: check-oss
|
||||
continue-on-error: true
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: metabase-oss-${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
output-name: metabase-oss.jar
|
||||
|
||||
- name: Decide if build is needed
|
||||
id: decide
|
||||
run: |
|
||||
EE_EXISTS="${{ steps.check-ee.outcome == 'success' }}"
|
||||
OSS_EXISTS="${{ steps.check-oss.outcome == 'success' }}"
|
||||
|
||||
echo "EE exists: $EE_EXISTS"
|
||||
echo "OSS exists: $OSS_EXISTS"
|
||||
|
||||
# Build only if artifacts don't exist for this commit hash
|
||||
# Since we build by hash, if artifacts exist for this hash, we don't need to rebuild
|
||||
if [[ "$EE_EXISTS" == "false" ]] || [[ "$OSS_EXISTS" == "false" ]]; then
|
||||
SHOULD_BUILD="true"
|
||||
echo "Will build: ee_exists=$EE_EXISTS, oss_exists=$OSS_EXISTS"
|
||||
else
|
||||
SHOULD_BUILD="false"
|
||||
echo "Skipping build: artifacts exist for this commit hash"
|
||||
fi
|
||||
|
||||
echo "should_build=$SHOULD_BUILD" >> $GITHUB_OUTPUT
|
||||
|
||||
# if this is on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
if: |
|
||||
!cancelled() &&
|
||||
contains(github.ref, 'release-x')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
|
||||
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get build dependencies
|
||||
- name: Get docker repo and tag
|
||||
id: info
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const { getBuildRequirements, getVersionFromReleaseBranch } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const branch = '${{ github.head_ref || github.ref_name }}';
|
||||
const owner = '${{ github.repository_owner }}';
|
||||
|
||||
const version = getVersionFromReleaseBranch('${{ github.ref }}');
|
||||
const requirements = getBuildRequirements(version);
|
||||
console.log(`Branch: ${branch}`);
|
||||
|
||||
if (branch === "master") {
|
||||
return {
|
||||
editions: ["ee", "oss"],
|
||||
ee: {
|
||||
repo: `${owner}/metabase-enterprise-head`,
|
||||
tag: "latest"
|
||||
},
|
||||
oss: {
|
||||
repo: `${owner}/metabase-head`,
|
||||
tag: "latest"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
java_version: requirements.java,
|
||||
node_version: requirements.node,
|
||||
editions: ["ee"],
|
||||
ee: {
|
||||
repo: `${owner}/metabase-dev`,
|
||||
tag: branch.replaceAll("/", "-"), // slashes make docker sad
|
||||
},
|
||||
};
|
||||
|
||||
- name: Print output
|
||||
run: |
|
||||
echo "${{ steps.info.outputs.result }}"
|
||||
|
||||
build:
|
||||
needs:
|
||||
[check-existing-artifacts, get-build-requirements]
|
||||
if: always() && !cancelled() && needs.check-existing-artifacts.outputs.should_build == 'true' && (needs.get-build-requirements.result == 'success' || needs.get-build-requirements.result == 'skipped')
|
||||
needs: get-container-info
|
||||
name: Build MB ${{ matrix.edition }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
edition: ["ee", "oss"]
|
||||
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
|
||||
env:
|
||||
MB_EDITION: ${{ matrix.edition }}
|
||||
INTERACTIVE: false
|
||||
SKIP_LICENSES: ${{ github.event_name == 'pull_request' }} # faster builds for dev
|
||||
SKIP_LICENSES: true # faster builds for dev
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
@@ -129,33 +90,28 @@ jobs:
|
||||
ref: ${{ github.event.inputs.commit }}
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
node-version: "${{ needs.get-build-requirements.outputs.node_version || '22' }}"
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: uberjar
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
|
||||
java-version: 21
|
||||
- name: Build
|
||||
run: ./bin/build.sh
|
||||
- name: Prepare uberjar artifact
|
||||
uses: ./.github/actions/prepare-uberjar-artifact
|
||||
with:
|
||||
name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.sha }}-uberjar
|
||||
|
||||
check-jar-health:
|
||||
runs-on: ubuntu-22.04
|
||||
name: Is ${{ matrix.edition }} (java ${{ matrix.java-version }}) healthy?
|
||||
needs: [build, check-existing-artifacts]
|
||||
if: always() && !cancelled() && (needs.build.result == 'success' || (needs.check-existing-artifacts.outputs.should_build == 'false' && needs.check-existing-artifacts.outputs.ee_exists == 'true' && needs.check-existing-artifacts.outputs.oss_exists == 'true'))
|
||||
needs: [build, get-container-info]
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
edition: ["ee", "oss"]
|
||||
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
|
||||
java-version: [21]
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare JRE (Java Run-time Environment)
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
@@ -163,12 +119,56 @@ jobs:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
distribution: "temurin"
|
||||
- run: java -version
|
||||
- name: Retrieve uberjar artifact
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Retrieve uberjar artifact
|
||||
with:
|
||||
name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.sha }}-uberjar
|
||||
- name: Launch uberjar
|
||||
run: >-
|
||||
java --add-opens java.base/java.nio=ALL-UNNAMED -jar ./metabase.jar &
|
||||
java --add-opens java.base/java.nio=ALL-UNNAMED -jar ./target/uberjar/metabase.jar &
|
||||
- name: Wait for Metabase to start
|
||||
run: while ! curl 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
|
||||
containerize:
|
||||
needs: [check-jar-health, get-container-info]
|
||||
strategy:
|
||||
matrix:
|
||||
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
|
||||
uses: ./.github/workflows/containerize-jar.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
artifact-name: metabase-${{ matrix.edition }}-${{ github.event.inputs.commit || github.sha }}-uberjar
|
||||
commit: ${{ github.event.inputs.commit || github.sha }}
|
||||
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
|
||||
tag: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).tag }}
|
||||
release: "false"
|
||||
build-args: |
|
||||
GIT_COMMIT_SHA=${{ github.event.inputs.commit || github.sha }}
|
||||
|
||||
run-trivy:
|
||||
needs: [get-container-info, containerize]
|
||||
strategy:
|
||||
matrix:
|
||||
edition: ${{ fromJson(needs.get-container-info.outputs.editions) }}
|
||||
runs-on: ubuntu-22.04
|
||||
name: Run Trivy vulnerability scanner
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit || github.sha }}
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
env:
|
||||
TRIVY_OFFLINE_SCAN: true
|
||||
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
|
||||
tag: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).tag }}
|
||||
with:
|
||||
image-ref: docker.io/${{ env.repo }}:${{ env.tag }}
|
||||
format: sarif
|
||||
output: trivy-results.sarif
|
||||
version: "v0.57.1"
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: "trivy-results.sarif"
|
||||
|
||||
92
.github/workflows/update-e2e-timings.yml
vendored
@@ -1,92 +0,0 @@
|
||||
name: Update E2E Timings
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
days_back:
|
||||
description: "Number of days to look back for timing artifacts"
|
||||
type: number
|
||||
default: 7
|
||||
required: false
|
||||
schedule:
|
||||
# Run weekly on Sundays at 6 AM UTC
|
||||
- cron: "0 6 * * 0"
|
||||
|
||||
jobs:
|
||||
collect-and-average-timings:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
DAYS_BACK: ${{ inputs.days_back || 7 }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Collect and average timing data
|
||||
id: collect-timings
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { collectAndAverageTimings } = require('${{ github.workspace }}/.github/scripts/average-e2e-timings.js');
|
||||
const result = await collectAndAverageTimings({
|
||||
github,
|
||||
context,
|
||||
token: '${{ secrets.GITHUB_TOKEN }}',
|
||||
daysBack: ${{ env.DAYS_BACK }},
|
||||
existingTimingsPath: 'e2e/support/timings.json'
|
||||
});
|
||||
|
||||
core.setOutput('artifact-count', result.artifactCount);
|
||||
|
||||
- name: Set up git configuration
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
BRANCH_NAME="update-e2e-timings-$(date +%Y%m%d-%H%M%S)"
|
||||
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
|
||||
echo "Branch name will be: $BRANCH_NAME"
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ env.BRANCH_NAME }}
|
||||
base: master
|
||||
commit-message: |
|
||||
Update E2E test timings with averaged data
|
||||
|
||||
Collected timing data from ${{ env.DAYS_BACK }} days of test runs.
|
||||
All specs with timing data were included (simple averages).
|
||||
|
||||
🤖 Generated automatically by update-e2e-timings workflow
|
||||
|
||||
Co-Authored-By: GitHub Actions <actions@github.com>
|
||||
committer: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
|
||||
author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
|
||||
title: "[chore] Update E2E test timings with recent data"
|
||||
body: |
|
||||
## Summary
|
||||
This PR updates the E2E test timing data with averaged values from recent test runs.
|
||||
|
||||
## Details
|
||||
- Collected data from the last **${{ env.DAYS_BACK }} days** of test runs
|
||||
- Averaged timing data from **${{ steps.collect-timings.outputs.artifact-count }}** test runs
|
||||
|
||||
## Generated automatically
|
||||
🤖 This PR was created automatically by the `update-e2e-timings` workflow.
|
||||
The timing data helps cypress-split optimize test distribution across parallel jobs.
|
||||
|
||||
Co-Authored-By: GitHub Actions <actions@github.com>
|
||||
labels: |
|
||||
.CI & Tests
|
||||
add-paths: e2e/support/timings.json
|
||||
1
.github/workflows/whitespace.yml
vendored
@@ -6,7 +6,6 @@ on:
|
||||
- 'master'
|
||||
- 'release-**'
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
whitespace-linter:
|
||||
|
||||
15
.gitignore
vendored
@@ -36,7 +36,6 @@
|
||||
/coverage
|
||||
/crate-*
|
||||
/cypress
|
||||
cypress.env.json
|
||||
/deploy/artifacts/*
|
||||
/docs/**/node_modules
|
||||
/docs/**/package.json
|
||||
@@ -51,7 +50,6 @@ cypress.env.json
|
||||
/lein-plugins/*/target
|
||||
/local
|
||||
/locales/metabase-*.pot
|
||||
/logs
|
||||
/modules/drivers/*/resources/namespaces.edn
|
||||
/modules/drivers/*/target
|
||||
/node_modules/
|
||||
@@ -61,9 +59,7 @@ cypress.env.json
|
||||
/resources/frontend_client/app/dist/
|
||||
/resources/frontend_client/app/locales
|
||||
/resources/frontend_client/app/embed.js
|
||||
/resources/frontend_client/app/embedding-sdk.js
|
||||
/resources/frontend_client/app/embedding-sdk.js.map
|
||||
/resources/frontend_client/tmp-*/
|
||||
/resources/frontend_client/tmp-embed-js/
|
||||
/resources/frontend_client/embed.html
|
||||
/resources/frontend_client/embed-sdk.html
|
||||
/resources/frontend_client/index.html
|
||||
@@ -98,13 +94,6 @@ xcuserdata
|
||||
dev/src/dev/nocommit/
|
||||
dev/serialization_deltas/
|
||||
*~
|
||||
|
||||
# TypeScript compilation artifacts
|
||||
/build/
|
||||
*.tsbuildinfo
|
||||
/trace-output/
|
||||
/temp-types/
|
||||
|
||||
**/cypress_sample_database.json
|
||||
**/cypress_sample_instance_data.json
|
||||
/frontend/src/cljs
|
||||
@@ -157,9 +146,7 @@ llm-payloads
|
||||
.aider*
|
||||
/config.yml
|
||||
CLAUDE.local.md
|
||||
.claude/settings.local.json
|
||||
|
||||
.eslintcache
|
||||
|
||||
/logs
|
||||
/.clojure-mcp
|
||||
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 42 KiB |