Compare commits
786 Commits
python-run
...
docs-gen-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f459a9654c | ||
|
|
f7a3f8c17a | ||
|
|
daf3118021 | ||
|
|
d5edabb6ad | ||
|
|
df787f6a49 | ||
|
|
93540da4dc | ||
|
|
88a77f1c8c | ||
|
|
9bdb273f7c | ||
|
|
8c026dfb6d | ||
|
|
abc3f3992e | ||
|
|
4efc9df1f6 | ||
|
|
f5951fdef2 | ||
|
|
94dc03c2b5 | ||
|
|
9b79cacf26 | ||
|
|
0bab73dacf | ||
|
|
29e1cce7c0 | ||
|
|
23c3586dae | ||
|
|
f1d996246f | ||
|
|
9df71bafbd | ||
|
|
f155489a73 | ||
|
|
2450e46c22 | ||
|
|
c9482edd2d | ||
|
|
638972e3cb | ||
|
|
6592640589 | ||
|
|
cc8584996b | ||
|
|
3fa8453cb1 | ||
|
|
1508bb42d6 | ||
|
|
3cddfe52f2 | ||
|
|
c3de448349 | ||
|
|
d11f52ce29 | ||
|
|
298876bfc9 | ||
|
|
cd74e4e6c7 | ||
|
|
f56d066222 | ||
|
|
c06de5fa77 | ||
|
|
084f26777c | ||
|
|
dc1851259d | ||
|
|
f4de7ae8b7 | ||
|
|
939c9880fe | ||
|
|
c33adf1683 | ||
|
|
58f541d279 | ||
|
|
f0f55865b7 | ||
|
|
2b6ef96219 | ||
|
|
bd574945cd | ||
|
|
72fb60819a | ||
|
|
22de35b2d6 | ||
|
|
dd27849b64 | ||
|
|
b61ee4c433 | ||
|
|
9e12302d7b | ||
|
|
69fc32080d | ||
|
|
9cb3f4cef8 | ||
|
|
b8a2222bc9 | ||
|
|
12e950f89c | ||
|
|
be02400472 | ||
|
|
87abc81bd6 | ||
|
|
ad3cb85793 | ||
|
|
77c621620d | ||
|
|
98c9121abb | ||
|
|
9ed688887d | ||
|
|
a1d5809e8e | ||
|
|
c3238c0d07 | ||
|
|
5ccb71da08 | ||
|
|
ed308d4c12 | ||
|
|
3db08f04ce | ||
|
|
a7fd04c6f3 | ||
|
|
4c1c0c8712 | ||
|
|
55992eef0d | ||
|
|
d8dafd4ddb | ||
|
|
0474782fcd | ||
|
|
703938f1e0 | ||
|
|
a17fa42461 | ||
|
|
b606e921db | ||
|
|
ffcad911c2 | ||
|
|
81f65b80cc | ||
|
|
c36dfb579c | ||
|
|
5b49f7f2f9 | ||
|
|
8909927ad7 | ||
|
|
4b5c7a9cad | ||
|
|
bf2309539f | ||
|
|
4ed2c0861c | ||
|
|
9bd54f910a | ||
|
|
d5b9f4c17e | ||
|
|
20b7108540 | ||
|
|
dc8134820f | ||
|
|
a6a033ad8b | ||
|
|
36afc7b089 | ||
|
|
b39d8a96f2 | ||
|
|
77bce68f25 | ||
|
|
eadddda453 | ||
|
|
989c9989ab | ||
|
|
b1edc5b6be | ||
|
|
947a0079e2 | ||
|
|
d3f9b11ee6 | ||
|
|
16d98cd62d | ||
|
|
68137eba95 | ||
|
|
e7163aa61f | ||
|
|
e2be5a0af9 | ||
|
|
46b919a571 | ||
|
|
15f5cb66ac | ||
|
|
86ec7435c0 | ||
|
|
2cb7ae4bad | ||
|
|
dee4a00b69 | ||
|
|
5d43373490 | ||
|
|
362a5cb343 | ||
|
|
32ee610ee4 | ||
|
|
0137be5ea0 | ||
|
|
ac8e5a7c0e | ||
|
|
56a33ff9c8 | ||
|
|
3710a38cfa | ||
|
|
60736a1f93 | ||
|
|
4067582607 | ||
|
|
2314b9f9f1 | ||
|
|
3a2289f910 | ||
|
|
60461d1b36 | ||
|
|
8cea041801 | ||
|
|
182e5207a1 | ||
|
|
9764ea4d64 | ||
|
|
cdc537681f | ||
|
|
ecb558cf98 | ||
|
|
e60a7a88e4 | ||
|
|
334ef6708c | ||
|
|
dd7d8eff61 | ||
|
|
f88021b069 | ||
|
|
2f4098e607 | ||
|
|
33e08c359a | ||
|
|
6ccb35ed3b | ||
|
|
7f0ed83da1 | ||
|
|
e695ff7f97 | ||
|
|
904e5ee64a | ||
|
|
faf97dbf9b | ||
|
|
4eabd91ab0 | ||
|
|
7fc2fc4266 | ||
|
|
9be95a58a7 | ||
|
|
5326b785f5 | ||
|
|
4f7a6c12b5 | ||
|
|
b847ab9b19 | ||
|
|
777232825d | ||
|
|
a747befda7 | ||
|
|
482ed3efa5 | ||
|
|
cd80683776 | ||
|
|
b6c06678a6 | ||
|
|
53501f679c | ||
|
|
7e4588158d | ||
|
|
009d267d66 | ||
|
|
1dd66e6ea5 | ||
|
|
56f3290b47 | ||
|
|
a9152e914d | ||
|
|
c930ffbc7a | ||
|
|
900e06e211 | ||
|
|
987228ebc3 | ||
|
|
b65e946285 | ||
|
|
24ff367b16 | ||
|
|
00a45e3706 | ||
|
|
dac87b2856 | ||
|
|
9168b7809a | ||
|
|
66c48fe435 | ||
|
|
88d8dc8464 | ||
|
|
50c690b5af | ||
|
|
ebf91a06e6 | ||
|
|
46b63c6369 | ||
|
|
99af690b3b | ||
|
|
c573538be1 | ||
|
|
9dde7e5dfc | ||
|
|
3f30e3ab4f | ||
|
|
288ca3ce46 | ||
|
|
ee3eec35c1 | ||
|
|
b05d683533 | ||
|
|
e1eecad194 | ||
|
|
01675b95aa | ||
|
|
b0ce515267 | ||
|
|
3807edc1a6 | ||
|
|
7e07736de9 | ||
|
|
4c6adacaac | ||
|
|
5aa5550758 | ||
|
|
b4f5b9f840 | ||
|
|
4086f9c723 | ||
|
|
3eddbf3e8f | ||
|
|
e058c53781 | ||
|
|
8ce83a964e | ||
|
|
782c83e97b | ||
|
|
67df1b1b36 | ||
|
|
4da22ca703 | ||
|
|
669648b133 | ||
|
|
2837c8380d | ||
|
|
4cad5bd78b | ||
|
|
84618e1660 | ||
|
|
ca68ed959c | ||
|
|
99982436f7 | ||
|
|
9e29a57a27 | ||
|
|
bf730c89a8 | ||
|
|
54b668c6fe | ||
|
|
163540b189 | ||
|
|
179f3ac2b2 | ||
|
|
e47b76cf6b | ||
|
|
faf54a43ba | ||
|
|
6671e09591 | ||
|
|
7ba6a4a3de | ||
|
|
6de8e4bab0 | ||
|
|
6af0da03ee | ||
|
|
b68e37e69e | ||
|
|
3d32109fa1 | ||
|
|
682ec82fcd | ||
|
|
e6a80107e1 | ||
|
|
ef8a2e965c | ||
|
|
94e6f2360c | ||
|
|
a222ffe128 | ||
|
|
0154e208c7 | ||
|
|
e967468ef7 | ||
|
|
0239eb5962 | ||
|
|
8cd35d4397 | ||
|
|
34e18606a2 | ||
|
|
7508f254b3 | ||
|
|
39d7fd1ea1 | ||
|
|
870d4cea0f | ||
|
|
2b7e68d50d | ||
|
|
7591f87026 | ||
|
|
b8ad458339 | ||
|
|
0f4723e0a9 | ||
|
|
347c58e4fa | ||
|
|
86e6925fc5 | ||
|
|
30f5f28af8 | ||
|
|
d1cf62b309 | ||
|
|
4cd48e5d27 | ||
|
|
49b1f212e5 | ||
|
|
0b2f7a11fe | ||
|
|
52e15c918d | ||
|
|
328ceccffc | ||
|
|
c680b8ddc3 | ||
|
|
6ae0664591 | ||
|
|
ab08d283db | ||
|
|
07bdefe1bf | ||
|
|
f3daa4b3f0 | ||
|
|
6995b164a9 | ||
|
|
249660b9c2 | ||
|
|
9865f68e2a | ||
|
|
765c7c5716 | ||
|
|
b3fefff9a3 | ||
|
|
1cd6e9daae | ||
|
|
f09fa75c77 | ||
|
|
ce5a8d927a | ||
|
|
99d1d01b4a | ||
|
|
fe5bbe4aac | ||
|
|
f3fb25ccf4 | ||
|
|
a6545b3920 | ||
|
|
c87c492209 | ||
|
|
91304b8e72 | ||
|
|
a43ea7f556 | ||
|
|
108009d956 | ||
|
|
bfdc47c72b | ||
|
|
d818222187 | ||
|
|
e2aac1199c | ||
|
|
88ef6cf9a1 | ||
|
|
18cdc645af | ||
|
|
e73225bb1e | ||
|
|
a22930ea50 | ||
|
|
aa8f5d3408 | ||
|
|
dd698bbb07 | ||
|
|
5ecdba2548 | ||
|
|
7049af0399 | ||
|
|
3931def2e0 | ||
|
|
715e6825f0 | ||
|
|
f37d1aa1c1 | ||
|
|
ea4b1c2590 | ||
|
|
7191951527 | ||
|
|
ef1c0a48bb | ||
|
|
bc0e6a1971 | ||
|
|
b2d7cf032b | ||
|
|
f3407e460a | ||
|
|
008e73af79 | ||
|
|
2d6883522e | ||
|
|
64ec32ce8b | ||
|
|
b206331368 | ||
|
|
48f00c21b0 | ||
|
|
b6f7bbee47 | ||
|
|
a8c5e22e89 | ||
|
|
a3c96cee1a | ||
|
|
795092960d | ||
|
|
a168633d06 | ||
|
|
ac02037a76 | ||
|
|
12dd763872 | ||
|
|
256a93c058 | ||
|
|
c72aa49bec | ||
|
|
2a7cfa8740 | ||
|
|
52523c7f9a | ||
|
|
8450f0159d | ||
|
|
f04744349d | ||
|
|
cc99b2ebec | ||
|
|
cefee6b02d | ||
|
|
6e64c33880 | ||
|
|
072105edf6 | ||
|
|
4af49337cc | ||
|
|
00044e105a | ||
|
|
e7a71d1efa | ||
|
|
f6d8bd76a9 | ||
|
|
e16f145031 | ||
|
|
7741b4cc3b | ||
|
|
a8ca5dbf3d | ||
|
|
16d2f76ae6 | ||
|
|
4b57af8b66 | ||
|
|
492b8a0a0c | ||
|
|
d1d61fd809 | ||
|
|
493cacb702 | ||
|
|
8f47f1399f | ||
|
|
b3129d3715 | ||
|
|
9fa3e61969 | ||
|
|
e29d43442e | ||
|
|
ed868b67ab | ||
|
|
eccc590e3f | ||
|
|
36e2c0a20b | ||
|
|
eed6f72504 | ||
|
|
d2b5746c69 | ||
|
|
fbd2944b2e | ||
|
|
31f46fc972 | ||
|
|
12d18f5942 | ||
|
|
d6dd1b1f0d | ||
|
|
27c5380f82 | ||
|
|
427c4a4d63 | ||
|
|
3f594ab51b | ||
|
|
2d76168409 | ||
|
|
71c7648f6b | ||
|
|
7827d85246 | ||
|
|
ed511f94bb | ||
|
|
4d8552da12 | ||
|
|
3df10e4f05 | ||
|
|
f28f9e1053 | ||
|
|
5b361ce08a | ||
|
|
e3ccd61e7c | ||
|
|
a3ecb6089c | ||
|
|
3353d7d8e5 | ||
|
|
24807042be | ||
|
|
2827f53fb1 | ||
|
|
73050dee32 | ||
|
|
0c210c5295 | ||
|
|
d1d4932d10 | ||
|
|
5ce9ff6cda | ||
|
|
d396ebb7e3 | ||
|
|
bea4df0e01 | ||
|
|
a8e1b00d9c | ||
|
|
a9dd49cb16 | ||
|
|
39f69806c2 | ||
|
|
d5bc65be80 | ||
|
|
c0440485fa | ||
|
|
9f210ff799 | ||
|
|
b9a03da16c | ||
|
|
249228df47 | ||
|
|
37b33db705 | ||
|
|
e335b5932f | ||
|
|
010153f55a | ||
|
|
241cce5e4d | ||
|
|
df76e8b001 | ||
|
|
ab8d0dcb4b | ||
|
|
3f70f686e6 | ||
|
|
d1b2e5208c | ||
|
|
74fd44cfcd | ||
|
|
f9bdeabb37 | ||
|
|
465d7b5051 | ||
|
|
1fac296d56 | ||
|
|
8f92d9f0c4 | ||
|
|
7981462797 | ||
|
|
580c9aec4a | ||
|
|
a6ed3e0654 | ||
|
|
a07050ab09 | ||
|
|
2097d002b5 | ||
|
|
cd8fe76902 | ||
|
|
1485b585da | ||
|
|
9a522e4357 | ||
|
|
3f18ca5567 | ||
|
|
4b2568a5f9 | ||
|
|
674de23c5f | ||
|
|
cf7a57c9d2 | ||
|
|
7c70ec75cf | ||
|
|
accb8b6863 | ||
|
|
328ec45212 | ||
|
|
265cefec41 | ||
|
|
21110531db | ||
|
|
d4f45d5066 | ||
|
|
08c1ef6804 | ||
|
|
7183edb63a | ||
|
|
93c684203a | ||
|
|
c29fa4777d | ||
|
|
dad51f169f | ||
|
|
b3cd4ab7e3 | ||
|
|
c69fbc140c | ||
|
|
53d061a06d | ||
|
|
ea08fad2ac | ||
|
|
b7e45ce014 | ||
|
|
242cacebf8 | ||
|
|
2329658a61 | ||
|
|
0da53ec082 | ||
|
|
b91d388b4c | ||
|
|
84f1d5cf6f | ||
|
|
becb001a36 | ||
|
|
2a02275714 | ||
|
|
7789039cdd | ||
|
|
71786a64ab | ||
|
|
25dcc59a06 | ||
|
|
515ab4821a | ||
|
|
6b92b628ac | ||
|
|
260a24f469 | ||
|
|
66dd725e59 | ||
|
|
dabc5bd06c | ||
|
|
65ec0d4abc | ||
|
|
b722cdfc5b | ||
|
|
98cdffb15d | ||
|
|
8f0ccda6dd | ||
|
|
6216ff701e | ||
|
|
cf0d10c9fd | ||
|
|
559e43ecce | ||
|
|
b2f594bbcc | ||
|
|
d00cde95b6 | ||
|
|
5626964894 | ||
|
|
605b37957c | ||
|
|
7ddb51a86f | ||
|
|
f5a8cef20e | ||
|
|
a27b81a528 | ||
|
|
a81eebe78a | ||
|
|
8d93d36e9a | ||
|
|
8f652a145b | ||
|
|
67bd60a670 | ||
|
|
27c9f350cd | ||
|
|
c2ce5bd7e6 | ||
|
|
3563535d8a | ||
|
|
e4a074b4ca | ||
|
|
7c99220bcb | ||
|
|
b7ce5d9d5e | ||
|
|
3fa14f4606 | ||
|
|
cf5480e41a | ||
|
|
40a0b9df6d | ||
|
|
7ebd9b3b06 | ||
|
|
82c545d2b2 | ||
|
|
0f1f8a48fd | ||
|
|
1530def412 | ||
|
|
eb99d8dc45 | ||
|
|
9cbc04851c | ||
|
|
094b834d30 | ||
|
|
8525f9cb44 | ||
|
|
c6bfcc918d | ||
|
|
4e80bb965c | ||
|
|
66856143c6 | ||
|
|
59349f0452 | ||
|
|
7e68f114ca | ||
|
|
38b35ed9a9 | ||
|
|
1197fdf649 | ||
|
|
b6863c365c | ||
|
|
8edc2fb085 | ||
|
|
eb779ed04c | ||
|
|
2f720242bd | ||
|
|
84444ce8a4 | ||
|
|
22b987b60a | ||
|
|
c7005df14b | ||
|
|
3339501fb2 | ||
|
|
9c59e252ef | ||
|
|
8becef9501 | ||
|
|
6213c8bc1f | ||
|
|
89379d1a71 | ||
|
|
90fbe7ce7e | ||
|
|
9539593e85 | ||
|
|
48cdff4e25 | ||
|
|
0dce68b39a | ||
|
|
e622b870d5 | ||
|
|
9bd8f5c3c2 | ||
|
|
7a01bda088 | ||
|
|
1a1679ff4a | ||
|
|
b36b5a9070 | ||
|
|
4320f3f57e | ||
|
|
c4d95de5b1 | ||
|
|
0a239d961f | ||
|
|
99310f1687 | ||
|
|
f1a67dfeae | ||
|
|
5de432b713 | ||
|
|
9aea2bdc57 | ||
|
|
b0878436af | ||
|
|
c0e7ff6fef | ||
|
|
f31b40511c | ||
|
|
ff19935cfc | ||
|
|
dd00985e13 | ||
|
|
149b880e7f | ||
|
|
848f775038 | ||
|
|
50ed7df469 | ||
|
|
b47aae5337 | ||
|
|
0b68d6d4a1 | ||
|
|
040ce33571 | ||
|
|
e1c22e0470 | ||
|
|
b91b364cc9 | ||
|
|
cbe83601c8 | ||
|
|
2846be2f66 | ||
|
|
1398f2b6c8 | ||
|
|
415687a509 | ||
|
|
9ea1c6d9ba | ||
|
|
8b9d8d5a78 | ||
|
|
f1254bae00 | ||
|
|
c117a4f452 | ||
|
|
5eb779397a | ||
|
|
7be3094d52 | ||
|
|
0bd585f015 | ||
|
|
d9d37028e8 | ||
|
|
f3826bb838 | ||
|
|
0f4954f864 | ||
|
|
55bfc3e228 | ||
|
|
939dd036d9 | ||
|
|
f8a5ef2417 | ||
|
|
a21b67e5e9 | ||
|
|
4bb6b28e54 | ||
|
|
f26393179e | ||
|
|
fdab63d90f | ||
|
|
4c4d76a9ca | ||
|
|
a136ee982d | ||
|
|
c8f4080c04 | ||
|
|
257322adb2 | ||
|
|
54d5df568c | ||
|
|
882e2619d3 | ||
|
|
fabf6fbde2 | ||
|
|
9642e41078 | ||
|
|
7523ad051c | ||
|
|
382d00cde6 | ||
|
|
002060862f | ||
|
|
dc836dec6c | ||
|
|
5c4fa8ba0e | ||
|
|
8a512e9d10 | ||
|
|
ebaf1fcbc9 | ||
|
|
86f2ddd283 | ||
|
|
6536760e45 | ||
|
|
48ee190e7e | ||
|
|
af7eac8e33 | ||
|
|
574d9f2500 | ||
|
|
716c32356d | ||
|
|
c0a1fa22ae | ||
|
|
3c1d00bc02 | ||
|
|
bddfa8a281 | ||
|
|
950c60564f | ||
|
|
785a817b30 | ||
|
|
1f2fdb7502 | ||
|
|
f7c03147dc | ||
|
|
e347c02d2b | ||
|
|
a3c0707ee9 | ||
|
|
3f6a3c55b6 | ||
|
|
163b55ee1d | ||
|
|
0b74590e8f | ||
|
|
183a1533f2 | ||
|
|
d5ee86f0b5 | ||
|
|
fe7aa6d784 | ||
|
|
bd460c4b7b | ||
|
|
600bd64749 | ||
|
|
11ac102702 | ||
|
|
14a77d063a | ||
|
|
6842a69cfa | ||
|
|
e8e9fffba9 | ||
|
|
6e9ab88eea | ||
|
|
b2a744afd0 | ||
|
|
c0b5736862 | ||
|
|
061d7db245 | ||
|
|
63e075773b | ||
|
|
804b5ad8fd | ||
|
|
3a19c27013 | ||
|
|
3bf004720f | ||
|
|
2225f9ab14 | ||
|
|
dca0d33efb | ||
|
|
3b65c9e963 | ||
|
|
9af73acecc | ||
|
|
8dc140dd7c | ||
|
|
151e48687e | ||
|
|
d1b593c771 | ||
|
|
b98ba7aaeb | ||
|
|
42ba82e8f5 | ||
|
|
d650a108f9 | ||
|
|
46aa224107 | ||
|
|
83742804de | ||
|
|
3cbb80301b | ||
|
|
22462722c8 | ||
|
|
2564b03af9 | ||
|
|
61d86da134 | ||
|
|
423d10958d | ||
|
|
5539219cb9 | ||
|
|
5fe02f1a9b | ||
|
|
8449a05425 | ||
|
|
57fd127f8d | ||
|
|
fe9dd26d34 | ||
|
|
96d10ae453 | ||
|
|
bb7ac65193 | ||
|
|
6470e66a8a | ||
|
|
1a31e2abdb | ||
|
|
b6da2535f8 | ||
|
|
918b27167c | ||
|
|
24bc4db285 | ||
|
|
24f0e2e004 | ||
|
|
f24dc49a81 | ||
|
|
7daaaf2631 | ||
|
|
adb8733cfe | ||
|
|
440914eb58 | ||
|
|
2dbe5ab801 | ||
|
|
7d79c7817f | ||
|
|
48a9de8ae2 | ||
|
|
b4a61a9ff7 | ||
|
|
db90245faf | ||
|
|
b79df9d426 | ||
|
|
a045d2d935 | ||
|
|
3dc5ffd0e8 | ||
|
|
4c97606679 | ||
|
|
573a772293 | ||
|
|
393ae5df9f | ||
|
|
9724ffac35 | ||
|
|
5c43cdda9c | ||
|
|
c720f49ac1 | ||
|
|
f27faa6b48 | ||
|
|
46787cade6 | ||
|
|
9b6e0fb82f | ||
|
|
ee19d6c5e0 | ||
|
|
f380b9c48c | ||
|
|
d982e9b3f4 | ||
|
|
674db9ccfd | ||
|
|
50e68704f8 | ||
|
|
27393e6fec | ||
|
|
4baabb8886 | ||
|
|
b70fbbf6e9 | ||
|
|
7e8128f9ed | ||
|
|
e2193b6053 | ||
|
|
82e18e17c6 | ||
|
|
1070af6d0e | ||
|
|
71e3d897b6 | ||
|
|
d5f5d1aaa9 | ||
|
|
f81bfc58a8 | ||
|
|
f8c0c8f336 | ||
|
|
82cb302e80 | ||
|
|
a4787998ba | ||
|
|
8c457edb5d | ||
|
|
133343a0e4 | ||
|
|
2fd11184d0 | ||
|
|
29dcbb1ca9 | ||
|
|
629be58a5b | ||
|
|
73e264a1d1 | ||
|
|
17ea3044c5 | ||
|
|
e8d5b8bfe8 | ||
|
|
43bdde7a53 | ||
|
|
591d61f32b | ||
|
|
c6cd8e4021 | ||
|
|
eb0c2b231e | ||
|
|
9105a01c0c | ||
|
|
f5ebacbdbe | ||
|
|
dcf507b631 | ||
|
|
77cbab87ce | ||
|
|
5e9c53543b | ||
|
|
306086f723 | ||
|
|
99668cdc4b | ||
|
|
0b9d891169 | ||
|
|
e85634c6cb | ||
|
|
db4364739c | ||
|
|
516d20216d | ||
|
|
04b6cb59bd | ||
|
|
d4b1e20376 | ||
|
|
887283071f | ||
|
|
d051b26fb2 | ||
|
|
2d20aed32b | ||
|
|
10c744f805 | ||
|
|
81c565fe7c | ||
|
|
7bf428a001 | ||
|
|
672a8a6ba4 | ||
|
|
071007f57f | ||
|
|
997c67a314 | ||
|
|
e26330a49f | ||
|
|
a1d9f916dd | ||
|
|
e7e962e219 | ||
|
|
609afc4acf | ||
|
|
42be2aaa4a | ||
|
|
8997b4890a | ||
|
|
039e423ddb | ||
|
|
b7a7afe7fa | ||
|
|
df86367fd2 | ||
|
|
f4b1c6e7ad | ||
|
|
d0341b46ac | ||
|
|
a078799f6a | ||
|
|
55eb0f4344 | ||
|
|
1c3113141b | ||
|
|
00c273908e | ||
|
|
87eb61ae83 | ||
|
|
3e9c577f6b | ||
|
|
1fcee87dc1 | ||
|
|
00afd29a94 | ||
|
|
caece0d184 | ||
|
|
3640d2d47f | ||
|
|
c187f54635 | ||
|
|
621b12bb94 | ||
|
|
264a4a42c8 | ||
|
|
6cb0d22474 | ||
|
|
89df04d705 | ||
|
|
43a527567d | ||
|
|
758c598dd7 | ||
|
|
e1fbcbd705 | ||
|
|
d35c021e08 | ||
|
|
dfbb83b75e | ||
|
|
d177d8da67 | ||
|
|
9a2e9c7e7f | ||
|
|
f1fe62d7d1 | ||
|
|
0cd6bbdb92 | ||
|
|
e8c5c0962c | ||
|
|
40236b296f | ||
|
|
5427a9c5ce | ||
|
|
e0afc360f6 | ||
|
|
39a12cf402 | ||
|
|
fea202b78e | ||
|
|
d0a7d6fd1b | ||
|
|
adb0bd65c9 | ||
|
|
bb9561b6fc | ||
|
|
cb387d1e64 | ||
|
|
f3ca5d2460 | ||
|
|
634ba75db9 | ||
|
|
9cc7309004 | ||
|
|
085fd404a8 | ||
|
|
bb6f1acedd | ||
|
|
6ad63bef74 | ||
|
|
d2debda352 | ||
|
|
eb2d6223fc | ||
|
|
c36d1e67d2 | ||
|
|
da4b36e291 | ||
|
|
c9ceabde56 | ||
|
|
a22add8f3b | ||
|
|
f41a70070d | ||
|
|
3265e57eb0 | ||
|
|
a4e504c16f | ||
|
|
0c03816365 | ||
|
|
076fb0124e | ||
|
|
a889c4bf1e | ||
|
|
3ac9df074f | ||
|
|
2ee7347a6f | ||
|
|
6dc4146cd3 | ||
|
|
3679efeab4 | ||
|
|
ea3393a2df | ||
|
|
a17e739ed4 | ||
|
|
3ff2db44ac | ||
|
|
500b84b580 | ||
|
|
d2721181f0 | ||
|
|
ec68fd1739 | ||
|
|
e72730f7bf | ||
|
|
e9ff150d79 | ||
|
|
aa70da2a65 | ||
|
|
ef77d0025f | ||
|
|
057c51c4e5 | ||
|
|
c82519b4ad | ||
|
|
a3da30c3b0 | ||
|
|
9ff4067665 | ||
|
|
9518c90626 | ||
|
|
8e4e7563bd | ||
|
|
60ed6b9827 | ||
|
|
56978ffa59 | ||
|
|
d327343866 | ||
|
|
b530bac0d1 | ||
|
|
64d03ed160 | ||
|
|
8fbbd9051e | ||
|
|
118e21e530 | ||
|
|
f95cd1617f | ||
|
|
7bb4b890fe | ||
|
|
3f46b20b04 | ||
|
|
cace37a49c | ||
|
|
8216bb8946 | ||
|
|
7810dd5002 | ||
|
|
d43e0fbf6e | ||
|
|
1ab51a8e0a | ||
|
|
f5f19dcc4b | ||
|
|
e0c18552a2 | ||
|
|
0d9045e428 | ||
|
|
1b4597e018 | ||
|
|
a7a362d704 | ||
|
|
6dd703140a | ||
|
|
8bc1bcca66 | ||
|
|
dcdf5e8767 | ||
|
|
23d523df1c | ||
|
|
2b84fc6b48 | ||
|
|
52708cb884 | ||
|
|
c21234df3a | ||
|
|
d99b06d21f | ||
|
|
c9a07af913 | ||
|
|
bfb896a82e | ||
|
|
20794e2f8a | ||
|
|
69cd77864e | ||
|
|
fc7c0d7946 | ||
|
|
2c03388dcc | ||
|
|
0fba9aa40a | ||
|
|
642c2eff1a | ||
|
|
6c7d5790a6 | ||
|
|
1ca79542d8 | ||
|
|
43e2fb3491 | ||
|
|
2f2341b8c3 | ||
|
|
3504bc5c34 | ||
|
|
1df5c4a00b | ||
|
|
3201fef18f | ||
|
|
7c67586d9b | ||
|
|
8c308dae8f | ||
|
|
ce9c0bef19 |
@@ -1,8 +1,7 @@
|
||||
;; -*- comment-column: 100; -*-
|
||||
{:config-paths ["macros" "src"]
|
||||
:linters
|
||||
{
|
||||
;;
|
||||
{;;
|
||||
;; enabled optional linters (these are disabled by default)
|
||||
;;
|
||||
|
||||
@@ -183,7 +182,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
|
||||
@@ -270,6 +269,9 @@
|
||||
clojure.pprint/pprint {:message "Use metabase.util.log instead of clojure.pprint/pprint"}
|
||||
clojure.string/lower-case {:message "Use metabase.util/lower-case-en instead of clojure.string/lower-case"}
|
||||
clojure.string/upper-case {:message "Use metabase.util/upper-case-en instead of clojure.string/upper-case"}
|
||||
compojure.core/context {:message "Use metabase.api.util.handlers/route-map-handler instead of compojure.core/context, it is much faster and supports OpenAPI spec generation"}
|
||||
compojure.core/defroutes {:message "Use metabase.api.util.handlers/defroutes instead of compojure.core/routes, it supports OpenAPI spec generation."}
|
||||
compojure.core/routes {:message "Use metabase.api.util.handlers/routes instead of compojure.core/routes, it supports OpenAPI spec generation."}
|
||||
fipp.edn/pprint {:message "Use metabase.util.log instead of fipp.edn/pprint"}
|
||||
honeysql.core/call {:message "Use hx/call instead because it is Honey SQL 2 friendly"}
|
||||
honeysql.core/raw {:message "Use hx/raw instead because it is Honey SQL 2 friendly"}
|
||||
@@ -282,9 +284,7 @@
|
||||
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.equality/find-column-indexes-for-refs {:message "Use lib.equality/closest-matches-in-metadata or lib.equality/find-closest-matches-for-refs instead of lib.equality/find-column-indexes-for-refs"}
|
||||
metabase.test/with-temp* {:message "Use mt/with-temp instead of mt/with-temp*"}
|
||||
toucan2.tools.with-temp/with-temp {:message "Use mt/with-temp instead of toucan2.tools.with-temp/with-temp"}}
|
||||
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"}}
|
||||
|
||||
:discouraged-namespace
|
||||
{camel-snake-kebab.core {:message "CSK is not Turkish-safe, use the versions in metabase.util instead."}
|
||||
@@ -297,224 +297,12 @@
|
||||
honeysql.types {:message "Use Honey SQL 2. Honey SQL 1 is removed as a dependency in Metabase 49+."}
|
||||
honeysql.util {:message "Use Honey SQL 2. Honey SQL 1 is removed as a dependency in Metabase 49+."}}
|
||||
|
||||
:discouraged-tag {p {:message "You can use it, but don't commit it." }}
|
||||
|
||||
:unresolved-var
|
||||
{:exclude
|
||||
[colorize.core]}
|
||||
|
||||
;; A "module" is any `metabase.x` namespace in `src`.
|
||||
:metabase/ns-module-checker
|
||||
{:level :warning
|
||||
|
||||
;; Map of module name => the "core" external public-facing API namespace(s). You have three options here:
|
||||
;;
|
||||
;; 1. Special sentinel value `:any` means means this module does not (yet) have external public-facing API
|
||||
;; namespace(s). This is mostly a temporary placeholder until we go in and create module namespaces, which means
|
||||
;; you should go create one.
|
||||
;;
|
||||
;; 2. A set of namespace symbols. All namespaces in other modules will only be allowed to use namespaces from
|
||||
;; this set. Ideally this set should only have one namespace, but restricting it to a set of several is still
|
||||
;; better than `:any`.
|
||||
;;
|
||||
;; 3. `nil` or not listed here -- we default to assuming there is one API namespace called `<module>.core`
|
||||
;;
|
||||
;; `nil` or an empty set Otherwise this should be a set of namespace symbols.
|
||||
;;
|
||||
;; PRO TIP: Check out the `dev.deps-graph` namespace for helpful tools to see where a module is used externally.
|
||||
:api-namespaces
|
||||
{metabase.actions #{metabase.actions.core}
|
||||
metabase.analytics #{metabase.analytics.prometheus
|
||||
metabase.analytics.snowplow
|
||||
metabase.analytics.stats
|
||||
metabase.analytics.sdk} ; TODO -- consolidate these into a real API namespace.
|
||||
metabase.analyze #{metabase.analyze.core}
|
||||
metabase.api #{metabase.api.common
|
||||
;; I don't really like making all these namespaces external but they have to be
|
||||
;; for now until we can shrink this mega-module down a little bit (by moving API
|
||||
;; namespaces out into different modules)... eventually `api` will just be a
|
||||
;; general low-level API utils/macros module and at that point it can probably
|
||||
;; get a single API namespace. Please try to avoid adding more stuff here if you
|
||||
;; can tho. -- Cam
|
||||
metabase.api.common.validation
|
||||
metabase.api.dataset
|
||||
metabase.api.macros
|
||||
metabase.api.permission-graph
|
||||
metabase.api.routes} ; TODO -- consolidate these into a real API namespace. I think the `*current-user*` type stuff might need to be moved into a separate module.
|
||||
metabase.audit #{metabase.audit}
|
||||
metabase.auth-provider #{metabase.auth-provider}
|
||||
metabase.cmd #{} ; there are no namespaces here since you shouldn't be using this in any other module.
|
||||
;; TODO -- this has too many API namespaces, ideally it would only expose `metabase.channel.core`,
|
||||
;; `metabase.channel.init`, and `metabase.channel.api`.
|
||||
metabase.channel #{metabase.channel.api
|
||||
metabase.channel.core
|
||||
metabase.channel.email
|
||||
metabase.channel.email.messages
|
||||
metabase.channel.init
|
||||
metabase.channel.render.core
|
||||
metabase.channel.template.core}
|
||||
metabase.config #{metabase.config}
|
||||
;; this is not even a real module, it's an external library but its starts with a `metabase.` namespace.
|
||||
metabase.connection-pool #{metabase.connection-pool}
|
||||
metabase.core #{metabase.core.initialization-status} ; TODO -- only namespace used outside of EE, this probably belongs in `metabase.server` anyway since that's the only place it's used.
|
||||
metabase.db #{metabase.db
|
||||
metabase.db.metadata-queries ; TODO this should probably be separate from metabase.db
|
||||
metabase.db.query ; TODO this is mostly util stuff like `metabase.db.query/query` that we don't even need anymore.
|
||||
metabase.db.setup} ; TODO these are only calling `metabase.db.setup/setup-db!` and there's a slightly different version in `metabase.db`
|
||||
metabase.driver :any ; TODO -- 19 namespaces!!!! CRY
|
||||
metabase.eid-translation #{metabase.eid-translation}
|
||||
metabase.embed #{metabase.embed.settings}
|
||||
metabase.events #{metabase.events metabase.events.init}
|
||||
metabase.formatter #{metabase.formatter}
|
||||
metabase.integrations #{metabase.integrations.common
|
||||
metabase.integrations.google
|
||||
metabase.integrations.ldap
|
||||
metabase.integrations.slack} ; TODO -- consolidate these into a real API namespace.
|
||||
metabase.legacy-mbql #{metabase.legacy-mbql.normalize
|
||||
metabase.legacy-mbql.predicates
|
||||
metabase.legacy-mbql.schema
|
||||
metabase.legacy-mbql.schema.helpers
|
||||
metabase.legacy-mbql.util} ; TODO -- consolidate these into a real API namespace.
|
||||
metabase.lib :any ; TODO -- :cry: 34 externally referenced namespaces, but maybe half of them are schema namespaces which technically don't need to be required.
|
||||
metabase.logger #{metabase.logger metabase.logger.init}
|
||||
metabase.models :any ; TODO -- scream, 62 namespaces used elsewhere, but to be fair a lot of these don't *need* to be required.
|
||||
metabase.moderation #{metabase.moderation}
|
||||
metabase.notification #{metabase.notification.core
|
||||
metabase.notification.init
|
||||
metabase.notification.payload.core}
|
||||
metabase.permissions #{metabase.permissions.util} ; TODO -- this is currently the only namespace in this module. Give it a real API namespace?
|
||||
metabase.plugins #{metabase.plugins
|
||||
metabase.plugins.classloader} ; TODO -- not 100% sure the classloader belongs here
|
||||
metabase.premium-features #{metabase.premium-features.core}
|
||||
metabase.public-settings #{metabase.public-settings}
|
||||
metabase.pulse #{metabase.pulse.core}
|
||||
metabase.query-processor :any ; TODO omg scream, 29 namespaces used outside of the module. WHAT THE HECC
|
||||
metabase.query-analysis #{metabase.query-analysis.core
|
||||
metabase.query-analysis.init}
|
||||
metabase.request #{metabase.request.core}
|
||||
metabase.sample-data #{metabase.sample-data}
|
||||
metabase.search #{metabase.search.core
|
||||
metabase.search.init
|
||||
;; this is used by `metabase.search.api`... once we move that into this module we
|
||||
;; can remove it as an API namespace
|
||||
metabase.search.config
|
||||
;; Need to expose this submodule, to break circular dependencies with models.
|
||||
metabase.search.spec}
|
||||
metabase.server #{metabase.server.core
|
||||
;;
|
||||
;; TODO FIXME -- this is loaded for side effects since it contains the JSON
|
||||
;; encoder logic for stuff like `java.time` classes. As a
|
||||
;; result, [[metabase.models.setting]] (and thus the `metabase.models` module)
|
||||
;; has to depend on the `metabase.server` module...we should create either a new
|
||||
;; `metabase.json` or `metabase.encoders` module for stuff like this to live. Or
|
||||
;; maybe it just belongs in a generic `metabase.config` module.
|
||||
metabase.server.middleware.json
|
||||
;;
|
||||
;; TODO -- we can't make this part of [[metabase.server.core]] YET because it
|
||||
;; causes circular dependencies. Maybe in the future once we tease apart more of
|
||||
;; the modules tangle we can.
|
||||
metabase.server.protocols
|
||||
;;
|
||||
;; TODO -- I really don't like having YET ANOTHER allowed external namespace for
|
||||
;; this module but some things need to import it so
|
||||
;; `metabase.server.streaming_response.StreamingReponse` comes into existence...
|
||||
metabase.server.streaming-response}
|
||||
metabase.setup #{metabase.setup.api
|
||||
metabase.setup.core}
|
||||
metabase.sync #{metabase.sync.api
|
||||
metabase.sync.core
|
||||
metabase.sync.init
|
||||
;; TODO -- see if we can expose the important parts in `metabase.sync.core`
|
||||
metabase.sync.schedules
|
||||
metabase.sync.util}
|
||||
metabase.task #{metabase.task
|
||||
metabase.task.bootstrap
|
||||
metabase.task.index-values
|
||||
metabase.task.init
|
||||
metabase.task.persist-refresh
|
||||
;; We need to expose this to the metabase.search module.
|
||||
;; Ideally, it would just live inside that module.
|
||||
metabase.task.search-index} ; TODO -- consolidate these into a real API namespace.
|
||||
metabase.types #{metabase.types}
|
||||
metabase.upload #{metabase.upload}
|
||||
metabase.util :any ; I think util being `:any` is actually something I am ok with. But this has 32 namespaces.
|
||||
metabase.xrays #{metabase.xrays.core}}
|
||||
|
||||
;; Map of module => other modules you're allowed to use there. You have two options here:
|
||||
;;
|
||||
;; 1. `:any` means namespaces in this module are allowed to use any other module -- allowed modules are not
|
||||
;; enforced for this module. Module API namespaces for modules that have them defined are still enforced. For
|
||||
;; ones that are `nil`, please go in and add a list of allowed modules. `:any` is mostly meant a temporary
|
||||
;; placeholder until we can fill these all out, so feel free to fix these.
|
||||
;;
|
||||
;; 2. A set of module symbols. This is the list of modules that are allowed to be referenced. An empty set means no
|
||||
;; other modules are allowed to be referenced; this is the default for any modules that aren't listed here.
|
||||
:allowed-modules
|
||||
{metabase.actions :any
|
||||
metabase.analytics :any
|
||||
metabase.analyze :any
|
||||
metabase.api :any
|
||||
metabase.audit :any
|
||||
metabase.auth-provider #{metabase.util
|
||||
metabase.premium-features}
|
||||
metabase.cmd :any
|
||||
metabase.channel :any
|
||||
metabase.config #{metabase.plugins}
|
||||
metabase.core :any
|
||||
metabase.db :any
|
||||
metabase.driver :any
|
||||
metabase.embed :any
|
||||
metabase.events :any
|
||||
metabase.formatter :any
|
||||
metabase.integrations :any
|
||||
metabase.legacy-mbql :any
|
||||
metabase.lib :any
|
||||
metabase.logger #{metabase.config
|
||||
metabase.plugins}
|
||||
metabase.models :any
|
||||
metabase.moderation :any
|
||||
metabase.notification :any
|
||||
metabase.permissions :any
|
||||
metabase.plugins :any
|
||||
metabase.premium-features :any
|
||||
metabase.public-settings :any
|
||||
metabase.pulse :any
|
||||
metabase.query-analysis #{metabase.config
|
||||
metabase.driver
|
||||
metabase.legacy-mbql
|
||||
metabase.lib
|
||||
metabase.models
|
||||
metabase.public-settings
|
||||
metabase.query-processor
|
||||
metabase.task
|
||||
metabase.util}
|
||||
metabase.query-processor :any
|
||||
metabase.request #{metabase.config
|
||||
metabase.models
|
||||
metabase.public-settings
|
||||
metabase.util}
|
||||
metabase.sample-data :any
|
||||
metabase.search :any
|
||||
metabase.server :any
|
||||
metabase.setup :any
|
||||
metabase.sync :any
|
||||
metabase.task :any
|
||||
metabase.types #{metabase.util}
|
||||
metabase.upload :any
|
||||
metabase.util :any ; TODO -- this should probably be #{}
|
||||
metabase.xrays :any}
|
||||
|
||||
;; namespaces matching these patterns (with `re-find`) are excluded from the module linter. Since regex literals
|
||||
;; aren't allowed in EDN just used the `(str <regex>)` version i.e. two slashes instead of one.
|
||||
;;
|
||||
;; this is mostly intended for excluding test namespaces or those rare 'glue' namespaces that glue multiple modules
|
||||
;; together, e.g. `metabase.lib.metadata.jvm`.
|
||||
:ignored-namespace-patterns
|
||||
#{"-test$" ; anything ending in `-test`
|
||||
"test[-.]util" ; anything that contains `test.util` or `test-util`
|
||||
"test\\.impl" ; anything that contains `test.impl`
|
||||
"test-setup" ; anything that contains `test-setup`
|
||||
"^metabase\\.test" ; anything starting with `metabase.test`
|
||||
"^metabase\\.http-client$"}} ; `metabase.http-client` which is a test-only namespace despite its name.
|
||||
|
||||
:consistent-alias
|
||||
{:aliases
|
||||
{buddy.core.hash buddy-hash
|
||||
@@ -554,6 +342,7 @@
|
||||
medley.core m
|
||||
metabase-enterprise.audit-app.pages.common common
|
||||
metabase-enterprise.sandbox.api.table table
|
||||
metabase-enterprise.sandbox.api.util sandbox.api.util
|
||||
metabase-enterprise.test met
|
||||
metabase.analytics.sdk sdk
|
||||
metabase.analytics.stats stats
|
||||
@@ -568,50 +357,10 @@
|
||||
metabase.analyze.fingerprint.fingerprinters fingerprinters
|
||||
metabase.analyze.fingerprint.insights insights
|
||||
metabase.analyze.fingerprint.schema fingerprint.schema
|
||||
metabase.api.activity api.activity
|
||||
metabase.api.alert api.alert
|
||||
metabase.api.automagic-dashboards api.magic
|
||||
metabase.api.bookmark api.bookmark
|
||||
metabase.api.card api.card
|
||||
metabase.api.card-test api.card-test
|
||||
metabase.api.collection api.collection
|
||||
metabase.api.common api
|
||||
metabase.api.dashboard api.dashboard
|
||||
metabase.api.dashboard-test api.dashboard-test
|
||||
metabase.api.database api.database
|
||||
metabase.api.dataset api.dataset
|
||||
metabase.api.email api.email
|
||||
metabase.api.embed api.embed
|
||||
metabase.api.field api.field
|
||||
metabase.api.geojson api.geojson
|
||||
metabase.api.ldap api.ldap
|
||||
metabase.api.logic-history api.login-history
|
||||
metabase.api.metric api.metric
|
||||
metabase.api.native-query-snippet api.native-query-snippet
|
||||
metabase.api.permission-graph api.permission-graph
|
||||
metabase.api.permissions api.permissions
|
||||
metabase.api.permissions-test-util perm-test-util
|
||||
metabase.api.pivots api.pivots
|
||||
metabase.api.premium-features api.premium-features
|
||||
metabase.api.preview-embed api.preview-embed
|
||||
metabase.api.public api.public
|
||||
metabase.api.pulse api.pulse
|
||||
metabase.api.revision api.revision
|
||||
metabase.api.search api.search
|
||||
metabase.api.segment api.segment
|
||||
metabase.api.session api.session
|
||||
metabase.api.setting api.setting
|
||||
metabase.api.setup api.setup
|
||||
metabase.api.slack api.slack
|
||||
metabase.api.table api.table
|
||||
metabase.api.task api.task
|
||||
metabase.api.tiles api.tiles
|
||||
metabase.api.timeline api.timeline
|
||||
metabase.api.timeline-event api.timeline-event
|
||||
metabase.api.user api.user
|
||||
metabase.api.util api.util
|
||||
metabase.channel.core channel
|
||||
metabase.channel.render.core channel.render
|
||||
metabase.cloud-migration.core cloud-migration
|
||||
metabase.cmd.copy.h2 copy.h2
|
||||
metabase.config config
|
||||
metabase.config.file config.file
|
||||
@@ -651,7 +400,7 @@
|
||||
metabase.driver.sql-jdbc.test-util sql-jdbc.tu
|
||||
metabase.driver.sql.parameters.substitute sql.params.substitute
|
||||
metabase.driver.sql.parameters.substitution sql.params.substitution
|
||||
metabase.eid-translation eid-translation
|
||||
metabase.eid-translation.core eid-translation
|
||||
metabase.formatter formatter
|
||||
metabase.http-client client
|
||||
metabase.legacy-mbql.normalize mbql.normalize
|
||||
@@ -659,35 +408,12 @@
|
||||
metabase.legacy-mbql.util mbql.u
|
||||
metabase.lib.util lib.util
|
||||
metabase.lib.util.match lib.util.match
|
||||
metabase.models.activity activity
|
||||
metabase.models.application-permissions-revision a-perm-revision
|
||||
metabase.models.bookmark bookmark
|
||||
metabase.models.collection collection
|
||||
metabase.models.collection.graph graph
|
||||
metabase.models.collection-permission-graph-revision c-perm-revision
|
||||
metabase.models.dashboard-card dashboard-card
|
||||
metabase.models.database database
|
||||
metabase.login-history.core login-history
|
||||
metabase.models.dependency dependency
|
||||
metabase.models.field-values field-values
|
||||
metabase.models.interface mi
|
||||
metabase.models.moderation-review moderation-review
|
||||
metabase.models.native-query-snippet native-query-snippet
|
||||
metabase.models.notification models.notification
|
||||
metabase.models.params params
|
||||
metabase.models.permissions perms
|
||||
metabase.models.permissions-group perms-group
|
||||
metabase.models.permissions-group-membership perms-group-membership
|
||||
metabase.models.permissions-revision perms-revision
|
||||
metabase.models.permissions.parse perms-parse
|
||||
metabase.models.pulse models.pulse
|
||||
metabase.models.pulse-channel pulse-channel
|
||||
metabase.models.pulse-channel-recipient pulse-channel-recipient
|
||||
metabase.models.query.permissions query-perms
|
||||
metabase.models.setting.cache setting.cache
|
||||
metabase.models.timeline timeline
|
||||
metabase.models.timeline-event timeline-event
|
||||
metabase.notification.core notification
|
||||
metabase.notification.test-util notification.tu
|
||||
metabase.permissions.core perms
|
||||
metabase.plugins.initialize plugins.init
|
||||
metabase.public-settings public-settings
|
||||
metabase.premium-features.core premium-features
|
||||
@@ -722,6 +448,7 @@
|
||||
metabase.query-processor.timezone qp.timezone
|
||||
metabase.query-processor.util qp.util
|
||||
metabase.request.core request
|
||||
metabase.revisions.core revisions
|
||||
metabase.search.config search.config
|
||||
metabase.search.filter search.filter
|
||||
metabase.search.in-place.filter search.in-place.filter
|
||||
@@ -733,6 +460,7 @@
|
||||
metabase.sync.field-values sync.field-values
|
||||
metabase.sync.interface i
|
||||
metabase.sync.schedules sync.schedules
|
||||
metabase.sync.sync-metadata.crufty crufty
|
||||
metabase.sync.sync-metadata.fields.our-metadata fields.our-metadata
|
||||
metabase.sync.util sync-util
|
||||
metabase.sync.util-test sync.util-test
|
||||
@@ -747,6 +475,7 @@
|
||||
metabase.test.redefs test.redefs
|
||||
metabase.test.util.js test.js
|
||||
metabase.test.util.timezone test.tz
|
||||
metabase.timeline.core timeline
|
||||
metabase.timeseries-query-processor-test.util tqpt
|
||||
metabase.util.cron u.cron
|
||||
metabase.util.currency currency
|
||||
@@ -794,12 +523,10 @@
|
||||
metabase-enterprise.serialization.test-util/with-dbs clojure.core/fn
|
||||
metabase-enterprise.serialization.test-util/with-random-dump-dir clojure.core/let
|
||||
metabase.actions.test-util/with-actions clojure.core/let
|
||||
metabase.api.common/defroutes clojure.core/def
|
||||
metabase.api.common/let-404 clojure.core/let
|
||||
metabase.api.search-test/do-test-users clojure.core/let
|
||||
metabase.dashboard-subscription-test/with-dashboard-sub-for-card clojure.core/let
|
||||
metabase.channel.impl.http-test/with-captured-http-requests clojure.core/fn
|
||||
metabase.channel.impl.http-test/with-server clojure.core/let
|
||||
metabase.dashboard-subscription-test/with-dashboard-sub-for-card clojure.core/let
|
||||
metabase.db.custom-migrations/define-migration clj-kondo.lint-as/def-catch-all
|
||||
metabase.db.custom-migrations/define-reversible-migration clj-kondo.lint-as/def-catch-all
|
||||
metabase.db.data-migrations/defmigration clojure.core/def
|
||||
@@ -817,8 +544,8 @@
|
||||
metabase.models.setting.multi-setting/define-multi-setting clojure.core/def
|
||||
metabase.models.setting/defsetting clj-kondo.lint-as/def-catch-all
|
||||
metabase.premium-features.core/defenterprise-schema clj-kondo.lint-as/def-catch-all
|
||||
metabase.premium-features.defenterprise/defenterprise-schema clj-kondo.lint-as/def-catch-all
|
||||
metabase.premium-features.core/define-premium-feature clojure.core/def
|
||||
metabase.premium-features.defenterprise/defenterprise-schema clj-kondo.lint-as/def-catch-all
|
||||
metabase.pulse.send-test/with-pulse-for-card clojure.core/let
|
||||
metabase.query-processor-test.pipeline-queries-test/pmbql-query clojure.core/->
|
||||
metabase.query-processor-test.pipeline-queries-test/run-pmbql-query clojure.core/->
|
||||
@@ -826,6 +553,7 @@
|
||||
metabase.query-processor.middleware.cache.impl/with-reducible-deserialized-results clojure.core/let
|
||||
metabase.query-processor.middleware.process-userland-query-test/with-query-execution! clojure.core/let
|
||||
metabase.query-processor.setup/with-qp-setup clojure.core/let
|
||||
metabase.search.api-test/do-test-users clojure.core/let
|
||||
metabase.sync.util/sum-for clojure.core/for
|
||||
metabase.sync.util/with-emoji-progress-bar clojure.core/let
|
||||
metabase.test.data.interface/defdataset clojure.core/def
|
||||
@@ -841,6 +569,7 @@
|
||||
metabase.test/with-temp-file clojure.core/let
|
||||
metabase.test/with-user-in-groups clojure.core/let
|
||||
metabase.upload-test/with-upload-table! clojure.core/let
|
||||
metabase.util/with-timer-ms clojure.core/fn
|
||||
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
|
||||
@@ -869,8 +598,8 @@
|
||||
{cljs.test/deftest hooks.clojure.test/deftest
|
||||
cljs.test/is hooks.clojure.test/is
|
||||
cljs.test/use-fixtures hooks.clojure.test/use-fixtures
|
||||
clojure.core/defmulti hooks.clojure.core/lint-defmulti
|
||||
clojure.core/ns hooks.clojure.core/lint-ns
|
||||
clojure.core/defmulti hooks.clojure.core.defmulti/lint-defmulti
|
||||
clojure.core/ns hooks.clojure.core.ns/lint-ns
|
||||
clojure.test/deftest hooks.clojure.test/deftest
|
||||
clojure.test/is hooks.clojure.test/is
|
||||
clojure.test/use-fixtures hooks.clojure.test/use-fixtures
|
||||
@@ -879,15 +608,11 @@
|
||||
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.serialization.test-util/with-temp-dpc hooks.toucan2.tools.with-temp/with-temp
|
||||
metabase.analytics.prometheus-test/with-prometheus-system! hooks.common/with-two-bindings
|
||||
metabase.api.alert-test/with-alert-in-collection! hooks.common/with-four-bindings
|
||||
metabase.api.automagic-dashboards-test/with-indexed-model! hooks.metabase.api.automagic-dashboards-test/with-indexed-model!
|
||||
metabase.api.card-test/with-card-param-values-fixtures hooks.common/let-one-with-optional-value
|
||||
metabase.api.card-test/with-persistence-setup! hooks.common/with-one-top-level-binding
|
||||
metabase.api.card-test/with-temp-native-card! hooks.common/with-two-bindings
|
||||
metabase.api.card-test/with-temp-native-card-with-params! hooks.common/with-two-bindings
|
||||
metabase.api.collection-test/with-french-user-and-personal-collection! hooks.common/with-two-top-level-bindings
|
||||
metabase.api.common/defendpoint hooks.metabase.api.common/defendpoint
|
||||
metabase.api.common.internal-test/with-jetty-server hooks.common/with-one-binding
|
||||
metabase.api.dashboard-test/with-chain-filter-fixtures hooks.common/let-one-with-optional-value
|
||||
metabase.api.dashboard-test/with-simple-dashboard-with-tabs hooks.common/with-one-binding
|
||||
@@ -901,8 +626,6 @@
|
||||
metabase.api.public-test/with-temp-public-card hooks.common/let-one-with-optional-value
|
||||
metabase.api.public-test/with-temp-public-dashboard hooks.common/let-one-with-optional-value
|
||||
metabase.api.public-test/with-temp-public-dashboard-and-card hooks.common/with-three-bindings
|
||||
metabase.api.search-test/with-search-items-in-collection hooks.metabase.api.search-test/with-search-items-in-collection
|
||||
metabase.api.search-test/with-search-items-in-root-collection hooks.common/do*
|
||||
metabase.api.user-test/with-temp-user-email! hooks.common/with-one-binding
|
||||
metabase.channel.template.handlebars-test/with-temp-template! hooks.common/with-vec-first-binding
|
||||
metabase.channel.template.handlebars-helper/defhelper hooks.metabase.channel.template.handlebars-helper/defhelper
|
||||
@@ -924,12 +647,15 @@
|
||||
metabase.models.dashboard-test/with-dash-in-collection! hooks.common/with-three-bindings
|
||||
metabase.models.interface/define-batched-hydration-method hooks.metabase.models.interface/define-hydration-method
|
||||
metabase.models.interface/define-simple-hydration-method hooks.metabase.models.interface/define-hydration-method
|
||||
metabase.models.pulse-test/with-dashboard-subscription-in-collection! hooks.common/with-four-bindings
|
||||
metabase.models.pulse-test/with-pulse-in-collection! hooks.common/with-four-bindings
|
||||
metabase.models.setting.multi-setting/define-multi-setting hooks.metabase.models.setting/define-multi-setting
|
||||
metabase.models.setting/defsetting hooks.metabase.models.setting/defsetting
|
||||
metabase.permissions.core/allow-changing-all-users-group-members hooks.common/do*
|
||||
metabase.permissions.core/disable-perms-cache hooks.common/do*
|
||||
metabase.premium-features.core/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
|
||||
metabase.premium-features.defenterprise/defenterprise hooks.metabase.premium-features.defenterprise/defenterprise
|
||||
metabase.pulse.api.alert-test/with-alert-in-collection! hooks.common/with-four-bindings
|
||||
metabase.pulse.models.pulse-test/with-dashboard-subscription-in-collection! hooks.common/with-four-bindings
|
||||
metabase.pulse.models.pulse-test/with-pulse-in-collection! hooks.common/with-four-bindings
|
||||
metabase.pulse.test-util/checkins-query-card hooks.metabase.test.data/$ids
|
||||
metabase.query-processor-test.expressions-test/calculate-bird-scarcity hooks.metabase.query-processor-test.expressions-test/calculate-bird-scarcity
|
||||
metabase.query-processor-test.filter-test/count-with-filter-clause hooks.metabase.test.data/$ids
|
||||
@@ -938,6 +664,8 @@
|
||||
metabase.request.core/with-current-user hooks.common/with-used-first-arg
|
||||
metabase.request.core/with-limit-and-offset hooks.common/with-used-first-two-args
|
||||
metabase.sample-data-test/with-temp-sample-database-db hooks.common/with-one-binding
|
||||
metabase.search.api-test/with-search-items-in-collection hooks.metabase.search.api-test/with-search-items-in-collection
|
||||
metabase.search.api-test/with-search-items-in-root-collection hooks.common/do*
|
||||
metabase.server.streaming-response-test/with-start-execution-chan hooks.common/with-one-binding
|
||||
metabase.stale-test/with-stale-items hooks.toucan2.tools.with-temp/with-temp
|
||||
metabase.test.data.datasets/test-driver hooks.metabase.test.data.datasets/test-driver
|
||||
@@ -952,6 +680,7 @@
|
||||
metabase.test.util/discard-setting-changes hooks.common/with-ignored-first-arg
|
||||
metabase.test.util/with-column-remappings hooks.common/with-ignored-first-arg
|
||||
metabase.test.util/with-non-admin-groups-no-root-collection-perms hooks.common/do*
|
||||
metabase.test.util/with-prometheus-system! hooks.common/with-two-bindings
|
||||
metabase.test.util/with-temp-file hooks.metabase.test.util/with-temp-file
|
||||
metabase.test.util/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values
|
||||
metabase.test/$ids hooks.metabase.test.data/$ids
|
||||
@@ -969,6 +698,7 @@
|
||||
metabase.test/with-non-admin-groups-no-root-collection-perms hooks.common/do*
|
||||
metabase.test/with-temp hooks.toucan2.tools.with-temp/with-temp
|
||||
metabase.test/with-temp-file hooks.metabase.test.util/with-temp-file
|
||||
metabase.test/with-prometheus-system! hooks.common/with-two-bindings
|
||||
metabase.test/with-temporary-setting-values hooks.metabase.test.util/with-temporary-setting-values
|
||||
metabase.util.log.capture/with-log-messages-for-level hooks.metabase.util.log.capture/with-log-messages-for-level
|
||||
metabase.util.log/debug hooks.metabase.util.log/info
|
||||
@@ -985,14 +715,15 @@
|
||||
metabase.util.log/warnf hooks.metabase.util.log/infof
|
||||
metabase.util.malli.registry/def hooks.metabase.util.malli.registry/def
|
||||
metabase.util/case-enum hooks.metabase.util/case-enum
|
||||
metabase.util/format-color hooks.metabase.util/format-color}
|
||||
metabase.util/format-color hooks.metabase.util/format-color
|
||||
metabase.xrays.api.automagic-dashboards-test/with-indexed-model! hooks.metabase.xrays.api.automagic-dashboards-test/with-indexed-model!}
|
||||
|
||||
:macroexpand
|
||||
{clojurewerkz.quartzite.jobs/build macros.quartz/build-job
|
||||
clojurewerkz.quartzite.schedule.cron/schedule macros.quartz/schedule
|
||||
clojurewerkz.quartzite.schedule.simple/schedule macros.quartz/simple-schedule
|
||||
clojurewerkz.quartzite.triggers/build macros.quartz/build-trigger
|
||||
metabase-enterprise.advanced-permissions.api.util-test/with-impersonations! macros.metabase-enterprise.advanced-permissions.api.util-test/with-impersonations!
|
||||
metabase-enterprise.impersonation.util-test/with-impersonations! macros.metabase-enterprise.impersonation.util-test/with-impersonations!
|
||||
metabase-enterprise.query-reference-validation.api-test/with-test-setup! macros.metabase-enterprise.query-reference-validation.api-test/with-test-setup!
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! macros.metabase-enterprise.sandbox.test-util/with-gtaps!
|
||||
@@ -1002,7 +733,6 @@
|
||||
metabase.api.card-test/with-ordered-items macros.metabase.api.card-test/with-ordered-items
|
||||
metabase.api.collection-test/with-collection-hierarchy! macros.metabase.api.collection-test/with-collection-hierarchy!
|
||||
metabase.api.collection-test/with-some-children-of-collection! macros.metabase.api.collection-test/with-some-children-of-collection!
|
||||
metabase.api.common/define-routes macros.metabase.api.common/define-routes
|
||||
metabase.api.embed-test/with-embedding-enabled-and-temp-card-referencing! macros.metabase.api.embed-test/with-embedding-enabled-and-temp-card-referencing!
|
||||
metabase.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing! macros.metabase.api.embed-test/with-embedding-enabled-and-temp-dashcard-referencing!
|
||||
metabase.api.public-test/with-sharing-enabled-and-temp-card-referencing! macros.metabase.api.public-test/with-sharing-enabled-and-temp-card-referencing!
|
||||
@@ -1045,7 +775,7 @@
|
||||
:name test-namespaces}
|
||||
{:pattern "^(?!.*-test(?:\\..*)?$)(?!.*test-util)(?!^metabase\\.test.*).*$"
|
||||
:name source-namespaces}
|
||||
{:pattern "(?:(?:^metabase\\.cmd.*)|(?:^build.*)|(?:^metabuild-common.*)|(?:^release.*)|(?:^i18n.*)|(?:^lint-migrations-file$))"
|
||||
{:pattern "(?:(?:^metabase\\.cmd.*)|(?:^build.*)|(?:^metabuild-common.*)|(?:^release.*)|(?:^i18n.*)|(?:^lint-migrations-file$))|(?:^mage.*)"
|
||||
:name printable-namespaces}
|
||||
{:pattern "^metabase\\.(?:(?:lib)|(?:legacy-mbql))\\.*"
|
||||
:name metabase-lib}
|
||||
@@ -1079,9 +809,9 @@
|
||||
|
||||
:hooks
|
||||
{:analyze-call
|
||||
{clojure.core/defmacro hooks.clojure.core/non-thread-safe-form-should-end-with-exclamation
|
||||
clojure.core/defn hooks.clojure.core/non-thread-safe-form-should-end-with-exclamation
|
||||
clojure.core/defn- hooks.clojure.core/non-thread-safe-form-should-end-with-exclamation}}}
|
||||
{clojure.core/defmacro hooks.clojure.core.defn/non-thread-safe-form-should-end-with-exclamation
|
||||
clojure.core/defn hooks.clojure.core.defn/non-thread-safe-form-should-end-with-exclamation
|
||||
clojure.core/defn- hooks.clojure.core.defn/non-thread-safe-form-should-end-with-exclamation}}}
|
||||
|
||||
source-namespaces
|
||||
{:linters
|
||||
|
||||
763
.clj-kondo/config/modules/config.edn
Normal file
@@ -0,0 +1,763 @@
|
||||
;; A "module" is any `metabase.<module>.core` namespace in `src`.
|
||||
;;
|
||||
;; ## `api`:
|
||||
;;
|
||||
;; Map of module name => the "core" external public-facing API namespace(s). You have three options here:
|
||||
;;
|
||||
;; 1. Special sentinel value `:any` means means this module does not (yet) have external public-facing API
|
||||
;; namespace(s). This is mostly a temporary placeholder until we go in and create module namespaces, which means
|
||||
;; you should go create one.
|
||||
;;
|
||||
;; 2. A set of namespace symbols. All namespaces in other modules will only be allowed to use namespaces from
|
||||
;; this set. Ideally this set should only have one namespace, but restricting it to a set of several is still
|
||||
;; better than `:any`.
|
||||
;;
|
||||
;; 3. `nil` or not listed here -- we default to assuming there is (some subset) of the 'standard' API namespaces
|
||||
;; `<module>.api`, `<module>.core`, and `<module>.init`
|
||||
;;
|
||||
;; ## `uses`:
|
||||
;;
|
||||
;; Map of module => other modules you're allowed to use there. You have two options here:
|
||||
;;
|
||||
;; 1. `:any` means namespaces in this module are allowed to use any other module -- allowed modules are not
|
||||
;; enforced for this module. Module API namespaces for modules that have them defined are still enforced. For
|
||||
;; ones that are `nil`, please go in and add a list of allowed modules. `:any` is mostly meant a temporary
|
||||
;; placeholder until we can fill these all out, so feel free to fix these.
|
||||
;;
|
||||
;; 2. A set of module symbols. This is the list of modules that are allowed to be referenced. An empty set means no
|
||||
;; other modules are allowed to be referenced; this is the default for any modules that aren't listed here.
|
||||
;;
|
||||
;; ## Tips
|
||||
;;
|
||||
;; PRO TIP: Check out the [[dev.deps-graph]] namespace for helpful tools to see where a module is used externally, and
|
||||
;; for tools for calculating this config.
|
||||
{:metabase/modules
|
||||
{actions
|
||||
{:api #{metabase.actions.api metabase.actions.core}
|
||||
:uses #{analytics api driver legacy-mbql lib models query-processor search util}}
|
||||
|
||||
activity-feed
|
||||
{:api #{metabase.activity-feed.api metabase.activity-feed.init}
|
||||
:uses #{api config db events models util}}
|
||||
|
||||
analytics
|
||||
{:api #{metabase.analytics.prometheus
|
||||
metabase.analytics.sdk
|
||||
metabase.analytics.snowplow
|
||||
metabase.analytics.stats}
|
||||
:uses #{api
|
||||
channel
|
||||
config
|
||||
db
|
||||
driver
|
||||
eid-translation
|
||||
embed
|
||||
integrations
|
||||
internal-stats
|
||||
models
|
||||
premium-features
|
||||
public-settings
|
||||
server
|
||||
util}}
|
||||
analyze
|
||||
{:api #{metabase.analyze.core metabase.analyze.query-results}
|
||||
:uses #{config driver legacy-mbql lib models sync util}}
|
||||
|
||||
api
|
||||
{:api #{metabase.api.auth
|
||||
metabase.api.cache
|
||||
metabase.api.card
|
||||
metabase.api.collection
|
||||
metabase.api.common
|
||||
metabase.api.common.validation
|
||||
metabase.api.dataset
|
||||
metabase.api.ldap
|
||||
metabase.api.macros
|
||||
metabase.api.open-api
|
||||
metabase.api.query-metadata
|
||||
metabase.api.routes
|
||||
metabase.api.routes.common
|
||||
metabase.api.table
|
||||
metabase.api.user
|
||||
metabase.api.util.handlers}
|
||||
:uses :any}
|
||||
|
||||
audit {:api #{metabase.audit}
|
||||
:uses #{db models}}
|
||||
|
||||
auth-provider
|
||||
{:api #{metabase.auth-provider}
|
||||
:uses #{premium-features}}
|
||||
|
||||
bookmarks
|
||||
{:api #{metabase.bookmarks.api}
|
||||
:uses #{api db models util}}
|
||||
|
||||
channel
|
||||
{:api #{metabase.channel.api
|
||||
metabase.channel.core
|
||||
metabase.channel.email
|
||||
metabase.channel.email.messages
|
||||
metabase.channel.init
|
||||
metabase.channel.render.core
|
||||
metabase.channel.template.core}
|
||||
:uses #{analytics
|
||||
api
|
||||
config
|
||||
db
|
||||
driver
|
||||
events
|
||||
formatter
|
||||
integrations
|
||||
lib
|
||||
models
|
||||
notification
|
||||
permissions
|
||||
premium-features
|
||||
public-settings
|
||||
query-processor
|
||||
timeline
|
||||
types
|
||||
util}}
|
||||
|
||||
cloud-migration
|
||||
{:api #{metabase.cloud-migration.api
|
||||
metabase.cloud-migration.core
|
||||
metabase.cloud-migration.init}
|
||||
:uses #{api cmd config db models premium-features util}}
|
||||
|
||||
cmd
|
||||
{:api #{metabase.cmd
|
||||
metabase.cmd.copy
|
||||
metabase.cmd.dump-to-h2}
|
||||
:uses #{api config db legacy-mbql models plugins query-processor search util}}
|
||||
|
||||
;; technically this 'uses' `enterprise/core` and `test` since it tries to load them to see if they exists so we know
|
||||
;; if EE/test code is available; however we can ignore them since they're not real usages.
|
||||
config
|
||||
{:api #{metabase.config}
|
||||
:uses #{}}
|
||||
|
||||
;; this is not actually a real module, but comes from one of our libraries.
|
||||
connection-pool
|
||||
{:api #{metabase.connection-pool}
|
||||
:uses #{}}
|
||||
|
||||
core
|
||||
{:api #{metabase.core.initialization-status}
|
||||
:uses :any}
|
||||
|
||||
db
|
||||
{:api #{metabase.db metabase.db.metadata-queries metabase.db.query metabase.db.setup}
|
||||
:uses #{auth-provider config connection-pool driver legacy-mbql lib plugins query-processor task util}}
|
||||
|
||||
driver
|
||||
{:api #{metabase.driver
|
||||
metabase.driver.common
|
||||
metabase.driver.common.parameters
|
||||
metabase.driver.common.parameters.dates
|
||||
metabase.driver.common.parameters.operators
|
||||
metabase.driver.common.parameters.parse
|
||||
metabase.driver.common.parameters.values
|
||||
metabase.driver.ddl.interface
|
||||
metabase.driver.h2
|
||||
metabase.driver.impl
|
||||
metabase.driver.init
|
||||
metabase.driver.mysql
|
||||
metabase.driver.postgres
|
||||
metabase.driver.sql
|
||||
metabase.driver.sql-jdbc.connection
|
||||
metabase.driver.sql-jdbc.execute
|
||||
metabase.driver.sql-jdbc.execute.diagnostic
|
||||
metabase.driver.sql-jdbc.sync
|
||||
metabase.driver.sql.query-processor
|
||||
metabase.driver.sql.query-processor.deprecated
|
||||
metabase.driver.sql.util
|
||||
metabase.driver.sync
|
||||
metabase.driver.util}
|
||||
:uses #{actions
|
||||
auth-provider
|
||||
config
|
||||
connection-pool
|
||||
db
|
||||
legacy-mbql
|
||||
lib
|
||||
logger
|
||||
models
|
||||
plugins
|
||||
premium-features
|
||||
public-settings
|
||||
pulse
|
||||
query-processor
|
||||
sync
|
||||
upload
|
||||
util}}
|
||||
|
||||
eid-translation
|
||||
{:api #{metabase.eid-translation.core}
|
||||
:uses #{}}
|
||||
|
||||
embed
|
||||
{:api #{metabase.embed.settings metabase.embed.app-origins-sdk}
|
||||
:uses #{analytics models premium-features tiles util}}
|
||||
|
||||
events
|
||||
{:api #{metabase.events metabase.events.init metabase.events.notification}
|
||||
:uses #{api driver models notification premium-features public-settings util}}
|
||||
|
||||
formatter
|
||||
{:api #{metabase.formatter}
|
||||
:uses #{models public-settings query-processor types util}}
|
||||
|
||||
indexed-entities
|
||||
{:api #{metabase.indexed-entities.api metabase.indexed-entities.init}
|
||||
:uses #{analytics api driver legacy-mbql lib models query-processor search sync task util}}
|
||||
|
||||
integrations
|
||||
{:api #{metabase.integrations.common
|
||||
metabase.integrations.google
|
||||
metabase.integrations.google.interface
|
||||
metabase.integrations.ldap
|
||||
metabase.integrations.ldap.default-implementation
|
||||
metabase.integrations.slack}
|
||||
:uses #{api config events models permissions plugins premium-features util enterprise/enhancements}}
|
||||
|
||||
internal-stats
|
||||
{:api #{metabase.internal-stats.core}
|
||||
:uses #{db models}}
|
||||
|
||||
legacy-mbql
|
||||
{:api #{metabase.legacy-mbql.normalize
|
||||
metabase.legacy-mbql.predicates
|
||||
metabase.legacy-mbql.schema
|
||||
metabase.legacy-mbql.schema.helpers
|
||||
metabase.legacy-mbql.util}
|
||||
:uses #{lib models types util}}
|
||||
|
||||
lib
|
||||
{:api #{metabase.lib.binning
|
||||
metabase.lib.binning.util
|
||||
metabase.lib.card
|
||||
metabase.lib.convert
|
||||
metabase.lib.core
|
||||
metabase.lib.equality
|
||||
metabase.lib.field
|
||||
metabase.lib.filter
|
||||
metabase.lib.ident
|
||||
metabase.lib.join.util
|
||||
metabase.lib.metadata
|
||||
metabase.lib.metadata.calculation
|
||||
metabase.lib.metadata.jvm
|
||||
metabase.lib.metadata.protocols
|
||||
metabase.lib.normalize
|
||||
metabase.lib.options
|
||||
metabase.lib.query
|
||||
metabase.lib.schema
|
||||
metabase.lib.schema.actions
|
||||
metabase.lib.schema.binning
|
||||
metabase.lib.schema.common
|
||||
metabase.lib.schema.expression
|
||||
metabase.lib.schema.expression.temporal
|
||||
metabase.lib.schema.expression.window
|
||||
metabase.lib.schema.id
|
||||
metabase.lib.schema.info
|
||||
metabase.lib.schema.literal
|
||||
metabase.lib.schema.metadata
|
||||
metabase.lib.schema.parameter
|
||||
metabase.lib.schema.template-tag
|
||||
metabase.lib.schema.temporal-bucketing
|
||||
metabase.lib.schema.util
|
||||
metabase.lib.temporal-bucket
|
||||
metabase.lib.types.isa
|
||||
metabase.lib.util
|
||||
metabase.lib.util.match
|
||||
metabase.lib.walk}
|
||||
:uses #{config driver legacy-mbql models types util}}
|
||||
|
||||
logger
|
||||
{:api #{metabase.logger metabase.logger.init}
|
||||
:uses #{config plugins}}
|
||||
|
||||
login-history
|
||||
{:api #{metabase.login-history.api
|
||||
metabase.login-history.core
|
||||
metabase.login-history.init}
|
||||
:uses #{analytics api channel models request util}}
|
||||
|
||||
models
|
||||
{:api #{metabase.models.api-key
|
||||
metabase.models.application-permissions-revision
|
||||
metabase.models.audit-log
|
||||
metabase.models.cache-config
|
||||
metabase.models.card
|
||||
metabase.models.card.metadata
|
||||
metabase.models.collection
|
||||
metabase.models.collection-permission-graph-revision
|
||||
metabase.models.collection.graph
|
||||
metabase.models.collection.root
|
||||
metabase.models.dashboard
|
||||
metabase.models.dashboard-card
|
||||
metabase.models.dashboard-tab
|
||||
metabase.models.dashboard.constants
|
||||
metabase.models.database
|
||||
metabase.models.dispatch
|
||||
metabase.models.field
|
||||
metabase.models.field-usage
|
||||
metabase.models.field-values
|
||||
metabase.models.humanization
|
||||
metabase.models.init
|
||||
metabase.models.interface
|
||||
metabase.models.moderation-review
|
||||
metabase.models.native-query-snippet
|
||||
metabase.models.native-query-snippet.permissions
|
||||
metabase.models.notification
|
||||
metabase.models.params
|
||||
metabase.models.params.chain-filter
|
||||
metabase.models.params.custom-values
|
||||
metabase.models.params.field-values
|
||||
metabase.models.params.shared
|
||||
metabase.models.persisted-info
|
||||
metabase.models.query
|
||||
metabase.models.query-analysis
|
||||
metabase.models.query.permissions
|
||||
metabase.models.resolution
|
||||
metabase.models.secret
|
||||
metabase.models.serialization
|
||||
metabase.models.setting
|
||||
metabase.models.setting.cache
|
||||
metabase.models.setting.multi-setting
|
||||
metabase.models.table
|
||||
metabase.models.task-history
|
||||
metabase.models.user
|
||||
metabase.models.user-parameter-value
|
||||
metabase.models.view-log-impl
|
||||
metabase.models.visualization-settings}
|
||||
:uses #{analytics
|
||||
analyze
|
||||
api
|
||||
audit
|
||||
channel
|
||||
config
|
||||
db
|
||||
driver
|
||||
events
|
||||
integrations
|
||||
legacy-mbql
|
||||
lib
|
||||
moderation
|
||||
permissions
|
||||
plugins
|
||||
premium-features
|
||||
public-settings
|
||||
pulse
|
||||
query-analysis
|
||||
query-processor
|
||||
request
|
||||
search
|
||||
server
|
||||
setup
|
||||
sync
|
||||
task
|
||||
types
|
||||
util
|
||||
xrays
|
||||
enterprise/advanced-permissions}}
|
||||
|
||||
moderation
|
||||
{:api #{metabase.moderation}
|
||||
:uses #{models util}}
|
||||
|
||||
notification
|
||||
{:api #{metabase.notification.core
|
||||
metabase.notification.init
|
||||
metabase.notification.payload.core
|
||||
metabase.notification.payload.execute}
|
||||
:uses #{analytics api channel events models permissions premium-features public-settings query-processor request util}}
|
||||
|
||||
permissions
|
||||
{:api #{metabase.permissions.api
|
||||
metabase.permissions.core
|
||||
metabase.permissions.models.data-permissions
|
||||
metabase.permissions.models.permissions
|
||||
metabase.permissions.models.permissions-group
|
||||
metabase.permissions.util}
|
||||
:uses #{api audit config db integrations models plugins premium-features request server util}}
|
||||
|
||||
pivot
|
||||
{:api #{metabase.pivot.core}
|
||||
:uses #{util models}}
|
||||
|
||||
plugins
|
||||
{:api #{metabase.plugins metabase.plugins.classloader}
|
||||
:uses #{config driver util}}
|
||||
|
||||
premium-features
|
||||
{:api #{metabase.premium-features.core
|
||||
metabase.premium-features.token-check}
|
||||
:uses #{config db internal-stats models plugins util}}
|
||||
|
||||
public-settings
|
||||
{:api #{metabase.public-settings}
|
||||
:uses #{api config models plugins premium-features util enterprise/sso}}
|
||||
|
||||
public-sharing
|
||||
{:api #{metabase.public-sharing.api
|
||||
metabase.public-sharing.core
|
||||
metabase.public-sharing.init}
|
||||
:uses #{actions analytics api db events lib models query-processor request tiles util}}
|
||||
|
||||
pulse
|
||||
{:api #{metabase.pulse.api
|
||||
metabase.pulse.core
|
||||
metabase.pulse.init
|
||||
metabase.pulse.task.send-pulses}
|
||||
:uses #{api channel config db driver events integrations models notification permissions plugins premium-features
|
||||
query-processor request task util
|
||||
enterprise/advanced-config enterprise/advanced-permissions enterprise/sandbox}}
|
||||
|
||||
query-analysis
|
||||
{:api #{metabase.query-analysis.core metabase.query-analysis.init}
|
||||
:uses #{config driver legacy-mbql lib public-settings query-processor task util}}
|
||||
|
||||
query-processor
|
||||
{:api #{metabase.query-processor
|
||||
metabase.query-processor.card
|
||||
metabase.query-processor.compile
|
||||
metabase.query-processor.dashboard
|
||||
metabase.query-processor.debug
|
||||
metabase.query-processor.error-type
|
||||
metabase.query-processor.interface
|
||||
metabase.query-processor.metadata
|
||||
metabase.query-processor.middleware.annotate
|
||||
metabase.query-processor.middleware.cache-backend.db
|
||||
metabase.query-processor.middleware.constraints
|
||||
metabase.query-processor.middleware.fetch-source-query
|
||||
metabase.query-processor.middleware.fetch-source-query-legacy
|
||||
metabase.query-processor.middleware.limit
|
||||
metabase.query-processor.middleware.permissions
|
||||
metabase.query-processor.middleware.wrap-value-literals
|
||||
metabase.query-processor.pipeline
|
||||
metabase.query-processor.pivot
|
||||
metabase.query-processor.preprocess
|
||||
metabase.query-processor.reducible
|
||||
metabase.query-processor.schema
|
||||
metabase.query-processor.setup
|
||||
metabase.query-processor.store
|
||||
metabase.query-processor.streaming
|
||||
metabase.query-processor.streaming.common
|
||||
metabase.query-processor.streaming.interface
|
||||
metabase.query-processor.timezone
|
||||
metabase.query-processor.util
|
||||
metabase.query-processor.util.add-alias-info
|
||||
metabase.query-processor.util.nest-query
|
||||
metabase.query-processor.util.persisted-cache
|
||||
metabase.query-processor.util.relative-datetime
|
||||
metabase.query-processor.util.transformations.nest-breakouts
|
||||
metabase.query-processor.writeback}
|
||||
:uses #{analytics
|
||||
analyze
|
||||
api
|
||||
audit
|
||||
config
|
||||
db
|
||||
driver
|
||||
events
|
||||
formatter
|
||||
legacy-mbql
|
||||
lib
|
||||
models
|
||||
permissions
|
||||
pivot
|
||||
premium-features
|
||||
public-settings
|
||||
request
|
||||
server
|
||||
types
|
||||
util}}
|
||||
|
||||
request
|
||||
{:api #{metabase.request.core}
|
||||
:uses #{api config models permissions public-settings util}}
|
||||
|
||||
revisions
|
||||
{:api #{metabase.revisions.api metabase.revisions.core metabase.revisions.init}
|
||||
:uses #{api config events models util}}
|
||||
|
||||
sample-data
|
||||
{:api #{metabase.sample-data}
|
||||
:uses #{plugins sync util}}
|
||||
|
||||
search
|
||||
{:api #{metabase.search.api
|
||||
metabase.search.appdb.scoring
|
||||
metabase.search.config
|
||||
metabase.search.core
|
||||
metabase.search.in-place.scoring
|
||||
metabase.search.ingestion
|
||||
metabase.search.init
|
||||
metabase.search.spec}
|
||||
:uses #{analytics api audit config db driver models permissions premium-features public-settings request startup task util}}
|
||||
|
||||
segments
|
||||
{:api #{metabase.segments.api}
|
||||
:uses #{api events legacy-mbql lib models permissions search util xrays}}
|
||||
|
||||
server
|
||||
{:api #{metabase.server.core
|
||||
metabase.server.middleware.json
|
||||
metabase.server.middleware.session
|
||||
metabase.server.streaming-response}
|
||||
:uses #{analytics api config core db driver embed models premium-features public-settings request util
|
||||
enterprise/sso}}
|
||||
|
||||
session
|
||||
{:api #{metabase.session.api metabase.session.init metabase.session.models.session}
|
||||
:uses #{api channel config db driver events integrations login-history models public-settings request task util}}
|
||||
|
||||
setup
|
||||
{:api #{metabase.setup.api metabase.setup.core}
|
||||
:uses #{analytics
|
||||
api
|
||||
channel
|
||||
config
|
||||
db
|
||||
embed
|
||||
events
|
||||
integrations
|
||||
models
|
||||
permissions
|
||||
premium-features
|
||||
public-settings
|
||||
request
|
||||
session
|
||||
util}}
|
||||
|
||||
startup
|
||||
{:api #{metabase.startup.core}
|
||||
:uses #{util}}
|
||||
|
||||
sync
|
||||
{:api #{metabase.sync.api
|
||||
metabase.sync.core
|
||||
metabase.sync.init
|
||||
metabase.sync.schedules
|
||||
metabase.sync.task.sync-databases
|
||||
metabase.sync.util}
|
||||
:uses #{analyze api audit config db driver events lib models plugins query-processor task util}}
|
||||
|
||||
task
|
||||
{:api #{metabase.task
|
||||
metabase.task.bootstrap
|
||||
metabase.task.init
|
||||
metabase.task.notification
|
||||
metabase.task.persist-refresh}
|
||||
:uses #{analytics
|
||||
channel
|
||||
config
|
||||
db
|
||||
driver
|
||||
integrations
|
||||
models
|
||||
notification
|
||||
plugins
|
||||
premium-features
|
||||
public-settings
|
||||
query-processor
|
||||
util
|
||||
enterprise/task}}
|
||||
|
||||
tiles
|
||||
{:api #{metabase.tiles.api
|
||||
metabase.tiles.init}
|
||||
:uses #{api legacy-mbql models query-processor util}}
|
||||
|
||||
timeline
|
||||
{:api #{metabase.timeline.api metabase.timeline.core}
|
||||
:uses #{analytics api models util}}
|
||||
|
||||
types
|
||||
{:api #{metabase.types}
|
||||
:uses #{util}}
|
||||
|
||||
upload
|
||||
{:api #{metabase.upload}
|
||||
:uses #{analytics api driver events legacy-mbql lib models permissions public-settings sync util}}
|
||||
|
||||
user-key-value
|
||||
{:api #{metabase.user-key-value.api metabase.user-key-value.init}
|
||||
:uses #{api config lib util}}
|
||||
|
||||
util
|
||||
{:api :any
|
||||
:uses #{config db driver legacy-mbql lib models plugins premium-features public-settings}}
|
||||
|
||||
xrays
|
||||
{:api #{metabase.xrays.api metabase.xrays.core}
|
||||
:uses #{analyze api db driver legacy-mbql lib models public-settings query-processor util}}
|
||||
|
||||
enterprise/advanced-config
|
||||
{:api #{metabase-enterprise.advanced-config.api.logs
|
||||
metabase-enterprise.advanced-config.models.pulse-channel
|
||||
metabase-enterprise.advanced-config.file
|
||||
metabase-enterprise.advanced-config.crufty}
|
||||
:uses #{api db driver models permissions premium-features setup sync util}}
|
||||
|
||||
enterprise/advanced-permissions
|
||||
{:api #{metabase-enterprise.advanced-permissions.api.routes
|
||||
metabase-enterprise.advanced-permissions.common
|
||||
metabase-enterprise.advanced-permissions.models.permissions.group-manager}
|
||||
:uses #{api audit driver enterprise/impersonation lib models permissions premium-features query-processor util enterprise/sandbox}}
|
||||
|
||||
enterprise/airgap
|
||||
{:api #{}
|
||||
:uses #{premium-features util}}
|
||||
|
||||
enterprise/api
|
||||
{:api #{metabase-enterprise.api.routes
|
||||
metabase-enterprise.api.routes.common}
|
||||
:uses :any}
|
||||
|
||||
enterprise/audit-app
|
||||
{:api #{metabase-enterprise.audit-app.api.routes}
|
||||
:uses #{api
|
||||
audit
|
||||
db
|
||||
driver
|
||||
lib
|
||||
models
|
||||
permissions
|
||||
plugins
|
||||
premium-features
|
||||
query-processor
|
||||
sync
|
||||
util
|
||||
enterprise/serialization}}
|
||||
|
||||
enterprise/auth-provider
|
||||
{:api #{}
|
||||
:uses #{premium-features util}}
|
||||
|
||||
enterprise/billing
|
||||
{:api #{metabase-enterprise.billing.api.routes}
|
||||
:uses #{api premium-features util}}
|
||||
|
||||
enterprise/cache
|
||||
{:api #{metabase-enterprise.cache.strategies}
|
||||
:uses #{api models premium-features query-processor util}}
|
||||
|
||||
enterprise/content-verification
|
||||
{:api #{metabase-enterprise.content-verification.api.routes}
|
||||
:uses #{api models moderation util enterprise/api}}
|
||||
|
||||
enterprise/core
|
||||
{:api #{metabase-enterprise.core.init}
|
||||
:uses #{enterprise/enhancements}}
|
||||
|
||||
enterprise/dashboard-subscription-filters
|
||||
{:api #{}
|
||||
:uses #{premium-features}}
|
||||
|
||||
enterprise/enhancements
|
||||
{:api #{metabase-enterprise.enhancements.init
|
||||
metabase-enterprise.enhancements.integrations.google
|
||||
metabase-enterprise.enhancements.integrations.ldap}
|
||||
:uses #{integrations models premium-features util enterprise/sso}}
|
||||
|
||||
enterprise/gsheets
|
||||
{:api #{metabase-enterprise.gsheets.api}
|
||||
:uses #{api analytics enterprise/harbormaster models premium-features server util}}
|
||||
|
||||
enterprise/harbormaster
|
||||
{:api #{metabase-enterprise.harbormaster.client}
|
||||
:uses #{api cloud-migration models util}}
|
||||
|
||||
enterprise/impersonation
|
||||
{:api #{metabase-enterprise.impersonation.core
|
||||
metabase-enterprise.impersonation.api}
|
||||
:uses #{api audit driver enterprise/sandbox lib models permissions premium-features query-processor util}}
|
||||
|
||||
enterprise/internal-stats
|
||||
{:api #{metabase-enterprise.internal-stats}
|
||||
:uses #{models premium-features}}
|
||||
|
||||
enterprise/llm
|
||||
{:api #{metabase-enterprise.llm.api}
|
||||
:uses #{analytics analyze api models query-processor util}}
|
||||
|
||||
enterprise/query-reference-validation
|
||||
{:api #{metabase-enterprise.query-reference-validation.api}
|
||||
:uses #{api models public-settings request util}}
|
||||
|
||||
enterprise/sandbox
|
||||
{:api #{metabase-enterprise.sandbox.api.routes
|
||||
metabase-enterprise.sandbox.api.util}
|
||||
:uses #{api
|
||||
audit
|
||||
db
|
||||
legacy-mbql
|
||||
lib
|
||||
models
|
||||
permissions
|
||||
plugins
|
||||
premium-features
|
||||
query-processor
|
||||
request
|
||||
util
|
||||
enterprise/impersonation
|
||||
enterprise/api}}
|
||||
|
||||
enterprise/scim
|
||||
{:api #{metabase-enterprise.scim.api metabase-enterprise.scim.routes}
|
||||
:uses #{analytics api models permissions public-settings server util enterprise/serialization}}
|
||||
|
||||
enterprise/search
|
||||
{:api #{}
|
||||
:uses #{premium-features search}}
|
||||
|
||||
enterprise/serialization
|
||||
{:api #{metabase-enterprise.serialization.api
|
||||
metabase-enterprise.serialization.cmd
|
||||
metabase-enterprise.serialization.v2.backfill-ids}
|
||||
:uses #{analytics api config db legacy-mbql lib logger models plugins premium-features public-settings setup util}}
|
||||
|
||||
enterprise/snippet-collections
|
||||
{:api #{}
|
||||
:uses #{models permissions premium-features util}}
|
||||
|
||||
enterprise/sso
|
||||
{:api #{metabase-enterprise.sso.api.routes
|
||||
metabase-enterprise.sso.integrations.sso-settings
|
||||
metabase-enterprise.sso.integrations.sso-utils}
|
||||
:uses #{api channel embed events integrations models premium-features public-settings request session util
|
||||
enterprise/scim}}
|
||||
|
||||
enterprise/stale
|
||||
{:api #{metabase-enterprise.stale.routes}
|
||||
:uses #{analytics api embed models premium-features public-settings request util}}
|
||||
|
||||
enterprise/stats
|
||||
{:api #{}
|
||||
:uses #{driver premium-features enterprise/advanced-config enterprise/scim enterprise/sso}}
|
||||
|
||||
enterprise/task
|
||||
{:api #{metabase-enterprise.task.truncate-audit-tables}
|
||||
:uses #{premium-features query-processor task util enterprise/cache}}
|
||||
|
||||
enterprise/upload-management
|
||||
{:api #{metabase-enterprise.upload-management.api}
|
||||
:uses #{api models premium-features upload util}}}
|
||||
|
||||
;; namespaces matching these patterns (with `re-find`) are excluded from the module linter. Since regex literals
|
||||
;; aren't allowed in EDN just used the `(str <regex>)` version i.e. two slashes instead of one.
|
||||
;;
|
||||
;; this is mostly intended for excluding test namespaces or those rare 'glue' namespaces that glue multiple modules
|
||||
;; together, e.g. `metabase.lib.metadata.jvm`.
|
||||
:linters
|
||||
{:metabase/modules
|
||||
{:ignored-namespace-patterns
|
||||
#{"-test$" ; anything ending in `-test`
|
||||
"test[-.]util" ; anything that contains `test.util` or `test-util`
|
||||
"test\\.impl" ; anything that contains `test.impl`
|
||||
"test-setup" ; anything that contains `test-setup`
|
||||
"^metabase(?:-enterprise)?\\.test" ; anything starting with `metabase.test` or `metabase-enterprise.test`
|
||||
"^metabase\\.http-client$"}}}} ; `metabase.http-client` which is a test-only namespace despite its name.
|
||||
132
.clj-kondo/config/test-helpers/config.edn
Normal file
@@ -0,0 +1,132 @@
|
||||
{:linters
|
||||
{:metabase/test-helpers-use-non-thread-safe-functions
|
||||
;; TODO -- seems silly to maintain different blacklists and whitelists here than we use for the deftest `^:parallel`
|
||||
;; checker...
|
||||
{:explicitly-safe
|
||||
#{ ;; these toucan methods might actually set global values if it's used outside of a transaction,
|
||||
;; but since mt/with-temp runs in a transaction, so we'll ignore them in this case.
|
||||
toucan2.core/delete!
|
||||
toucan2.core/update!
|
||||
toucan2.core/insert!
|
||||
toucan2.core/insert-returning-instance!
|
||||
toucan2.core/insert-returning-instances!
|
||||
toucan2.core/insert-returning-pk!
|
||||
toucan2.core/insert-returning-pks!
|
||||
clojure.core.async/<!!
|
||||
clojure.core.async/>!!
|
||||
clojure.core.async/alts!!
|
||||
clojure.core.async/close!
|
||||
clojure.core.async/poll!
|
||||
clojure.core.memoize/memo-clear!
|
||||
clojure.core/conj!
|
||||
clojure.core/persistent!
|
||||
clojure.core/reset!
|
||||
clojure.core/swap!
|
||||
clojure.core/volatile!
|
||||
clojure.core/vreset!
|
||||
clojure.core/vswap!
|
||||
clojure.java.jdbc/execute!
|
||||
methodical.core/add-aux-method-with-unique-key!
|
||||
methodical.core/remove-aux-method-with-unique-key!
|
||||
next.jdbc/execute!
|
||||
|
||||
;; Definitely thread safe
|
||||
metabase.test.util.dynamic-redefs/patch-vars!
|
||||
|
||||
;; TODO: most of these symbols shouldn't be here, we should go through them and
|
||||
;; find the functions/macros that use them and make sure their names end with !
|
||||
;; best way to do this is try remove each of these and rely on kondo output to find places where it's used
|
||||
clojure.test/grant-collection-perms!
|
||||
clojure.test/grant-collection-perms-fn!
|
||||
clojure.test/grant-perms-fn!
|
||||
clojure.test/purge-old-entries!
|
||||
clojure.test/revoke-collection-perms!
|
||||
clojure.test/save-results!
|
||||
metabase-enterprise.advanced-permissions.models.permissions/update-db-download-permissions!
|
||||
metabase-enterprise.internal-user/install-internal-user!
|
||||
metabase-enterprise.sso.integrations.saml-test/call-with-login-attributes-cleared!
|
||||
metabase.actions.actions/perform-action!
|
||||
metabase.actions.models/insert!
|
||||
metabase.analytics.snowplow-test/fake-track-event-impl!
|
||||
metabase.analytics.snowplow/track-event-impl!
|
||||
metabase.api.public-test/add-card-to-dashboard!
|
||||
metabase.channel.email-test/reset-inbox!
|
||||
metabase.channel.email/send-email!
|
||||
metabase.cmd.dump-to-h2/dump-to-h2!
|
||||
metabase.cmd.load-from-h2/load-from-h2!
|
||||
metabase.core.core/ensure-audit-db-installed!
|
||||
metabase.db.schema-migrations-test.impl/run-migrations-in-range!
|
||||
metabase.db.setup/migrate!
|
||||
metabase.db.setup/setup-db!
|
||||
metabase.db/migrate!
|
||||
metabase.db/setup-db!
|
||||
metabase.driver.mongo-test/create-database-from-row-maps!
|
||||
metabase.driver.postgres-test/create-enums-db!
|
||||
metabase.driver.postgres-test/drop-if-exists-and-create-db!
|
||||
metabase.driver.sql-jdbc.execute/execute-statement!
|
||||
metabase.indexed-entities.models.model-index/add-values!
|
||||
metabase.indexed-entities.task.index-values/job-init!
|
||||
metabase.models.collection.graph-test/clear-graph-revisions!
|
||||
metabase.models.collection.graph-test/do-with-n-temp-users-with-personal-collections!
|
||||
metabase.models.field-values/create-or-update-full-field-values!
|
||||
metabase.models.moderation-review/create-review!
|
||||
metabase.models.on-demand-test/add-dashcard-with-parameter-mapping!
|
||||
metabase.models.persisted-info/ready-database!
|
||||
metabase.models.setting-test/test-user-local-allowed-setting!
|
||||
metabase.models.setting-test/test-user-local-only-setting!
|
||||
metabase.models.setting.cache/restore-cache!
|
||||
metabase.models.setting/set!
|
||||
metabase.models.setting/validate-settings-formatting!
|
||||
metabase.permissions.models.permissions/grant-application-permissions!
|
||||
metabase.permissions.models.permissions/grant-collection-read-permissions!
|
||||
metabase.permissions.models.permissions/grant-collection-readwrite-permissions!
|
||||
metabase.permissions.models.permissions/grant-full-data-permissions!
|
||||
metabase.permissions.models.permissions/grant-native-readwrite-permissions!
|
||||
metabase.permissions.models.permissions/grant-permissions!
|
||||
metabase.permissions.models.permissions/revoke-application-permissions!
|
||||
metabase.permissions.models.permissions/revoke-data-perms!
|
||||
metabase.permissions.models.permissions/update-data-perms-graph!
|
||||
metabase.permissions.models.permissions/update-group-permissions!
|
||||
metabase.permissions.test-util/with-restored-perms!
|
||||
metabase.pulse.send/send-notifications!
|
||||
metabase.pulse.send/send-pulse!
|
||||
metabase.query-processor.streaming.interface/begin!
|
||||
metabase.query-processor.streaming.interface/finish!
|
||||
metabase.query-processor.streaming.interface/write-row!
|
||||
metabase.sample-data/try-to-extract-sample-database!
|
||||
metabase.setup.core/create-token!
|
||||
metabase.sync.core/sync-database!
|
||||
metabase.sync.sync-metadata.fields.sync-metadata/update-field-metadata-if-needed!
|
||||
metabase.sync.sync-metadata/sync-db-metadata!
|
||||
metabase.sync.util-test/sync-database!
|
||||
metabase.sync.util/store-sync-summary!
|
||||
metabase.task.persist-refresh/job-init!
|
||||
metabase.task.persist-refresh/refresh-tables!
|
||||
metabase.task.persist-refresh/schedule-persistence-for-database!
|
||||
metabase.task/delete-task!
|
||||
metabase.test.data.bigquery-cloud-sdk/execute!
|
||||
metabase.test.data.impl/copy-db-tables-and-fields!
|
||||
metabase.test.data.impl/get-or-create-database!
|
||||
metabase.test.data.impl/get-or-create-default-dataset!
|
||||
metabase.test.data.impl.get-or-create/set-test-db-permissions!
|
||||
metabase.test.data.interface/create-db!
|
||||
metabase.test.data.interface/destroy-db!
|
||||
metabase.test.data.oracle/create-user!
|
||||
metabase.test.data.oracle/drop-user!
|
||||
metabase.test.data.sql-jdbc.load-data/make-insert!
|
||||
metabase.test.data.users/clear-cached-session-tokens!
|
||||
metabase.test.initialize/do-initialization!
|
||||
metabase.test.initialize/initialize-if-needed!
|
||||
metabase.test.integrations.ldap/start-ldap-server!
|
||||
metabase.test.util.log/ensure-unique-logger!
|
||||
metabase.test.util.log/set-ns-log-level!
|
||||
metabase.test.util/do-with-temp-env-var-value!
|
||||
metabase.test.util/restore-raw-setting!
|
||||
metabase.test.util/upsert-raw-setting!
|
||||
metabase.test/initialize-if-needed!
|
||||
metabase.test/test-helpers-set-global-values!
|
||||
metabase.test/with-temp-env-var-value!
|
||||
metabase.upload-test/set-local-infile!
|
||||
metabase.util.files/create-dir-if-not-exists!
|
||||
metabase.util.ssh-test/start-mock-servers!
|
||||
metabase.util.ssh-test/stop-mock-servers!}}}}
|
||||
@@ -1,7 +0,0 @@
|
||||
(ns macros.metabase.api.common)
|
||||
|
||||
(defmacro define-routes
|
||||
"Macro for api.common/define-routes"
|
||||
[& args]
|
||||
`(do (def ~'routes "docstring" nil)
|
||||
~@args))
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns macros.metabase-enterprise.advanced-permissions.api.util-test
|
||||
(:require [macros.common]))
|
||||
(ns macros.metabase-enterprise.impersonation.util-test
|
||||
(:require
|
||||
[macros.common]))
|
||||
|
||||
(defmacro with-impersonations! [impersonations-and-attributes-map & body]
|
||||
`(let [~(macros.common/ignore-unused '&group) ~impersonations-and-attributes-map]
|
||||
@@ -1,375 +0,0 @@
|
||||
(ns hooks.clojure.core
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[clojure.string :as str]
|
||||
[hooks.common]))
|
||||
|
||||
;;; TODO -- seems silly to maintain different blacklists and whitelists here than we use for the deftest `^:parallel`
|
||||
;;; checker... those lists live in the Clj Kondo config file
|
||||
(def ^:private symbols-allowed-in-fns-not-ending-in-an-exclamation-point
|
||||
'#{;; these toucan methods might actually set global values if it's used outside of a transaction,
|
||||
;; but since mt/with-temp runs in a transaction, so we'll ignore them in this case.
|
||||
toucan2.core/delete!
|
||||
toucan2.core/update!
|
||||
toucan2.core/insert!
|
||||
toucan2.core/insert-returning-instance!
|
||||
toucan2.core/insert-returning-instances!
|
||||
toucan2.core/insert-returning-pk!
|
||||
toucan2.core/insert-returning-pks!
|
||||
clojure.core.async/<!!
|
||||
clojure.core.async/>!!
|
||||
clojure.core.async/alts!!
|
||||
clojure.core.async/close!
|
||||
clojure.core.async/poll!
|
||||
clojure.core.memoize/memo-clear!
|
||||
clojure.core/conj!
|
||||
clojure.core/persistent!
|
||||
clojure.core/reset!
|
||||
clojure.core/swap!
|
||||
clojure.core/volatile!
|
||||
clojure.core/vreset!
|
||||
clojure.core/vswap!
|
||||
clojure.java.jdbc/execute!
|
||||
methodical.core/add-aux-method-with-unique-key!
|
||||
methodical.core/remove-aux-method-with-unique-key!
|
||||
next.jdbc/execute!
|
||||
|
||||
;; Definitely thread safe
|
||||
metabase.test.util.dynamic-redefs/patch-vars!
|
||||
|
||||
;; TODO: most of these symbols shouldn't be here, we should go through them and
|
||||
;; find the functions/macros that use them and make sure their names end with !
|
||||
;; best way to do this is try remove each of these and rely on kondo output to find places where it's used
|
||||
clojure.test/grant-collection-perms!
|
||||
clojure.test/grant-collection-perms-fn!
|
||||
clojure.test/grant-perms-fn!
|
||||
clojure.test/purge-old-entries!
|
||||
clojure.test/revoke-collection-perms!
|
||||
clojure.test/save-results!
|
||||
metabase-enterprise.advanced-permissions.models.permissions/update-db-download-permissions!
|
||||
metabase-enterprise.internal-user/install-internal-user!
|
||||
metabase-enterprise.sso.integrations.saml-test/call-with-login-attributes-cleared!
|
||||
metabase.actions.actions/perform-action!
|
||||
metabase.analytics.snowplow-test/fake-track-event-impl!
|
||||
metabase.analytics.snowplow/track-event-impl!
|
||||
metabase.api.public-test/add-card-to-dashboard!
|
||||
metabase.channel.email-test/reset-inbox!
|
||||
metabase.channel.email/send-email!
|
||||
metabase.cmd.dump-to-h2/dump-to-h2!
|
||||
metabase.cmd.load-from-h2/load-from-h2!
|
||||
metabase.core.core/ensure-audit-db-installed!
|
||||
metabase.db.schema-migrations-test.impl/run-migrations-in-range!
|
||||
metabase.db.setup/migrate!
|
||||
metabase.db.setup/setup-db!
|
||||
metabase.db/migrate!
|
||||
metabase.db/setup-db!
|
||||
metabase.driver.mongo-test/create-database-from-row-maps!
|
||||
metabase.driver.postgres-test/create-enums-db!
|
||||
metabase.driver.postgres-test/drop-if-exists-and-create-db!
|
||||
metabase.driver.sql-jdbc.execute/execute-statement!
|
||||
metabase.models.action/insert!
|
||||
metabase.models.collection.graph-test/clear-graph-revisions!
|
||||
metabase.models.collection.graph-test/do-with-n-temp-users-with-personal-collections!
|
||||
metabase.models.field-values/create-or-update-full-field-values!
|
||||
metabase.models.model-index/add-values!
|
||||
metabase.models.moderation-review/create-review!
|
||||
metabase.models.on-demand-test/add-dashcard-with-parameter-mapping!
|
||||
metabase.models.permissions/grant-application-permissions!
|
||||
metabase.models.permissions/grant-collection-read-permissions!
|
||||
metabase.models.permissions/grant-collection-readwrite-permissions!
|
||||
metabase.models.permissions/grant-full-data-permissions!
|
||||
metabase.models.permissions/grant-native-readwrite-permissions!
|
||||
metabase.models.permissions/grant-permissions!
|
||||
metabase.models.permissions/revoke-application-permissions!
|
||||
metabase.models.permissions/revoke-data-perms!
|
||||
metabase.models.permissions/update-data-perms-graph!
|
||||
metabase.models.permissions/update-group-permissions!
|
||||
metabase.models.persisted-info/ready-database!
|
||||
metabase.models.setting-test/test-user-local-allowed-setting!
|
||||
metabase.models.setting-test/test-user-local-only-setting!
|
||||
metabase.models.setting.cache/restore-cache!
|
||||
metabase.models.setting/set!
|
||||
metabase.models.setting/validate-settings-formatting!
|
||||
metabase.permissions.test-util/with-restored-perms!
|
||||
metabase.pulse.send/send-notifications!
|
||||
metabase.pulse.send/send-pulse!
|
||||
metabase.query-processor.streaming.interface/begin!
|
||||
metabase.query-processor.streaming.interface/finish!
|
||||
metabase.query-processor.streaming.interface/write-row!
|
||||
metabase.sample-data/try-to-extract-sample-database!
|
||||
metabase.setup.core/create-token!
|
||||
metabase.sync.core/sync-database!
|
||||
metabase.sync.sync-metadata.fields.sync-metadata/update-field-metadata-if-needed!
|
||||
metabase.sync.sync-metadata/sync-db-metadata!
|
||||
metabase.sync.util-test/sync-database!
|
||||
metabase.sync.util/store-sync-summary!
|
||||
metabase.task.index-values/job-init!
|
||||
metabase.task.persist-refresh/job-init!
|
||||
metabase.task.persist-refresh/refresh-tables!
|
||||
metabase.task.persist-refresh/schedule-persistence-for-database!
|
||||
metabase.task/delete-task!
|
||||
metabase.test.data.bigquery-cloud-sdk/execute!
|
||||
metabase.test.data.impl/copy-db-tables-and-fields!
|
||||
metabase.test.data.impl/get-or-create-database!
|
||||
metabase.test.data.impl/get-or-create-default-dataset!
|
||||
metabase.test.data.impl.get-or-create/set-test-db-permissions!
|
||||
metabase.test.data.interface/create-db!
|
||||
metabase.test.data.interface/destroy-db!
|
||||
metabase.test.data.oracle/create-user!
|
||||
metabase.test.data.oracle/drop-user!
|
||||
metabase.test.data.sql-jdbc.load-data/make-insert!
|
||||
metabase.test.data.users/clear-cached-session-tokens!
|
||||
metabase.test.initialize/do-initialization!
|
||||
metabase.test.initialize/initialize-if-needed!
|
||||
metabase.test.integrations.ldap/start-ldap-server!
|
||||
metabase.test.util.log/ensure-unique-logger!
|
||||
metabase.test.util.log/set-ns-log-level!
|
||||
metabase.test.util/do-with-temp-env-var-value!
|
||||
metabase.test.util/restore-raw-setting!
|
||||
metabase.test.util/upsert-raw-setting!
|
||||
metabase.test/initialize-if-needed!
|
||||
metabase.test/test-helpers-set-global-values!
|
||||
metabase.test/with-temp-env-var-value!
|
||||
metabase.upload-test/set-local-infile!
|
||||
metabase.util.files/create-dir-if-not-exists!
|
||||
metabase.util.ssh-test/start-mock-servers!
|
||||
metabase.util.ssh-test/stop-mock-servers!})
|
||||
|
||||
(defn- end-with-exclamation?
|
||||
[s]
|
||||
(str/ends-with? s "!"))
|
||||
|
||||
(defn- explicitly-safe? [qualified-symbol]
|
||||
(contains? symbols-allowed-in-fns-not-ending-in-an-exclamation-point qualified-symbol))
|
||||
|
||||
(defn- explicitly-unsafe? [config qualified-symbol]
|
||||
(contains? (get-in config [:linters :metabase/validate-deftest :parallel/unsafe]) qualified-symbol))
|
||||
|
||||
(defn- unsafe? [config qualified-symbol]
|
||||
(and (or (end-with-exclamation? qualified-symbol)
|
||||
(explicitly-unsafe? config qualified-symbol))
|
||||
(not (explicitly-safe? qualified-symbol))))
|
||||
|
||||
(defn- non-thread-safe-form-should-end-with-exclamation*
|
||||
[{[defn-or-defmacro form-name] :children, :as node} config]
|
||||
(when-not (and (:string-value form-name)
|
||||
(end-with-exclamation? (:string-value form-name)))
|
||||
(letfn [(walk [f form]
|
||||
(f form)
|
||||
(doseq [child (:children form)]
|
||||
(walk f child)))
|
||||
(check-node [form]
|
||||
(when-let [qualified-symbol (hooks.common/node->qualified-symbol form)]
|
||||
(when (unsafe? config qualified-symbol)
|
||||
(hooks/reg-finding!
|
||||
(assoc (meta form-name)
|
||||
:message (format "The name of this %s should end with `!` because it contains calls to non thread safe form `%s`. [:metabase/test-helpers-use-non-thread-safe-functions]"
|
||||
(:string-value defn-or-defmacro) qualified-symbol)
|
||||
:type :metabase/test-helpers-use-non-thread-safe-functions)))))]
|
||||
(walk check-node node))
|
||||
node))
|
||||
|
||||
(defn non-thread-safe-form-should-end-with-exclamation
|
||||
"Used to ensure defn and defmacro in test namespace to have name ending with `!` if it's non-thread-safe.
|
||||
A function or a macro can be defined as 'not thread safe' when their funciton name ends with a `!`.
|
||||
|
||||
Only used in tests to identify thread-safe/non-thread-safe test helpers. See #37126"
|
||||
[{:keys [node cljc lang config]}]
|
||||
(when (or (not cljc)
|
||||
(= lang :clj))
|
||||
(non-thread-safe-form-should-end-with-exclamation* node config))
|
||||
{:node node})
|
||||
|
||||
(comment
|
||||
(require '[clj-kondo.core :as clj-kondo])
|
||||
(def form (str '(defmacro a
|
||||
[x]
|
||||
`(fun-call x))))
|
||||
|
||||
(def form "(defmacro a
|
||||
[x]
|
||||
`(some! ~x))")
|
||||
|
||||
(def form "(defun f
|
||||
[x]
|
||||
(let [g! (fn [] 1)]
|
||||
(g!)))")
|
||||
|
||||
(str (hooks/parse-string form))
|
||||
(hooks/sexpr (hooks/parse-string form))
|
||||
|
||||
(binding [hooks/*reload* true]
|
||||
(-> form
|
||||
(with-in-str (clj-kondo/run! {:lint ["-"]}))
|
||||
:findings))
|
||||
|
||||
(do (non-thread-safe-form-should-end-with-exclamation* (hooks/parse-string form)) nil))
|
||||
|
||||
(defn- ns-form-node->require-node [ns-form-node]
|
||||
(some (fn [node]
|
||||
(when (and (hooks/list-node? node)
|
||||
(let [first-child (first (:children node))]
|
||||
(and (hooks/keyword-node? first-child)
|
||||
(= (hooks/sexpr first-child) :require))))
|
||||
node))
|
||||
(:children ns-form-node)))
|
||||
|
||||
(defn- lint-require-shapes [ns-form-node]
|
||||
(doseq [node (-> ns-form-node
|
||||
ns-form-node->require-node
|
||||
:children
|
||||
rest)]
|
||||
(cond
|
||||
(not (hooks/vector-node? node))
|
||||
(hooks/reg-finding! (assoc (meta node)
|
||||
:message "All :required namespaces should be wrapped in vectors [:metabase/require-shape-checker]"
|
||||
:type :metabase/require-shape-checker))
|
||||
|
||||
(hooks/vector-node? (second (:children node)))
|
||||
(hooks/reg-finding! (assoc (meta node)
|
||||
:message "Don't use prefix forms inside :require [:metabase/require-shape-checker]"
|
||||
:type :metabase/require-shape-checker)))))
|
||||
|
||||
(defn- require-node->namespace-symb-nodes [require-node]
|
||||
(let [[_ns & args] (:children require-node)]
|
||||
(into []
|
||||
;; prefixed namespace forms are NOT SUPPORTED!!!!!!!!1
|
||||
(keep (fn [node]
|
||||
(cond
|
||||
(hooks/vector-node? node)
|
||||
;; propagate the metadata attached to this vector in case there's a `:clj-kondo/ignore` form.
|
||||
(let [symbol-node (first (:children node))]
|
||||
(hooks.common/merge-ignored-linters symbol-node require-node node))
|
||||
|
||||
;; this should also be dead code since we require requires to be vectors
|
||||
(hooks/token-node? node)
|
||||
(hooks.common/merge-ignored-linters node require-node)
|
||||
|
||||
:else
|
||||
(printf "Don't know how to figure out what namespace is being required in %s\n" (pr-str node)))))
|
||||
args)))
|
||||
|
||||
(defn- ns-form-node->ns-symb [ns-form-node]
|
||||
(some-> (some (fn [node]
|
||||
(when (and (hooks/token-node? node)
|
||||
(not= (hooks/sexpr node) 'ns))
|
||||
node))
|
||||
(:children ns-form-node))
|
||||
hooks/sexpr))
|
||||
|
||||
(defn- module
|
||||
"E.g.
|
||||
|
||||
(module 'metabase.qp.middleware.wow) => 'metabase.qp"
|
||||
[ns-symb]
|
||||
(some-> (re-find #"^metabase\.[^.]+" (str ns-symb)) symbol))
|
||||
|
||||
(defn- ignored-namespace? [ns-symb config]
|
||||
(some
|
||||
(fn [pattern-str]
|
||||
(re-find (re-pattern pattern-str) (str ns-symb)))
|
||||
(:ignored-namespace-patterns config)))
|
||||
|
||||
(defn- module-api-namespaces
|
||||
"Set API namespaces for a given module. `:any` means you can use anything, there are no API namespaces for this
|
||||
module (yet). If unspecified, the default is just the `<module>.core` namespace."
|
||||
[module config]
|
||||
(let [module-config (get-in config [:api-namespaces module])]
|
||||
(cond
|
||||
(= module-config :any)
|
||||
nil
|
||||
|
||||
(set? module-config)
|
||||
module-config
|
||||
|
||||
:else
|
||||
#{(symbol (str module ".core"))})))
|
||||
|
||||
(defn- lint-modules [ns-form-node config]
|
||||
(let [ns-symb (ns-form-node->ns-symb ns-form-node)]
|
||||
(when-not (ignored-namespace? ns-symb config)
|
||||
(when-let [current-module (module ns-symb)]
|
||||
(let [allowed-modules (get-in config [:allowed-modules current-module])
|
||||
required-namespace-symb-nodes (-> ns-form-node
|
||||
ns-form-node->require-node
|
||||
require-node->namespace-symb-nodes)]
|
||||
(doseq [node required-namespace-symb-nodes
|
||||
:when (not (contains? (hooks.common/ignored-linters node) :metabase/ns-module-checker))
|
||||
:let [required-namespace (hooks/sexpr node)
|
||||
required-module (module required-namespace)]
|
||||
;; ignore stuff not in a module i.e. non-Metabase stuff.
|
||||
:when required-module
|
||||
:let [in-current-module? (= required-module current-module)]
|
||||
:when (not in-current-module?)
|
||||
:let [allowed-module? (or (= allowed-modules :any)
|
||||
(contains? (set allowed-modules) required-module))
|
||||
module-api-namespaces (module-api-namespaces required-module config)
|
||||
allowed-module-namespace? (or (empty? module-api-namespaces)
|
||||
(contains? module-api-namespaces required-namespace))]]
|
||||
(when-let [error (cond
|
||||
(not allowed-module?)
|
||||
(format "Module %s should not be used in the %s module. [:metabase/ns-module-checker :allowed-modules %s]"
|
||||
required-module
|
||||
current-module
|
||||
current-module)
|
||||
|
||||
(not allowed-module-namespace?)
|
||||
(format "Namespace %s is not an allowed external API namespace for the %s module. [:metabase/ns-module-checker :api-namespaces %s]"
|
||||
required-namespace
|
||||
required-module
|
||||
required-module))]
|
||||
(hooks/reg-finding! (assoc (meta node)
|
||||
:message error
|
||||
:type :metabase/ns-module-checker)))))))))
|
||||
|
||||
(defn lint-ns [x]
|
||||
(lint-require-shapes (:node x))
|
||||
(lint-modules (:node x) (get-in x [:config :linters :metabase/ns-module-checker]))
|
||||
x)
|
||||
|
||||
(defn- check-arglists [report-node arglists]
|
||||
(letfn [(reg-bad-arglists! []
|
||||
(hooks/reg-finding!
|
||||
(assoc (meta report-node)
|
||||
:message ":arglists should be a quoted list of vectors [:metabase/check-defmulti-arglists]"
|
||||
:type :metabase/check-defmulti-arglists)))
|
||||
(reg-bad-arg! []
|
||||
(hooks/reg-finding!
|
||||
(assoc (meta report-node)
|
||||
:message ":arglists should contain actual arg names, not underscore (unused) symbols [:metabase/check-defmulti-arglists]"
|
||||
:type :metabase/check-defmulti-arglists)))
|
||||
(underscore-arg? [arg]
|
||||
(and (symbol? arg)
|
||||
(str/starts-with? arg "_")))
|
||||
(check-arglist [arglist]
|
||||
(cond
|
||||
(not (vector? arglist)) (reg-bad-arglists!)
|
||||
(some underscore-arg? arglist) (reg-bad-arg!)))]
|
||||
(if-not (and (seq? arglists)
|
||||
(= (first arglists) 'quote)
|
||||
(seq (second arglists)))
|
||||
(reg-bad-arglists!)
|
||||
(let [[_quote arglists] arglists]
|
||||
(doseq [arglist arglists]
|
||||
(check-arglist arglist))))))
|
||||
|
||||
(defn- defmulti-check-for-arglists-metadata
|
||||
"Make sure a [[defmulti]] has an attribute map with `:arglists` metadata."
|
||||
[node]
|
||||
(let [[_defmulti _symb & args] (:children node)
|
||||
[_docstring & args] (if (hooks/string-node? (first args))
|
||||
args
|
||||
(cons nil args))
|
||||
attr-map (when (hooks/map-node? (first args))
|
||||
(first args))
|
||||
arglists (some-> attr-map hooks/sexpr :arglists seq)]
|
||||
(if (not (seq? arglists))
|
||||
(hooks/reg-finding!
|
||||
(assoc (meta node)
|
||||
:message "All defmultis should have an attribute map with :arglists metadata. [:metabase/check-defmulti-arglists]"
|
||||
:type :metabase/check-defmulti-arglists))
|
||||
(check-arglists attr-map arglists))))
|
||||
|
||||
(defn lint-defmulti [x]
|
||||
(defmulti-check-for-arglists-metadata (:node x))
|
||||
x)
|
||||
52
.clj-kondo/src/hooks/clojure/core/defmulti.clj
Normal file
@@ -0,0 +1,52 @@
|
||||
(ns hooks.clojure.core.defmulti
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[clojure.string :as str]
|
||||
[hooks.common]))
|
||||
|
||||
(defn- check-arglists [report-node arglists]
|
||||
(letfn [(reg-bad-arglists! []
|
||||
(hooks/reg-finding!
|
||||
(assoc (meta report-node)
|
||||
:message ":arglists should be a quoted list of vectors [:metabase/check-defmulti-arglists]"
|
||||
:type :metabase/check-defmulti-arglists)))
|
||||
(reg-bad-arg! []
|
||||
(hooks/reg-finding!
|
||||
(assoc (meta report-node)
|
||||
:message ":arglists should contain actual arg names, not underscore (unused) symbols [:metabase/check-defmulti-arglists]"
|
||||
:type :metabase/check-defmulti-arglists)))
|
||||
(underscore-arg? [arg]
|
||||
(and (symbol? arg)
|
||||
(str/starts-with? arg "_")))
|
||||
(check-arglist [arglist]
|
||||
(cond
|
||||
(not (vector? arglist)) (reg-bad-arglists!)
|
||||
(some underscore-arg? arglist) (reg-bad-arg!)))]
|
||||
(if-not (and (seq? arglists)
|
||||
(= (first arglists) 'quote)
|
||||
(seq (second arglists)))
|
||||
(reg-bad-arglists!)
|
||||
(let [[_quote arglists] arglists]
|
||||
(doseq [arglist arglists]
|
||||
(check-arglist arglist))))))
|
||||
|
||||
(defn- defmulti-check-for-arglists-metadata
|
||||
"Make sure a [[defmulti]] has an attribute map with `:arglists` metadata."
|
||||
[node]
|
||||
(let [[_defmulti _symb & args] (:children node)
|
||||
[_docstring & args] (if (hooks/string-node? (first args))
|
||||
args
|
||||
(cons nil args))
|
||||
attr-map (when (hooks/map-node? (first args))
|
||||
(first args))
|
||||
arglists (some-> attr-map hooks/sexpr :arglists seq)]
|
||||
(if-not (seq? arglists) ; should be a list or at least list-like
|
||||
(hooks/reg-finding!
|
||||
(assoc (meta node)
|
||||
:message "All defmultis should have an attribute map with :arglists metadata. [:metabase/check-defmulti-arglists]"
|
||||
:type :metabase/check-defmulti-arglists))
|
||||
(check-arglists attr-map arglists))))
|
||||
|
||||
(defn lint-defmulti [x]
|
||||
(defmulti-check-for-arglists-metadata (:node x))
|
||||
x)
|
||||
76
.clj-kondo/src/hooks/clojure/core/defn.clj
Normal file
@@ -0,0 +1,76 @@
|
||||
(ns hooks.clojure.core.defn
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[clojure.string :as str]
|
||||
[hooks.common]))
|
||||
|
||||
(defn- end-with-exclamation?
|
||||
[s]
|
||||
(str/ends-with? s "!"))
|
||||
|
||||
(defn- explicitly-safe? [config qualified-symbol]
|
||||
(contains? (get-in config [:linters :metabase/test-helpers-use-non-thread-safe-functions :explicitly-safe])
|
||||
qualified-symbol))
|
||||
|
||||
(defn- explicitly-unsafe? [config qualified-symbol]
|
||||
(contains? (get-in config [:linters :metabase/validate-deftest :parallel/unsafe]) qualified-symbol))
|
||||
|
||||
(defn- unsafe? [config qualified-symbol]
|
||||
(and (or (end-with-exclamation? qualified-symbol)
|
||||
(explicitly-unsafe? config qualified-symbol))
|
||||
(not (explicitly-safe? config qualified-symbol))))
|
||||
|
||||
(defn- non-thread-safe-form-should-end-with-exclamation*
|
||||
[{[defn-or-defmacro form-name] :children, :as node} config]
|
||||
(when-not (and (:string-value form-name)
|
||||
(end-with-exclamation? (:string-value form-name)))
|
||||
(letfn [(walk [f form]
|
||||
(f form)
|
||||
(doseq [child (:children form)]
|
||||
(walk f child)))
|
||||
(check-node [form]
|
||||
(when-let [qualified-symbol (hooks.common/node->qualified-symbol form)]
|
||||
(when (unsafe? config qualified-symbol)
|
||||
(hooks/reg-finding!
|
||||
(assoc (meta form-name)
|
||||
:message (format "The name of this %s should end with `!` because it contains calls to non thread safe form `%s`. [:metabase/test-helpers-use-non-thread-safe-functions]"
|
||||
(:string-value defn-or-defmacro) qualified-symbol)
|
||||
:type :metabase/test-helpers-use-non-thread-safe-functions)))))]
|
||||
(walk check-node node))
|
||||
node))
|
||||
|
||||
(defn non-thread-safe-form-should-end-with-exclamation
|
||||
"Used to ensure defn and defmacro in test namespace to have name ending with `!` if it's non-thread-safe.
|
||||
A function or a macro can be defined as 'not thread safe' when their funciton name ends with a `!`.
|
||||
|
||||
Only used in tests to identify thread-safe/non-thread-safe test helpers. See #37126"
|
||||
[{:keys [node cljc lang config]}]
|
||||
(when (or (not cljc)
|
||||
(= lang :clj))
|
||||
(non-thread-safe-form-should-end-with-exclamation* node config))
|
||||
{:node node})
|
||||
|
||||
(comment
|
||||
(require '[clj-kondo.core :as clj-kondo])
|
||||
(def form (str '(defmacro a
|
||||
[x]
|
||||
`(fun-call x))))
|
||||
|
||||
(def form "(defmacro a
|
||||
[x]
|
||||
`(some! ~x))")
|
||||
|
||||
(def form "(defun f
|
||||
[x]
|
||||
(let [g! (fn [] 1)]
|
||||
(g!)))")
|
||||
|
||||
(str (hooks/parse-string form))
|
||||
(hooks/sexpr (hooks/parse-string form))
|
||||
|
||||
(binding [hooks/*reload* true]
|
||||
(-> form
|
||||
(with-in-str (clj-kondo/run! {:lint ["-"]}))
|
||||
:findings))
|
||||
|
||||
(do (non-thread-safe-form-should-end-with-exclamation* (hooks/parse-string form)) nil))
|
||||
91
.clj-kondo/src/hooks/clojure/core/ns.clj
Normal file
@@ -0,0 +1,91 @@
|
||||
(ns hooks.clojure.core.ns
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[hooks.common]
|
||||
[hooks.common.modules :as modules]))
|
||||
|
||||
(defn- ns-form-node->require-node [ns-form-node]
|
||||
(some (fn [node]
|
||||
(when (and (hooks/list-node? node)
|
||||
(let [first-child (first (:children node))]
|
||||
(and (hooks/keyword-node? first-child)
|
||||
(= (hooks/sexpr first-child) :require))))
|
||||
node))
|
||||
(:children ns-form-node)))
|
||||
|
||||
(defn- lint-require-shapes [ns-form-node]
|
||||
(doseq [node (-> ns-form-node
|
||||
ns-form-node->require-node
|
||||
:children
|
||||
rest)]
|
||||
(cond
|
||||
(not (hooks/vector-node? node))
|
||||
(hooks/reg-finding! (assoc (meta node)
|
||||
:message "All :required namespaces should be wrapped in vectors [:metabase/require-shape-checker]"
|
||||
:type :metabase/require-shape-checker))
|
||||
|
||||
(hooks/vector-node? (second (:children node)))
|
||||
(hooks/reg-finding! (assoc (meta node)
|
||||
:message "Don't use prefix forms inside :require [:metabase/require-shape-checker]"
|
||||
:type :metabase/require-shape-checker)))))
|
||||
|
||||
(defn- lint-requires-on-new-lines [ns-form-node]
|
||||
(let [[require-keyword first-require] (-> ns-form-node
|
||||
ns-form-node->require-node
|
||||
:children)]
|
||||
(when-let [require-keyword-line (:row (meta require-keyword))]
|
||||
(when-let [first-require-line (:row (meta first-require))]
|
||||
(when (= require-keyword-line first-require-line)
|
||||
(hooks/reg-finding! (assoc (meta first-require)
|
||||
:message "Put your requires on a newline from the :require keyword [:metabase/require-shape-checker]"
|
||||
:type :metabase/require-shape-checker)))))))
|
||||
|
||||
(defn- require-node->namespace-symb-nodes [require-node]
|
||||
(let [[_ns & args] (:children require-node)]
|
||||
(into []
|
||||
;; prefixed namespace forms are NOT SUPPORTED!!!!!!!!1
|
||||
(keep (fn [node]
|
||||
(cond
|
||||
(hooks/vector-node? node)
|
||||
;; propagate the metadata attached to this vector in case there's a `:clj-kondo/ignore` form.
|
||||
(let [symbol-node (first (:children node))]
|
||||
(hooks.common/merge-ignored-linters symbol-node require-node node))
|
||||
|
||||
;; this should also be dead code since we require requires to be vectors
|
||||
(hooks/token-node? node)
|
||||
(hooks.common/merge-ignored-linters node require-node)
|
||||
|
||||
:else
|
||||
(printf "Don't know how to figure out what namespace is being required in %s\n" (pr-str node)))))
|
||||
args)))
|
||||
|
||||
(defn- ns-form-node->ns-symb [ns-form-node]
|
||||
(some-> (some (fn [node]
|
||||
(when (and (hooks/token-node? node)
|
||||
(not= (hooks/sexpr node) 'ns))
|
||||
node))
|
||||
(:children ns-form-node))
|
||||
hooks/sexpr))
|
||||
|
||||
(defn- lint-modules [ns-form-node config]
|
||||
(let [ns-symb (ns-form-node->ns-symb ns-form-node)]
|
||||
(when-not (modules/ignored-namespace? config ns-symb)
|
||||
(when-let [current-module (modules/module ns-symb)]
|
||||
(let [required-namespace-symb-nodes (-> ns-form-node
|
||||
ns-form-node->require-node
|
||||
require-node->namespace-symb-nodes)]
|
||||
(doseq [node required-namespace-symb-nodes
|
||||
:when (not (contains? (hooks.common/ignored-linters node) :metabase/modules))
|
||||
:let [required-namespace (hooks/sexpr node)
|
||||
error (modules/usage-error config current-module required-namespace)]
|
||||
:when error]
|
||||
(hooks/reg-finding! (assoc (meta node)
|
||||
:message error
|
||||
:type :metabase/modules))))))))
|
||||
|
||||
(defn lint-ns [x]
|
||||
(doto (:node x)
|
||||
lint-require-shapes
|
||||
lint-requires-on-new-lines
|
||||
(lint-modules (modules/config x)))
|
||||
x)
|
||||
69
.clj-kondo/src/hooks/clojure/core/require.clj
Normal file
@@ -0,0 +1,69 @@
|
||||
(ns hooks.clojure.core.require
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[hooks.common.modules :as modules]))
|
||||
|
||||
(defn- lint* [node current-ns config required-namespaces]
|
||||
{:pre [(map? node)
|
||||
(simple-symbol? current-ns)
|
||||
(map? config)
|
||||
(sequential? required-namespaces)
|
||||
(every? simple-symbol? required-namespaces)]}
|
||||
(when-not (modules/ignored-namespace? config current-ns)
|
||||
;; ignore namespaces outside of the module system
|
||||
(when-let [current-module (modules/module current-ns)]
|
||||
(doseq [required-namespace required-namespaces]
|
||||
;; ignore namespaces outside of the module system.
|
||||
(when (modules/module required-namespace)
|
||||
(when-let [error (modules/usage-error config current-module required-namespace)]
|
||||
(hooks/reg-finding! (assoc (meta node)
|
||||
:message error
|
||||
:type :metabase/modules))))))))
|
||||
|
||||
(defn- unwrap-require [node]
|
||||
(case (hooks/tag node)
|
||||
;; some.namespace of some.namespace/some-var
|
||||
:token
|
||||
(let [sexpr (hooks/sexpr node)]
|
||||
(if (qualified-symbol? sexpr)
|
||||
(symbol (namespace sexpr))
|
||||
sexpr))
|
||||
|
||||
;; (quote some.namespace)
|
||||
:list
|
||||
(let [[x y] (:children node)]
|
||||
(when (and (hooks/token-node? x)
|
||||
(= (hooks/sexpr x) 'quote))
|
||||
(recur y)))
|
||||
|
||||
;; 'some.namespace
|
||||
:quote
|
||||
(recur (first (:children node)))
|
||||
|
||||
;; something like [some.namespace :as whatever]
|
||||
:vector
|
||||
(recur (first (:children node)))))
|
||||
|
||||
(comment
|
||||
(unwrap-require (-> "(requiring-resolve 'metabase.notification.payload.execute/execute-dashboard)"
|
||||
hooks/parse-string
|
||||
:children
|
||||
second)))
|
||||
|
||||
(defn- lint-require* [node current-ns config]
|
||||
(let [[_require & args] (:children node)
|
||||
required-namespaces (keep unwrap-require args)]
|
||||
(lint* node current-ns config required-namespaces)))
|
||||
|
||||
(defn- lint-requiring-resolve* [node current-ns config]
|
||||
(let [[_requiring-resolve symb-node] (:children node)
|
||||
required-namespace (unwrap-require symb-node)]
|
||||
(lint* node current-ns config [required-namespace])))
|
||||
|
||||
(defn lint-require [x]
|
||||
(lint-require* (:node x) (:ns x) (modules/config x))
|
||||
x)
|
||||
|
||||
(defn lint-requiring-resolve [x]
|
||||
(lint-requiring-resolve* (:node x) (:ns x) (modules/config x))
|
||||
x)
|
||||
81
.clj-kondo/src/hooks/common/modules.clj
Normal file
@@ -0,0 +1,81 @@
|
||||
(ns hooks.common.modules)
|
||||
|
||||
(defn ignored-namespace? [config ns-symb]
|
||||
(some
|
||||
(fn [pattern-str]
|
||||
(re-find (re-pattern pattern-str) (str ns-symb)))
|
||||
(:ignored-namespace-patterns config)))
|
||||
|
||||
(defn config [{:keys [config], :as _hook-input}]
|
||||
(merge (get-in config [:linters :metabase/modules])
|
||||
(select-keys config [:metabase/modules])))
|
||||
|
||||
(defn module
|
||||
"E.g.
|
||||
|
||||
(module 'metabase.qp.middleware.wow) => 'qp
|
||||
(module 'metabase-enterprise.whatever.core) => enterprise/whatever"
|
||||
[ns-symb]
|
||||
{:pre [(simple-symbol? ns-symb)]}
|
||||
(or (some->> (re-find #"^metabase-enterprise\.([^.]+)" (str ns-symb))
|
||||
second
|
||||
(symbol "enterprise"))
|
||||
(some-> (re-find #"^metabase\.([^.]+)" (str ns-symb))
|
||||
second
|
||||
symbol)))
|
||||
|
||||
(defn module-api-namespaces
|
||||
"Set of API namespace symbols for a given module. `:any` means you can use anything, there are no API namespaces for
|
||||
this module (yet). If unspecified, the default is just the `<module>.core` namespace."
|
||||
[config module]
|
||||
(let [module-config (get-in config [:metabase/modules module :api])]
|
||||
(cond
|
||||
(= module-config :any)
|
||||
nil
|
||||
|
||||
(set? module-config)
|
||||
module-config
|
||||
|
||||
:else
|
||||
(let [ns-prefix (if (= (namespace module) "enterprise")
|
||||
(str "metabase-enterprise." (name module))
|
||||
(name module))]
|
||||
#{(symbol (str ns-prefix ".api"))
|
||||
(symbol (str ns-prefix ".core"))
|
||||
(symbol (str ns-prefix ".init"))}))))
|
||||
|
||||
(defn allowed-modules
|
||||
"Set of namespace symbols that `module` is allowed to use. `:any` means it's allowed to use anything."
|
||||
[config module]
|
||||
(get-in config [:metabase/modules module :uses]))
|
||||
|
||||
(defn allowed-module? [config module required-module]
|
||||
(let [allowed-modules (allowed-modules config module)]
|
||||
(or (= allowed-modules :any)
|
||||
(contains? (set allowed-modules) required-module))))
|
||||
|
||||
(defn allowed-module-namespace? [config ns-symb]
|
||||
(let [module (module ns-symb)
|
||||
module-api-namespaces (module-api-namespaces config module)]
|
||||
(or (empty? module-api-namespaces)
|
||||
(contains? module-api-namespaces ns-symb))))
|
||||
|
||||
(defn usage-error
|
||||
"Find usage errors when a `required-namespace` is required in the `current-module`. Returns a string describing the
|
||||
error type if there is one, otherwise `nil` if there are no errors."
|
||||
[config current-module required-namespace]
|
||||
;; ignore stuff not in a module i.e. non-Metabase stuff.
|
||||
(when-let [required-module (module required-namespace)]
|
||||
(when-not (= current-module required-module)
|
||||
(cond
|
||||
(not (allowed-module? config current-module required-module))
|
||||
(format "Module %s should not be used in the %s module. [:metabase/modules %s :uses]"
|
||||
required-module
|
||||
current-module
|
||||
current-module)
|
||||
|
||||
(not (allowed-module-namespace? config required-namespace))
|
||||
(format "Namespace %s is not an allowed external API namespace for the %s module. [:metabase/modules %s :api]"
|
||||
required-namespace
|
||||
required-module
|
||||
required-module)))))
|
||||
@@ -1,32 +0,0 @@
|
||||
(ns hooks.metabase.api.common
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as api]
|
||||
[clojure.string :as str]
|
||||
[hooks.common]))
|
||||
|
||||
(defn route-fn-name
|
||||
"route fn hook"
|
||||
[method route]
|
||||
(let [route (if (vector? route) (first route) route)]
|
||||
(-> (str (name method) route)
|
||||
(str/replace #"/" "_")
|
||||
symbol)))
|
||||
|
||||
(defn defendpoint
|
||||
[arg]
|
||||
(letfn [(update-defendpoint [node]
|
||||
(let [[_defendpoint method route & body] (:children node)]
|
||||
(-> (api/list-node
|
||||
(list
|
||||
(api/token-node 'do)
|
||||
(-> (api/token-node (symbol "compojure.core" (str (api/sexpr method))))
|
||||
(with-meta (meta method)))
|
||||
(api/list-node
|
||||
(list*
|
||||
(api/token-node 'clojure.core/defn)
|
||||
(-> (api/token-node (route-fn-name (api/sexpr method) (api/sexpr route)))
|
||||
(with-meta (meta route)))
|
||||
body))))
|
||||
(with-meta (meta node))
|
||||
hooks.common/add-lsp-ignore-unused-public-var-metadata)))]
|
||||
(update arg :node update-defendpoint)))
|
||||
@@ -3,6 +3,7 @@
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[hooks.common :as common]))
|
||||
|
||||
;;; TODO -- move this into
|
||||
(def ^:private ignored-implicit-export?
|
||||
'#{active-users-count
|
||||
admin-email
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(ns hooks.metabase.api.search-test
|
||||
(ns hooks.metabase.search.api-test
|
||||
(:require [clj-kondo.hooks-api :as hooks]))
|
||||
|
||||
(defn with-search-items-in-collection [{{[_ & args] :children, :as node} :node}]
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns hooks.metabase.api.automagic-dashboards-test
|
||||
(:require [clj-kondo.hooks-api :as hooks]))
|
||||
(ns hooks.metabase.xrays.api.automagic-dashboards-test
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]))
|
||||
|
||||
(comment
|
||||
|
||||
@@ -10,7 +11,7 @@
|
||||
|
||||
(println (hooks/sexpr input))
|
||||
|
||||
(let [f (load-file ".clj-kondo/hooks/metabase/api/automagic_dashboards_test.clj")]
|
||||
(let [f (load-file ".clj-kondo/hooks/metabase/xrays/api/automagic_dashboards_test.clj")]
|
||||
(f {:node input})))
|
||||
|
||||
(defn with-indexed-model!
|
||||
63
.clj-kondo/test/hooks/clojure/core/defmulti_test.clj
Normal file
@@ -0,0 +1,63 @@
|
||||
(ns hooks.clojure.core.defmulti-test
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[clj-kondo.impl.utils]
|
||||
[clojure.test :refer :all]
|
||||
[hooks.clojure.core.defmulti]))
|
||||
|
||||
(defn- lint-defmulti [form]
|
||||
(binding [clj-kondo.impl.utils/*ctx* {:config {:linters {:metabase/check-defmulti-arglists {:level :warning}}}
|
||||
:ignores (atom nil)
|
||||
:findings (atom [])
|
||||
:namespaces (atom {})}]
|
||||
(hooks.clojure.core.defmulti/lint-defmulti {:node (hooks/parse-string (pr-str form))})
|
||||
@(:findings clj-kondo.impl.utils/*ctx*)))
|
||||
|
||||
(deftest ^:parallel defmulti-missing-arglists-test
|
||||
(testing "missing arglists"
|
||||
(is (=? [{:message "All defmultis should have an attribute map with :arglists metadata. [:metabase/check-defmulti-arglists]"}]
|
||||
(lint-defmulti '(defmulti expand-visualization
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first))))))))
|
||||
|
||||
(deftest ^:parallel defmulti-invalid-arglists-test
|
||||
(testing "invalid arglists"
|
||||
(are [form] (=? [{:message ":arglists should be a quoted list of vectors [:metabase/check-defmulti-arglists]"}]
|
||||
(lint-defmulti form))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '()}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first)))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists ([a])} ; unquoted
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first)))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '[(a)]}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first)))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '((a))}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first))))))
|
||||
|
||||
(deftest ^:parallel defmulti-arglists-with-underscore-args-test
|
||||
(testing "arglists, but some symbols are _"
|
||||
(are [form] (=? [{:message ":arglists should contain actual arg names, not underscore (unused) symbols [:metabase/check-defmulti-arglists]"}]
|
||||
(lint-defmulti form))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '([card _ _])}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first)))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '([card _bad _arg])}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first))))))
|
||||
|
||||
(deftest ^:parallel defmulti-arglists-ok-test
|
||||
(testing "good"
|
||||
(is (= []
|
||||
(lint-defmulti '(defmulti expand-visualization
|
||||
{:arglists '([card x y])}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first))))))))
|
||||
74
.clj-kondo/test/hooks/clojure/core/ns_test.clj
Normal file
@@ -0,0 +1,74 @@
|
||||
(ns hooks.clojure.core.ns-test
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[clj-kondo.impl.utils]
|
||||
[clojure.test :refer :all]
|
||||
[hooks.clojure.core.ns]))
|
||||
|
||||
(defn- lint-ns [form config]
|
||||
(binding [clj-kondo.impl.utils/*ctx* {:config config
|
||||
:ignores (atom nil)
|
||||
:findings (atom [])
|
||||
:namespaces (atom {})}]
|
||||
(hooks.clojure.core.ns/lint-ns {:node (hooks/parse-string
|
||||
(binding [*print-meta* true]
|
||||
(pr-str form)))
|
||||
:config config})
|
||||
@(:findings clj-kondo.impl.utils/*ctx*)))
|
||||
|
||||
(defn- lint-modules [form config]
|
||||
(lint-ns form (assoc-in config [:linters :metabase/modules :level] :warning)))
|
||||
|
||||
(deftest ^:parallel module-checker-allowed-modules-test
|
||||
(is (=? [{:message "Module task should not be used in the api module. [:metabase/modules api :uses]"
|
||||
:type :metabase/modules}]
|
||||
(lint-modules
|
||||
'(ns metabase.api.search
|
||||
(:require
|
||||
[metabase.request.core :as request]
|
||||
[metabase.task :as task]))
|
||||
'{:metabase/modules {api {:uses #{request}}
|
||||
request {:api #{metabase.request.core}}}}))))
|
||||
|
||||
(deftest ^:parallel module-checker-api-namespaces-test
|
||||
(is (=? [{:message "Namespace metabase.search.config is not an allowed external API namespace for the search module. [:metabase/modules search :api]"
|
||||
:type :metabase/modules}]
|
||||
(lint-modules
|
||||
'(ns metabase.api.search
|
||||
(:require
|
||||
[metabase.search.config :as search.config]
|
||||
[metabase.search.core :as search]))
|
||||
'{:metabase/modules {api {:uses #{search}}
|
||||
search {:api #{metabase.search.core}}}}))))
|
||||
|
||||
(deftest ^:parallel module-checker-api-namespaces-ignore-test
|
||||
(is (empty? (lint-modules
|
||||
'(ns metabase.api.search
|
||||
(:require
|
||||
^{:clj-kondo/ignore [:metabase/modules]}
|
||||
[metabase.search.config :as search.config]
|
||||
[metabase.search.core :as search]))
|
||||
'{:metabase/modules {api {:uses #{search}}
|
||||
search {:api #{metabase.search.core}}}}))))
|
||||
|
||||
(deftest ^:parallel module-checker-enterprise-test
|
||||
(is (=? [{:message "Module search should not be used in the enterprise/search module. [:metabase/modules enterprise/search :uses]"
|
||||
:type :metabase/modules}]
|
||||
(lint-modules
|
||||
'(ns metabase-enterprise.search.core
|
||||
(:require
|
||||
[metabase.search.core :as search]))
|
||||
'{:metabase/modules {search {:api #{metabase.search.core}}}}))))
|
||||
|
||||
(deftest ^:parallel require-newlines-test
|
||||
(binding [clj-kondo.impl.utils/*ctx* {:config {:linters {:metabase/require-shape-checker {:level :warnings}}}
|
||||
:ignores (atom nil)
|
||||
:findings (atom [])
|
||||
:namespaces (atom {})}]
|
||||
(hooks.clojure.core.ns/lint-ns {:node (hooks/parse-string "
|
||||
(ns metabase.task.cache
|
||||
(:require [metabase.task.bunny]
|
||||
[metabase.task.rabbit]))")})
|
||||
(is (=? [{:message "Put your requires on a newline from the :require keyword [:metabase/require-shape-checker]",
|
||||
:type :metabase/require-shape-checker}]
|
||||
@(:findings clj-kondo.impl.utils/*ctx*)))))
|
||||
@@ -1,111 +0,0 @@
|
||||
(ns hooks.clojure.core-test
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]
|
||||
[clojure.test :refer :all]
|
||||
[hooks.clojure.core]))
|
||||
|
||||
(deftest module-checker-allowed-modules-test
|
||||
(let [findings (atom [])]
|
||||
(with-redefs [hooks/reg-finding! (fn [node]
|
||||
(swap! findings conj node))]
|
||||
(hooks.clojure.core/lint-ns
|
||||
{:node (hooks/parse-string "
|
||||
(ns metabase.api.search
|
||||
(:require
|
||||
[metabase.request.core :as request]
|
||||
[metabase.task :as task]))")
|
||||
:config '{:linters {:metabase/ns-module-checker {:allowed-modules {metabase.api #{metabase.request}}
|
||||
:api-namespaces {metabase.request #{metabase.request.core}}}}}})
|
||||
(is (=? [{:message (str "Module metabase.task should not be used in the metabase.api module."
|
||||
" [:metabase/ns-module-checker :allowed-modules metabase.api]")
|
||||
:type :metabase/ns-module-checker}]
|
||||
@findings)))))
|
||||
|
||||
(deftest module-checker-api-namespaces-test
|
||||
(let [findings (atom [])]
|
||||
(with-redefs [hooks/reg-finding! (fn [node]
|
||||
(swap! findings conj node))]
|
||||
(hooks.clojure.core/lint-ns
|
||||
{:node (hooks/parse-string "
|
||||
(ns metabase.api.search
|
||||
(:require
|
||||
[metabase.search.config :as search.config]
|
||||
[metabase.search.core :as search]))")
|
||||
:config '{:linters {:metabase/ns-module-checker {:allowed-modules {metabase.api #{metabase.search}}
|
||||
:api-namespaces {metabase.search #{metabase.search.core}}}}}})
|
||||
(is (=? [{:message (str "Namespace metabase.search.config is not an allowed external API namespace for the"
|
||||
" metabase.search module. [:metabase/ns-module-checker :api-namespaces metabase.search]")
|
||||
:type :metabase/ns-module-checker}]
|
||||
@findings)))))
|
||||
|
||||
(deftest module-checker-api-namespaces-ignore-test
|
||||
(let [findings (atom [])]
|
||||
(with-redefs [hooks/reg-finding! (fn [node]
|
||||
(swap! findings conj node))]
|
||||
(hooks.clojure.core/lint-ns
|
||||
{:node (hooks/parse-string "
|
||||
(ns metabase.api.search
|
||||
(:require
|
||||
^{:clj-kondo/ignore [:metabase/ns-module-checker]}
|
||||
[metabase.search.config :as search.config]
|
||||
[metabase.search.core :as search]))")
|
||||
:config '{:linters {:metabase/ns-module-checker {:allowed-modules {metabase.api #{metabase.search}}
|
||||
:api-namespaces {metabase.search #{metabase.search.core}}}}}})
|
||||
(is (empty? @findings)))))
|
||||
|
||||
(defn- lint-defmulti! [form]
|
||||
(let [findings (atom [])
|
||||
node (hooks/parse-string (pr-str form))]
|
||||
(with-redefs [hooks/reg-finding! (fn [node]
|
||||
(swap! findings conj node))]
|
||||
(hooks.clojure.core/lint-defmulti {:node node})
|
||||
@findings)))
|
||||
|
||||
(deftest defmulti-missing-arglists-test
|
||||
(testing "missing arglists"
|
||||
(is (=? [{:message "All defmultis should have an attribute map with :arglists metadata. [:metabase/check-defmulti-arglists]"}]
|
||||
(lint-defmulti! '(defmulti expand-visualization
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first))))))))
|
||||
|
||||
(deftest defmulti-invalid-arglists-test
|
||||
(testing "invalid arglists"
|
||||
(are [form] (=? [{:message ":arglists should be a quoted list of vectors [:metabase/check-defmulti-arglists]"}]
|
||||
(lint-defmulti! form))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '()}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first)))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists ([a])} ; unquoted
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first)))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '[(a)]}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first)))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '((a))}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first))))))
|
||||
|
||||
(deftest defmulti-arglists-with-underscore-args-test
|
||||
(testing "arglists, but some symbols are _"
|
||||
(are [form] (=? [{:message ":arglists should contain actual arg names, not underscore (unused) symbols [:metabase/check-defmulti-arglists]"}]
|
||||
(lint-defmulti! form))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '([card _ _])}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first)))
|
||||
'(defmulti expand-visualization
|
||||
{:arglists '([card _bad _arg])}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first))))))
|
||||
|
||||
(deftest defmulti-arglists-ok-test
|
||||
(testing "good"
|
||||
(is (= []
|
||||
(lint-defmulti! '(defmulti expand-visualization
|
||||
{:arglists '([card x y])}
|
||||
(fn [card _ _]
|
||||
(-> card :visualization first))))))))
|
||||
10
.clj-kondo/test/hooks/common/modules_test.clj
Normal file
@@ -0,0 +1,10 @@
|
||||
(ns hooks.common.modules-test
|
||||
(:require
|
||||
[clojure.test :refer :all]
|
||||
[hooks.common.modules]))
|
||||
|
||||
(deftest ^:parallel module-test
|
||||
(are [symb expected] (= expected
|
||||
(hooks.common.modules/module symb))
|
||||
'metabase.qp.middleware.wow 'qp
|
||||
'metabase-enterprise.whatever.core 'enterprise/whatever))
|
||||
@@ -6,16 +6,16 @@
|
||||
[hooks.common]))
|
||||
|
||||
(deftest ^:parallel ignored-linters-test
|
||||
(doseq [ignored [":metabase/ns-module-checker"
|
||||
"[:metabase/ns-module-checker]"
|
||||
"(:metabase/ns-module-checker)"]
|
||||
(doseq [ignored [":metabase/modules"
|
||||
"[:metabase/modules]"
|
||||
"(:metabase/modules)"]
|
||||
prefix ["#_"
|
||||
"^"]
|
||||
:let [s (format "%s{:clj-kondo/ignore %s} [metabase.search.config :as search.config]"
|
||||
prefix
|
||||
ignored)]]
|
||||
(testing (pr-str s)
|
||||
(is (= #{:metabase/ns-module-checker}
|
||||
(is (= #{:metabase/modules}
|
||||
(hooks.common/ignored-linters (api/parse-string s)))))))
|
||||
|
||||
(deftest ^:parallel merge-ignored-linters-test
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
(ns hooks.metabase.api.common-test
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as api]
|
||||
[clojure.test :refer :all]
|
||||
[hooks.metabase.api.common]))
|
||||
|
||||
(deftest ^:parallel defendpoint-test
|
||||
(is (= '(do
|
||||
compojure.core/POST
|
||||
(clojure.core/defn
|
||||
POST_:id_copy
|
||||
"Copy a `Card`, with the new name 'Copy of _name_'"
|
||||
[id]
|
||||
{id [:maybe ms/PositiveInt]}
|
||||
(let [orig-card (api/read-check Card id)
|
||||
new-name (str (trs "Copy of ") (:name orig-card))
|
||||
new-card (assoc orig-card :name new-name)]
|
||||
(-> (card/create-card! new-card @api/*current-user*)
|
||||
hydrate-card-details
|
||||
(assoc :last-edit-info (last-edit/edit-information-for-user @api/*current-user*))))))
|
||||
(-> {:node (-> '(api/defendpoint POST "/:id/copy"
|
||||
"Copy a `Card`, with the new name 'Copy of _name_'"
|
||||
[id]
|
||||
{id [:maybe ms/PositiveInt]}
|
||||
(let [orig-card (api/read-check Card id)
|
||||
new-name (str (trs "Copy of ") (:name orig-card))
|
||||
new-card (assoc orig-card :name new-name)]
|
||||
(-> (card/create-card! new-card @api/*current-user*)
|
||||
hydrate-card-details
|
||||
(assoc :last-edit-info (last-edit/edit-information-for-user @api/*current-user*)))))
|
||||
pr-str
|
||||
api/parse-string)}
|
||||
hooks.metabase.api.common/defendpoint
|
||||
:node
|
||||
api/sexpr))))
|
||||
21
.cljfmt.edn
@@ -81,7 +81,7 @@
|
||||
throttle.core/with-throttling [[:block 1]]
|
||||
;; the rest of the stuff in this list is generated automatically based on `:style/indent` specs
|
||||
;; by running [[dev.generate-cljfmt-config/specs]]... use that code to update this list
|
||||
metabase-enterprise.advanced-permissions.api.util-test/with-impersonations! [[:inner 0]]
|
||||
metabase-enterprise.impersonation.util-test/with-impersonations! [[:inner 0]]
|
||||
metabase-enterprise.cache.config-test/with-temp-persist-models [[:block 1]]
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps! [[:inner 0]]
|
||||
metabase-enterprise.sandbox.test-util/with-gtaps-for-user! [[:inner 0]]
|
||||
@@ -102,7 +102,6 @@
|
||||
metabase.actions.test-util/with-actions-test-data-tables [[:block 1]]
|
||||
metabase.actions.test-util/with-empty-db [[:inner 0]]
|
||||
metabase.actions.test-util/with-temp-test-data [[:inner 0]]
|
||||
metabase.api.alert-test/with-alert-in-collection [[:block 1]]
|
||||
metabase.api.card-test/with-cards-in-readable-collection [[:block 1]]
|
||||
metabase.api.card-test/with-cards-in-writeable-collection [[:block 1]]
|
||||
metabase.api.card-test/with-ordered-items [[:inner 0]]
|
||||
@@ -114,9 +113,6 @@
|
||||
metabase.api.common/check [[:default]]
|
||||
metabase.api.common/define-routes [[:block 0]]
|
||||
metabase.api.common/let-404 [[:block 1]]
|
||||
metabase.api.common.internal/auto-coerce [[:block 1]]
|
||||
metabase.api.common.internal/auto-parse [[:block 1]]
|
||||
metabase.api.common.internal-test/no-route-regexes [[:block 0]]
|
||||
metabase.api.embed-test/do-response-formats [[:block 1]]
|
||||
metabase.api.embed-test/with-embedding-enabled-and-new-secret-key [[:block 0]]
|
||||
metabase.api.embed-test/with-embedding-enabled-and-temp-card-referencing [[:block 3]]
|
||||
@@ -131,8 +127,6 @@
|
||||
metabase.api.public-test/with-temp-public-card [[:block 1]]
|
||||
metabase.api.public-test/with-temp-public-dashboard [[:block 1]]
|
||||
metabase.api.public-test/with-temp-public-dashboard-and-card [[:block 1]]
|
||||
metabase.api.search-test/do-test-users [[:block 1]]
|
||||
metabase.api.session/http-401-on-error [[:block 0]]
|
||||
metabase.cmd.copy/step [[:block 1]]
|
||||
metabase.cmd.copy/with-connection-rollback-only [[:block 1]]
|
||||
metabase.cmd.copy/with-disabled-db-constraints [[:block 2]]
|
||||
@@ -175,11 +169,14 @@
|
||||
metabase.models.params.chain-filter-test/with-clean-field-values-for-field [[:block 1]]
|
||||
metabase.models.params.chain-filter-test/with-fk-field-to-field-remapping [[:block 0]]
|
||||
metabase.models.params.chain-filter-test/with-human-readable-values-remapping [[:block 0]]
|
||||
metabase.models.pulse-test/with-dashboard-subscription-in-collection [[:block 1]]
|
||||
metabase.models.pulse-test/with-pulse-in-collection [[:inner 0]]
|
||||
metabase.models.setting/defsetting [[:block 1]]
|
||||
metabase.models.setting.multi-setting/define-multi-setting [[:inner 0]]
|
||||
metabase.models.setting/defsetting [[:block 1]]
|
||||
metabase.models.task-history/with-task-history [[:block 1]]
|
||||
metabase.permissions.core/allow-changing-all-users-group-members [[:block 0]]
|
||||
metabase.permissions.core/disable-perms-cache [[:block 0]]
|
||||
metabase.pulse.api.alert-test/with-alert-in-collection [[:block 1]]
|
||||
metabase.pulse.models.pulse-test/with-dashboard-subscription-in-collection [[:block 1]]
|
||||
metabase.pulse.models.pulse-test/with-pulse-in-collection [[:inner 0]]
|
||||
metabase.query-processor-test.pipeline-queries-test/pmbql-query [[:block 1]]
|
||||
metabase.query-processor-test.pipeline-queries-test/run-pmbql-query [[:block 1]]
|
||||
metabase.query-processor.debug/debug> [[:block 0]]
|
||||
@@ -197,11 +194,13 @@
|
||||
metabase.request.session/as-admin [[:block 0]]
|
||||
metabase.request.session/with-current-user [[:inner 0]]
|
||||
metabase.sample-data-test/with-temp-sample-database-db [[:block 1]]
|
||||
metabase.search.api-test/do-test-users [[:block 1]]
|
||||
metabase.server.middleware.session/as-admin [[:block 0]]
|
||||
metabase.server.middleware.session/with-current-user [[:inner 0]]
|
||||
metabase.server.streaming-response/streaming-response [[:block 2]]
|
||||
metabase.server.streaming-response-test/with-streaming-response-thread-pool [[:block 0]]
|
||||
metabase.server.streaming-response-test/with-test-driver-db! [[:block 0]]
|
||||
metabase.server.streaming-response/streaming-response [[:block 2]]
|
||||
metabase.session.api/http-401-on-error [[:block 0]]
|
||||
metabase.sync.fetch-metadata/log-if-error [[:block 1]]
|
||||
metabase.sync.sync-metadata.fields-test/with-test-db [[:block 0]]
|
||||
metabase.sync.util/do-with-start-and-finish-debug-logging [[:default]]
|
||||
|
||||
@@ -20,11 +20,7 @@
|
||||
(clojure-mode
|
||||
;; Specify which arg is the docstring for certain macros
|
||||
;; (Add more as needed)
|
||||
(eval . (put 'defendpoint-schema 'clojure-doc-string-elt 3))
|
||||
(eval . (put 'defendpoint 'clojure-doc-string-elt 3))
|
||||
(eval . (put 'define-premium-feature 'clojure-doc-string-elt 2))
|
||||
(eval . (put 'api/defendpoint-schema 'clojure-doc-string-elt 3))
|
||||
(eval . (put 'api/defendpoint 'clojure-doc-string-elt 3))
|
||||
(eval . (put 'api.macros/defendpoint 'clojure-doc-string-elt 3))
|
||||
(eval . (put 'defsetting 'clojure-doc-string-elt 2))
|
||||
(eval . (put 'setting/defsetting 'clojure-doc-string-elt 2))
|
||||
|
||||
44
.eslintrc.js
@@ -6,6 +6,10 @@
|
||||
// can use this flag to enable it. This is set to true in CI
|
||||
const shouldLintCssModules =
|
||||
process.env.LINT_CSS_MODULES === "true" || process.env.CI;
|
||||
const plugins = ["react", "no-only-tests", "ttag", "i18next"];
|
||||
if (shouldLintCssModules) {
|
||||
plugins.push("postcss-modules");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
rules: {
|
||||
@@ -17,7 +21,7 @@ module.exports = {
|
||||
{
|
||||
vars: "all",
|
||||
args: "none",
|
||||
varsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_.+$",
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
@@ -108,7 +112,6 @@ module.exports = {
|
||||
"When",
|
||||
"Then",
|
||||
"describeWithSnowplow",
|
||||
"describeEE",
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -132,7 +135,7 @@ module.exports = {
|
||||
"jest/globals": true,
|
||||
},
|
||||
parser: "babel-eslint",
|
||||
plugins: ["react", "no-only-tests", "postcss-modules"],
|
||||
plugins: ["react", "no-only-tests", "postcss-modules", "ttag"],
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
@@ -143,6 +146,7 @@ module.exports = {
|
||||
"plugin:import/typescript",
|
||||
"plugin:depend/recommended",
|
||||
"plugin:storybook/recommended",
|
||||
"plugin:i18next/recommended",
|
||||
],
|
||||
settings: {
|
||||
"import/internal-regex": "^metabase/|^metabase-lib/",
|
||||
@@ -199,6 +203,22 @@ module.exports = {
|
||||
"no-literal-metabase-strings": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
"*.unit.spec.*",
|
||||
"frontend/lint/**/*",
|
||||
"*.stories.*",
|
||||
"stories-data.*",
|
||||
"e2e/**/*",
|
||||
"**/tests/*",
|
||||
"release/**/*",
|
||||
"webpack.config.js",
|
||||
"rspack.config.js",
|
||||
],
|
||||
rules: {
|
||||
"i18next/no-literal-string": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: ["plugin:@typescript-eslint/recommended"],
|
||||
files: ["*.ts", "*.tsx"],
|
||||
@@ -220,10 +240,10 @@ module.exports = {
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
argsIgnorePattern: "^_.+$",
|
||||
varsIgnorePattern: "^_.+$",
|
||||
ignoreRestSiblings: true,
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_.+$",
|
||||
},
|
||||
],
|
||||
// This was introduced in 6.0.0
|
||||
@@ -284,5 +304,17 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["docs/**/snippets/**/*.{ts,tsx,js,jsx}"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"import/no-commonjs": "off",
|
||||
"import/no-default-export": "off",
|
||||
"import/order": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"no-color-literals": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
230
.github/CODEOWNERS
vendored
@@ -1,34 +1,248 @@
|
||||
# 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/backend/src/metabase_enterprise/advanced_permissions @noahmoss
|
||||
enterprise/backend/src/metabase_enterprise/audit_app @noahmoss
|
||||
enterprise/backend/src/metabase_enterprise/sso @noahmoss
|
||||
enterprise/backend/src/metabase_enterprise/task/truncate_audit_table.clj @noahmoss
|
||||
enterprise/backend/src/metabase_enterprise/internal_user.clj @noahmoss
|
||||
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-admin-webapp
|
||||
src/metabase/**/*permissions* @noahmoss
|
||||
src/metabase/integrations @noahmoss
|
||||
snowplow/* @metabase/data
|
||||
e2e/* @filiphric
|
||||
e2e @filiphric
|
||||
|
||||
.github @metabase/devexp
|
||||
.clj-kondo @metabase/devexp
|
||||
|
||||
#
|
||||
# Backend modules <=> teams that own them
|
||||
#
|
||||
|
||||
modules/drivers @metabase/drivers
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/advanced_config @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/advanced_config @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/advanced_permissions @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/advanced_permissions @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/airgap @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/airgap @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/api @metabase/devexp
|
||||
enterprise/backend/test/metabase_enterprise/api @metabase/devexp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/audit_app @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/audit_app @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/auth_provider @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/auth_provider @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/billing @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/billing @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/cache @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/cache @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/content_verification @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/content_verification @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/core @metabase/devexp
|
||||
enterprise/backend/test/metabase_enterprise/core @metabase/devexp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/dashboard_subscription_filters @metabase/core-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
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/llm
|
||||
# enterprise/backend/test/metabase_enterprise/llm
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/query_reference_validation @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/query_reference_validation @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/sandbox @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/sandbox @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/scim @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/scim @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/search @metabase/core-workflows
|
||||
enterprise/backend/test/metabase_enterprise/search @metabase/core-workflows
|
||||
|
||||
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
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/sso @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/sso @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/stale @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/stale @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/stats @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/stats @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/task @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/task @metabase/core-backend-admin-webapp
|
||||
|
||||
enterprise/backend/src/metabase_enterprise/upload_management @metabase/core-backend-admin-webapp
|
||||
enterprise/backend/test/metabase_enterprise/upload_management @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/actions @metabase/core-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
|
||||
|
||||
src/metabase/analytics @metabase/core-backend-admin-webapp
|
||||
test/metabase/analytics @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/analyze @metabase/drivers
|
||||
test/metabase/analyze @metabase/drivers
|
||||
|
||||
src/metabase/api @metabase/devexp
|
||||
test/metabase/api @metabase/devexp
|
||||
|
||||
src/metabase/audit @metabase/core-backend-admin-webapp
|
||||
test/metabase/audit @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/auth_provider @metabase/core-backend-admin-webapp
|
||||
test/metabase/auth_provider @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/bookmarks @metabase/core-backend-admin-webapp
|
||||
test/metabase/bookmarks @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/cmd @metabase/devexp
|
||||
test/metabase/cmd @metabase/devexp
|
||||
|
||||
src/metabase/channel @metabase/core-workflows
|
||||
test/metabase/channel @metabase/core-workflows
|
||||
|
||||
src/metabase/cloud_migration @metabase/core-workflows
|
||||
test/metabase/cloud_migration @metabase/core-workflows
|
||||
|
||||
src/metabase/cmd @metabase/devexp
|
||||
test/metabase/cmd @metabase/devexp
|
||||
|
||||
src/metabase/config @metabase/devexp
|
||||
test/metabase/config @metabase/devexp
|
||||
|
||||
src/metabase/core @metabase/devexp
|
||||
test/metabase/core @metabase/devexp
|
||||
|
||||
src/metabase/db @metabase/core-backend-admin-webapp
|
||||
test/metabase/db @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/driver @metabase/drivers
|
||||
test/metabase/driver @metabase/drivers
|
||||
|
||||
src/metabase/eid_translation @metabase/core-backend-admin-webapp
|
||||
test/metabase/eid_translation @metabase/core-backend-admin-webapp
|
||||
|
||||
|
||||
src/metabase/events @metabase/devexp
|
||||
test/metabase/events @metabase/devexp
|
||||
|
||||
src/metabase/formatter @metabase/core-backend-querying
|
||||
test/metabase/formatter @metabase/core-backend-querying
|
||||
|
||||
src/metabase/indexed_entities @metabase/core-backend-querying
|
||||
test/metabase/indexed_entities @metabase/core-backend-querying
|
||||
|
||||
src/metabase/integrations @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
|
||||
|
||||
src/metabase/lib @metabase/core-backend-querying
|
||||
test/metabase/lib @metabase/core-backend-querying
|
||||
|
||||
src/metabase/logger @metabase/devexp
|
||||
test/metabase/logger @metabase/devexp
|
||||
|
||||
src/metabase/login_history @metabase/core-backend-admin-webapp
|
||||
test/metabase/login_history @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/models @metabase/devexp
|
||||
test/metabase/models @metabase/devexp
|
||||
|
||||
src/metabase/moderation @metabase/core-backend-admin-webapp
|
||||
test/metabase/moderation @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/notification @metabase/core-workflows
|
||||
test/metabase/notification @metabase/core-workflows
|
||||
|
||||
src/metabase/permissions @metabase/core-backend-admin-webapp
|
||||
test/metabase/permissions @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/plugins @metabase/devexp
|
||||
test/metabase/plugins @metabase/devexp
|
||||
|
||||
src/metabase/premium_features @metabase/core-backend-admin-webapp
|
||||
test/metabase/premium_features @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/public_settings @metabase/core-backend-admin-webapp
|
||||
test/metabase/public_settings @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/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
|
||||
|
||||
src/metabase/query_processor @metabase/core-backend-querying
|
||||
test/metabase/query_processor @metabase/core-backend-querying
|
||||
|
||||
src/metabase/request @metabase/devexp
|
||||
test/metabase/request @metabase/devexp
|
||||
|
||||
src/metabase/revisions @metabase/core-backend-admin-webapp
|
||||
test/metabase/revisions @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/sample_data @metabase/core-backend-admin-webapp
|
||||
test/metabase/sample_data @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/search @metabase/core-workflows
|
||||
test/metabase/search @metabase/core-workflows
|
||||
|
||||
src/metabase/segments @metabase/core-backend-querying
|
||||
test/metabase/segments @metabase/core-backend-querying
|
||||
|
||||
src/metabase/server @metabase/devexp
|
||||
test/metabase/server @metabase/devexp
|
||||
|
||||
src/metabase/session @metabase/core-backend-admin-webapp
|
||||
test/metabase/session @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/setup @metabase/core-backend-admin-webapp
|
||||
test/metabase/setup @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/sync @metabase/drivers
|
||||
test/metabase/sync @metabase/drivers
|
||||
|
||||
src/metabase/task @metabase/devexp
|
||||
test/metabase/task @metabase/devexp
|
||||
|
||||
src/metabase/tiles @metabase/core-backend-dashviz
|
||||
test/metabase/tiles @metabase/core-backend-dashviz
|
||||
|
||||
src/metabase/timeline @metabase/core-backend-dashviz
|
||||
test/metabase/timeline @metabase/core-backend-dashviz
|
||||
|
||||
src/metabase/types @metabase/core-backend-querying
|
||||
test/metabase/types @metabase/core-backend-querying
|
||||
|
||||
src/metabase/upload @metabase/core-backend-admin-webapp
|
||||
test/metabase/upload @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/user_key_value @metabase/core-backend-admin-webapp
|
||||
test/metabase/user_key_value @metabase/core-backend-admin-webapp
|
||||
|
||||
src/metabase/util @metabase/devexp
|
||||
test/metabase/util @metabase/devexp
|
||||
|
||||
src/metabase/xrays @metabase/core-backend-querying
|
||||
test/metabase/xrays @metabase/core-backend-querying
|
||||
|
||||
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) (unless it's a tiny documentation change). Also, if you're attempting to fix a translation issue, please submit your changes to our [POEditor project](https://poeditor.com/join/project/ynjQmwSsGh) instead of opening a PR.
|
||||
> 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]
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ PENDING CI / PENDING REVIEW / READY _(choose one and update accordingly)_
|
||||
- Reproduces #XXXXX
|
||||
|
||||
### How to test this manually?
|
||||
- `yarn test-cypress-open`
|
||||
- `yarn test-cypress`
|
||||
- `relative/path/to/the/file`
|
||||
> _(For still unfixed bug)_
|
||||
- Replace `it.skip()` with `it.only()` to run the test in isolation
|
||||
|
||||
22
.github/actions/build-ee-extra/action.yml
vendored
@@ -30,7 +30,7 @@ runs:
|
||||
shell: bash
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/Hydrogen # 18.x.x
|
||||
node-version: lts/Jod # 22.x.x
|
||||
- run: rm package.json && npm install yaml
|
||||
shell: bash
|
||||
- name: parse drivers registry
|
||||
@@ -64,10 +64,7 @@ runs:
|
||||
const extraDrivers = {
|
||||
firebolt: getDriverUrl('firebolt'),
|
||||
starburst: getDriverUrl('starburst'),
|
||||
exasol: getDriverUrl('exasol'),
|
||||
exasol_jdbc: drivers.find(d => d.name === 'exasol').deps[0],
|
||||
clickhouse: getDriverUrl('clickhouse'),
|
||||
ocient: getDriverUrl('ocient'),
|
||||
materialize: getDriverUrl('materialize'),
|
||||
};
|
||||
|
||||
@@ -75,38 +72,31 @@ runs:
|
||||
|
||||
return extraDrivers;
|
||||
- name: Download Firebolt driver
|
||||
if: ${{ fromJson(steps.drivers_registry.outputs.result).firebolt != 'noop' }}
|
||||
run: |
|
||||
curl -OL ${{ fromJson(steps.drivers_registry.outputs.result).firebolt }}
|
||||
working-directory: modules
|
||||
shell: bash
|
||||
- name: Download Starburst driver
|
||||
if: ${{ fromJson(steps.drivers_registry.outputs.result).starburst != 'noop' }}
|
||||
run: |
|
||||
curl -OL ${{ fromJson(steps.drivers_registry.outputs.result).starburst }}
|
||||
shell: bash
|
||||
working-directory: modules
|
||||
- name: Download Exasol driver
|
||||
run: |
|
||||
curl -OL ${{ fromJson(steps.drivers_registry.outputs.result).exasol }}
|
||||
curl -OL ${{ fromJson(steps.drivers_registry.outputs.result).exasol_jdbc }}
|
||||
shell: bash
|
||||
working-directory: modules
|
||||
- name: Download Clickhouse driver
|
||||
if: ${{ fromJson(steps.drivers_registry.outputs.result).clickhouse != 'noop' }}
|
||||
run: |
|
||||
curl -OL ${{ fromJson(steps.drivers_registry.outputs.result).clickhouse }}
|
||||
shell: bash
|
||||
working-directory: modules
|
||||
- name: Download Ocient driver
|
||||
run: |
|
||||
curl -OL ${{ fromJson(steps.drivers_registry.outputs.result).ocient }}
|
||||
shell: bash
|
||||
working-directory: modules
|
||||
- name: Download Materialize driver
|
||||
if: ${{ fromJson(steps.drivers_registry.outputs.result).materialize != 'noop' }}
|
||||
run: |
|
||||
curl -OL ${{ fromJson(steps.drivers_registry.outputs.result).materialize }}
|
||||
shell: bash
|
||||
working-directory: modules
|
||||
- name: Bundle additional drivers into the Uberjar
|
||||
run: jar uf bin/docker/metabase.jar modules/*.jar
|
||||
run: test -n "$(find modules -maxdepth 1 -name '*.jar' -print -quit)" && jar uf bin/docker/metabase.jar modules/*.jar
|
||||
shell: bash
|
||||
- name: Launch uberjar
|
||||
run: >-
|
||||
|
||||
63
.github/actions/fetch-artifact/action.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Fetch artifact
|
||||
description: Fetches a specific artifact and downloads it to the current directory
|
||||
|
||||
inputs:
|
||||
name:
|
||||
description: The name of the artifact to fetch
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: find_release_artifact
|
||||
id: find_release_artifact
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const fs = require('fs');
|
||||
|
||||
const artifacts = await github.rest.actions.listArtifactsForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: "${{ inputs.name }}",
|
||||
per_page: 1,
|
||||
});
|
||||
|
||||
if (!artifacts.data?.artifacts?.[0]?.id) {
|
||||
throw new Error(`No artifacts found for ${{ inputs.commit }}`);
|
||||
}
|
||||
|
||||
const artifact_id = artifacts.data.artifacts[0].id;
|
||||
|
||||
const download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: artifact_id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
|
||||
|
||||
- name: unzip uberjar artifact
|
||||
shell: bash
|
||||
run: unzip mb.zip
|
||||
- name: Move the Uberjar
|
||||
shell: bash
|
||||
run: | # bash
|
||||
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 the metabase.jar file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify that this is a valid JAR file
|
||||
shell: bash
|
||||
run: file --mime-type ./metabase.jar
|
||||
- name: Reveal its version.properties
|
||||
shell: bash
|
||||
run: jar xf metabase.jar version.properties && cat version.properties
|
||||
48
.github/actions/notify-pull-request/action.yml
vendored
@@ -5,16 +5,56 @@ inputs:
|
||||
include-log:
|
||||
required: false
|
||||
default: true
|
||||
remove-previous-comment:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
id: get-latest-release-branch
|
||||
- name: Remove previous pull-request comment
|
||||
uses: actions/github-script@v7
|
||||
if: inputs.remove-previous-comment == 'true'
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: context.issue.number,
|
||||
}).catch(console.error);
|
||||
|
||||
if (!comments) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = "${{ inputs.message }}";
|
||||
const comment = comments.find((comment) => comment.body.includes(message));
|
||||
|
||||
if (comment) {
|
||||
await github.rest.issues.deleteComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: comment.id,
|
||||
});
|
||||
}
|
||||
|
||||
- name: Notify pull-request
|
||||
uses: actions/github-script@v7
|
||||
id: notify-pull-request
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const message = "${{ inputs.message }}"
|
||||
script: | # js
|
||||
const pr = context.payload.pull_request ?? context.payload.issue;
|
||||
|
||||
if (!pr) {
|
||||
console.error("This action can only be run on pull requests");
|
||||
return;
|
||||
}
|
||||
|
||||
const message = "${{ inputs.message }}";
|
||||
const shouldIncludeLog = ${{ inputs.include-log }}.toString() === "true";
|
||||
const author = context.payload.sender.login;
|
||||
|
||||
|
||||
7
.github/actions/prepare-cypress/action.yml
vendored
@@ -1,6 +1,12 @@
|
||||
name: Prepare cypress environment
|
||||
description: Cypress preparation steps
|
||||
|
||||
inputs:
|
||||
working-directory:
|
||||
description: "Optional working directory for yarn commands"
|
||||
required: false
|
||||
default: "."
|
||||
|
||||
outputs:
|
||||
chrome-path:
|
||||
description: Path to the custom Chrome executable
|
||||
@@ -33,3 +39,4 @@ runs:
|
||||
yarn cypress cache list
|
||||
yarn cypress verify
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
2
.github/actions/test-driver/action.yml
vendored
@@ -23,6 +23,8 @@ runs:
|
||||
run: yarn build-static-viz
|
||||
shell: bash
|
||||
if: ${{ inputs.build-static-viz == 'true' }}
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Test database driver
|
||||
run: clojure -X:dev:ci:ee:ee-dev:drivers:drivers-dev:test ${{ inputs.test-args }}
|
||||
shell: bash
|
||||
|
||||
4
.github/file-paths.yaml
vendored
@@ -5,6 +5,7 @@ default: &default
|
||||
shared_sources: &shared_sources
|
||||
- "src/**/*.cljc"
|
||||
- "src/**/*.cljs"
|
||||
- "package.json"
|
||||
|
||||
shared_specs: &shared_specs
|
||||
- "test/**/*.cljc"
|
||||
@@ -30,6 +31,9 @@ frontend_sources: &frontend_sources
|
||||
- "webpack.static-viz.config.js"
|
||||
- ".nvmrc"
|
||||
|
||||
frontend_embedding_sdk_sources:
|
||||
- *frontend_sources
|
||||
|
||||
frontend_specs: &frontend_specs
|
||||
- *shared_specs
|
||||
- "frontend/test/**"
|
||||
|
||||
3
.github/team.json
vendored
@@ -47,7 +47,8 @@
|
||||
"piranha",
|
||||
"crisptrutski",
|
||||
"deniskaber",
|
||||
"wotbrew"
|
||||
"wotbrew",
|
||||
"spalt08"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
2
.github/workflows/backend-cloverage.yml
vendored
@@ -19,6 +19,8 @@ jobs:
|
||||
m2-cache-key: "cloverage"
|
||||
- name: Build static viz frontend
|
||||
run: yarn build-static-viz
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Collect the test coverage
|
||||
run: clojure -X:dev:ci:ee:ee-dev:test:cloverage
|
||||
- name: Upload coverage to codecov.io
|
||||
|
||||
137
.github/workflows/backend.yml
vendored
@@ -1,70 +1,19 @@
|
||||
name: Backend
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review, labeled]
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-backend
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
backend_all: ${{ steps.changes.outputs.backend_all }}
|
||||
frontend_sources: ${{ steps.changes.outputs.frontend_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
|
||||
|
||||
static-viz-files-changed:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.backend_all != 'true' && needs.files-changed.outputs.frontend_sources == 'true'
|
||||
name: Check whether static-viz files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
static_viz: ${{ steps.static_viz.outputs.static_viz }}
|
||||
steps:
|
||||
- uses: actions/checkout@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: "files-changed"
|
||||
- name: Build static-viz frontend
|
||||
run: yarn build-static-viz
|
||||
- name: Upload Static Viz Bundle Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-viz-bundle-${{ github.sha }}
|
||||
path: resources/frontend_client/app/dist
|
||||
|
||||
- name: Check for static viz changes
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: static_viz
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/static-viz-sources.yaml
|
||||
|
||||
be-linter-clj-kondo:
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
name: Clj-Kondo
|
||||
@@ -78,11 +27,7 @@ jobs:
|
||||
run: ./bin/kondo.sh
|
||||
|
||||
be-linter-eastwood:
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
github.event.pull_request.draft == false &&
|
||||
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
name: Eastwood
|
||||
@@ -95,51 +40,29 @@ jobs:
|
||||
- run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood
|
||||
name: Run Eastwood linter
|
||||
|
||||
# Because it's not possible to conditionally run only `java-11-ee` test in the draft mode,
|
||||
# we have to extract that job manually here. Backend developers have requested that this
|
||||
# test runs at all times to give them an early warning sign is something is broken.
|
||||
be-tests-pre-check:
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
github.event.pull_request.draft == true &&
|
||||
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
|
||||
mage-tests:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
name: Backend Tests Pre-Check
|
||||
timeout-minutes: 60
|
||||
name: Mage
|
||||
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: backend-pre-check
|
||||
|
||||
- run: yarn install --frozen-lockfile --prefer-offline
|
||||
- name: Build static viz frontend
|
||||
run: yarn build-static-viz
|
||||
|
||||
- name: Run tests
|
||||
run: clojure -X:dev:ci:test:ee:ee-dev
|
||||
|
||||
- name: Publish Test Report (JUnit)
|
||||
uses: dorny/test-reporter@v1
|
||||
if: failure()
|
||||
m2-cache-key: 'kondo'
|
||||
- name: Setup Babashka
|
||||
uses: turtlequeue/setup-babashka@v1.7.0
|
||||
with:
|
||||
path: "target/junit/**/*_test.xml"
|
||||
name: JUnit Test Report (Backend Tests Pre-Check)
|
||||
reporter: java-junit
|
||||
list-suites: failed
|
||||
list-tests: failed
|
||||
fail-on-error: false
|
||||
babashka-version: 1.12.196
|
||||
- name: Check bb runs
|
||||
run: bb --version
|
||||
- name: bb task list
|
||||
run: bb tasks
|
||||
- name: Run mage tests
|
||||
run: bb ./test/mage/core_test.clj
|
||||
|
||||
be-tests:
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
github.event.pull_request.draft == false &&
|
||||
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -210,10 +133,12 @@ jobs:
|
||||
# Depending on which files changed, we might either have to build static-viz
|
||||
# from scratch or download the previously built artifact
|
||||
- name: Build static-viz frontend
|
||||
if: ${{ matrix.job.build-static-viz == 'true' && needs.files-changed.outputs.backend_all == 'true' }}
|
||||
if: ${{ matrix.job.build-static-viz == 'true' && !inputs.skip }}
|
||||
run: yarn build-static-viz
|
||||
env:
|
||||
MB_EDITION: ${{ matrix.job.edition }}
|
||||
- name: Download Static Viz Bundle Artifact
|
||||
if: ${{ matrix.job.build-static-viz == 'true' && needs.static-viz-files-changed.outputs.static_viz == 'true' }}
|
||||
if: ${{ matrix.job.build-static-viz == 'true' && !inputs.skip }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static-viz-bundle-${{ github.sha }}
|
||||
@@ -252,11 +177,7 @@ jobs:
|
||||
# checks that all the namespaces we actually ship can be compiled, without any dependencies that we don't ship (such
|
||||
# as `:dev` dependencies). See #27009 for more context.
|
||||
be-check:
|
||||
needs: [files-changed, static-viz-files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
github.event.pull_request.draft == false &&
|
||||
(needs.files-changed.outputs.backend_all == 'true' || needs.static-viz-files-changed.outputs.static_viz == 'true')
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
@@ -274,9 +195,7 @@ jobs:
|
||||
run: clojure -M:ee:drivers:check
|
||||
|
||||
be-cljfmt:
|
||||
needs: [files-changed]
|
||||
if: |
|
||||
!cancelled() && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
name: Cljfmt
|
||||
|
||||
22
.github/workflows/codenotify.yml
vendored
@@ -1,22 +0,0 @@
|
||||
name: Codenotify
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, ready_for_review]
|
||||
# We're currently notifying developers only for frontend source changes.
|
||||
# Update this glob if that ever changes and we start including backend source code changes.
|
||||
paths:
|
||||
- 'frontend/src/metabase/**'
|
||||
- 'enterprise/frontend/src/metabase-enterprise/**'
|
||||
|
||||
jobs:
|
||||
codenotify:
|
||||
runs-on: ubuntu-22.04
|
||||
name: Codenotify
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- uses: sourcegraph/codenotify@v0.6.4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
135
.github/workflows/containerize-jar.yml
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
name: Containerize Uberjar
|
||||
run-name: Containerize ${{ inputs.tag }} Uberjar
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
artifact-name:
|
||||
type: string
|
||||
repo:
|
||||
description: owner and repo like "metabase/metabase"
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
commit:
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
artifact-name:
|
||||
description: usually something like "metabase-release-{{ edition }}-{{ hash }}-uberjar"
|
||||
type: string
|
||||
repo:
|
||||
description: docker owner and repo like "metabase/metabase"
|
||||
type: string
|
||||
tag:
|
||||
description: usually a version like v1.87.2
|
||||
type: string
|
||||
commit:
|
||||
description: the full commit hash
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
containerize:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
outputs:
|
||||
platforms: ${{ steps.platforms.outputs.result }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# we want the dockerfile from the commit we're building
|
||||
- run: git checkout ${{ inputs.commit }} -- bin/docker
|
||||
- name: Retrieve test build uberjar artifact
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: ${{ inputs.artifact-name }}
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get platforms
|
||||
id: platforms
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const { getBuildRequirements, isValidVersionString} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const version = "${{ inputs.tag }}".split('-')[0];
|
||||
|
||||
if (!isValidVersionString(version)) {
|
||||
return "linux/amd64";
|
||||
}
|
||||
// versions before 53 need to be handled differently
|
||||
const buildInfo = getBuildRequirements(version);
|
||||
return buildInfo.platforms;
|
||||
- name: Move the Uberjar to the context dir
|
||||
run: mv ./metabase.jar bin/docker/.
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
|
||||
- name: Set up QEMU
|
||||
if: contains(steps.platforms.outputs.result, 'arm')
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
if: contains(steps.platforms.outputs.result, 'arm')
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build multi-arch container
|
||||
if: contains(steps.platforms.outputs.result, 'arm')
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: ${{ steps.platforms.outputs.result }}
|
||||
network: host
|
||||
tags: ${{ inputs.repo }}:${{ inputs.tag }}
|
||||
no-cache: true
|
||||
push: true
|
||||
|
||||
# we need to do this to get the nice fallback behavior of the legacy manifest file
|
||||
- name: Build container (x86 only)
|
||||
if: contains(steps.platforms.outputs.result, 'arm') == false
|
||||
run: | # bash
|
||||
docker build \
|
||||
-t ${{ inputs.repo }}:${{ inputs.tag }} \
|
||||
--network=host \
|
||||
--push \
|
||||
bin/docker/.
|
||||
|
||||
verify-docker-pull:
|
||||
runs-on: ubuntu-latest
|
||||
needs: containerize
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
|
||||
- name: Pull the container image
|
||||
run: docker pull ${{ inputs.repo }}:${{ inputs.tag }}
|
||||
- name: Launch container
|
||||
run: docker run --rm -dp 3000:3000 ${{ inputs.repo }}:${{ inputs.tag }}
|
||||
timeout-minutes: 5
|
||||
- name: Wait for Metabase to start
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
timeout-minutes: 5
|
||||
|
||||
verify-docker-pull-arm:
|
||||
runs-on: arm-ubuntu-24-2core
|
||||
needs: containerize
|
||||
if: contains(needs.containerize.outputs.platforms, 'linux/arm64')
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
|
||||
- name: Pull the container image
|
||||
run: docker pull ${{ inputs.repo }}:${{ inputs.tag }}
|
||||
- name: Launch container
|
||||
run: docker run --rm -dp 3000:3000 ${{ inputs.repo }}:${{ inputs.tag }}
|
||||
timeout-minutes: 5
|
||||
- name: Wait for Metabase to start
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
timeout-minutes: 5
|
||||
37
.github/workflows/docs_bump_detected.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Docs Bump Detected
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-x.[0-9]+.x'
|
||||
paths:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
push_workflow_to_docs_metabase_github_io_job:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SOURCE_BRANCH: ${{ github.head_ref || github.ref }}
|
||||
TARGET_BRANCH: ${{ github.event.pull_request.base.ref }}
|
||||
steps:
|
||||
- name: Report changed docs files exist
|
||||
run: |
|
||||
echo "A change in the docs directory was detected."
|
||||
|
||||
- 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_update",
|
||||
client_payload: {
|
||||
source_branch: process.env.SOURCE_BRANCH,
|
||||
target_branch: process.env.TARGET_BRANCH}});
|
||||
36
.github/workflows/docs_merge_detected.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Docs Merge 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 == true
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SOURCE_BRANCH: ${{ github.head_ref || github.ref }}
|
||||
TARGET_BRANCH: ${{ github.event.pull_request.base.ref }}
|
||||
steps:
|
||||
- name: Report changed docs files exist
|
||||
run: |
|
||||
echo "A PR with changes to the docs directory was merged."
|
||||
|
||||
- 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_merge",
|
||||
client_payload: {
|
||||
source_branch: process.env.SOURCE_BRANCH,
|
||||
target_branch: process.env.TARGET_BRANCH}});
|
||||
136
.github/workflows/drivers.yml
vendored
@@ -1,36 +1,19 @@
|
||||
name: Driver Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-**'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-drivers
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
backend_all: ${{ steps.changes.outputs.backend_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
|
||||
|
||||
be-tests-h2:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -57,10 +40,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-athena-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: athena
|
||||
@@ -93,10 +75,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-bigquery-cloud-sdk-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: bigquery-cloud-sdk
|
||||
@@ -128,10 +109,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-druid-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: druid
|
||||
@@ -164,10 +144,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-databricks-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: databricks
|
||||
@@ -199,10 +178,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-druid-jdbc-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: druid-jdbc
|
||||
@@ -235,10 +213,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mysql-mariadb:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -362,10 +339,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mongo:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -415,10 +391,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mongo-ssl:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -483,10 +458,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mongo-sharded-cluster-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mongo
|
||||
@@ -517,10 +491,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-oracle:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -589,10 +562,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-postgres:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -694,10 +666,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-presto-jdbc-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: presto-jdbc
|
||||
@@ -763,21 +734,24 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-redshift-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- name: Redshift Upload Tests
|
||||
skip: ${{ github.event_name == 'pull_request' }} # FIXME: temporary to alleviate pressure on redshift instances
|
||||
test-args: >-
|
||||
:only-tags [:mb/upload-tests]
|
||||
:fail-fast? true
|
||||
- name: Redshift (Excluding Upload Tests)
|
||||
skip: false
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/upload-tests]
|
||||
:fail-fast? true
|
||||
|
||||
env:
|
||||
CI: 'true'
|
||||
@@ -790,6 +764,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.job.name }}
|
||||
if: ${{ !matrix.job.skip }}
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-redshift-ee'
|
||||
@@ -797,7 +772,7 @@ jobs:
|
||||
${{ matrix.job.test-args }}
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
if: ${{ !matrix.job.skip && !cancelled() }}
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
@@ -808,10 +783,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-snowflake-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: snowflake
|
||||
@@ -848,10 +822,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-sparksql-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: sparksql
|
||||
@@ -882,10 +855,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-sqlite-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: sqlite
|
||||
@@ -911,17 +883,16 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-sqlserver:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 90
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: SQL Server 2017
|
||||
junit-name: be-tests-sqlserver-2017-ee
|
||||
image: mcr.microsoft.com/mssql/server:2017-latest
|
||||
# - name: SQL Server 2017
|
||||
# junit-name: be-tests-sqlserver-2017-ee
|
||||
# image: mcr.microsoft.com/mssql/server:2017-latest
|
||||
- name: SQL Server 2022
|
||||
junit-name: be-tests-sqlserver-2022-ee
|
||||
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||
@@ -962,10 +933,9 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-vertica-ee:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.backend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: vertica
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
name: E2E Cross-version Component Tests for Embedding SDK
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
branches:
|
||||
- "release-**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
@@ -66,8 +56,7 @@ jobs:
|
||||
build:
|
||||
needs: [files-changed, get-build-requirements]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
github.event.pull_request.draft == false
|
||||
!cancelled()
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
env:
|
||||
@@ -93,7 +82,7 @@ jobs:
|
||||
- name: Prepare uberjar artifact
|
||||
uses: ./.github/actions/prepare-uberjar-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-cross-version
|
||||
|
||||
resolve-sdk-version:
|
||||
if: |
|
||||
@@ -165,8 +154,13 @@ jobs:
|
||||
|
||||
- name: Move tests from the release commit into the repo
|
||||
run: |
|
||||
rm -rf ./e2e/support/helpers/*
|
||||
mv ./sdk-release-e2e/e2e/support/helpers/* ./e2e/support/helpers
|
||||
# remove this line after we release a new SDK 0.53.x, since it will already contain the fixed version then.
|
||||
mv ./e2e/support/component-webpack.config.js .
|
||||
|
||||
rm -rf ./e2e/support/*
|
||||
mv ./sdk-release-e2e/e2e/support/* ./e2e/support
|
||||
mv ./component-webpack.config.js ./e2e/support
|
||||
|
||||
rm -rf ./e2e/test-component/*
|
||||
mv ./sdk-release-e2e/e2e/test-component/* ./e2e/test-component
|
||||
shell: bash
|
||||
@@ -174,7 +168,7 @@ jobs:
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar-cross-version
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
@@ -192,8 +186,14 @@ jobs:
|
||||
- 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 E2E tests for Embedding SDK
|
||||
run: yarn run test-cypress-run-component-sdk --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
- 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
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
name: E2E Component Tests for Embedding SDK
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
workflow_call:
|
||||
inputs:
|
||||
cached-embedding-sdk-dist-artifact-name:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
@@ -63,7 +58,7 @@ jobs:
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
build:
|
||||
build-uberjar:
|
||||
needs: [files-changed, get-build-requirements]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
@@ -78,15 +73,17 @@ jobs:
|
||||
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 }}"
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
|
||||
|
||||
- name: Build uberjar with ./bin/build.sh
|
||||
run: ./bin/build.sh
|
||||
@@ -97,12 +94,17 @@ jobs:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
e2e-tests:
|
||||
needs: [get-build-requirements, build]
|
||||
needs: [get-build-requirements, build-uberjar]
|
||||
if: |
|
||||
!cancelled() && needs.build.result == 'success'
|
||||
!cancelled() &&
|
||||
needs.build-uberjar.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-tests-embedding-sdk
|
||||
strategy:
|
||||
matrix:
|
||||
react-version: [18, 19]
|
||||
fail-fast: false
|
||||
name: React ${{ matrix.react-version }}
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
@@ -111,6 +113,7 @@ jobs:
|
||||
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
|
||||
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
|
||||
@@ -132,10 +135,10 @@ jobs:
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare JDK ${{ needs.get-build-requirements.outputs.java_version || 11 }}
|
||||
- name: Prepare JDK ${{ needs.get-build-requirements.outputs.java_version || 21 }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ needs.get-build-requirements.outputs.java_version || 11 }}
|
||||
java-version: ${{ needs.get-build-requirements.outputs.java_version || 21 }}
|
||||
distribution: "temurin"
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
@@ -147,22 +150,31 @@ jobs:
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
shell: bash
|
||||
- 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: Build Embedding SDK package
|
||||
run: yarn build-embedding-sdk
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
- name: Run E2E tests for Embedding SDK
|
||||
run: yarn run test-cypress-run-component-sdk --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
- name: Make app db snapshot
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
# Has to be run after the app db snapshot is made, as it is not compatible with React 19
|
||||
- name: Change React version to ${{ matrix.react-version }}
|
||||
run: ./bin/embedding-sdk/change-react-version.bash ${{ matrix.react-version }}
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
run: node e2e/runner/run_cypress_ci.js component --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-embedding-sdk
|
||||
output-name: e2e-embedding-sdk-react-${{ matrix.react-version }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
@@ -173,7 +185,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-embedding-sdk-latest
|
||||
name: cypress-recording-embedding-sdk-react-${{ matrix.react-version }}
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
@@ -195,15 +207,3 @@ jobs:
|
||||
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
|
||||
steps:
|
||||
- run: |
|
||||
echo "Didn't run due to conditional filtering"
|
||||
|
||||
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 }}
|
||||
2
.github/workflows/e2e-cross-version.yml
vendored
@@ -126,7 +126,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/Hydrogen # 18.x.x
|
||||
node-version: lts/Jod # 22.x.x
|
||||
- run: npm install @slack/web-api
|
||||
- name: notify slack of failure
|
||||
uses: actions/github-script@v7
|
||||
|
||||
4
.github/workflows/e2e-matrix-builder.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version: '22.x'
|
||||
- uses: actions/github-script@v7
|
||||
id: matrix
|
||||
env:
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
// see config.js function getSplittableSpecs for logic that excludes some tests from auto splitting
|
||||
const specialTests = [
|
||||
{ name: "embedding-sdk", specs: "./e2e/test/scenarios/embedding-sdk/**.cy.spec.*" },
|
||||
{ name: "oss-subset", edition: 'oss', tags: "@OSS", specs: allSpecs },
|
||||
{ name: "oss-subset", edition: 'oss', tags: "@OSS @smoke+-@EE", specs: allSpecs },
|
||||
{ name: "mongo", tags: "@mongo", specs: allSpecs },
|
||||
];
|
||||
|
||||
|
||||
273
.github/workflows/e2e-sample-apps-tests.yml
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
name: E2E Sample Apps tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
|
||||
env:
|
||||
# we must support last two stable releases
|
||||
SUPPORTED_RELEASES_COUNT: 2
|
||||
TEST_SUITES: |
|
||||
[
|
||||
"metabase-nodejs-react-sdk-embedding-sample-e2e",
|
||||
"metabase-nextjs-sdk-embedding-sample-e2e"
|
||||
]
|
||||
TEST_SUITES_TO_RUN_IN_MASTER_ONLY: |
|
||||
[
|
||||
"shoppy-e2e"
|
||||
]
|
||||
SAMPLE_APP_MAIN_BRANCH_NAME: "main"
|
||||
SAMPLE_APP_STABLE_RELEASE_BRANCH_NAME_SUFFIX: "stable"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
e2e_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() &&
|
||||
contains(github.base_ref || github.ref, 'release-x')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
java_version: ${{ fromJson(steps.dependencies.outputs.result).java_version }}
|
||||
node_version: ${{ fromJson(steps.dependencies.outputs.result).node_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Get build dependencies
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
with:
|
||||
script: | # js
|
||||
const {
|
||||
getBuildRequirements,
|
||||
getVersionFromReleaseBranch,
|
||||
} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const targetBranchName = '${{ github.base_ref || github.ref }}';
|
||||
|
||||
const version = getVersionFromReleaseBranch(targetBranchName);
|
||||
const requirements = getBuildRequirements(version);
|
||||
|
||||
return {
|
||||
java_version: requirements.java,
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
get-sample-apps-data:
|
||||
if: |
|
||||
!cancelled()
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
branch_name: ${{ fromJson(steps.build-sample-apps-data.outputs.result).branch_name }}
|
||||
matrix: ${{ toJSON(fromJson(steps.build-sample-apps-data.outputs.result).matrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
|
||||
- name: Build sample app compatibility data
|
||||
uses: actions/github-script@v7
|
||||
id: build-sample-apps-data
|
||||
with:
|
||||
script: | # js
|
||||
const {
|
||||
isReleaseBranch,
|
||||
getMajorVersionNumberFromReleaseBranch,
|
||||
} = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
|
||||
const testSuites = JSON.parse(process.env.TEST_SUITES);
|
||||
const testSuitesToRunInMasterOnly = JSON.parse(process.env.TEST_SUITES_TO_RUN_IN_MASTER_ONLY);
|
||||
|
||||
const ref = '${{ github.base_ref || github.ref }}';
|
||||
const branchName = ref.replace(/^refs\/heads\//, '');
|
||||
|
||||
const isMainBranchRef = branchName === 'master';
|
||||
const isReleaseBranchRef = isReleaseBranch(branchName);
|
||||
|
||||
if (isMainBranchRef) {
|
||||
return {
|
||||
branch_name: '${{ env.SAMPLE_APP_MAIN_BRANCH_NAME }}',
|
||||
matrix: testSuites,
|
||||
};
|
||||
}
|
||||
|
||||
if (isReleaseBranchRef) {
|
||||
const supportedReleasesCount = Number(${{ env.SUPPORTED_RELEASES_COUNT }});
|
||||
|
||||
const currentReleaseVersion = Number(${{ vars.CURRENT_VERSION }});
|
||||
const currentBranchVersion = Number(getMajorVersionNumberFromReleaseBranch(branchName));
|
||||
|
||||
const sampleAppBranchName = `${currentBranchVersion}-${{ env.SAMPLE_APP_STABLE_RELEASE_BRANCH_NAME_SUFFIX }}`
|
||||
|
||||
const shouldRunTestsForVersion = (currentReleaseVersion - currentBranchVersion) <= (supportedReleasesCount - 1);
|
||||
const releaseBranchTestSuites = testSuites.filter(testSuite => !testSuitesToRunInMasterOnly.includes(testSuite));
|
||||
|
||||
return {
|
||||
branch_name: sampleAppBranchName,
|
||||
matrix: shouldRunTestsForVersion
|
||||
? releaseBranchTestSuites
|
||||
: []
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
branch_name: '',
|
||||
matrix: []
|
||||
};
|
||||
|
||||
build:
|
||||
needs: [files-changed, get-build-requirements, get-sample-apps-data]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.files-changed.outputs.e2e_all == 'true' &&
|
||||
fromJSON(needs.get-sample-apps-data.outputs.matrix)[0] != null
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
INTERACTIVE: false
|
||||
# make sure that builds on release branches get licenses, because we use them for releases
|
||||
SKIP_LICENSES: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
with:
|
||||
node-version: "${{ needs.get-build-requirements.outputs.node_version }}"
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: uberjar
|
||||
java-version: "${{ needs.get-build-requirements.outputs.java_version || 21 }}"
|
||||
|
||||
- name: Build uberjar with ./bin/build.sh
|
||||
run: ./bin/build.sh
|
||||
|
||||
- name: Prepare uberjar artifact
|
||||
uses: ./.github/actions/prepare-uberjar-artifact
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
shell: bash
|
||||
|
||||
- name: Build Embedding SDK package
|
||||
run: yarn build-embedding-sdk
|
||||
|
||||
- name: Prepare Embedding SDK artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: resources/embedding-sdk
|
||||
|
||||
e2e-tests:
|
||||
needs: [get-build-requirements, get-sample-apps-data, build]
|
||||
strategy:
|
||||
matrix:
|
||||
test_suite: ${{ fromJSON(needs.get-sample-apps-data.outputs.matrix) }}
|
||||
if: |
|
||||
!cancelled() && needs.build.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-sample-app-${{ matrix.test_suite }}-tests
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
DISPLAY: ""
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
|
||||
HASH: ${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_attempt }}
|
||||
# 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 || '' }}
|
||||
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
|
||||
# Sample Apps testing variables
|
||||
CYPRESS_CI: true
|
||||
TEST_SUITE: ${{ matrix.test_suite }}
|
||||
SAMPLE_APP_BRANCH_NAME: ${{ needs.get-sample-apps-data.outputs.branch_name }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-ee-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: ${{ github.workspace }}/resources/embedding-sdk
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Check if sample app branch exists
|
||||
id: check-sample-app-branch
|
||||
run: |
|
||||
REPO_NAME="${TEST_SUITE%-e2e}"
|
||||
REPO_URL="https://github.com/metabase/${REPO_NAME}.git"
|
||||
echo "Checking if branch '${SAMPLE_APP_BRANCH_NAME}' exists in ${REPO_URL}..."
|
||||
if git ls-remote --exit-code --heads "$REPO_URL" "${SAMPLE_APP_BRANCH_NAME}"; then
|
||||
echo "sample_app_branch_exists=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "sample_app_branch_exists=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Prepare and launch Sample Apps
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
|
||||
run: npx tsx ./e2e/runner/start-sample-app-containers/start-ci.ts
|
||||
|
||||
- name: Run e2e tests for Sample App
|
||||
id: run-e2e-tests
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
|
||||
run: node e2e/runner/run_cypress_ci.js ${{ matrix.test_suite }} --browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ steps.check-sample-app-branch.outputs.branch_exists == 'true' && steps.run-e2e-tests.outcome != 'success' }}
|
||||
with:
|
||||
name: cypress-recording-sample-app-${{ matrix.test_suite }}-latest
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
@@ -97,12 +97,16 @@ jobs:
|
||||
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
|
||||
run: node e2e/runner/run_cypress_ci.js snapshot
|
||||
|
||||
# For all options and fine-grained control, take a look at the documentation
|
||||
# https://github.com/cypress-io/cypress/tree/develop/npm/grep
|
||||
- name: Stress-test ${{ github.event.inputs.spec }} ${{ github.event.inputs.burn_in }} times
|
||||
run: |
|
||||
yarn run test-cypress-run \
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--spec '${{ github.event.inputs.spec }}' \
|
||||
--env burn=${{ github.event.inputs.burn_in }},grep='${{ github.event.inputs.grep }}' \
|
||||
--config-file e2e/support/cypress-stress-test.config.js \
|
||||
|
||||
33
.github/workflows/e2e-test.yml
vendored
@@ -48,8 +48,10 @@ jobs:
|
||||
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
|
||||
MB_SNOWPLOW_AVAILABLE: true
|
||||
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
|
||||
MB_SEARCH_ENGINE: "appdb"
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
CYPRESS_QA_DB_MONGO: ${{ inputs.name == 'mongo' }}
|
||||
@@ -97,6 +99,18 @@ jobs:
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Download failed test artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
if: github.run_attempt != '1'
|
||||
continue-on-error: true
|
||||
id: failed-specs
|
||||
with:
|
||||
name: failed-tests-${{ inputs.name }}-${{ inputs.edition }}
|
||||
|
||||
- name: Get failed specs
|
||||
if: github.run_attempt != '1'
|
||||
run: echo "specs=$(cat failed-specs)" >> $GITHUB_ENV
|
||||
|
||||
- name: Run Snowplow micro
|
||||
uses: ./.github/actions/run-snowplow-micro
|
||||
|
||||
@@ -116,19 +130,25 @@ jobs:
|
||||
SPLIT_TIME_THRESHOLD: 5.0
|
||||
# do not write cypress-split summary to github
|
||||
SPLIT_SUMMARY: false
|
||||
SPEC_PATH: ${{ env.specs || inputs.specs }}
|
||||
shell: bash
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js test \
|
||||
echo "Running tests with specs: $SPEC_PATH"
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="-@mongo+-@flaky+-@OSS --@quarantine",grepOmitFiltered=true \
|
||||
--spec "$SPEC_PATH" \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run Tagged EE Cypress tests on ${{ inputs.name }}
|
||||
if: ${{ inputs.specs }}
|
||||
shell: bash
|
||||
env:
|
||||
SPEC_PATH: ${{ env.specs || inputs.specs }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js test \
|
||||
echo "Running tests with specs: $SPEC_PATH"
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="${{inputs.tags}} --@quarantine",grepOmitFiltered=true \
|
||||
--spec '${{ inputs.specs }}' \
|
||||
--spec "$SPEC_PATH" \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Upload Test Results
|
||||
@@ -153,6 +173,13 @@ jobs:
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Upload Failed Tests
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: failed-tests-${{ inputs.name }}-${{ inputs.edition }}
|
||||
path: ./cypress/test-results
|
||||
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
|
||||
35
.github/workflows/e2e-tests.yml
vendored
@@ -1,33 +1,17 @@
|
||||
name: E2E Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-e2e
|
||||
cancel-in-progress: true
|
||||
|
||||
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
|
||||
|
||||
e2e-matrix-builder:
|
||||
uses: ./.github/workflows/e2e-matrix-builder.yml
|
||||
with:
|
||||
@@ -65,11 +49,8 @@ jobs:
|
||||
};
|
||||
|
||||
build:
|
||||
needs: [files-changed, get-build-requirements]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
github.event.pull_request.draft == false &&
|
||||
needs.files-changed.outputs.e2e_all == 'true'
|
||||
needs: [get-build-requirements]
|
||||
if: ${{ !cancelled() && !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
strategy:
|
||||
|
||||
141
.github/workflows/embedding-sdk.yml
vendored
@@ -13,10 +13,30 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
sdk-build:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'embedding-sdk-build')
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
documentation: ${{ steps.changes.outputs.documentation }}
|
||||
frontend_embedding_sdk_sources: ${{ steps.changes.outputs.frontend_embedding_sdk_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
|
||||
|
||||
build:
|
||||
needs: [files-changed]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
(needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' ||
|
||||
needs.files-changed.outputs.documentation == 'true')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
@@ -25,15 +45,110 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: "cljs"
|
||||
- name: Build SDK bundle
|
||||
run: yarn run build-embedding-sdk
|
||||
- name: Generate SDK package.json
|
||||
run: yarn run embedding-sdk:generate-package "$GITHUB_SHA"
|
||||
- name: Generate .npmrc for Github npm registry
|
||||
uses: actions/setup-node@v4
|
||||
- name: Build Embedding SDK package
|
||||
run: yarn build-embedding-sdk
|
||||
- name: Prepare Embedding SDK artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
registry-url: "https://npm.pkg.github.com"
|
||||
- name: Publish package
|
||||
run: yarn run embedding-sdk:publish
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: resources/embedding-sdk
|
||||
|
||||
embedding-sdk-cli-snippets-type-check:
|
||||
needs: [files-changed, build]
|
||||
if: |
|
||||
needs.files-changed.outputs.frontend_embedding_sdk_sources == 'true' &&
|
||||
needs.build.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: ${{ github.workspace }}/resources/embedding-sdk
|
||||
- name: Run frontend embedding SDK snippets type check
|
||||
run: yarn run embedding-sdk:docs-snippets:type-check
|
||||
|
||||
embedding-sdk-docs-snippets-type-check:
|
||||
needs: [files-changed, build]
|
||||
if: |
|
||||
needs.files-changed.outputs.documentation == 'true' &&
|
||||
needs.build.result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: ${{ github.workspace }}/resources/embedding-sdk
|
||||
- name: Run frontend embedding SDK snippets type check
|
||||
run: yarn run embedding-sdk:cli-snippets:type-check
|
||||
|
||||
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-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:
|
||||
fe-ref: ${{ github.base_ref }}
|
||||
be-ref: ${{ github.head_ref }}
|
||||
|
||||
embedding-sdk-tests-result:
|
||||
needs:
|
||||
- embedding-sdk-docs-snippets-type-check
|
||||
- embedding-sdk-cli-snippets-type-check
|
||||
- sdk-e2e-tests
|
||||
- e2e-component-tests-embedding-sdk-cross-version
|
||||
if: always() && !cancelled()
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
steps:
|
||||
- name: Check embedding SDK job status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
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('All jobs are skipped or successful');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// otherwise, something failed
|
||||
console.log('Some embedding SDK jobs failed');
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success') {
|
||||
console.log(`${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
process.exit(1);
|
||||
|
||||
43
.github/workflows/frontend.yml
vendored
@@ -1,37 +1,19 @@
|
||||
name: Frontend
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-frontend
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 3
|
||||
outputs:
|
||||
frontend_all: ${{ steps.changes.outputs.frontend_all }}
|
||||
e2e_specs: ${{ steps.changes.outputs.e2e_specs }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
fe-lint:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.frontend_all == 'true' || needs.files-changed.outputs.e2e_specs == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
@@ -44,14 +26,15 @@ jobs:
|
||||
m2-cache-key: "cljs"
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
- name: Find CSS variables that are used but never defined
|
||||
run: yarn find-never-defined-css-variables
|
||||
- name: Run Prettier formatting linter
|
||||
run: yarn run lint-prettier-pure
|
||||
- name: Run ESLint linter
|
||||
run: yarn run lint-eslint-pure
|
||||
|
||||
fe-type-check:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.frontend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
@@ -68,8 +51,7 @@ jobs:
|
||||
name: Check types
|
||||
|
||||
fe-tests-unit:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.frontend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -109,8 +91,7 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
fe-tests-timezones:
|
||||
needs: files-changed
|
||||
if: github.event.pull_request.draft == false && needs.files-changed.outputs.frontend_all == 'true'
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
|
||||
2
.github/workflows/links.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
uses: lycheeverse/lychee-action@v2
|
||||
with:
|
||||
fail: false
|
||||
args: docs --config ./.lychee/config.toml --verbose --no-progress
|
||||
args: docs --include-fragments --config ./.lychee/config.toml --verbose --no-progress
|
||||
format: markdown
|
||||
|
||||
- name: Create Issue From File
|
||||
|
||||
22
.github/workflows/loki-update.yml
vendored
@@ -45,17 +45,35 @@ jobs:
|
||||
echo "Storybook is being served!"
|
||||
|
||||
- name: Update Loki Snapshots
|
||||
run: yarn test-visual:loki-update
|
||||
run: yarn test-visual:loki || yarn test-visual:loki-approve-diff
|
||||
|
||||
- name: Check for changes
|
||||
id: check-changes
|
||||
run: |
|
||||
if [[ -n "$(git status --porcelain .loki/reference/)" ]]; then
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Commit and Push Changes
|
||||
if: steps.check-changes.outputs.has_changes == 'true'
|
||||
run: |
|
||||
git config --global user.name "Metabase Automation"
|
||||
git config --global user.email "github-automation@metabase.com"
|
||||
git add .loki/**
|
||||
git add .loki/reference/
|
||||
git commit -m "Update Loki Snapshots"
|
||||
git push
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
- name: Notify No Changes
|
||||
if: steps.check-changes.outputs.has_changes == 'false'
|
||||
uses: ./.github/actions/notify-pull-request
|
||||
with:
|
||||
include-log: false
|
||||
message: No visual differences were found. All Loki snapshots are up to date.
|
||||
|
||||
- name: Remove 'loki-update' label
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
|
||||
198
.github/workflows/pre-release.yml
vendored
@@ -35,41 +35,15 @@ jobs:
|
||||
oss_version: ${{ steps.version-properties.outputs.oss_version }}
|
||||
commit: ${{ steps.version-properties.outputs.commit }}
|
||||
steps:
|
||||
- name: find_release_artifact
|
||||
id: find_release_artifact
|
||||
uses: actions/github-script@v7
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const fs = require('fs');
|
||||
sparse-checkout: .github
|
||||
- name: Retrieve test build uberjar artifact for ${{ matrix.edition }}
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: metabase-release-${{ matrix.edition }}-${{ inputs.commit }}-uberjar
|
||||
|
||||
const artifacts = await github.rest.actions.listArtifactsForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: `metabase-release-${{ matrix.edition }}-${{ inputs.commit }}-uberjar`,
|
||||
per_page: 1,
|
||||
});
|
||||
|
||||
if (!artifacts.data?.artifacts?.[0]?.id) {
|
||||
throw new Error(`No artifacts found for ${{ inputs.commit }}`);
|
||||
}
|
||||
|
||||
const artifact_id = artifacts.data.artifacts[0].id;
|
||||
|
||||
const download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: artifact_id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
|
||||
- name: Unzip Metabase artifact containing an uberjar
|
||||
run: unzip mb.zip
|
||||
- name: Extract the version.properties file from the JAR
|
||||
run: |
|
||||
jar xf metabase.jar version.properties
|
||||
- name: Reveal Metabase ${{ matrix.edition }} properties
|
||||
- name: Extract version number from version.properties
|
||||
id: version-properties
|
||||
run: |
|
||||
cat version.properties
|
||||
@@ -168,11 +142,29 @@ jobs:
|
||||
run: |
|
||||
jar xf metabase.jar version.properties
|
||||
mv version.properties resources/
|
||||
- name: Run a few important Cypress tests as sanity check
|
||||
|
||||
- name: Move jar
|
||||
run: mkdir -p ./target/uberjar && cp metabase.jar ./target/uberjar/metabase.jar
|
||||
|
||||
- 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
|
||||
|
||||
- name: Run a few important OSS Cypress tests as sanity check
|
||||
if: ${{ matrix.edition == 'oss' }}
|
||||
run: |
|
||||
mkdir -p ./target/uberjar && cp metabase.jar ./target/uberjar/metabase.jar
|
||||
yarn test-cypress-run \
|
||||
--env grepTags="@smoke",grepOmitFiltered=true \
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="@smoke+-@EE",grepOmitFiltered=true \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run a few important EE Cypress tests as sanity check
|
||||
if: ${{ matrix.edition == 'ee' }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js e2e \
|
||||
--env grepTags="@smoke+-@OSS",grepOmitFiltered=true \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
@@ -186,121 +178,30 @@ jobs:
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
containerize:
|
||||
needs: [release-artifact]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
edition: [oss, ee]
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
outputs:
|
||||
image_tag: ${{ steps.target.outputs.image_tag }}
|
||||
steps:
|
||||
- name: Get the version
|
||||
run: |
|
||||
if [[ "${{ matrix.edition }}" == "ee" ]]; then
|
||||
echo "VERSION=${{ needs.release-artifact.outputs.ee_version }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "VERSION=${{ needs.release-artifact.outputs.oss_version }}" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.release-artifact.outputs.commit }}
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Retrieve uberjar artifact
|
||||
with:
|
||||
name: metabase-test-${{ matrix.edition }}-uberjar
|
||||
- name: Move the Uberjar to the context dir
|
||||
run: mv metabase.jar bin/docker/.
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
driver-opts: network=host
|
||||
- name: Build ${{ matrix.edition }} container
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: linux/amd64
|
||||
network: host
|
||||
tags: localhost:5000/local-metabase:${{ env.VERSION }}
|
||||
no-cache: true
|
||||
push: true
|
||||
containerize-oss:
|
||||
needs: release-artifact
|
||||
uses: ./.github/workflows/containerize-jar.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
artifact-name: metabase-release-oss-${{ inputs.commit }}-uberjar
|
||||
commit: ${{ inputs.commit }}
|
||||
repo: ${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}
|
||||
tag: ${{ needs.release-artifact.outputs.oss_version }}-${{ inputs.commit }}
|
||||
|
||||
- name: Launch container
|
||||
run: docker run --rm -dp 3000:3000 localhost:5000/local-metabase:${{ env.VERSION }}
|
||||
timeout-minutes: 5
|
||||
- name: Wait for Metabase to start
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
|
||||
- name: Retag and push container image to ${{ vars.DOCKERHUB_OWNER }} staging Docker Hub repo
|
||||
id: target
|
||||
run: |
|
||||
# SHA-1 truncated to 7 digits should be enough
|
||||
SHORT_HASH=${COMMIT:0:7}
|
||||
|
||||
IMAGE_TAG=$VERSION-$SHORT_HASH
|
||||
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
|
||||
|
||||
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "Retag local image for staging"
|
||||
docker tag localhost:5000/local-metabase:$VERSION $CONTAINER_IMAGE
|
||||
|
||||
echo "Pushing container image $CONTAINER_IMAGE ..."
|
||||
docker push $CONTAINER_IMAGE
|
||||
|
||||
echo "Finished!"
|
||||
shell: bash
|
||||
env:
|
||||
COMMIT: ${{ needs.release-artifact.outputs.commit }}
|
||||
|
||||
verify-docker-pull:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [release-artifact, containerize]
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
edition: [oss, ee]
|
||||
env:
|
||||
IMAGE_TAG: ${{ needs.containerize.outputs.image_tag }}
|
||||
steps:
|
||||
- name: Login to Docker Hub # authenticated, to avoid being rate-throttled
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
|
||||
- name: Pull the container image
|
||||
run: |
|
||||
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
|
||||
echo "Pulling container image $CONTAINER_IMAGE ..."
|
||||
docker pull $CONTAINER_IMAGE
|
||||
echo "Successful!"
|
||||
- name: Launch container
|
||||
run: |
|
||||
CONTAINER_IMAGE=${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}:$IMAGE_TAG
|
||||
docker run --rm -dp 3000:3000 $CONTAINER_IMAGE
|
||||
timeout-minutes: 5
|
||||
- name: Wait for Metabase to start
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
timeout-minutes: 3
|
||||
containerize-ee:
|
||||
needs: release-artifact
|
||||
uses: ./.github/workflows/containerize-jar.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
artifact-name: metabase-release-ee-${{ inputs.commit }}-uberjar
|
||||
commit: ${{ inputs.commit }}
|
||||
repo: ${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_STAGING_REPO }}
|
||||
tag: ${{ needs.release-artifact.outputs.ee_version }}-${{ inputs.commit }}
|
||||
|
||||
tests-complete-message:
|
||||
if: always()
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [run-sanity-check, check-uberjar-health, verify-docker-pull]
|
||||
needs: [run-sanity-check, check-uberjar-health, containerize-ee, containerize-oss]
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -320,7 +221,8 @@ jobs:
|
||||
const allJobsSuccessful =
|
||||
'${{ needs.run-sanity-check.result }}' === 'success' &&
|
||||
'${{ needs.check-uberjar-health.result }}' === 'success' &&
|
||||
'${{ needs.verify-docker-pull.result }}' === 'success';
|
||||
'${{ needs.containerize-ee.result }}' === 'success' &&
|
||||
'${{ needs.containerize-oss.result }}' === 'success';
|
||||
|
||||
await sendTestsCompleteMessage({
|
||||
owner: context.repo.owner,
|
||||
|
||||
207
.github/workflows/release-embedding-sdk.yml
vendored
@@ -13,6 +13,8 @@ on:
|
||||
- master
|
||||
- release-x.51.x
|
||||
- release-x.52.x
|
||||
- release-x.53.x
|
||||
- release-x.54.x
|
||||
|
||||
concurrency:
|
||||
# We want to ensure only one job is running at a time per branch because
|
||||
@@ -36,12 +38,143 @@ jobs:
|
||||
triggered by: @${{ github.event.sender.login }}
|
||||
EOF
|
||||
|
||||
determine-version:
|
||||
name: Determine SDK version
|
||||
send-slack-notification-start:
|
||||
name: "Send Slack notification: SDK release started"
|
||||
needs: determine-sdk-version
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
slackMessageId: ${{ steps.send-slack-message-start.outputs.result }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
|
||||
- name: Cache build scripts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-scripts
|
||||
path: release/dist/index.cjs
|
||||
|
||||
- name: Send build start message
|
||||
id: send-slack-message-start
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
with:
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const { mentionUserByGithubLogin, sendSlackMessage, githubRunLink } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const sdkVersion = "${{ needs.determine-sdk-version.outputs.sdk_version }}";
|
||||
|
||||
const title = `:rocket: *SDK release: ${sdkVersion}* :rocket:`
|
||||
const runLink = githubRunLink(
|
||||
`:building_construction: _CI Run_ :building_construction:`,
|
||||
context.runId,
|
||||
context.repo.owner,
|
||||
context.repo.repo,
|
||||
);
|
||||
const body = `${runLink} - run by ${mentionUserByGithubLogin("${{ github.event.sender.login }}")}`
|
||||
const sendSlackMessageResponse = await sendSlackMessage({
|
||||
channelName: 'team-embedding-releases',
|
||||
message: `${title}\n${body}`,
|
||||
});
|
||||
|
||||
return sendSlackMessageResponse.message.ts
|
||||
|
||||
# Technically this should be placed last, since it runs after other jobs, but I want to co-locate all Slack jobs
|
||||
send-slack-notification-failure:
|
||||
name: "Send Slack notification: SDK release failed"
|
||||
needs:
|
||||
[
|
||||
"determine-sdk-version",
|
||||
"send-slack-notification-start",
|
||||
"check-git-tag",
|
||||
"test",
|
||||
"build-sdk",
|
||||
"publish-npm",
|
||||
"git-tag",
|
||||
]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
slackMessageId: ${{ needs.send-slack-notification-start.outputs.slackMessageId }}
|
||||
if: ${{ always() && contains(needs.*.result, 'failure') }}
|
||||
steps:
|
||||
- name: Retrieve build scripts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build-scripts
|
||||
path: release/dist/
|
||||
|
||||
- name: Send build failure message
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
with:
|
||||
script: | # js
|
||||
const { githubRunLink, sendSlackReply } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const sdkVersion = "${{ needs.determine-sdk-version.outputs.sdk_version }}";
|
||||
|
||||
const runLink = githubRunLink(
|
||||
`:x: ${sdkVersion} Release has failed`,
|
||||
context.runId,
|
||||
context.repo.owner,
|
||||
context.repo.repo,
|
||||
);
|
||||
sendSlackReply({
|
||||
channelName: 'team-embedding-releases',
|
||||
message: runLink,
|
||||
messageId: ${{ env.slackMessageId}},
|
||||
broadcast: true
|
||||
});
|
||||
|
||||
send-slack-notification-successful:
|
||||
name: "Send Slack notification: SDK release successful"
|
||||
needs:
|
||||
["determine-sdk-version", "send-slack-notification-start", "publish-npm"]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
slackMessageId: ${{ needs.send-slack-notification-start.outputs.slackMessageId }}
|
||||
steps:
|
||||
- name: Retrieve build scripts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build-scripts
|
||||
path: release/dist/
|
||||
|
||||
- name: Send build successful message
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
with:
|
||||
script: | # js
|
||||
const { githubRunLink, sendSlackReply } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
const sdkVersion = "${{ needs.determine-sdk-version.outputs.sdk_version }}";
|
||||
|
||||
const runLink = githubRunLink(
|
||||
`:partydeploy: ${sdkVersion} Release is complete :partydeploy:`,
|
||||
context.runId,
|
||||
context.repo.owner,
|
||||
context.repo.repo,
|
||||
);
|
||||
sendSlackReply({
|
||||
channelName: 'team-embedding-releases',
|
||||
message: runLink,
|
||||
messageId: ${{ env.slackMessageId}},
|
||||
broadcast: true
|
||||
});
|
||||
|
||||
determine-sdk-version:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
outputs:
|
||||
sdk_version: ${{ steps.new-sdk-version.outputs.result }}
|
||||
sdk_version: ${{ fromJson(steps.new-sdk-version.outputs.result).version }}
|
||||
major_version: ${{ fromJson(steps.new-sdk-version.outputs.result).majorVersion }}
|
||||
steps:
|
||||
- name: Check out the code using the provided branch
|
||||
uses: actions/checkout@v4
|
||||
@@ -58,19 +191,25 @@ jobs:
|
||||
id: new-sdk-version
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
result-encoding: string
|
||||
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
|
||||
|
||||
if (!suffix) {
|
||||
return newVersion;
|
||||
return {
|
||||
version: newVersion,
|
||||
majorVersion
|
||||
};
|
||||
}
|
||||
|
||||
return `${newVersion}-${suffix}`
|
||||
return {
|
||||
version: `${newVersion}-${suffix}`,
|
||||
majorVersion
|
||||
}
|
||||
|
||||
- name: Append workflow summary
|
||||
run: |
|
||||
@@ -78,15 +217,16 @@ jobs:
|
||||
**Derived inputs:**
|
||||
|
||||
- \`current_version\`: "${{ steps.current-sdk-version.outputs.sdk_current_version }}"
|
||||
- \`next_version\`: "${{ steps.new-sdk-version.outputs.result }}"
|
||||
- \`next_version\`: "${{ fromJson(steps.new-sdk-version.outputs.result).version }}"
|
||||
- \`major_version\`: "${{ fromJson(steps.new-sdk-version.outputs.result).majorVersion }}"
|
||||
EOF
|
||||
|
||||
check-git-tag:
|
||||
needs: determine-version
|
||||
needs: determine-sdk-version
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
tag: embedding-sdk-${{ needs.determine-version.outputs.sdk_version }}
|
||||
tag: embedding-sdk-${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
steps:
|
||||
- name: Check out the code using the provided branch
|
||||
uses: actions/checkout@v4
|
||||
@@ -127,7 +267,7 @@ jobs:
|
||||
run: yarn embedding-sdk:test-unit
|
||||
|
||||
build-sdk:
|
||||
needs: [test, determine-version]
|
||||
needs: [test, determine-sdk-version]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
@@ -149,7 +289,7 @@ jobs:
|
||||
- name: Bump published npm package version
|
||||
# NOTE: this should happen before "Build SDK bundle" as we inject SDK version into the code during build step
|
||||
run: |
|
||||
./bin/embedding-sdk/release_utils.bash update_package_json_template ${{ needs.determine-version.outputs.sdk_version }}
|
||||
./bin/embedding-sdk/release_utils.bash update_package_json_template ${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
|
||||
- name: Update changelog
|
||||
run: |
|
||||
@@ -174,15 +314,16 @@ jobs:
|
||||
path: ./changelog-diff
|
||||
|
||||
publish-npm:
|
||||
needs: [build-sdk, determine-version]
|
||||
needs: [build-sdk, determine-sdk-version]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
sdk_version: ${{ needs.determine-version.outputs.sdk_version }}
|
||||
sdk_version: ${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
steps:
|
||||
- name: Check out branch to prepare for a SDK version bump PR
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
# when we created version update PR, it would only have diff from 2 files SDK readme and SDK package.json template
|
||||
ref: ${{ inputs.branch}}
|
||||
|
||||
@@ -211,10 +352,6 @@ jobs:
|
||||
|
||||
- name: Create a PR updating readme + published version, and changelog (using GitHub Metabase Automation account)
|
||||
run: |
|
||||
# TODO: Remove this step once github includes the new gh CLI version 2.65.0
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
brew install gh
|
||||
|
||||
git checkout -b update-sdk-version-${{ env.sdk_version }}
|
||||
git commit -a -m 'Update Readme version references and published npm version to ${{ env.sdk_version }}'
|
||||
git push origin HEAD
|
||||
@@ -225,17 +362,11 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Edit PR adding useful information (using Metabase bot app)
|
||||
run: |
|
||||
gh pr edit --add-reviewer @metabase/embedding,albertoperdomo --add-label no-backport
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
- name: Auto approve PR (using GitHub Actions account)
|
||||
run: |
|
||||
@@ -260,32 +391,40 @@ jobs:
|
||||
run: |
|
||||
echo //registry.npmjs.org/:_authToken=${{ secrets.NPM_RELEASE_TOKEN }} > .npmrc
|
||||
# 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"}')[inputs.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"}')[inputs.branch]}}
|
||||
|
||||
- name: Add `latest` tag to the latest release branch (`release-x.52.x`) deployment
|
||||
if: ${{ inputs.branch == 'release-x.52.x' }}
|
||||
- name: Add `latest` tag to the latest release branch (`release-x.54.x`) deployment
|
||||
if: ${{ inputs.branch == 'release-x.54.x' }}
|
||||
working-directory: sdk
|
||||
run: |
|
||||
npm dist-tag add @metabase/embedding-sdk-react@${{ env.sdk_version }} latest
|
||||
|
||||
git-tag:
|
||||
needs: [publish-npm, determine-version]
|
||||
needs: [publish-npm, determine-sdk-version]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
tag: embedding-sdk-${{ needs.determine-version.outputs.sdk_version }}
|
||||
tag: embedding-sdk-${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
stable_tag: embedding-sdk-${{ needs.determine-sdk-version.outputs.major_version }}-stable
|
||||
steps:
|
||||
- name: Check out the code using the provided branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
ref: ${{ inputs.branch }}
|
||||
|
||||
# Lightweight tags don't need committer name and email
|
||||
- name: Create a new git tag
|
||||
- name: Setup git user
|
||||
run: |
|
||||
git tag ${{ env.tag }}
|
||||
git config --global user.email "github-automation@metabase.com"
|
||||
git config --global user.name "Metabase Automation"
|
||||
|
||||
- name: Push the new tag
|
||||
id: push-tag
|
||||
- name: Create and push SDK version tag
|
||||
run: |
|
||||
git tag -a ${{ env.tag }} -m "Tagging SDK version ${{ env.tag }}"
|
||||
git push origin ${{ env.tag }}
|
||||
|
||||
- name: Create and push SDK stable version tag
|
||||
if: ${{ startsWith(inputs.branch, 'release-x.') }}
|
||||
run: |
|
||||
git tag -a -f ${{ env.stable_tag }} -m "Tagging SDK stable version ${{ env.stable_tag }}"
|
||||
git push origin -f ${{ env.stable_tag }}
|
||||
|
||||
202
.github/workflows/release.yml
vendored
@@ -99,7 +99,11 @@ jobs:
|
||||
throw new Error("This version has already been released!", version);
|
||||
}
|
||||
|
||||
return versions;
|
||||
console.log({
|
||||
versions,
|
||||
});
|
||||
|
||||
return versions
|
||||
|
||||
publish-start-message:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -137,41 +141,13 @@ jobs:
|
||||
matrix:
|
||||
edition: [oss, ee]
|
||||
steps:
|
||||
- name: find_release_artifact
|
||||
id: find_release_artifact
|
||||
uses: actions/github-script@v7
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const fs = require('fs');
|
||||
|
||||
const artifacts = await github.rest.actions.listArtifactsForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: `metabase-release-${{ matrix.edition }}-${{ inputs.commit }}-uberjar`,
|
||||
per_page: 1,
|
||||
});
|
||||
|
||||
if (!artifacts.data?.artifacts?.[0]?.id) {
|
||||
throw new Error(`No artifacts found for ${{ inputs.commit }}`);
|
||||
}
|
||||
|
||||
const artifact_id = artifacts.data.artifacts[0].id;
|
||||
|
||||
const download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: artifact_id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
|
||||
- name: unzip uberjar artifact
|
||||
run: unzip mb.zip
|
||||
- name: Verify that this is a valid JAR file
|
||||
run: file --mime-type ./metabase.jar | grep "application/zip"
|
||||
- name: Reveal its version.properties
|
||||
run: jar xf metabase.jar version.properties && cat version.properties
|
||||
sparse-checkout: .github
|
||||
- name: Retrieve test build uberjar artifact for ${{ matrix.edition }}
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
name: metabase-release-${{ matrix.edition }}-${{ inputs.commit }}-uberjar
|
||||
- name: Check JAR version properties
|
||||
run: |
|
||||
# ensure actual jar checksum matches checksum file
|
||||
@@ -247,7 +223,6 @@ jobs:
|
||||
--distribution-id ${{ vars.AWS_CLOUDFRONT_DOWNLOADS_ID }} \
|
||||
--paths /${{ steps.version_path.outputs.result }}/metabase.jar
|
||||
|
||||
|
||||
verify-s3-download:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: upload-to-s3
|
||||
@@ -281,143 +256,29 @@ jobs:
|
||||
- name: Verify Checksum
|
||||
run: grep -q $(sha256sum ./metabase-downloaded.jar) SHA256.sum && echo "checksums match" || exit 1
|
||||
|
||||
containerize:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [check-version, download-uberjar]
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
edition: [oss, ee]
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# checkout the commit that was tagged so we get the right dockerfile
|
||||
ref: ${{ inputs.commit }}
|
||||
fetch-depth: 0 # IMPORTANT! to get all the tags
|
||||
- name: prepare release scripts
|
||||
run: cd release && yarn && yarn build
|
||||
- name: Determine the docker version tag
|
||||
uses: actions/github-script@v7
|
||||
id: canonical_version
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const version = '${{ inputs.version }}';
|
||||
const edition = '${{ matrix.edition }}';
|
||||
containerize-oss:
|
||||
needs: check-version
|
||||
uses: ./.github/workflows/containerize-jar.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
artifact-name: metabase-release-oss-${{ inputs.commit }}-uberjar
|
||||
commit: ${{ inputs.commit }}
|
||||
repo: ${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_REPO }}
|
||||
tag: ${{ needs.check-version.outputs.oss }}
|
||||
|
||||
const canonical_version = edition === 'ee'
|
||||
? '${{ needs.check-version.outputs.ee }}'
|
||||
: '${{ needs.check-version.outputs.oss }}';
|
||||
|
||||
console.log("The canonical version of this Metabase", edition, "edition is", canonical_version);
|
||||
|
||||
return canonical_version;
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Retrieve previously downloaded Uberjar
|
||||
with:
|
||||
name: metabase-${{ matrix.edition }}-uberjar
|
||||
- name: Move the Uberjar to the context dir
|
||||
run: mv ./metabase.jar bin/docker/.
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver-opts: network=host
|
||||
- name: Build ${{ matrix.edition }} container
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: linux/amd64
|
||||
network: host
|
||||
tags: localhost:5000/local-metabase:${{ steps.canonical_version.outputs.result }}
|
||||
no-cache: true
|
||||
push: true
|
||||
|
||||
- name: Launch container
|
||||
run: docker run --rm -dp 3000:3000 localhost:5000/local-metabase:${{ steps.canonical_version.outputs.result }}
|
||||
timeout-minutes: 5
|
||||
- name: Wait for Metabase to start
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Determine the target Docker Hub repository
|
||||
run: |
|
||||
if [[ "${{ matrix.edition }}" == "ee" ]]; then
|
||||
echo "Metabase EE: image is going to be pushed to ${{ github.repository_owner }}/metabase-enterprise"
|
||||
echo "DOCKERHUB_REPO=${{ github.repository_owner }}/metabase-enterprise" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Metabase OSS: image is going to be pushed to ${{ github.repository_owner }}/metabase"
|
||||
echo "DOCKERHUB_REPO=${{ github.repository_owner }}/metabase" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
|
||||
- name: Retag and push container image to Docker Hub
|
||||
run: |
|
||||
echo "Pushing ${{ steps.canonical_version.outputs.result }} to ${{ env.DOCKERHUB_REPO }} ..."
|
||||
docker tag localhost:5000/local-metabase:${{ steps.canonical_version.outputs.result }} ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }}
|
||||
docker push ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }}
|
||||
echo "Finished!"
|
||||
|
||||
verify-docker-pull:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: containerize
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
edition: [oss, ee]
|
||||
steps:
|
||||
- name: Login to Docker Hub # authenticated, to avoid being rate-throttled
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
|
||||
- name: Determine the container image to pull
|
||||
run: |
|
||||
if [[ "${{ matrix.edition }}" = "ee" ]]; then
|
||||
echo "DOCKERHUB_REPO=${{ github.repository_owner }}/metabase-enterprise" >> $GITHUB_ENV
|
||||
else
|
||||
echo "DOCKERHUB_REPO=${{ github.repository_owner }}/metabase" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Determine the docker version tag
|
||||
uses: actions/github-script@v7
|
||||
id: canonical_version
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const version = '${{ inputs.version }}';
|
||||
const edition = '${{ matrix.edition }}';
|
||||
|
||||
const canonical_version = edition === "ee"
|
||||
? version.replace(/^v0\./, "v1.") // always e.g. v1.47.2
|
||||
: version.replace(/^v1\./, "v0."); // always e.g. v0.47.2
|
||||
|
||||
console.log("The canonical version of this Metabase", edition, "edition is", canonical_version);
|
||||
|
||||
return canonical_version;
|
||||
- name: Pull the container image
|
||||
run: |
|
||||
echo "Pulling container image ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }} ..."
|
||||
docker pull ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }}
|
||||
echo "Successful!"
|
||||
- name: Launch container
|
||||
run: docker run --rm -dp 3000:3000 ${{ env.DOCKERHUB_REPO }}:${{ steps.canonical_version.outputs.result }}
|
||||
timeout-minutes: 5
|
||||
- name: Wait for Metabase to start
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
timeout-minutes: 3
|
||||
containerize-ee:
|
||||
needs: check-version
|
||||
uses: ./.github/workflows/containerize-jar.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
artifact-name: metabase-release-ee-${{ inputs.commit }}-uberjar
|
||||
commit: ${{ inputs.commit }}
|
||||
repo: ${{ vars.DOCKERHUB_OWNER }}/${{ vars.DOCKERHUB_REPO }}-enterprise
|
||||
tag: ${{ needs.check-version.outputs.ee }}
|
||||
|
||||
push-tags:
|
||||
permissions: write-all
|
||||
needs: [verify-s3-download, verify-docker-pull, check-version]
|
||||
needs: [verify-s3-download, containerize-oss, containerize-ee, check-version]
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
@@ -476,7 +337,8 @@ jobs:
|
||||
needs:
|
||||
- get-extra-tags
|
||||
- push-tags
|
||||
- verify-docker-pull
|
||||
- containerize-ee
|
||||
- containerize-oss
|
||||
- verify-s3-download
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
4
.github/workflows/rerun-workflows.yml
vendored
@@ -2,7 +2,7 @@ name: Re-run Flaky Workflows
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [Backend, Driver Tests, E2E Tests, Frontend, Backend-Cloverage]
|
||||
workflows: [run-tests]
|
||||
types: [completed]
|
||||
branches: [master, 'release-x.**', 'backport-**']
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/Hydrogen # 18.x.x
|
||||
node-version: lts/Jod # 22.x.x
|
||||
- run: npm install @slack/web-api
|
||||
- name: Trigger a re-run
|
||||
uses: actions/github-script@v7
|
||||
|
||||
88
.github/workflows/run-tests.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: Run tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Check which files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
e2e_all: ${{ steps.changes.outputs.e2e_all }}
|
||||
backend_all: ${{ steps.changes.outputs.backend_all }}
|
||||
frontend_all: ${{ steps.changes.outputs.frontend_all }}
|
||||
frontend_sources: ${{ steps.changes.outputs.frontend_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
|
||||
|
||||
static-viz-files-changed:
|
||||
name: Check whether static-viz files changed
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
static_viz: ${{ steps.static_viz.outputs.static_viz }}
|
||||
steps:
|
||||
- uses: actions/checkout@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: "files-changed"
|
||||
- name: Build static-viz frontend
|
||||
run: yarn build-static-viz
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Upload Static Viz Bundle Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-viz-bundle-${{ github.sha }}
|
||||
path: resources/frontend_client/app/dist
|
||||
|
||||
- name: Check for static viz changes
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: static_viz
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/static-viz-sources.yaml
|
||||
|
||||
backend-tests:
|
||||
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' }}
|
||||
|
||||
driver-tests:
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/drivers.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.backend_all != 'true' }}
|
||||
|
||||
frontend-tests:
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/frontend.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.frontend_all != 'true' }}
|
||||
|
||||
e2e-tests:
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/e2e-tests.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.e2e_all != 'true' }}
|
||||
17
.github/workflows/snyk.yml
vendored
@@ -29,12 +29,23 @@ jobs:
|
||||
- uses: snyk/actions/setup@0.4.0
|
||||
- name: Generate all pom.xml
|
||||
run: .github/scripts/write-poms.sh
|
||||
- name: Run snyk
|
||||
- name: Run snyk main project
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
continue-on-error: true
|
||||
run: snyk test --all-projects --sarif-file-output=snyk.sarif
|
||||
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.sarif
|
||||
sarif_file: snyk-main.sarif
|
||||
category: main-project
|
||||
- name: Run snyk drivers
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
continue-on-error: true
|
||||
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: modules/snyk-drivers.sarif
|
||||
category: drivers
|
||||
|
||||
52
.github/workflows/tag-for-release.yml
vendored
@@ -135,6 +135,9 @@ jobs:
|
||||
env:
|
||||
INTERACTIVE: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github
|
||||
- name: Get correct version number
|
||||
run: | # bash
|
||||
if [ '${{matrix.edition}}' == 'ee' ]; then
|
||||
@@ -151,54 +154,9 @@ jobs:
|
||||
java-version: 21
|
||||
|
||||
- name: Retrieve test build uberjar artifact for ${{ matrix.edition }}
|
||||
id: find_release_artifact
|
||||
uses: actions/github-script@v7
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const fs = require('fs');
|
||||
|
||||
const artifacts = await github.rest.actions.listArtifactsForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: `metabase-${{ matrix.edition }}-${{ inputs.commit }}-uberjar`,
|
||||
per_page: 1,
|
||||
});
|
||||
|
||||
if (!artifacts.data?.artifacts?.[0]?.id) {
|
||||
console.warn(`No existing artifacts found for ${{ inputs.commit }}, need to build jar`);
|
||||
|
||||
if ("${{matrix.edition}}" === 'oss') {
|
||||
// only trigger the build job on OSS, otherwise we'll get double builds
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'build-for-release.yml',
|
||||
ref: 'refs/heads/master',
|
||||
inputs: {
|
||||
commit: '${{ inputs.commit }}',
|
||||
version: '${{ inputs.version }}',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error('aborting in favor of build script');
|
||||
}
|
||||
|
||||
const artifact_id = artifacts.data.artifacts[0].id;
|
||||
|
||||
const download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: artifact_id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
|
||||
- name: Unzip Metabase artifact containing an uberjar
|
||||
run: |
|
||||
unzip mb.zip
|
||||
mv target/uberjar/metabase.jar metabase.jar
|
||||
name: metabase-${{ matrix.edition }}-${{ inputs.commit }}-uberjar
|
||||
|
||||
- name: Update version.properties in jar
|
||||
run: | # sh
|
||||
|
||||
85
.github/workflows/uberjar.yml
vendored
@@ -133,7 +133,7 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: linux/amd64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
network: host
|
||||
tags: localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-${{ matrix.edition }}
|
||||
build-args: |
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
- name: Is Docker running?
|
||||
run: docker ps
|
||||
- name: Wait for Metabase to start and reach 100% health
|
||||
run: while ! curl -s 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
run: while ! curl 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
timeout-minutes: 3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
@@ -154,30 +154,75 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Retag and push images if master (ee)
|
||||
- name: Retag and push images if master (ee) - should be cached at this point
|
||||
if: ${{ (github.ref_name == 'master') && matrix.edition == 'ee' }}
|
||||
run: docker tag localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-ee ${{ github.repository_owner }}/metabase-enterprise-head:latest && docker push ${{ github.repository_owner }}/metabase-enterprise-head:latest
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: linux/amd64,linux/arm64
|
||||
network: host
|
||||
tags: ${{ github.repository_owner }}/metabase-enterprise-head:latest
|
||||
build-args: |
|
||||
GIT_COMMIT_SHA=${{ github.sha }}
|
||||
no-cache: false
|
||||
push: true
|
||||
|
||||
# TODO: remove when we're done testing metabot
|
||||
- name: Retag and push images if metabot-v3-second-try (ee)
|
||||
- name: Retag and push images if metabot-v3-second-try (ee) - should be cached at this point
|
||||
if: ${{ (github.ref_name == 'metabot-v3-second-try') && matrix.edition == 'ee' }}
|
||||
run: docker tag localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-ee ${{ github.repository_owner }}/metabase-enterprise-head:metabot && docker push ${{ github.repository_owner }}/metabase-enterprise-head:metabot
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: linux/amd64,linux/arm64
|
||||
network: host
|
||||
tags: ${{ github.repository_owner }}/metabase-enterprise-head:metabot
|
||||
build-args: |
|
||||
GIT_COMMIT_SHA=${{ github.sha }}
|
||||
no-cache: false
|
||||
push: true
|
||||
|
||||
- name: Retag and push images if master (oss)
|
||||
- name: Retag and push images if master (oss) - should be cached at this point
|
||||
if: ${{ (github.ref_name == 'master') && matrix.edition == 'oss' }}
|
||||
run: docker tag localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-oss ${{ github.repository_owner }}/metabase-head:latest && docker push ${{ github.repository_owner }}/metabase-head:latest
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: linux/amd64,linux/arm64
|
||||
network: host
|
||||
tags: ${{ github.repository_owner }}/metabase-head:latest
|
||||
build-args: |
|
||||
GIT_COMMIT_SHA=${{ github.sha }}
|
||||
no-cache: false
|
||||
push: true
|
||||
|
||||
- name: Retag and push images if master (ee-extra)
|
||||
- name: Retag and push images if master (ee-extra) - should be cached at this point
|
||||
if: ${{ (github.ref_name == 'master') && matrix.edition == 'ee-extra' }}
|
||||
run: docker tag localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-ee-extra ${{ secrets.METABASE_EE_EXTRA_CONTAINER_REGISTRY }}/metabase-enterprise-head:latest && docker push ${{ secrets.METABASE_EE_EXTRA_CONTAINER_REGISTRY }}/metabase-enterprise-head:latest
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: linux/amd64,linux/arm64
|
||||
network: host
|
||||
tags: ${{ secrets.METABASE_EE_EXTRA_CONTAINER_REGISTRY }}/metabase-enterprise-head:latest
|
||||
build-args: |
|
||||
GIT_COMMIT_SHA=${{ github.sha }}
|
||||
no-cache: false
|
||||
push: true
|
||||
|
||||
- name: Retag and push images if dev branch
|
||||
if: ${{ !(startsWith(github.ref_name,'master') || startsWith(github.ref_name,'backport')) && matrix.edition == 'ee' }}
|
||||
run: docker tag localhost:5000/metabase-dev:${{ steps.extract_branch.outputs.branch }}-ee ${{ github.repository_owner }}/metabase-dev:${{ steps.extract_branch.outputs.branch }} && docker push ${{ github.repository_owner }}/metabase-dev:${{ steps.extract_branch.outputs.branch }}
|
||||
- name: Retag and push images if dev branch - should be cached at this point
|
||||
if: ${{ !(startsWith(github.ref_name,'master') || startsWith(github.ref_name,'backport')) && matrix.edition == 'ee-extra' }}
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: bin/docker/.
|
||||
platforms: linux/amd64,linux/arm64
|
||||
network: host
|
||||
tags: ${{ github.repository_owner }}/metabase-dev:${{ steps.extract_branch.outputs.branch }}
|
||||
build-args: |
|
||||
GIT_COMMIT_SHA=${{ github.sha }}
|
||||
no-cache: false
|
||||
push: true
|
||||
|
||||
- name: Run Trivy vulnerability scanner if master (ee)
|
||||
if: ${{ (github.ref_name == 'master') && matrix.edition == 'ee' }}
|
||||
uses: aquasecurity/trivy-action@0.28.0
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
env:
|
||||
TRIVY_OFFLINE_SCAN: true
|
||||
with:
|
||||
@@ -188,7 +233,7 @@ jobs:
|
||||
|
||||
- name: Run Trivy vulnerability scanner if master (oss)
|
||||
if: ${{ (github.ref_name == 'master') && matrix.edition == 'oss' }}
|
||||
uses: aquasecurity/trivy-action@0.28.0
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
env:
|
||||
TRIVY_OFFLINE_SCAN: true
|
||||
with:
|
||||
@@ -199,7 +244,7 @@ jobs:
|
||||
|
||||
- name: Run Trivy vulnerability scanner if dev branch
|
||||
if: ${{ !(startsWith(github.ref_name,'master') || startsWith(github.ref_name,'backport')) && matrix.edition == 'ee' }}
|
||||
uses: aquasecurity/trivy-action@0.28.0
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
env:
|
||||
TRIVY_OFFLINE_SCAN: true
|
||||
with:
|
||||
@@ -210,19 +255,19 @@ jobs:
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab if master (ee)
|
||||
if: ${{ (github.ref_name == 'master') && matrix.edition == 'ee' }}
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab if master (oss)
|
||||
if: ${{ (github.ref_name == 'master') && matrix.edition == 'oss' }}
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab if dev branch
|
||||
if: ${{ !(startsWith(github.ref_name,'master') || startsWith(github.ref_name,'backport')) && matrix.edition == 'ee' }}
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
|
||||
@@ -285,7 +330,7 @@ jobs:
|
||||
- name: Is Docker with Ubuntu running?
|
||||
run: docker ps
|
||||
- name: Wait for Ubuntu-based Metabase container to start and reach 100% health
|
||||
run: while ! curl -s 'http://localhost:3001/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
run: while ! curl 'http://localhost:3001/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
timeout-minutes: 3
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ github.ref_name == 'master' }}
|
||||
|
||||
9
.gitignore
vendored
@@ -36,8 +36,12 @@
|
||||
/crate-*
|
||||
/cypress
|
||||
/deploy/artifacts/*
|
||||
/docs/**/node_modules
|
||||
/docs/**/package.json
|
||||
/docs/**/yarn.lock
|
||||
/frontend/test/snapshots/*
|
||||
/e2e/snapshots/*
|
||||
/e2e/tmp/*
|
||||
/lein-plugins/*/target
|
||||
/local
|
||||
/locales/metabase-*.pot
|
||||
@@ -66,6 +70,7 @@
|
||||
/storybook-static
|
||||
/target
|
||||
/test-report-*
|
||||
**/cli-snippets-tmp/**
|
||||
\#*\#
|
||||
bin/node_modules/
|
||||
bin/release/aws-eb/metabase-aws-eb.zip
|
||||
@@ -98,6 +103,7 @@ modules/drivers/*/.lsp/*
|
||||
.clj-kondo/*
|
||||
!.clj-kondo/README.md
|
||||
!.clj-kondo/config.edn
|
||||
!.clj-kondo/config/
|
||||
!.clj-kondo/src/
|
||||
!.clj-kondo/test/
|
||||
!.clj-kondo/macros/
|
||||
@@ -119,3 +125,6 @@ modules/drivers/*/.lsp/*
|
||||
.swc/
|
||||
|
||||
*storybook.log
|
||||
|
||||
# jar downloader
|
||||
/jars/
|
||||
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
BIN
.loki/reference/chrome_laptop_Design_System_Icons_List.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |