Compare commits
588 Commits
sqlglot-sq
...
python-run
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2076d909d9 | ||
|
|
b2c2816d86 | ||
|
|
d8104c911d | ||
|
|
5d5930ab64 | ||
|
|
2738ec81b7 | ||
|
|
cd77eb5140 | ||
|
|
2a4046736a | ||
|
|
660c1ddfe6 | ||
|
|
6845e78a5d | ||
|
|
eea9a750d6 | ||
|
|
123a4f966d | ||
|
|
659d0a37b1 | ||
|
|
59a52b2631 | ||
|
|
a83196324b | ||
|
|
9ab2bfbae8 | ||
|
|
c261122999 | ||
|
|
0b65ccce3a | ||
|
|
bb2adc5f46 | ||
|
|
c5dcdc3238 | ||
|
|
5537f0f258 | ||
|
|
bfdd8a0b23 | ||
|
|
3c05a30b8e | ||
|
|
106a707fcc | ||
|
|
21c8136e7e | ||
|
|
7319599ceb | ||
|
|
13bc693357 | ||
|
|
2b11a3bed2 | ||
|
|
c910ca3fd3 | ||
|
|
d8b40292d1 | ||
|
|
b773f68f23 | ||
|
|
647861b624 | ||
|
|
e741639713 | ||
|
|
5765b0df15 | ||
|
|
40d00b8e7f | ||
|
|
58cde65c4e | ||
|
|
5995621da8 | ||
|
|
70d4a78914 | ||
|
|
cf6e1c032a | ||
|
|
e70f22e0eb | ||
|
|
4fd189509f | ||
|
|
f7fd7fe5c5 | ||
|
|
a0e28bc775 | ||
|
|
a5b4f10c10 | ||
|
|
57502d5ef1 | ||
|
|
b2f981e967 | ||
|
|
fb8f72b7bd | ||
|
|
502b66528c | ||
|
|
2838b6501c | ||
|
|
7603550836 | ||
|
|
ba8074bb87 | ||
|
|
90097715d2 | ||
|
|
12839276af | ||
|
|
2ef536a0fe | ||
|
|
9ad40ab1c3 | ||
|
|
6351e5bc0e | ||
|
|
18d8dadf1b | ||
|
|
d5f606e8f2 | ||
|
|
acbf476d8b | ||
|
|
21af2827f2 | ||
|
|
04b141b975 | ||
|
|
ff9d9acdac | ||
|
|
d7848ee998 | ||
|
|
47c037a3f8 | ||
|
|
ab1ccbf8ec | ||
|
|
8662aaaa87 | ||
|
|
81b10ad959 | ||
|
|
0a67d24d79 | ||
|
|
258305b914 | ||
|
|
21b67b2892 | ||
|
|
3dcbfef5c5 | ||
|
|
1ccf800dee | ||
|
|
d4d83d2f8e | ||
|
|
3e655a0bb2 | ||
|
|
0409c4a20b | ||
|
|
b51c0c9efa | ||
|
|
51f8fb9af1 | ||
|
|
bfb0b42b42 | ||
|
|
09544d950d | ||
|
|
03dd34ec4a | ||
|
|
d08e561420 | ||
|
|
214c38b3f2 | ||
|
|
52ae8a14db | ||
|
|
e153a3290b | ||
|
|
2830b3c94a | ||
|
|
2fa8abc247 | ||
|
|
be2a29d138 | ||
|
|
ab089eb84d | ||
|
|
b40d76f5b4 | ||
|
|
b12dfa4fd9 | ||
|
|
5e2e510811 | ||
|
|
c6a2880b31 | ||
|
|
5cd8e165fa | ||
|
|
05c26a4363 | ||
|
|
8ceeb1fce4 | ||
|
|
49a3037d2a | ||
|
|
fa959aaab4 | ||
|
|
eadbd4b00d | ||
|
|
1da8e5751a | ||
|
|
d47651a2cf | ||
|
|
92001b00de | ||
|
|
186c9dca65 | ||
|
|
42aebbf137 | ||
|
|
df7d834e2e | ||
|
|
bc7ffe094f | ||
|
|
205205174c | ||
|
|
05567456f2 | ||
|
|
9bfb9ed7a4 | ||
|
|
b161e19442 | ||
|
|
c28beea344 | ||
|
|
b22ab2010d | ||
|
|
6f4d71c279 | ||
|
|
c8d5e94ffa | ||
|
|
22a1f275f0 | ||
|
|
ce8660f161 | ||
|
|
dcf260f4c9 | ||
|
|
4897452a08 | ||
|
|
c87ad18e21 | ||
|
|
78a8c59d71 | ||
|
|
336c2a8b1a | ||
|
|
567bbd68bb | ||
|
|
ba6895665f | ||
|
|
09c04d91bb | ||
|
|
8977542307 | ||
|
|
ec678a17fc | ||
|
|
6eda44fc53 | ||
|
|
4219500b7a | ||
|
|
2f2ab17ebb | ||
|
|
89c1712267 | ||
|
|
ba03f8d368 | ||
|
|
6d84d80c06 | ||
|
|
b130d0d812 | ||
|
|
caafceff99 | ||
|
|
5fe95dbbae | ||
|
|
65d9029a55 | ||
|
|
e4ec44c4af | ||
|
|
0e9fd4c48c | ||
|
|
cd32f3b89c | ||
|
|
6d79bbc173 | ||
|
|
7c8125d4bb | ||
|
|
54439261a4 | ||
|
|
18706c2456 | ||
|
|
3d5b673c9a | ||
|
|
0e1325ba1e | ||
|
|
9c801dfbd9 | ||
|
|
c27eee3f51 | ||
|
|
1cfc71fed7 | ||
|
|
619f2b2be6 | ||
|
|
64978428b8 | ||
|
|
58d2cd37b6 | ||
|
|
6901847ed2 | ||
|
|
10286754ee | ||
|
|
8f1606adc5 | ||
|
|
5b6e3071ac | ||
|
|
76e96af834 | ||
|
|
92017bab3f | ||
|
|
c86caa092e | ||
|
|
29590b8e09 | ||
|
|
e822288497 | ||
|
|
a6c90cc7ff | ||
|
|
1571e58188 | ||
|
|
ae1e0d6311 | ||
|
|
3f547462c9 | ||
|
|
05aaad4f3a | ||
|
|
92ec0df7b1 | ||
|
|
35b542b651 | ||
|
|
ab7e4712b1 | ||
|
|
47471f9518 | ||
|
|
86868639b4 | ||
|
|
c61c93e825 | ||
|
|
6dfde80586 | ||
|
|
009911d60a | ||
|
|
237bbe2919 | ||
|
|
c8b1767e66 | ||
|
|
18e327042f | ||
|
|
f02d26aa14 | ||
|
|
8056c1835a | ||
|
|
04055423f0 | ||
|
|
4e7d009a53 | ||
|
|
a41303a180 | ||
|
|
8474a8f7f5 | ||
|
|
2b61cce77b | ||
|
|
0a0625d163 | ||
|
|
bb42762372 | ||
|
|
420c4e27e0 | ||
|
|
7d8f3849bf | ||
|
|
def46a7c0c | ||
|
|
3a69ad8f49 | ||
|
|
3a62030c09 | ||
|
|
8a93fb4fcd | ||
|
|
483117981c | ||
|
|
88f48ce996 | ||
|
|
7eb9d27107 | ||
|
|
c5b8eef4d9 | ||
|
|
9c44b33887 | ||
|
|
3b030bcd58 | ||
|
|
4b9c2e7720 | ||
|
|
57586f2427 | ||
|
|
d813be6b09 | ||
|
|
ccd3e61ff0 | ||
|
|
5e647df5be | ||
|
|
04a5c90dab | ||
|
|
bbc00a8677 | ||
|
|
a43d62a505 | ||
|
|
693ac2a9ce | ||
|
|
d4a888c2ec | ||
|
|
73a53fd72d | ||
|
|
7a88dfba69 | ||
|
|
b6b44f3cd6 | ||
|
|
e586b78513 | ||
|
|
330fb2727e | ||
|
|
ea89a862ec | ||
|
|
0e6915287b | ||
|
|
d0ffa5115d | ||
|
|
86dcec8883 | ||
|
|
b2c736e4ca | ||
|
|
4a3e155ac9 | ||
|
|
0f687a4a6b | ||
|
|
5274ca8dcb | ||
|
|
e0123c6246 | ||
|
|
4301a00c1b | ||
|
|
7dc0929cc1 | ||
|
|
cf40c24b36 | ||
|
|
5c4c8753d7 | ||
|
|
d6540d2d62 | ||
|
|
d7f7d8285f | ||
|
|
cd68964a13 | ||
|
|
5e35a33837 | ||
|
|
1734750c21 | ||
|
|
bb7604b53e | ||
|
|
ea43ff2816 | ||
|
|
e8c624b479 | ||
|
|
97d10fee1c | ||
|
|
5c6250fe4d | ||
|
|
21a620c6e8 | ||
|
|
1e3b836a7b | ||
|
|
a6ae573321 | ||
|
|
14740891cd | ||
|
|
908ca1a1d6 | ||
|
|
94309cd070 | ||
|
|
d54e6b64e2 | ||
|
|
763d5e1777 | ||
|
|
b746306e12 | ||
|
|
f0c90980ff | ||
|
|
e53f226b40 | ||
|
|
40cbf2ffb5 | ||
|
|
55a0afbd2a | ||
|
|
f759d9f473 | ||
|
|
39951a570a | ||
|
|
6b369ef818 | ||
|
|
f3c352d920 | ||
|
|
9c0576cc5c | ||
|
|
fc00027fca | ||
|
|
3be69174e6 | ||
|
|
11a73d8621 | ||
|
|
d1e7b483dd | ||
|
|
aa66ab25d6 | ||
|
|
8207fc3181 | ||
|
|
b429839f0a | ||
|
|
451f080ede | ||
|
|
4f00289729 | ||
|
|
369d410d4d | ||
|
|
b8c13482df | ||
|
|
126eb6b1b1 | ||
|
|
4ab19dce4d | ||
|
|
39b4a21830 | ||
|
|
3ba0f90cf0 | ||
|
|
6e799b2ef6 | ||
|
|
dcd2d7394d | ||
|
|
3c693179e4 | ||
|
|
b01732fd69 | ||
|
|
a75ee49fd1 | ||
|
|
dbfe313b4a | ||
|
|
088bd180d5 | ||
|
|
0226eaa5d2 | ||
|
|
3d68aa1ac4 | ||
|
|
c18c08b02c | ||
|
|
851bedf5bb | ||
|
|
9981cd5323 | ||
|
|
264604b61e | ||
|
|
a5a0773eaf | ||
|
|
c463756c60 | ||
|
|
0f7e8983ca | ||
|
|
3401c9e902 | ||
|
|
0aeda87a3d | ||
|
|
b1ece80c0d | ||
|
|
486a6fc826 | ||
|
|
29f75c1251 | ||
|
|
66dc6b6cdc | ||
|
|
7433f38a22 | ||
|
|
71606de98c | ||
|
|
d2e8fcac5b | ||
|
|
e4d1706837 | ||
|
|
cc1361936a | ||
|
|
69c0a2ca90 | ||
|
|
3d3bbe22db | ||
|
|
ba0ddc9a32 | ||
|
|
da223ea0a1 | ||
|
|
f53c8d1513 | ||
|
|
51d7669d7d | ||
|
|
eb20991259 | ||
|
|
ec4874cfe5 | ||
|
|
8f442ca39b | ||
|
|
8c6e4aa60b | ||
|
|
3d3d49dfca | ||
|
|
bba60088cb | ||
|
|
981113ef47 | ||
|
|
9c8e505c9f | ||
|
|
f4bbf2984f | ||
|
|
273f1677a1 | ||
|
|
12e6b46118 | ||
|
|
f27597b904 | ||
|
|
b2889824b3 | ||
|
|
28cfae2e67 | ||
|
|
60707e20ec | ||
|
|
a4e418f280 | ||
|
|
c20bf2a943 | ||
|
|
c66d31fec1 | ||
|
|
93cb3bc327 | ||
|
|
52325a7503 | ||
|
|
dadf042982 | ||
|
|
18e1fb02aa | ||
|
|
5dd8b26770 | ||
|
|
b6c1a51630 | ||
|
|
09ee4178ca | ||
|
|
a68b644ec8 | ||
|
|
ca002fd953 | ||
|
|
ba5de0f2fa | ||
|
|
b4f717609e | ||
|
|
3f898be45a | ||
|
|
071fc345b4 | ||
|
|
08c4a89f91 | ||
|
|
7d2d6571cb | ||
|
|
f6f2a31b6a | ||
|
|
1342901722 | ||
|
|
a554da88bd | ||
|
|
7fa10713ea | ||
|
|
43e3fa694d | ||
|
|
370a4dff14 | ||
|
|
dc319ca76a | ||
|
|
b08d62e316 | ||
|
|
ba302fc0ef | ||
|
|
716a26ef75 | ||
|
|
f1de74d0d2 | ||
|
|
54bd11c5b9 | ||
|
|
2e3efef978 | ||
|
|
9d9433b5b0 | ||
|
|
f56c4e39cb | ||
|
|
f3fa8226cf | ||
|
|
4376ecc379 | ||
|
|
2c04482d27 | ||
|
|
be492dc637 | ||
|
|
b2fb97a98a | ||
|
|
347f3f11b4 | ||
|
|
d59d58e61a | ||
|
|
c8d8369fec | ||
|
|
ecaff38ac0 | ||
|
|
951f74665d | ||
|
|
acf4530cc6 | ||
|
|
8eccd8a0f9 | ||
|
|
83fdafad01 | ||
|
|
71cfad67bb | ||
|
|
103e9056e1 | ||
|
|
984cf44eca | ||
|
|
d2f7234d7f | ||
|
|
4ab6f9df02 | ||
|
|
4c0c9aa08f | ||
|
|
32a00c99e5 | ||
|
|
559c2f27de | ||
|
|
fd775c440d | ||
|
|
4a79872533 | ||
|
|
127132f8a6 | ||
|
|
140d1adcb7 | ||
|
|
639c8debc9 | ||
|
|
795fa21b9d | ||
|
|
2f043ea9d2 | ||
|
|
0e113a5c68 | ||
|
|
e3ec99133a | ||
|
|
cd8c4b1e98 | ||
|
|
ead4e7d4d9 | ||
|
|
f3fe43bb86 | ||
|
|
e385103a21 | ||
|
|
13681a004a | ||
|
|
a8715223ae | ||
|
|
3cbf8919a7 | ||
|
|
d4702e10e0 | ||
|
|
c8cd828410 | ||
|
|
aa5c870dc4 | ||
|
|
f4aa0cc722 | ||
|
|
764befaffb | ||
|
|
dbced47022 | ||
|
|
224d17965d | ||
|
|
0b644a038f | ||
|
|
65c102daf2 | ||
|
|
5da65b6c52 | ||
|
|
5f66a26a53 | ||
|
|
75db6b5973 | ||
|
|
085fc52dca | ||
|
|
b404c13d0c | ||
|
|
727664ccc6 | ||
|
|
97fa5da0c0 | ||
|
|
a1b8cbca83 | ||
|
|
e0d953e257 | ||
|
|
f405f3d36a | ||
|
|
554bb10750 | ||
|
|
c51eeace1d | ||
|
|
da01813210 | ||
|
|
2a12752ec7 | ||
|
|
7da41b4874 | ||
|
|
7cab7b1458 | ||
|
|
801720f077 | ||
|
|
bd1e91191c | ||
|
|
d4cb2fb735 | ||
|
|
5259af771e | ||
|
|
f3f72f8cb3 | ||
|
|
a46e4da604 | ||
|
|
4568848fb0 | ||
|
|
a6bf8e9326 | ||
|
|
32b8ab866d | ||
|
|
bbf5aebd5a | ||
|
|
55d4145794 | ||
|
|
98f4633388 | ||
|
|
d987764034 | ||
|
|
7bf8c80d42 | ||
|
|
a971f7972b | ||
|
|
cfcba5160e | ||
|
|
04e14dcee2 | ||
|
|
832a04aa91 | ||
|
|
c5591f5ece | ||
|
|
bf1610f1cd | ||
|
|
e1813fedb5 | ||
|
|
3339413f6e | ||
|
|
2958401e6e | ||
|
|
63bae84a2d | ||
|
|
ea30b8973d | ||
|
|
7beb2e3eb1 | ||
|
|
1f5eef83b0 | ||
|
|
8a39fdc126 | ||
|
|
c2aaa12429 | ||
|
|
d38d54ec94 | ||
|
|
e525135245 | ||
|
|
d6da6f1198 | ||
|
|
4d9dc19271 | ||
|
|
93e5f86792 | ||
|
|
b57eb0d2c7 | ||
|
|
c55a5d23a1 | ||
|
|
ce9a7027cf | ||
|
|
ff9690156a | ||
|
|
34fc6597f3 | ||
|
|
6cc10beb56 | ||
|
|
fe3b6f83d7 | ||
|
|
45a8927bb8 | ||
|
|
60b86d85a5 | ||
|
|
9c8bf32532 | ||
|
|
fe325b3de7 | ||
|
|
07fbc6997e | ||
|
|
28585e4119 | ||
|
|
b6b77a19e7 | ||
|
|
890771e40e | ||
|
|
3c724995fb | ||
|
|
3bd26a0179 | ||
|
|
55959131e9 | ||
|
|
9845f0e964 | ||
|
|
7efad1c105 | ||
|
|
82d5e7b7ed | ||
|
|
b69c4d4e26 | ||
|
|
92157c0c06 | ||
|
|
3e5a4dc818 | ||
|
|
72699a1927 | ||
|
|
9f22d94341 | ||
|
|
b7b9d20ad7 | ||
|
|
aa4389bd60 | ||
|
|
e70631b3ab | ||
|
|
52c4ff856d | ||
|
|
7bf638e16e | ||
|
|
c950231ec9 | ||
|
|
3c774eccc8 | ||
|
|
81d20a97b9 | ||
|
|
7c05a09619 | ||
|
|
0a05ffd76a | ||
|
|
e1346eb492 | ||
|
|
1f03ce8fca | ||
|
|
bb9c1a1171 | ||
|
|
2291e7f38b | ||
|
|
d0d678d9d8 | ||
|
|
09ff1de363 | ||
|
|
adb448828b | ||
|
|
a9c4781d78 | ||
|
|
2391e04ea3 | ||
|
|
636dc63968 | ||
|
|
867aa1ae83 | ||
|
|
985082f059 | ||
|
|
6b5577a351 | ||
|
|
8d285e7bee | ||
|
|
fe11f27110 | ||
|
|
3bceaecf2b | ||
|
|
85b82b52f6 | ||
|
|
8e03727b66 | ||
|
|
b5e052b0c9 | ||
|
|
bdb2ce7dae | ||
|
|
17a3afa85e | ||
|
|
50801fc441 | ||
|
|
31516230f5 | ||
|
|
a678c2ff8e | ||
|
|
c4090623a9 | ||
|
|
570952497a | ||
|
|
ef622a4832 | ||
|
|
43daa3b222 | ||
|
|
f0582cb46a | ||
|
|
360ae8c713 | ||
|
|
34837bc9bb | ||
|
|
dccfcc9db1 | ||
|
|
6d1b58aaa2 | ||
|
|
4dcab243b9 | ||
|
|
4501e69eb0 | ||
|
|
b9363e3f75 | ||
|
|
1ad08d2ac9 | ||
|
|
1be2cb8497 | ||
|
|
b9e6c5bc06 | ||
|
|
b806fb5b31 | ||
|
|
f94f6a847a | ||
|
|
f37171d483 | ||
|
|
77cb535614 | ||
|
|
3532b35b00 | ||
|
|
d77ae0cedb | ||
|
|
8239cbfa00 | ||
|
|
dd2b2929a1 | ||
|
|
9cd60a5d77 | ||
|
|
d0cef57a4f | ||
|
|
f2bb215346 | ||
|
|
7a1aec685f | ||
|
|
5474fca0c0 | ||
|
|
ab7baf7ca7 | ||
|
|
d7607ccf8a | ||
|
|
d6082bba08 | ||
|
|
1d0eaf9ca8 | ||
|
|
70878781e4 | ||
|
|
d4229d9cd8 | ||
|
|
00c573febe | ||
|
|
6602db9fd2 | ||
|
|
78d36e43e4 | ||
|
|
88a769d9a8 | ||
|
|
4789ed4666 | ||
|
|
135d176247 | ||
|
|
43b3ecf3e2 | ||
|
|
843350757c | ||
|
|
0464d028b3 | ||
|
|
9c9c18d653 | ||
|
|
38bf8580ea | ||
|
|
ce11a50d64 | ||
|
|
b1212ac263 | ||
|
|
d77f52936e | ||
|
|
f0abb34fe4 | ||
|
|
a2a08af4b1 | ||
|
|
33a572f9a6 | ||
|
|
89bb889883 | ||
|
|
8e33138caf | ||
|
|
355a5fc29c | ||
|
|
50241cb516 | ||
|
|
3fcac725b2 | ||
|
|
e2e2c70165 | ||
|
|
df0622c46a | ||
|
|
05ab4e7919 | ||
|
|
b8ad77a580 | ||
|
|
22c7912005 | ||
|
|
6c73d0a9d4 | ||
|
|
084e3ea6df | ||
|
|
2957fc98c6 | ||
|
|
3286107b15 | ||
|
|
fb97c31b6b | ||
|
|
9529117f1e | ||
|
|
cb119f840b | ||
|
|
551108d317 | ||
|
|
9c5b714279 | ||
|
|
19d9dcb047 | ||
|
|
13a641e649 | ||
|
|
c5fe906f7b | ||
|
|
045a331f78 | ||
|
|
5602bcee19 | ||
|
|
30b32c0c7f | ||
|
|
976f73531b | ||
|
|
98d8aef4f0 | ||
|
|
f3e1558740 | ||
|
|
aa7f30169f | ||
|
|
c5c1d2424d | ||
|
|
744ba25909 | ||
|
|
4c6d90cf00 | ||
|
|
b6427ea107 | ||
|
|
530ca1eab1 |
@@ -12,14 +12,7 @@
|
||||
|
||||
## Testing
|
||||
|
||||
- **Run a test:** `./bin/mage run-tests namespace/test-name`
|
||||
- **Run all tests in a namespace:** `./bin/mage run-tests namespace`
|
||||
- **Run all tests for a module:** `./bin/mage run-tests test/metabase/notification` Because the module lives in that directory.
|
||||
|
||||
Note: the `./bin/mage run-tests` command accepts multiple args, so you can pass
|
||||
`./bin/mage run-tests namespace/test-name namespace/other-test namespace/third-test`
|
||||
to run 3 tests, or
|
||||
`./bin/mage run-tests test/metabase/module1 test/metabase/module2` to run 2 modules.
|
||||
Use `./bin/test-agent` to run backend tests. See the top-level CLAUDE.md for usage.
|
||||
|
||||
## Code Readability
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
## Linting and Formatting
|
||||
|
||||
- **Lint:** `yarn lint-eslint-pure`
|
||||
- **Lint:** `bun run lint-eslint-pure`
|
||||
- Run ESLint on the codebase
|
||||
- **Format:** `yarn prettier`
|
||||
- **Format:** `bun run prettier`
|
||||
- Format code using Prettier
|
||||
- **Type Check:** `yarn type-check-pure`
|
||||
- **Type Check:** `bun run type-check-pure`
|
||||
- Run TypeScript type checking
|
||||
|
||||
## Testing
|
||||
|
||||
### JavaScript/TypeScript Tests
|
||||
|
||||
- **Test a specific file:** `yarn test-unit-keep-cljs path/to/file.unit.spec.js`
|
||||
- **Test by pattern:** `yarn test-unit-keep-cljs -t "pattern"`
|
||||
- **Test a specific file:** `bun run test-unit-keep-cljs path/to/file.unit.spec.js`
|
||||
- **Test by pattern:** `bun run test-unit-keep-cljs -t "pattern"`
|
||||
- Runs tests matching the given pattern
|
||||
|
||||
### ClojureScript Tests
|
||||
|
||||
- **Test ClojureScript:** `yarn test-cljs`
|
||||
- **Test ClojureScript:** `bun run test-cljs`
|
||||
- Run ClojureScript tests
|
||||
|
||||
357
.claude/skills/add-tracing/SKILL.md
Normal file
357
.claude/skills/add-tracing/SKILL.md
Normal file
@@ -0,0 +1,357 @@
|
||||
--b-
|
||||
name: add-tracing
|
||||
description: Add OpenTelemetry tracing spans to Clojure code following Metabase tracing conventions. Use when instrumenting backend code with trace coverage.
|
||||
---
|
||||
|
||||
# Add Tracing Spans to Clojure Code
|
||||
|
||||
This skill helps you add OpenTelemetry (OTel) tracing spans to the Metabase backend codebase using the custom `tracing/with-span` macro.
|
||||
|
||||
## Reference Files
|
||||
|
||||
- `src/metabase/tracing/core.clj` - `with-span` macro, group registry, SDK lifecycle, `best-effort-sanitize-sql`, Pyroscope integration
|
||||
- `src/metabase/task/impl.clj` - `defjob` macro that wraps Quartz jobs with root spans
|
||||
- `.clj-kondo/config/modules/config.edn` - Module boundary configuration
|
||||
|
||||
## Module Architecture
|
||||
|
||||
The tracing module has a deliberately minimal API surface. **Only 2 namespaces are public** (listed in `:api` in the module config):
|
||||
|
||||
| Namespace | Role | Status |
|
||||
|---|---|---|
|
||||
| `tracing.core` | Primary API: `with-span`, groups, SDK lifecycle, Pyroscope, MDC, `best-effort-sanitize-sql` | **Public API** |
|
||||
| `tracing.init` | Side-effect loader for `quartz` and `settings` | **Public API** (init convention) |
|
||||
| `tracing.attributes` | `best-effort-sanitize-sql` implementation (re-exported via `tracing.core`) | Internal |
|
||||
| `tracing.settings` | Setting definitions (`MB_TRACING_*` env vars) | Internal |
|
||||
| `tracing.quartz` | Quartz JDBC proxy + JobListener | Internal |
|
||||
|
||||
**Rules:**
|
||||
- Only require `[metabase.tracing.core :as tracing]` from outside the module. `tracing/best-effort-sanitize-sql` and all other public functions are available from this single namespace.
|
||||
- Do not add new API namespaces. Add new public functions to `tracing.core` instead.
|
||||
- Do not require internal namespaces (`tracing.attributes`, `tracing.settings`, `tracing.quartz`) from outside the module.
|
||||
- `:uses :any` on the `core` module does NOT bypass the target module's `:api` check — internal namespaces are still enforced.
|
||||
|
||||
### Cyclic Dependency Avoidance
|
||||
|
||||
`tracing/core.clj` is required by many modules across the codebase. It **must NOT** compile-time require `tracing.settings`, as this creates transitive cyclic load dependencies (e.g., `settings/core -> tracing/settings -> tracing/core -> events/impl -> events/core`).
|
||||
|
||||
Instead, `tracing/core.clj` uses `requiring-resolve` for settings access:
|
||||
|
||||
```clojure
|
||||
;; CORRECT — lazy runtime resolution, no compile-time dependency
|
||||
((requiring-resolve 'metabase.tracing.settings/tracing-enabled))
|
||||
|
||||
;; WRONG — creates cyclic load dependency
|
||||
(require '[metabase.tracing.settings :as settings])
|
||||
(settings/tracing-enabled)
|
||||
```
|
||||
|
||||
External library namespaces (clj-otel API, SDK, exporters) are safe to require normally — they don't participate in Metabase namespace cycles.
|
||||
|
||||
**Important:** `requiring-resolve` must use **literal quoted symbols**. Kondo hooks validate that `required-namespaces` are all simple symbols, so dynamic construction fails:
|
||||
|
||||
```clojure
|
||||
;; CORRECT — literal quoted symbol
|
||||
(requiring-resolve 'metabase.tracing.settings/tracing-endpoint)
|
||||
|
||||
;; WRONG — kondo hook rejects this: "Assert failed: (every? simple-symbol? required-namespaces)"
|
||||
(requiring-resolve (symbol "metabase.tracing.settings" "tracing-endpoint"))
|
||||
```
|
||||
|
||||
## Quick Checklist
|
||||
|
||||
When adding tracing spans:
|
||||
|
||||
- [ ] Module has `tracing` in its `:uses` set in `.clj-kondo/config/modules/config.edn`
|
||||
- [ ] Added `[metabase.tracing.core :as tracing]` to ns requires (alphabetically sorted)
|
||||
- [ ] Span wraps a meaningful I/O boundary (not pure computation)
|
||||
- [ ] Group matches the domain (check `src/metabase/tracing/core.clj` for registered groups; add a new one if none fit)
|
||||
- [ ] Span name follows dot-notation convention (`"domain.subsystem.operation"`)
|
||||
- [ ] Attributes use namespaced keywords (`:search/query-length`, `:db/id`)
|
||||
- [ ] No sensitive data in attributes (use `best-effort-sanitize-sql` for HoneySQL, never raw SQL)
|
||||
- [ ] No new tracing namespaces created (add to `tracing.core` instead)
|
||||
- [ ] No `DO_NOT_ADD_NEW_FILES_HERE.txt` violations in the target directory
|
||||
- [ ] Run `clj-kondo --lint <files>` to verify 0 errors, 0 warnings
|
||||
- [ ] Add or update tests in the corresponding `test/` path (see Testing section below)
|
||||
- [ ] Run tests: `clojure -X:dev:test :only <test-ns>`
|
||||
|
||||
## The `with-span` Macro
|
||||
|
||||
```clojure
|
||||
(tracing/with-span group span-name attrs & body)
|
||||
```
|
||||
|
||||
- **group** - A keyword selecting which trace group this span belongs to (e.g., `:tasks`, `:sync`)
|
||||
- **span-name** - A string identifying the span in traces (e.g., `"search.execute"`)
|
||||
- **attrs** - A map of span attributes (e.g., `{:db/id 42}`)
|
||||
- **body** - The code to execute inside the span
|
||||
|
||||
**When disabled:** zero overhead -- single atom deref + boolean check, body runs directly.
|
||||
**When enabled:** creates OTel span AND injects `trace_id`/`span_id` into Log4j2 MDC for log-to-trace correlation.
|
||||
|
||||
## Trace Groups
|
||||
|
||||
Groups are registered in `src/metabase/tracing/core.clj`. Check that file for the current list. The general rule: **match the group to the domain, not the call site.** If code runs inside a Quartz job but is logically search work, use `:search`, not `:tasks`.
|
||||
|
||||
To add a new group:
|
||||
|
||||
```clojure
|
||||
;; In src/metabase/tracing/core.clj
|
||||
(register-group! :my-domain "Description of what this covers")
|
||||
```
|
||||
|
||||
Users enable groups via `MB_TRACING_GROUPS=tasks,search,sync` (comma-separated, or `"all"`).
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Span Names
|
||||
|
||||
Use dot-separated hierarchical names: `"domain.subsystem.operation"`. The domain prefix should match the group name:
|
||||
|
||||
```
|
||||
search.execute -- `:search` group
|
||||
sync.fingerprint.table -- `:sync` group
|
||||
task.session-cleanup.delete -- `:tasks` group
|
||||
db-app.collection-items -- `:db-app` group
|
||||
```
|
||||
|
||||
### Attributes
|
||||
|
||||
Use namespaced keywords. The namespace groups related attributes:
|
||||
|
||||
```clojure
|
||||
:db/id -- Database ID (integer)
|
||||
:db/engine -- Database engine name (string)
|
||||
:db/statement -- Sanitized SQL (string, via best-effort-sanitize-sql)
|
||||
:search/engine -- Search engine name (string)
|
||||
:search/query-length -- Query string length (integer)
|
||||
:sync/table -- Table name (string)
|
||||
:sync/step -- Sync step name (string)
|
||||
:task/name -- Task name (string)
|
||||
:http/method -- HTTP method (string)
|
||||
:http/url -- Request URL (string)
|
||||
```
|
||||
|
||||
Invent new namespaced attributes as needed (e.g., `:pulse/id`, `:transform/count`). Keep values as primitives (strings, numbers, booleans) -- no maps or collections.
|
||||
|
||||
## Step-by-Step: Adding a Span
|
||||
|
||||
### 1. Check module boundaries
|
||||
|
||||
Look up the module for your namespace in `.clj-kondo/config/modules/config.edn`. If `tracing` is not in the module's `:uses` set, add it (keep alphabetically sorted):
|
||||
|
||||
```edn
|
||||
my-module
|
||||
{:team "MyTeam"
|
||||
:uses #{analytics config tracing util}}
|
||||
```
|
||||
|
||||
### 2. Add the require
|
||||
|
||||
```clojure
|
||||
(ns metabase.my-module.thing
|
||||
(:require
|
||||
[metabase.tracing.core :as tracing]
|
||||
[metabase.util :as u]))
|
||||
```
|
||||
|
||||
`best-effort-sanitize-sql` is available from `tracing.core` — no additional require needed.
|
||||
|
||||
### 3. Identify the I/O boundary
|
||||
|
||||
Only wrap code at **meaningful I/O boundaries**:
|
||||
|
||||
**DO trace:**
|
||||
- External API calls (embedding APIs, metabot, webhooks)
|
||||
- Database queries (both app DB and user DB)
|
||||
- Network requests (HTTP calls to external services)
|
||||
- Heavy batch processing (batch indexing, batch embedding)
|
||||
- Top-level orchestration functions that coordinate multiple sub-operations
|
||||
|
||||
**DO NOT trace:**
|
||||
- Pure computation (sorting, filtering, mapping)
|
||||
- Simple single-row lookups (`t2/select-one :model/Setting :key k`)
|
||||
- Every function in a call chain (only boundaries matter)
|
||||
- Trivial operations (string formatting, hash calculations)
|
||||
|
||||
### 4. Wrap with `with-span`
|
||||
|
||||
```clojure
|
||||
;; Simple span (no attributes needed)
|
||||
(tracing/with-span :search "search.init-index" {}
|
||||
(do-expensive-thing))
|
||||
|
||||
;; Span with static attributes
|
||||
(tracing/with-span :sync "sync.fingerprint.table"
|
||||
{:db/id (:db_id table)
|
||||
:sync/table (:name table)}
|
||||
(fingerprint-fields! table fields))
|
||||
|
||||
;; Span with computed attributes
|
||||
(tracing/with-span :search "search.execute"
|
||||
{:search/engine (name (:search-engine ctx))
|
||||
:search/query-length (count (:search-string ctx))}
|
||||
(search.engine/results ctx))
|
||||
|
||||
;; Span with sanitized SQL (for dynamic HoneySQL queries)
|
||||
(let [hsql {:delete-from [(t2/table-name :model/Session)]
|
||||
:where [:< :created_at oldest-allowed]}]
|
||||
(tracing/with-span :tasks "task.session-cleanup.delete"
|
||||
{:db/statement (tracing/best-effort-sanitize-sql hsql)}
|
||||
(t2/query-one hsql)))
|
||||
|
||||
;; Sub-spans breaking a function into I/O phases
|
||||
(let [embedding (tracing/with-span :search "search.semantic.embedding"
|
||||
{:search.semantic/provider (:provider model)}
|
||||
(get-embedding model search-string))
|
||||
results (tracing/with-span :search "search.semantic.db-query" {}
|
||||
(into [] xform reducible))]
|
||||
(process results))
|
||||
|
||||
;; Per-item iteration spans
|
||||
(doseq [e (search.engine/active-engines)]
|
||||
(tracing/with-span :search "search.ingestion.update" {:search/engine (name e)}
|
||||
(search.engine/update! e batch)))
|
||||
```
|
||||
|
||||
### 5. Add tests
|
||||
|
||||
Create or update tests in the corresponding `test/` path. Follow the patterns in existing tracing tests:
|
||||
|
||||
- **Reference tests:** `test/metabase/tracing/quartz_test.clj`, `test/metabase/server/middleware/trace_test.clj`
|
||||
- Use `tracing/init-enabled-groups!` / `tracing/shutdown-groups!` with `try`/`finally` to manage group lifecycle
|
||||
- Test both enabled and disabled paths (verify zero overhead when group is off)
|
||||
- Use `reify` mocks for Java interfaces (Connection, PreparedStatement, JobListener, etc.)
|
||||
- Add `(set! *warn-on-reflection* true)` and type-hint proxy/reify calls to avoid reflection warnings
|
||||
|
||||
```clojure
|
||||
(deftest my-span-enabled-test
|
||||
(testing "when group is enabled, span is created"
|
||||
(try
|
||||
(tracing/init-enabled-groups! "my-group" "INFO")
|
||||
;; ... test that span behavior occurs ...
|
||||
(finally
|
||||
(tracing/shutdown-groups!)))))
|
||||
|
||||
(deftest my-span-disabled-test
|
||||
(testing "when group is disabled, code runs without tracing"
|
||||
(tracing/shutdown-groups!)
|
||||
;; ... test that code still works, no wrapping applied ...
|
||||
))
|
||||
```
|
||||
|
||||
### 6. Lint and run tests
|
||||
|
||||
```bash
|
||||
# Lint modified source and test files — expect 0 errors, 0 warnings
|
||||
clj-kondo --lint path/to/modified/file.clj path/to/test/file.clj
|
||||
|
||||
# Run tests (requires Java 21+)
|
||||
clojure -X:dev:test :only my-ns.test-ns
|
||||
```
|
||||
|
||||
Expect: all tests pass, 0 failures, 0 errors, no reflection warnings from your files.
|
||||
|
||||
## Sanitizing SQL for Attributes
|
||||
|
||||
When including SQL in span attributes, **always** use `tracing/best-effort-sanitize-sql`. This converts HoneySQL maps to parameterized SQL strings where values become `?` placeholders -- no data leaks.
|
||||
|
||||
```clojure
|
||||
(let [hsql {:delete-from [:core_session]
|
||||
:where [:< :created_at some-timestamp]}]
|
||||
(tracing/with-span :tasks "task.cleanup.delete"
|
||||
{:db/statement (tracing/best-effort-sanitize-sql hsql)}
|
||||
(t2/query-one hsql)))
|
||||
;; Trace attribute: db/statement = "DELETE FROM core_session WHERE created_at < ?"
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Never put raw SQL strings or user-provided values in attributes
|
||||
- Use `best-effort-sanitize-sql` only for app DB (HoneySQL) queries
|
||||
- For external/user DB queries, trace only timing and counts, not SQL content
|
||||
|
||||
## Defjob and Root Spans
|
||||
|
||||
The `defjob` macro in `metabase.task.impl` automatically wraps every Quartz job with a `:tasks` root span:
|
||||
|
||||
```clojure
|
||||
(task/defjob ^{DisallowConcurrentExecution true} SessionCleanup [_]
|
||||
(cleanup-sessions!))
|
||||
;; Automatically creates span: "task.SessionCleanup" {:task/name "SessionCleanup"}
|
||||
```
|
||||
|
||||
You do NOT need a root span inside `defjob` bodies. Add **child spans** for I/O inside the job.
|
||||
|
||||
For code on plain `Thread`s (not Quartz), add the root span manually:
|
||||
|
||||
```clojure
|
||||
(defn init! []
|
||||
(tracing/with-span :search "search.task.init" {}
|
||||
(search/init-index!)))
|
||||
```
|
||||
|
||||
## What NOT to Do
|
||||
|
||||
### Span Usage Mistakes
|
||||
|
||||
```clojure
|
||||
;; WRONG - pure computation, no I/O
|
||||
(tracing/with-span :search "search.format-results" {}
|
||||
(map format-result results))
|
||||
|
||||
;; WRONG - trivial single-row lookup
|
||||
(tracing/with-span :db-app "db-app.get-setting" {}
|
||||
(t2/select-one :model/Setting :key "my-setting"))
|
||||
|
||||
;; WRONG - raw SQL in attributes (data leak)
|
||||
(tracing/with-span :tasks "task.cleanup" {:db/statement raw-sql-string}
|
||||
(execute! raw-sql-string))
|
||||
|
||||
;; WRONG - wrong group (search work should use :search, not :tasks)
|
||||
(tracing/with-span :tasks "search.execute" {} ...)
|
||||
|
||||
;; WRONG - redundant nesting (do-search already has a span)
|
||||
(tracing/with-span :search "search.process" {}
|
||||
(let [results (do-search ctx)]
|
||||
(tracing/with-span :search "search.format" {}
|
||||
(format-results results))))
|
||||
```
|
||||
|
||||
### Architecture Mistakes
|
||||
|
||||
```clojure
|
||||
;; WRONG - creating a new tracing namespace
|
||||
(ns metabase.tracing.my-feature ...)
|
||||
|
||||
;; WRONG - requiring internal tracing namespaces from outside the module
|
||||
(ns metabase.my-module.thing
|
||||
(:require [metabase.tracing.attributes :as trace-attrs] ;; internal!
|
||||
[metabase.tracing.settings :as tracing.settings] ;; internal!
|
||||
[metabase.tracing.quartz :as tracing.quartz])) ;; internal!
|
||||
|
||||
;; WRONG - adding compile-time requires to tracing/core.clj for settings or SDK
|
||||
;; This creates cyclic load dependencies because tracing/core is widely required
|
||||
(ns metabase.tracing.core
|
||||
(:require [metabase.tracing.settings :as settings])) ;; causes cycle!
|
||||
|
||||
;; WRONG - dynamic symbol construction with requiring-resolve (kondo rejects it)
|
||||
(requiring-resolve (symbol "metabase.tracing.settings" "tracing-enabled"))
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
All settings are env-var-only (defined in `src/metabase/tracing/settings.clj`):
|
||||
|
||||
```bash
|
||||
# Core
|
||||
MB_TRACING_ENABLED=true # Enable tracing (default: false)
|
||||
MB_TRACING_ENDPOINT=host:4317 # OTLP collector endpoint (default: http://localhost:4317)
|
||||
MB_TRACING_GROUPS=tasks,search,sync # Comma-separated groups or "all" (default: all)
|
||||
MB_TRACING_SERVICE_NAME=metabase # Service name in traces (default: hostname)
|
||||
MB_TRACING_LOG_LEVEL=DEBUG # Log threshold for traced threads: TRACE/DEBUG/INFO (default: INFO)
|
||||
|
||||
# Batch span processor tuning
|
||||
MB_TRACING_MAX_QUEUE_SIZE=2048 # Max spans queued for export; drops when full (default: 2048)
|
||||
MB_TRACING_EXPORT_TIMEOUT_MS=10000 # Max wait for batch export to complete (default: 10000)
|
||||
MB_TRACING_SCHEDULE_DELAY_MS=5000 # Delay between consecutive batch exports (default: 5000)
|
||||
```
|
||||
@@ -40,7 +40,7 @@ allowed-tools: Read, Write, Grep, Bash, Glob
|
||||
|
||||
**Format:**
|
||||
|
||||
- Run prettier on the file after making edits: `yarn prettier --write <file-path>`
|
||||
- Run prettier on the file after making edits: `bun run prettier --write <file-path>`
|
||||
- This ensures consistent formatting across all documentation
|
||||
|
||||
### Common patterns
|
||||
|
||||
260
.claude/skills/e2e-test-create/SKILL.md
Normal file
260
.claude/skills/e2e-test-create/SKILL.md
Normal file
@@ -0,0 +1,260 @@
|
||||
---
|
||||
name: e2e-test-create
|
||||
description: >
|
||||
Analyze React component source code to understand UI structure,
|
||||
then generate idiomatic Cypress E2E tests following Metabase conventions.
|
||||
Falls back to Playwright MCP browser exploration only when code reading
|
||||
and screenshot debugging are insufficient.
|
||||
disable-model-invocation: true
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Grep
|
||||
- Glob
|
||||
- Skill
|
||||
- "mcp__playwright__*"
|
||||
---
|
||||
|
||||
# Code-Reading-First → Generate Cypress Tests (Metabase)
|
||||
|
||||
You are writing Cypress E2E tests for the **Metabase** codebase.
|
||||
Before generating ANY test code, you MUST analyze React component source code
|
||||
to understand DOM structure, selectors, and user flows.
|
||||
|
||||
## Phase 0 — Research
|
||||
|
||||
1. Read existing helpers before writing anything:
|
||||
- `e2e/support/helpers/` — all shared helpers (restore, signInAs, openOrdersTable, etc.)
|
||||
- `e2e/support/cypress_sample_database.ts` — table/field schema constants (ORDERS, PRODUCTS, etc.)
|
||||
- `e2e/support/cypress_sample_instance_data.ts` — instance-specific IDs (ORDERS_DASHBOARD_ID, NORMAL_USER_ID, etc.)
|
||||
2. Glob `e2e/test/scenarios/` to find the closest existing spec to the area under test.
|
||||
Study its patterns — match them exactly.
|
||||
3. Glob `frontend/src/metabase/` to find React components for the feature area.
|
||||
|
||||
## Phase 1 — Code Analysis
|
||||
|
||||
Read React component source to understand DOM structure. No browser needed — source code has everything.
|
||||
|
||||
1. **Find relevant components**: Glob and grep `frontend/src/metabase/` for the feature area.
|
||||
2. **Extract selectors**: Grep for `data-testid` in relevant components.
|
||||
3. **Note visible text**: Read component JSX for button labels, headings, placeholders.
|
||||
4. **Note aria attributes**: Grep for `aria-label` in relevant components.
|
||||
5. **Understand user flows**: Read event handlers (onClick, onSubmit, onChange) to understand interactions.
|
||||
6. **Find API calls**: Grep for `Api.use`, `fetch`, `useQuery`, endpoint definitions to identify API calls to intercept.
|
||||
7. **Cross-reference with existing specs**: Find specs in the same area and reuse their proven selectors and `cy.intercept` patterns.
|
||||
|
||||
## Phase 2 — Start Backend
|
||||
|
||||
Use `MB_EDITION=oss` by default. Only use `MB_EDITION=ee` when the user explicitly asks to write an enterprise test.
|
||||
|
||||
Start the backend using `run_in_background: true` (NOT `&`).
|
||||
`bin/e2e-backend` automatically detects if a backend is already running and reuses it.
|
||||
```bash
|
||||
MB_EDITION=oss bin/e2e-backend
|
||||
```
|
||||
|
||||
Do NOT manually generate snapshots by running unrelated test specs.
|
||||
The `bun test-cypress` runner has `GENERATE_SNAPSHOTS: true` by default and automatically
|
||||
generates snapshots before running any spec. When running tests in Phase 4 via the `/e2e-test` skill,
|
||||
snapshots will be generated on the first run if they don't already exist.
|
||||
|
||||
Restore clean test data:
|
||||
```bash
|
||||
curl -sf -X POST http://localhost:4000/api/testing/restore/default
|
||||
```
|
||||
|
||||
## Phase 3 — Generate Cypress Spec
|
||||
|
||||
### File location & naming
|
||||
|
||||
Place specs in `e2e/test/scenarios/<area>/` mirroring the URL structure.
|
||||
Name: `<feature>.cy.spec.js` (NOT `.cy.spec.ts`).
|
||||
|
||||
### Metabase conventions (mandatory)
|
||||
|
||||
- **Helpers via `cy.H`**: All helpers are accessed via `const { H } = cy;` — NOT via direct imports from `e2e/support/helpers`.
|
||||
|
||||
```js
|
||||
const { H } = cy;
|
||||
|
||||
describe("feature name", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- **Sample Database schema** (table/field definitions): Import from `cypress_sample_database`.
|
||||
|
||||
```js
|
||||
import { ORDERS, ORDERS_ID, PRODUCTS } from "e2e/support/cypress_sample_database";
|
||||
```
|
||||
|
||||
- **Instance data** (dashboard IDs, user IDs, etc.): Import from `cypress_sample_instance_data`.
|
||||
|
||||
```js
|
||||
import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data";
|
||||
```
|
||||
|
||||
- **Selectors** (priority order):
|
||||
1. `cy.findByText()` / `cy.findByLabelText()` / `cy.findByRole()` — from `@testing-library/cypress`
|
||||
2. `cy.findByTestId()` — for `data-testid` attributes
|
||||
3. `cy.get("[data-testid='...']")` — fallback
|
||||
4. NEVER use positional selectors, CSS class names, or XPaths.
|
||||
|
||||
- **Navigation helpers**: Use existing helpers like `H.openOrdersTable()`, `H.openNativeEditor()`,
|
||||
`H.visitDashboard(id)`, `H.visitQuestion(id)` instead of raw `cy.visit()` chains.
|
||||
Grep `e2e/support/helpers/` to discover what's available for your area.
|
||||
|
||||
- **API setup over UI setup**: Use `cy.request()` or existing API helpers to set up state.
|
||||
Only use the UI for the flow you're actually testing.
|
||||
|
||||
- **Assertions**: Assert on visible text, URL, aria state — not DOM structure.
|
||||
|
||||
- **Waits**: Never use `cy.wait(ms)`. Use `cy.intercept()` + `cy.wait("@alias")` for API calls,
|
||||
or `cy.findByText().should("be.visible")` for DOM readiness.
|
||||
|
||||
- **Isolation**: Each `it()` block must be independently runnable.
|
||||
Don't depend on state from a previous `it()`.
|
||||
|
||||
### Spec structure template
|
||||
|
||||
```js
|
||||
const { H } = cy;
|
||||
import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data";
|
||||
import { ORDERS, ORDERS_ID } from "e2e/support/cypress_sample_database";
|
||||
|
||||
describe("area > sub-area > feature (#issue-number)", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
});
|
||||
|
||||
it("should do the primary happy-path thing", () => {
|
||||
// test
|
||||
});
|
||||
|
||||
it("should handle the edge case", () => {
|
||||
// test
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Intercepts
|
||||
|
||||
When you identified API calls during code analysis, stub or wait on them:
|
||||
|
||||
```js
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
// ... trigger action ...
|
||||
cy.wait("@dataset");
|
||||
```
|
||||
|
||||
## Phase 4 — Validate
|
||||
|
||||
After generating specs:
|
||||
1. Check that all imported helpers exist (Grep `e2e/support/helpers/`).
|
||||
2. **You MUST use the `/e2e-test` skill** to run tests — do NOT run `bun test-cypress` directly.
|
||||
The `/e2e-test` skill handles edition selection, snapshot management, and correct env vars.
|
||||
`/e2e-test GREP="should do the thing" --spec e2e/test/scenarios/<path>`
|
||||
If you created multiple `it()` blocks, run each one individually to isolate failures.
|
||||
|
||||
## Phase 5 — Fix Failures (up to 2 attempts)
|
||||
|
||||
When a test fails, **try to fix it from Cypress output first**:
|
||||
1. Read the failure screenshot (path printed under `(Screenshots)`).
|
||||
2. Read the error message and code frame from the console output.
|
||||
3. Fix the test and re-run (back to Phase 4, step 2).
|
||||
|
||||
If you cannot diagnose the issue after 2 attempts, proceed to Phase 6.
|
||||
|
||||
## Phase 6 — Playwright Fallback
|
||||
|
||||
Only reach this phase after 2 failed fix attempts from Phase 5. The backend is already running.
|
||||
|
||||
Restore clean test data:
|
||||
```bash
|
||||
curl -sf -X POST http://localhost:4000/api/testing/restore/default
|
||||
```
|
||||
|
||||
**Bypass CSP** headers before navigating (Metabase serves strict CSP that blocks dev server scripts).
|
||||
Use `browser_run_code` to set this up:
|
||||
|
||||
```js
|
||||
async (page) => {
|
||||
// Strip CSP headers so the page loads (mirrors Cypress chromeWebSecurity: false)
|
||||
await page.context().route('**/*', async (route) => {
|
||||
const response = await route.fetch();
|
||||
const headers = { ...response.headers() };
|
||||
delete headers['content-security-policy'];
|
||||
delete headers['content-security-policy-report-only'];
|
||||
await route.fulfill({ response, headers });
|
||||
});
|
||||
|
||||
// Sign in via API
|
||||
const response = await page.request.post('http://localhost:4000/api/session', {
|
||||
data: { username: 'admin@metabase.test', password: '12341234' }
|
||||
});
|
||||
const session = await response.json();
|
||||
await page.context().addCookies([{
|
||||
name: 'metabase.DEVICE',
|
||||
value: session.id,
|
||||
domain: 'localhost',
|
||||
path: '/'
|
||||
}]);
|
||||
|
||||
await page.goto('http://localhost:4000');
|
||||
await page.waitForLoadState('networkidle');
|
||||
return 'signed in';
|
||||
}
|
||||
```
|
||||
|
||||
**Maintain an observation log incrementally.** After EVERY significant Playwright interaction,
|
||||
IMMEDIATELY append what you observed to the scratch file BEFORE performing the next interaction:
|
||||
|
||||
```bash
|
||||
cat >> /tmp/e2e-observations.md << 'OBSERVATION'
|
||||
## [Page/Flow name]
|
||||
- URL: /question/notebook#...
|
||||
- Clicked: "Box plot" button → visible text "Box plot", role: radio
|
||||
- Selectors: data-testid="viz-type-button", findByText("Box plot")
|
||||
- API call: POST /api/dataset (triggered on viz change)
|
||||
- Key state: after selecting viz type, summary sidebar shows metric picker
|
||||
OBSERVATION
|
||||
```
|
||||
|
||||
For each page/flow:
|
||||
- Take an accessibility snapshot (`browser_snapshot`).
|
||||
- Click through interactive elements, fill forms, trigger modals.
|
||||
- **Append to the observation log immediately after each step**: URLs, visible text, aria labels, `data-testid` attrs, API calls.
|
||||
- Screenshot key states.
|
||||
|
||||
After exploration:
|
||||
1. Read back your observation log: `cat /tmp/e2e-observations.md`
|
||||
2. Fix the test using observed selectors and behavior.
|
||||
3. Re-run the test (back to Phase 4).
|
||||
4. Clean up: `rm -f /tmp/e2e-observations.md`
|
||||
|
||||
## Phase 7 — Cleanup
|
||||
|
||||
After all tests pass (or after giving up on fixing failures), **always kill the backend on port 4000**:
|
||||
```bash
|
||||
lsof -ti:4000 | xargs kill 2>/dev/null || true
|
||||
```
|
||||
|
||||
Do NOT use broad `pkill` patterns — there may be other Metabase instances on different ports.
|
||||
The backend process started in Phase 2 will NOT be killed automatically when the Claude session ends.
|
||||
Leaving it running wastes resources and can interfere with future sessions. Always clean up.
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- Do NOT use Playwright as the first step — always analyze source code first.
|
||||
- Do NOT kill the backend between phases — it stays running throughout.
|
||||
- Do NOT invent selectors you didn't find in source code or observe in the browser.
|
||||
- Do NOT hardcode database IDs — import from `cypress_sample_database` or `cypress_sample_instance_data`.
|
||||
- Do NOT use `cy.wait(1000)` or any numeric wait.
|
||||
- Do NOT create setup flows through the UI when an API helper exists.
|
||||
- Do NOT put tests in `cypress/e2e/` — Metabase uses `e2e/test/scenarios/`.
|
||||
- Do NOT import helpers directly from `e2e/support/helpers` — use `const { H } = cy;`.
|
||||
78
.claude/skills/e2e-test/SKILL.md
Normal file
78
.claude/skills/e2e-test/SKILL.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: e2e-test
|
||||
description: Run Cypress E2E tests, analyze failures including screenshots, and stress test for flakiness
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
Run Cypress E2E test: $ARGUMENTS
|
||||
|
||||
## Edition selection
|
||||
|
||||
**Default: `MB_EDITION=oss`** — runs without enterprise tokens, simpler and faster.
|
||||
|
||||
Only use `MB_EDITION=ee` when the user explicitly asks to write or run an enterprise test.
|
||||
|
||||
**For `MB_EDITION=ee`:**
|
||||
These tokens must be exported in the shell before starting Claude Code:
|
||||
- `CYPRESS_MB_ALL_FEATURES_TOKEN` — all features (most EE tests need this)
|
||||
- `CYPRESS_MB_PRO_SELF_HOSTED_TOKEN` — pro self-hosted (permissions, SSO, sandboxing)
|
||||
- `CYPRESS_MB_STARTER_CLOUD_TOKEN` — starter plan (rarely needed)
|
||||
- `CYPRESS_MB_PRO_CLOUD_TOKEN` — pro cloud (rarely needed)
|
||||
|
||||
**NEVER echo, print, or log token values. Only check if they are set:**
|
||||
```bash
|
||||
echo "ALL_FEATURES: ${CYPRESS_MB_ALL_FEATURES_TOKEN:+set}" && echo "PRO_SELF_HOSTED: ${CYPRESS_MB_PRO_SELF_HOSTED_TOKEN:+set}" && echo "STARTER: ${CYPRESS_MB_STARTER_CLOUD_TOKEN:+set}" && echo "PRO_CLOUD: ${CYPRESS_MB_PRO_CLOUD_TOKEN:+set}"
|
||||
```
|
||||
|
||||
If tokens are missing, tell the user: "EE tokens are not set. Either export them and restart Claude Code, or add `MB_EDITION=oss` to run OSS-only tests."
|
||||
|
||||
## Running
|
||||
|
||||
First check if snapshots exist:
|
||||
```bash
|
||||
ls e2e/snapshots/default.sql 2>/dev/null && echo "snapshots exist" || echo "snapshots missing"
|
||||
```
|
||||
|
||||
If snapshots exist, skip regeneration for speed:
|
||||
```bash
|
||||
MB_EDITION=oss CYPRESS_VIDEO=false CYPRESS_RETRIES=0 CYPRESS_GUI=false GENERATE_SNAPSHOTS=false bun test-cypress $ARGUMENTS
|
||||
```
|
||||
|
||||
If snapshots are missing (first run), let the runner generate them:
|
||||
```bash
|
||||
MB_EDITION=oss CYPRESS_VIDEO=false CYPRESS_RETRIES=0 CYPRESS_GUI=false bun test-cypress $ARGUMENTS
|
||||
```
|
||||
|
||||
When running enterprise tests, replace `MB_EDITION=oss` with `MB_EDITION=ee` in the commands above.
|
||||
|
||||
If `$ARGUMENTS` is empty, ask the user which spec to run.
|
||||
|
||||
If the user provides a test name or fuzzy name instead of a spec path, find the spec file:
|
||||
```bash
|
||||
find e2e/test/scenarios -name "*FUZZY_NAME*.cy.spec.*" -type f
|
||||
```
|
||||
Then run with `--spec "path/to/matched.cy.spec.js"`.
|
||||
|
||||
To filter by test name, use the `GREP` env var (not `--env grep=`, which breaks on commas):
|
||||
```bash
|
||||
GREP="test name here" MB_EDITION=oss bun test-cypress --spec path/to/spec.js
|
||||
```
|
||||
|
||||
## Stress test (flaky detection)
|
||||
|
||||
When the user asks to verify a test is not flaky:
|
||||
|
||||
```bash
|
||||
MB_EDITION=oss bin/e2e-stress-test $ARGUMENTS
|
||||
```
|
||||
|
||||
Set `E2E_STRESS_RUNS=N` for more iterations (default: 5). Stops on first failure and prints screenshot paths. Read those screenshots to analyze the failure.
|
||||
|
||||
## Analyzing failures
|
||||
|
||||
When tests fail:
|
||||
|
||||
1. Read the **screenshot** for each failure — paths are printed in Cypress output under `(Screenshots)`, typically at `cypress/screenshots/<spec-name>/<test-name> (failed).png`
|
||||
2. Read the error message and code frame from the console output
|
||||
3. Identify the root cause: is it a test bug, a product bug, or a timing issue?
|
||||
4. Suggest a fix with specific code changes
|
||||
@@ -5,227 +5,110 @@ description: Run mutation testing on a Clojure namespace, generate tests to kill
|
||||
|
||||
# Mutation Testing Skill
|
||||
|
||||
This skill runs mutation testing on a target namespace, writes tests to kill surviving mutations, and creates draft PRs with Linear issues for each function.
|
||||
This skill runs mutation testing end-to-end: generates a coverage report, groups functions, shells out to `claude -p` to write tests, verifies mutations are killed, and creates draft PRs with Linear issues.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A running nREPL connected to the Metabase dev environment
|
||||
- `LINEAR_API_KEY` environment variable set with a valid Linear personal API key
|
||||
- `gh` CLI authenticated with GitHub
|
||||
- `claude` CLI available on PATH
|
||||
|
||||
## Reference Files
|
||||
|
||||
- `dev/src/dev/coverage.clj` — mutation testing tool (generates reports, runs mutations)
|
||||
- `dev/src/dev/mutation_testing.clj` — Linear API client and PR template helpers
|
||||
- `mutation-testing-report.lib.card.md` — example report from the pilot
|
||||
- `dev/src/dev/coverage.clj` — mutation testing engine (generates reports, runs mutations)
|
||||
- `dev/src/dev/mutation_testing.clj` — orchestration, Linear API, PR helpers
|
||||
|
||||
## Invocation
|
||||
|
||||
The argument is a Clojure namespace, e.g.:
|
||||
The argument is a Clojure namespace, optionally followed by a base branch:
|
||||
|
||||
```
|
||||
/mutation-testing metabase.lib.order-by
|
||||
/mutation-testing metabase.lib.order-by --base-branch release-x.52.x
|
||||
/mutation-testing metabase.lib.order-by --project-id abc-123
|
||||
/mutation-testing metabase.lib.order-by --base-branch release-x.52.x --project-id abc-123
|
||||
```
|
||||
|
||||
## Workflow
|
||||
- `--base-branch`: defaults to the value in config (set via `set-config!`), or `"master"` if not configured.
|
||||
- `--project-id`: if provided, issues are added to this existing Linear project instead of creating a new one.
|
||||
|
||||
### Step 1: Parse the namespace
|
||||
## Steps
|
||||
|
||||
From the argument (e.g., `metabase.lib.order-by`), derive:
|
||||
- **Test namespace**: append `-test` (e.g., `metabase.lib.order-by-test`)
|
||||
- **Short name**: the last segment (e.g., `order-by`)
|
||||
- **Source path**: `src/metabase/lib/order_by.cljc` (replace `-` with `_` in path segments)
|
||||
- **Test path**: `test/metabase/lib/order_by_test.cljc`
|
||||
### 1. Parse arguments
|
||||
|
||||
### Step 2: Set up Linear and load tools
|
||||
Parse `$ARGUMENTS` to extract:
|
||||
- **namespace** (required) — the first positional argument
|
||||
- **`--base-branch`** (optional) — if present, the next argument is the base branch name
|
||||
- **`--project-id`** (optional) — if present, the next argument is an existing Linear project ID
|
||||
|
||||
### 2. Load and configure
|
||||
|
||||
```clojure
|
||||
(require '[dev.coverage :as cov] :reload)
|
||||
(require '[dev.mutation-testing :as mut-test] :reload)
|
||||
(require '[dev.coverage :as cov] :reload)
|
||||
```
|
||||
|
||||
If this is the first invocation (or the REPL was restarted), set up the Linear team:
|
||||
|
||||
```clojure
|
||||
;; Find the team
|
||||
(mut-test/list-teams!)
|
||||
;; Set team ID from the output
|
||||
(mut-test/set-config! {:team-id "<team-id>"})
|
||||
(mut-test/set-config! {:team-id "<id>"})
|
||||
```
|
||||
|
||||
If config is already set from a previous invocation, skip this.
|
||||
|
||||
### Step 3: Generate baseline report and create Linear project
|
||||
### 3. Run
|
||||
|
||||
```clojure
|
||||
(cov/generate-report
|
||||
'<target-ns>
|
||||
['<test-ns>]
|
||||
"mutation-testing-report.lib.<short-name>.before.md")
|
||||
(mut-test/run! '<target-ns>)
|
||||
;; Or with options:
|
||||
(mut-test/run! '<target-ns> {:base-branch "release-x.52.x"})
|
||||
(mut-test/run! '<target-ns> {:project-id "abc-123"})
|
||||
(mut-test/run! '<target-ns> {:base-branch "release-x.52.x" :project-id "abc-123"})
|
||||
```
|
||||
|
||||
Read the generated report file. It has three sections:
|
||||
- **Uncovered Functions** — never called by any test
|
||||
- **Partially Covered Functions** — called but with surviving mutations
|
||||
- **Fully Covered Functions** — all mutations killed
|
||||
Pass the opts map with `:base-branch` and/or `:project-id` if those flags were provided in the arguments.
|
||||
|
||||
Then create a Linear project for this namespace (uses report stats in the description):
|
||||
This will:
|
||||
1. Generate a baseline mutation testing report
|
||||
2. Create a Linear project for the namespace
|
||||
3. Group functions by coverage relationships
|
||||
4. For each group: create branch → invoke Claude to write tests → verify → retry if needed → commit & push → create Linear issue → create draft PR
|
||||
5. Print a summary with PR links
|
||||
|
||||
### 4. Handle failures
|
||||
|
||||
If a group fails mid-processing, `run!` catches the error and continues to the next group. To retry a single group manually:
|
||||
|
||||
```clojure
|
||||
(mut-test/create-project-for-namespace!
|
||||
"<target-ns>"
|
||||
"mutation-testing-report.lib.<short-name>.before.md")
|
||||
;; => {:project-id "...", :name "Mutation Testing: metabase.lib.order-by"}
|
||||
;; Automatically sets :project-id in config for subsequent create-issue! calls
|
||||
;; Get the parsed namespace info
|
||||
(def parsed (mut-test/parse-namespace '<target-ns>))
|
||||
|
||||
;; Run coverage to get the data
|
||||
(def coverage-results (cov/test-namespace (:target-ns parsed) [(:test-ns parsed)]))
|
||||
|
||||
;; Group and find the one you want
|
||||
(def groups (mut-test/group-functions coverage-results))
|
||||
|
||||
;; Process just that group
|
||||
(mut-test/process-group! parsed (nth groups <index>))
|
||||
```
|
||||
|
||||
### Step 4: Plan the work
|
||||
### 5. Verify the prompt (dry run)
|
||||
|
||||
Before creating branches, analyze the report to plan how to group the work:
|
||||
|
||||
1. **Identify killable functions.** For each function with surviving mutations, assess whether any mutations are killable. Run `cov/test-mutations` to see the full mutation list. If ALL mutations for a function are unkillable (schema-only, semantically equivalent, etc.), skip that function — no branch or PR needed.
|
||||
|
||||
2. **Group private functions by public entry point.** Private functions cannot be tested directly. Group them with the public function that exercises them. Create one branch/PR per group, not per private function.
|
||||
|
||||
3. **Track mutations per group.** The `:mutations-before` and `:not-killed` fields in the PR should only reflect mutations for functions covered by that specific PR — never mutations from other functions.
|
||||
|
||||
### Step 5: Process each group
|
||||
|
||||
For each group of functions that has killable mutations:
|
||||
|
||||
#### 5a. Create a branch
|
||||
To inspect what Claude will see without invoking it:
|
||||
|
||||
```clojure
|
||||
(mut-test/create-branch! "<target-ns>" "<primary-fn-name>")
|
||||
;; => "mutation-testing-lib-order-by-orderable-columns"
|
||||
(def parsed (mut-test/parse-namespace '<target-ns>))
|
||||
(def coverage-results (cov/test-namespace (:target-ns parsed) [(:test-ns parsed)]))
|
||||
(def groups (mut-test/group-functions coverage-results))
|
||||
(println (mut-test/build-test-prompt
|
||||
(merge (select-keys parsed [:target-ns :test-ns :source-path :test-path])
|
||||
(select-keys (first groups) [:fn-names :mutations]))))
|
||||
```
|
||||
|
||||
Use the primary public function name for the branch. If the group covers private functions too, name the branch after the public entry point.
|
||||
|
||||
#### 5b. Read context
|
||||
|
||||
- Read the source function(s) from the source file
|
||||
- Read the full test file to understand existing test patterns, helpers, and metadata providers used
|
||||
- If the function is private, identify which public function(s) call it
|
||||
|
||||
#### 5c. Write tests
|
||||
|
||||
Generate the **simplest tests** that kill the surviving mutations while still making semantic sense. Guidelines:
|
||||
|
||||
- **Never call private functions directly.** Always test through public API functions. Use the coverage data to identify which public functions exercise the private function.
|
||||
- **Keep tests simple.** Each test should verify one meaningful behavior. Don't over-engineer tests just to chase mutation kills.
|
||||
- **Follow existing patterns.** Match the style of the existing tests: same metadata providers, same helper functions, same assertion patterns.
|
||||
- **Insert tests near related existing tests** for the same function, not at the end of the file. This minimizes merge conflicts between branches. If there are no existing tests for the function, find the most logical location based on the order of functions in the source file.
|
||||
|
||||
**IMPORTANT: Use `file_edit` (not `clojure_edit`) when editing test files.** The `clojure_edit` tool reformats the entire file, introducing unnecessary whitespace changes that pollute the diff. Use `file_edit` with exact string matching to make surgical insertions that only touch the lines you intend to change.
|
||||
|
||||
#### 5d. Verify mutations are killed
|
||||
|
||||
Use the REPL to check that the new tests kill the targeted mutations:
|
||||
|
||||
```clojure
|
||||
;; Reload the test namespace to pick up new tests
|
||||
(require '<test-ns> :reload)
|
||||
|
||||
;; Test mutations for the specific function
|
||||
(cov/test-mutations
|
||||
'<target-ns>/<fn-name>
|
||||
;; Set of test names that cover this function
|
||||
#{'<test-ns>/<test-name-1> '<test-ns>/<test-name-2>})
|
||||
```
|
||||
|
||||
Check the `:survived` key in the result. If mutations still survive:
|
||||
- Try writing additional tests
|
||||
- If a mutation is truly unkillable (semantically equivalent), note it for the PR description
|
||||
- If a mutation reveals dead/unreachable code, consider suggesting removal as a code improvement
|
||||
|
||||
#### 5e. Handle unkillable mutations and improvements
|
||||
|
||||
**Do NOT edit the source namespace just for documentation.** Instead:
|
||||
|
||||
- **Unkillable mutations**: Note them in the PR description with rationale
|
||||
- **Code improvements** (dead code removal, expression simplification) that directly relate to surviving mutants:
|
||||
1. Make the change in the source file
|
||||
2. Verify all tests still pass with the change
|
||||
3. Note the file path, line range, and the improved code
|
||||
4. **Reset the change**: `git checkout -- <source-file>` — do NOT commit it
|
||||
5. After creating the PR, use `add-suggested-change!` to post the improvement as a GitHub suggested change (Step 5h)
|
||||
6. The reviewer decides whether to accept it
|
||||
|
||||
#### 5f. Commit and push
|
||||
|
||||
```clojure
|
||||
;; Only commit the test file — never commit source changes for improvements
|
||||
(mut-test/commit-and-push! "<target-ns>" "<primary-fn-name>"
|
||||
["test/metabase/lib/<short_name>_test.cljc"])
|
||||
```
|
||||
|
||||
#### 5g. Create Linear issue and draft PR
|
||||
|
||||
```clojure
|
||||
;; Create a Linear issue (use the primary function name)
|
||||
(def issue (mut-test/create-issue-for-function! "<target-ns>" "<primary-fn-name>"))
|
||||
;; => {:identifier "QUE-1234", :url "https://linear.app/...", ...}
|
||||
|
||||
;; Create draft PR linked to the Linear issue
|
||||
;; :fn-names lists ALL functions covered by this PR
|
||||
;; :mutations-before counts only mutations for functions in this PR
|
||||
;; :not-killed lists only unkillable mutations for functions in this PR
|
||||
(def pr-url
|
||||
(mut-test/create-draft-pr!
|
||||
{:target-ns "<target-ns>"
|
||||
:fn-name "<primary-fn-name>"
|
||||
:linear-identifier (:identifier issue)
|
||||
:mutations-before 9 ;; surviving mutations for these functions only
|
||||
:tests-added 2 ;; number of new test functions
|
||||
:killed ["Replace :segment-id with :segment-id__"
|
||||
"Replace cycle-path with nil"]
|
||||
:not-killed [{:description "Replace acc with nil"
|
||||
:rationale "reduce always has single iteration"}]
|
||||
:suggested-changes []}))
|
||||
;; => "https://github.com/metabase/metabase/pull/12345"
|
||||
```
|
||||
|
||||
#### 5h. Add code improvement suggestions (if any)
|
||||
|
||||
If you identified code improvements in step 5e (and reset them), post each as a GitHub suggested change:
|
||||
|
||||
```clojure
|
||||
(mut-test/add-suggested-change!
|
||||
{:pr-url pr-url
|
||||
:path "src/metabase/lib/<short_name>.cljc"
|
||||
:start-line 42 ;; first line of the code to replace
|
||||
:end-line 45 ;; last line of the code to replace
|
||||
:suggestion "(improved-code-here)" ;; the replacement code
|
||||
:comment "**Suggested improvement:** <description of the change and why it's related to the surviving mutant>"})
|
||||
```
|
||||
|
||||
### Step 6: Return to master and repeat
|
||||
|
||||
```clojure
|
||||
(mut-test/return-to-master!)
|
||||
```
|
||||
|
||||
Repeat Step 5 for the next group.
|
||||
|
||||
### Step 7: Summary
|
||||
|
||||
Print a summary of what was done:
|
||||
- Link to the Linear project for this namespace
|
||||
- Number of functions processed
|
||||
- Number of functions skipped (all mutations unkillable)
|
||||
- Number of draft PRs created
|
||||
- Number of mutations killed vs. unkillable
|
||||
- List of test PRs with links
|
||||
- List of mutation rule PRs with links (if any were created)
|
||||
|
||||
## Suggesting New Mutation Rules
|
||||
|
||||
If you notice patterns in surviving mutations that suggest the mutation testing tool should have additional rules (e.g., a common Clojure form that isn't being mutated), open a separate PR proposing additions to `dev.coverage/mutation-rules` with an explanation.
|
||||
|
||||
## Configuration
|
||||
|
||||
The `dev.mutation-testing` namespace reads `LINEAR_API_KEY` from the environment and stores team/project IDs in an atom.
|
||||
|
||||
- **Team ID**: Set once per REPL session via `(mut-test/list-teams!)` and `(mut-test/set-config! {:team-id "..."})` (Step 2)
|
||||
- **Project ID**: Set automatically when `create-project-for-namespace!` is called (Step 3) — one project per namespace
|
||||
- **Team ID**: Set once per REPL session via `(mut-test/list-teams!)` and `(mut-test/set-config! {:team-id "..."})`
|
||||
- **Project ID**: Set automatically when `run!` calls `create-project-for-namespace!`
|
||||
- **Base Branch**: Defaults to `"master"`. Override via `(mut-test/set-config! {:base-branch "release-x.52.x"})` or pass `{:base-branch "..."}` as the second arg to `run!`
|
||||
- **Project ID**: If set (via `set-config!` or `run!` opts), `run!` reuses the existing Linear project instead of creating a new one
|
||||
|
||||
@@ -12,8 +12,9 @@ allowed-tools: Read, Grep, Bash, Glob
|
||||
|
||||
Review pull requests with a focus on:
|
||||
|
||||
- Readability and maintainability
|
||||
- Appropriate test coverage
|
||||
- Compliance with project coding standards and conventions
|
||||
- Code quality and best practices
|
||||
- Clear and correct JSDoc comments
|
||||
- Type safety and proper TypeScript usage
|
||||
- React best practices (when applicable)
|
||||
- React best practices (when applicable)
|
||||
|
||||
@@ -227,7 +227,9 @@
|
||||
;;
|
||||
|
||||
:invalid-arity {:skip-args [metabase.lib.util.match/match
|
||||
metabase.lib.util.match/match-lite]} ; TODO (cam): can we fix these?
|
||||
metabase.lib.util.match/match-lite
|
||||
metabase.lib.util.match/match-many
|
||||
metabase.lib.util.match/replace-lite]} ; TODO (cam): can we fix these?
|
||||
:refer-all {:level :warning, :exclude [clojure.test]}
|
||||
:unused-referred-var {:exclude {compojure.core [GET DELETE POST PUT]}}
|
||||
|
||||
@@ -246,12 +248,17 @@
|
||||
(metabase.lib.util.match/match)
|
||||
(metabase.lib.util.match/match-one)
|
||||
(metabase.lib.util.match/match-lite)
|
||||
(metabase.lib.util.match/match-many)
|
||||
(metabase.lib.util.match/replace)
|
||||
(metabase.lib.util.match/replace-in)
|
||||
(metabase.lib.util.match/replace-lite)
|
||||
(metabase.driver-api.core/match)
|
||||
(metabase.driver-api.core/match-one)
|
||||
(metabase.driver-api.core/match-lite)
|
||||
(metabase.driver-api.core/match-many)
|
||||
(metabase.driver-api.core/replace)
|
||||
(metabase.driver-api.core/replace-in)
|
||||
(metabase.driver-api.core/replace-lite)
|
||||
(metabase.query-processor.middleware.cache-backend.interface/with-cached-results)
|
||||
(metabase.util.regex/rx [opt])
|
||||
(metabase.util/prog1 [<>])
|
||||
@@ -303,11 +310,14 @@
|
||||
metabase.lib.convert/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
|
||||
metabase.lib.core/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
|
||||
metabase.lib.equality/find-column-indexes-for-refs {:message "Use lib.equality/closest-matches-in-metadata or lib.equality/find-closest-matches-for-refs instead of lib.equality/find-column-indexes-for-refs"}
|
||||
metabase.lib.util.match/match {:message "Use lib.util.match/match-many instead of lib.util.match/match."}
|
||||
metabase.lib.util.match/match-one {:message "Use lib.util.match/match-lite instead of lib.util.match/match-one."}
|
||||
metabase.lib.util.match/replace {:message "Use lib.util.match/replace-lite instead of lib.util.match/replace."}
|
||||
metabase-enterprise.transforms.interface/execute! {:message "Use metabase-enterprise.transforms.execute/execute! instead of metabase-enterprise.transforms.interface/execute!"}
|
||||
next.jdbc/active-tx? {:message "Use metabase.app-db.core/in-transaction? to see if we are in a transaction"}
|
||||
toucan2.core/debug {:message "Don't commit code using t2/debug"}
|
||||
toucan2.tools.debug/debug {:message "Don't commit code using t2/debug"}
|
||||
macaw.core/parsed-query {:message "Use metabase.driver.util/parsed-query instead of macaw.core/parsed-query"}}
|
||||
}
|
||||
|
||||
:discouraged-namespace
|
||||
{camel-snake-kebab.core {:message "CSK is not Turkish-safe, use the versions in metabase.util instead."}
|
||||
@@ -412,6 +422,8 @@
|
||||
ring.util.codec codec
|
||||
ring.util.response response
|
||||
saml20-clj.core saml
|
||||
metabase.transforms-base.util transforms-base.u
|
||||
metabase.transforms.util transforms.u
|
||||
toucan2.core t2}}}
|
||||
|
||||
:lint-as
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
version}}
|
||||
|
||||
analyze
|
||||
{:team "Querying"
|
||||
{:team "Graphy"
|
||||
:api #{metabase.analyze.core
|
||||
metabase.analyze.query-results}
|
||||
:uses #{config
|
||||
@@ -152,6 +152,7 @@
|
||||
metabase.api.docs
|
||||
metabase.api.init
|
||||
metabase.api.macros
|
||||
metabase.api.macros.scope
|
||||
metabase.api.open-api
|
||||
metabase.api.response
|
||||
metabase.api.routes.common
|
||||
@@ -214,6 +215,7 @@
|
||||
settings
|
||||
task
|
||||
task-history
|
||||
tracing
|
||||
util}}
|
||||
|
||||
auth-identity
|
||||
@@ -275,6 +277,7 @@
|
||||
config
|
||||
events
|
||||
models
|
||||
permissions
|
||||
premium-features
|
||||
request
|
||||
settings
|
||||
@@ -395,6 +398,7 @@
|
||||
request
|
||||
revisions
|
||||
settings
|
||||
tracing
|
||||
transforms
|
||||
upload
|
||||
util}}
|
||||
@@ -410,7 +414,6 @@
|
||||
models
|
||||
request
|
||||
users
|
||||
users-rest
|
||||
util}}
|
||||
|
||||
;; technically this 'uses' `enterprise/core` and `test` since it tries to load them to see if they exists so we know
|
||||
@@ -543,11 +546,13 @@
|
||||
view-log}}
|
||||
|
||||
driver
|
||||
{:team "Drivers"
|
||||
{:team "Querying"
|
||||
:api #{metabase.driver
|
||||
metabase.driver.common
|
||||
metabase.driver.common.parameters
|
||||
metabase.driver.common.parameters.parse
|
||||
metabase.driver.connection
|
||||
metabase.driver.connection.workspaces
|
||||
metabase.driver.ddl.interface
|
||||
metabase.driver.h2
|
||||
metabase.driver.impl
|
||||
@@ -556,6 +561,7 @@
|
||||
metabase.driver.postgres
|
||||
metabase.driver.settings
|
||||
metabase.driver.sql
|
||||
metabase.driver.sql-jdbc
|
||||
metabase.driver.sql-jdbc.connection
|
||||
metabase.driver.sql-jdbc.execute
|
||||
metabase.driver.sql-jdbc.execute.diagnostic
|
||||
@@ -569,7 +575,8 @@
|
||||
metabase.driver.sql.util
|
||||
metabase.driver.sync
|
||||
metabase.driver.util}
|
||||
:uses #{app-db
|
||||
:uses #{analytics
|
||||
app-db
|
||||
appearance
|
||||
auth-provider
|
||||
classloader
|
||||
@@ -582,13 +589,15 @@
|
||||
premium-features
|
||||
query-processor
|
||||
settings
|
||||
sql-tools
|
||||
system
|
||||
tracing
|
||||
util
|
||||
warehouse-schema
|
||||
warehouses}}
|
||||
|
||||
driver-api
|
||||
{:team "Drivers"
|
||||
{:team "Querying"
|
||||
:api #{metabase.driver-api.core}
|
||||
:uses #{actions
|
||||
api
|
||||
@@ -661,7 +670,8 @@
|
||||
:api #{metabase.events.core
|
||||
metabase.events.init}
|
||||
:uses #{models
|
||||
util}}
|
||||
util}
|
||||
:friends #{tracing}}
|
||||
|
||||
formatter
|
||||
{:team "UX West"
|
||||
@@ -690,7 +700,7 @@
|
||||
util}}
|
||||
|
||||
graph
|
||||
{:team "Querying"
|
||||
{:team "Graphy"
|
||||
:api #{metabase.graph.core}
|
||||
:uses #{util}}
|
||||
|
||||
@@ -732,32 +742,18 @@
|
||||
|
||||
lib
|
||||
{:team "Querying"
|
||||
:api #{metabase.lib.binning
|
||||
metabase.lib.convert ; Only so [[metabase.queries.models.card]] can set dynamic vars.
|
||||
metabase.lib.core
|
||||
:api #{metabase.lib.core
|
||||
metabase.lib.equality
|
||||
metabase.lib.field
|
||||
metabase.lib.filter.desugar.jvm ; only so [[metabase.legacy-mbql.jvm-util]] can use defined regexes
|
||||
metabase.lib.hierarchy
|
||||
metabase.lib.init
|
||||
metabase.lib.metadata
|
||||
metabase.lib.metadata.cached-provider
|
||||
metabase.lib.metadata.column
|
||||
metabase.lib.metadata.invocation-tracker
|
||||
metabase.lib.metadata.protocols
|
||||
metabase.lib.metadata.result-metadata
|
||||
metabase.lib.normalize
|
||||
metabase.lib.options
|
||||
metabase.lib.query
|
||||
metabase.lib.schema
|
||||
metabase.lib.schema.actions
|
||||
metabase.lib.schema.aggregation
|
||||
metabase.lib.schema.binning
|
||||
metabase.lib.schema.common
|
||||
metabase.lib.schema.constraints
|
||||
metabase.lib.schema.expression
|
||||
metabase.lib.schema.expression.temporal
|
||||
metabase.lib.schema.expression.window
|
||||
metabase.lib.schema.filter
|
||||
metabase.lib.schema.id
|
||||
metabase.lib.schema.info
|
||||
metabase.lib.schema.join
|
||||
@@ -766,26 +762,27 @@
|
||||
metabase.lib.schema.measure
|
||||
metabase.lib.schema.metadata
|
||||
metabase.lib.schema.metadata.fingerprint
|
||||
metabase.lib.schema.middleware-options
|
||||
metabase.lib.schema.parameter
|
||||
metabase.lib.schema.ref
|
||||
metabase.lib.schema.settings
|
||||
metabase.lib.schema.template-tag
|
||||
metabase.lib.schema.temporal-bucketing
|
||||
metabase.lib.schema.test-spec
|
||||
metabase.lib.schema.util
|
||||
metabase.lib.schema.validate
|
||||
metabase.lib.temporal-bucket
|
||||
metabase.lib.types.isa
|
||||
metabase.lib.util
|
||||
metabase.lib.util.match
|
||||
metabase.lib.walk}
|
||||
metabase.lib.util.match}
|
||||
:uses #{config
|
||||
graph
|
||||
legacy-mbql
|
||||
types
|
||||
util}
|
||||
:friends #{query-processor}}
|
||||
:friends #{legacy-mbql ;; Reuses internal schemas and a few helper functions. Deeply coupled to MBQL.
|
||||
lib-be ;; BE-specific extension of lib; tight coupling is acceptable.
|
||||
query-processor ;; Necessarily manipulates MBQL; better to pull most logic into a new module.
|
||||
source-swap ;; Manipulates MBQL queries; tight coupling with lib is expected.
|
||||
enterprise/sandbox ;; Really part of the QP, though its lives in the enterprise/ tree.
|
||||
}}
|
||||
|
||||
lib-be
|
||||
{:team "Querying"
|
||||
@@ -796,6 +793,16 @@
|
||||
settings
|
||||
util}}
|
||||
|
||||
lib-metric
|
||||
{:team "UX West"
|
||||
:api #{metabase.lib-metric.core
|
||||
metabase.lib-metric.schema}
|
||||
:uses #{lib
|
||||
lib-be
|
||||
settings
|
||||
types
|
||||
util}}
|
||||
|
||||
llm
|
||||
{:team "Metabot"
|
||||
:api #{metabase.llm.api
|
||||
@@ -809,6 +816,7 @@
|
||||
premium-features
|
||||
request
|
||||
settings
|
||||
sql-tools
|
||||
sync
|
||||
util
|
||||
warehouse-schema}}
|
||||
@@ -844,12 +852,29 @@
|
||||
events
|
||||
lib
|
||||
lib-be
|
||||
metrics
|
||||
models
|
||||
permissions
|
||||
remote-sync
|
||||
search
|
||||
util}}
|
||||
|
||||
metrics
|
||||
{:team "UX West"
|
||||
:api #{metabase.metrics.api
|
||||
metabase.metrics.core}
|
||||
:uses #{api
|
||||
collections
|
||||
lib
|
||||
lib-be
|
||||
lib-metric
|
||||
models
|
||||
parameters
|
||||
query-processor
|
||||
request
|
||||
server
|
||||
util}}
|
||||
|
||||
model-persistence
|
||||
{:team "Querying"
|
||||
:api #{metabase.model-persistence.api
|
||||
@@ -870,6 +895,7 @@
|
||||
system
|
||||
task
|
||||
task-history
|
||||
tracing
|
||||
util}}
|
||||
|
||||
models
|
||||
@@ -879,6 +905,7 @@
|
||||
metabase.models.interface
|
||||
metabase.models.resolution
|
||||
metabase.models.serialization
|
||||
metabase.models.transforms.transform
|
||||
metabase.models.util.spec-update
|
||||
metabase.models.visualization-settings}
|
||||
:uses #{api
|
||||
@@ -895,6 +922,7 @@
|
||||
search
|
||||
settings
|
||||
transforms
|
||||
transforms-base
|
||||
util}
|
||||
:friends #{transforms}}
|
||||
|
||||
@@ -941,6 +969,7 @@
|
||||
system
|
||||
task
|
||||
task-history
|
||||
tracing
|
||||
users
|
||||
util}}
|
||||
|
||||
@@ -952,6 +981,7 @@
|
||||
metabase.parameters.custom-values
|
||||
metabase.parameters.dashboard
|
||||
metabase.parameters.field
|
||||
metabase.parameters.field-values
|
||||
metabase.parameters.init
|
||||
metabase.parameters.params
|
||||
metabase.parameters.schema
|
||||
@@ -983,6 +1013,7 @@
|
||||
premium-features
|
||||
remote-sync
|
||||
settings
|
||||
tracing
|
||||
util
|
||||
enterprise/advanced-permissions}}
|
||||
|
||||
@@ -1026,7 +1057,9 @@
|
||||
events
|
||||
internal-stats
|
||||
settings
|
||||
startup
|
||||
task
|
||||
tracing
|
||||
util}}
|
||||
|
||||
product-feedback
|
||||
@@ -1094,6 +1127,7 @@
|
||||
request
|
||||
task
|
||||
task-history
|
||||
tracing
|
||||
util
|
||||
enterprise/advanced-permissions
|
||||
enterprise/sandbox}}
|
||||
@@ -1119,6 +1153,7 @@
|
||||
events
|
||||
lib
|
||||
lib-be
|
||||
metrics
|
||||
models
|
||||
parameters
|
||||
permissions
|
||||
@@ -1181,8 +1216,10 @@
|
||||
metabase.query-processor.init
|
||||
metabase.query-processor.interface
|
||||
metabase.query-processor.metadata
|
||||
metabase.query-processor.middleware.add-remaps
|
||||
metabase.query-processor.middleware.annotate
|
||||
metabase.query-processor.middleware.cache-backend.db
|
||||
metabase.query-processor.middleware.catch-exceptions
|
||||
metabase.query-processor.middleware.constraints
|
||||
metabase.query-processor.middleware.limit
|
||||
metabase.query-processor.middleware.permissions
|
||||
@@ -1234,6 +1271,7 @@
|
||||
server
|
||||
settings
|
||||
system
|
||||
tracing
|
||||
users
|
||||
util}}
|
||||
|
||||
@@ -1315,12 +1353,12 @@
|
||||
settings
|
||||
startup
|
||||
task
|
||||
tracing
|
||||
transforms
|
||||
util
|
||||
warehouses}}
|
||||
util}}
|
||||
|
||||
secrets
|
||||
{:team "Drivers"
|
||||
{:team "Querying"
|
||||
:api #{metabase.secrets.core}
|
||||
:uses #{api driver models premium-features util}}
|
||||
|
||||
@@ -1362,6 +1400,7 @@
|
||||
session
|
||||
settings
|
||||
system
|
||||
tracing
|
||||
users
|
||||
util
|
||||
enterprise/sso}}
|
||||
@@ -1383,6 +1422,7 @@
|
||||
sso
|
||||
system
|
||||
task
|
||||
tracing
|
||||
util}}
|
||||
|
||||
settings
|
||||
@@ -1429,6 +1469,32 @@
|
||||
system
|
||||
util}}
|
||||
|
||||
source-swap
|
||||
{:team "Graphy"
|
||||
:api #{metabase.source-swap.core
|
||||
metabase.source-swap.schema}
|
||||
:uses #{driver
|
||||
lib
|
||||
sql-tools
|
||||
util}}
|
||||
|
||||
sql-parsing
|
||||
{:team "DevEx"
|
||||
:api #{metabase.sql-parsing.core}
|
||||
:uses #{analytics
|
||||
util}}
|
||||
|
||||
sql-tools
|
||||
{:team "DevEx"
|
||||
:api #{metabase.sql-tools.core
|
||||
metabase.sql-tools.init}
|
||||
:uses #{analytics
|
||||
driver
|
||||
lib
|
||||
settings
|
||||
sql-parsing
|
||||
util}}
|
||||
|
||||
sso
|
||||
{:team "UX West"
|
||||
:api #{metabase.sso.api
|
||||
@@ -1457,7 +1523,7 @@
|
||||
util}}
|
||||
|
||||
sync
|
||||
{:team "Querying"
|
||||
{:team "Graphy"
|
||||
:api #{metabase.sync.api
|
||||
metabase.sync.core
|
||||
metabase.sync.init
|
||||
@@ -1479,6 +1545,7 @@
|
||||
settings
|
||||
task
|
||||
task-history
|
||||
tracing
|
||||
util
|
||||
warehouse-schema
|
||||
warehouses}}
|
||||
@@ -1495,6 +1562,7 @@
|
||||
metabase.task.core}
|
||||
:uses #{app-db
|
||||
classloader
|
||||
tracing
|
||||
util}}
|
||||
|
||||
task-history
|
||||
@@ -1511,6 +1579,7 @@
|
||||
query-processor
|
||||
request
|
||||
task
|
||||
tracing
|
||||
util}}
|
||||
|
||||
tenants
|
||||
@@ -1558,12 +1627,18 @@
|
||||
models
|
||||
util}}
|
||||
|
||||
tracing
|
||||
{:team "DevEx"
|
||||
:api #{metabase.tracing.core
|
||||
metabase.tracing.init}
|
||||
:uses #{events
|
||||
settings
|
||||
task
|
||||
util}}
|
||||
|
||||
transforms
|
||||
{:team "Querying"
|
||||
:api #{metabase.transforms.api.transform
|
||||
metabase.transforms.api.transform-job
|
||||
metabase.transforms.api.transform-tag
|
||||
metabase.transforms.core
|
||||
:api #{metabase.transforms.core
|
||||
metabase.transforms.feature-gating
|
||||
metabase.transforms.init
|
||||
metabase.transforms.schema
|
||||
@@ -1575,22 +1650,66 @@
|
||||
driver
|
||||
events
|
||||
lib
|
||||
lib-be
|
||||
models
|
||||
premium-features
|
||||
queries
|
||||
query-processor
|
||||
request
|
||||
revisions
|
||||
settings
|
||||
sync
|
||||
task
|
||||
task-history
|
||||
tracing
|
||||
transforms-base
|
||||
util}
|
||||
:friends #{models
|
||||
enterprise/transforms-python
|
||||
enterprise/workspaces}}
|
||||
|
||||
transforms-base
|
||||
{:team "Querying"
|
||||
:api #{metabase.transforms-base.init
|
||||
metabase.transforms-base.interface
|
||||
metabase.transforms-base.util}
|
||||
:uses #{driver
|
||||
events
|
||||
lib
|
||||
lib-be
|
||||
query-processor
|
||||
sync
|
||||
util}
|
||||
:friends #{transforms
|
||||
enterprise/transforms-python
|
||||
enterprise/workspaces}}
|
||||
|
||||
transforms-inspector
|
||||
{:team "Gadget"
|
||||
:api #{metabase.transforms-inspector.core
|
||||
metabase.transforms-inspector.schema}
|
||||
:uses #{driver
|
||||
lib
|
||||
lib-be
|
||||
query-processor
|
||||
sql-tools
|
||||
transforms-base
|
||||
util}}
|
||||
|
||||
transforms-rest
|
||||
{:team "Gadget"
|
||||
:api #{metabase.transforms-rest.api.transform
|
||||
metabase.transforms-rest.api.transform-job
|
||||
metabase.transforms-rest.api.transform-tag}
|
||||
:uses #{api
|
||||
driver
|
||||
events
|
||||
models
|
||||
query-processor
|
||||
request
|
||||
server
|
||||
sql-tools
|
||||
transforms
|
||||
transforms-base
|
||||
transforms-inspector
|
||||
util}}
|
||||
|
||||
types
|
||||
{:team "Querying"
|
||||
@@ -1630,41 +1749,42 @@
|
||||
|
||||
users
|
||||
{:team "UX West"
|
||||
:api #{metabase.users.init
|
||||
:api #{metabase.users.core
|
||||
metabase.users.init
|
||||
metabase.users.models.user
|
||||
metabase.users.models.user-parameter-value
|
||||
metabase.users.schema
|
||||
metabase.users.settings}
|
||||
:uses #{api
|
||||
batch-processing
|
||||
channel
|
||||
config
|
||||
events
|
||||
models
|
||||
permissions
|
||||
premium-features
|
||||
settings
|
||||
setup
|
||||
system
|
||||
tenants
|
||||
util}}
|
||||
|
||||
users-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.users-rest.api}
|
||||
:uses #{analytics
|
||||
api
|
||||
appearance
|
||||
auth-identity
|
||||
collections
|
||||
batch-processing
|
||||
channel
|
||||
config
|
||||
events
|
||||
models
|
||||
notification
|
||||
permissions
|
||||
premium-features
|
||||
request
|
||||
settings
|
||||
setup
|
||||
system
|
||||
tenants
|
||||
util
|
||||
enterprise/advanced-permissions}}
|
||||
|
||||
users-rest
|
||||
{:team "UX West"
|
||||
:api #{metabase.users-rest.api}
|
||||
:uses #{api
|
||||
appearance
|
||||
auth-identity
|
||||
collections
|
||||
config
|
||||
events
|
||||
models
|
||||
permissions
|
||||
premium-features
|
||||
request
|
||||
sso
|
||||
tenants
|
||||
users
|
||||
@@ -1884,7 +2004,9 @@
|
||||
request
|
||||
server
|
||||
util
|
||||
enterprise/metabot-v3}}
|
||||
enterprise/api
|
||||
enterprise/metabot-v3
|
||||
enterprise/workspaces}}
|
||||
|
||||
enterprise/ai-entity-analysis
|
||||
{:team "Metabot"
|
||||
@@ -1955,7 +2077,7 @@
|
||||
setup
|
||||
startup
|
||||
sync
|
||||
users-rest
|
||||
users
|
||||
util
|
||||
enterprise/serialization}}
|
||||
|
||||
@@ -2070,13 +2192,14 @@
|
||||
settings
|
||||
util
|
||||
warehouse-schema
|
||||
warehouses-rest}}
|
||||
warehouses}}
|
||||
|
||||
enterprise/dependencies
|
||||
{:team "Querying"
|
||||
{:team "Graphy"
|
||||
:api #{metabase-enterprise.dependencies.api
|
||||
metabase-enterprise.dependencies.core
|
||||
metabase-enterprise.dependencies.init}
|
||||
metabase-enterprise.dependencies.init
|
||||
metabase-enterprise.dependencies.models.dependency}
|
||||
:uses #{analyze
|
||||
api
|
||||
app-db
|
||||
@@ -2097,8 +2220,10 @@
|
||||
request
|
||||
revisions
|
||||
settings
|
||||
sql-tools
|
||||
task
|
||||
transforms
|
||||
transforms-base
|
||||
util}}
|
||||
|
||||
enterprise/email
|
||||
@@ -2141,7 +2266,7 @@
|
||||
util}}
|
||||
|
||||
enterprise/impersonation
|
||||
{:team "Drivers"
|
||||
{:team "Querying"
|
||||
:api #{metabase-enterprise.impersonation.api}
|
||||
:uses #{api
|
||||
audit-app
|
||||
@@ -2201,6 +2326,7 @@
|
||||
lib-be
|
||||
models
|
||||
native-query-snippets
|
||||
parameters
|
||||
permissions
|
||||
premium-features
|
||||
pulse
|
||||
@@ -2209,15 +2335,16 @@
|
||||
search
|
||||
server
|
||||
settings
|
||||
sql-tools
|
||||
store-api
|
||||
sync
|
||||
system
|
||||
task
|
||||
transforms
|
||||
users-rest
|
||||
transforms-base
|
||||
users
|
||||
util
|
||||
warehouse-schema
|
||||
warehouses-rest
|
||||
warehouses
|
||||
enterprise/dependencies
|
||||
enterprise/transforms-python}}
|
||||
|
||||
@@ -2253,7 +2380,23 @@
|
||||
task
|
||||
task-history
|
||||
util
|
||||
enterprise/serialization}}
|
||||
enterprise/serialization
|
||||
enterprise/transforms-python}}
|
||||
|
||||
enterprise/replacement
|
||||
{:team "Graphy"
|
||||
:api #{metabase-enterprise.replacement.api}
|
||||
:uses #{api
|
||||
app-db
|
||||
events
|
||||
lib
|
||||
lib-be
|
||||
models
|
||||
queries
|
||||
source-swap
|
||||
task
|
||||
util
|
||||
enterprise/dependencies}}
|
||||
|
||||
enterprise/sandbox
|
||||
{:team "UX West"
|
||||
@@ -2318,6 +2461,7 @@
|
||||
search
|
||||
settings
|
||||
task
|
||||
tracing
|
||||
util
|
||||
enterprise/llm
|
||||
enterprise/metabot-v3}}
|
||||
@@ -2378,7 +2522,6 @@
|
||||
:uses #{analytics
|
||||
api
|
||||
collections
|
||||
collections-rest
|
||||
embedding
|
||||
premium-features
|
||||
queries
|
||||
@@ -2417,11 +2560,12 @@
|
||||
enterprise/transforms
|
||||
{:team "Querying"
|
||||
:api #{}
|
||||
:uses #{}}
|
||||
:uses #{premium-features}}
|
||||
|
||||
enterprise/transforms-python
|
||||
{:team "Querying"
|
||||
:api #{metabase-enterprise.transforms-python.api
|
||||
metabase-enterprise.transforms-python.core
|
||||
metabase-enterprise.transforms-python.init
|
||||
metabase-enterprise.transforms-python.python-runner}
|
||||
:uses #{analytics
|
||||
@@ -2437,6 +2581,7 @@
|
||||
query-processor
|
||||
settings
|
||||
transforms
|
||||
transforms-base
|
||||
util}}
|
||||
|
||||
enterprise/upload-management
|
||||
@@ -2452,8 +2597,9 @@
|
||||
{:team "Metabot"
|
||||
:api #{metabase-enterprise.workspaces.api
|
||||
metabase-enterprise.workspaces.init}
|
||||
:friends #{enterprise/agent-api}
|
||||
:uses #{api
|
||||
api-keys
|
||||
app-db
|
||||
config
|
||||
database-routing
|
||||
driver
|
||||
@@ -2461,15 +2607,23 @@
|
||||
lib
|
||||
lib-be
|
||||
models
|
||||
premium-features
|
||||
queries
|
||||
;; NOTE: we will remove this once we get "pure transforms" module.
|
||||
query-processor
|
||||
request
|
||||
settings
|
||||
sql-tools
|
||||
system
|
||||
transforms
|
||||
transforms-base
|
||||
util
|
||||
enterprise/transforms-python}}}
|
||||
enterprise/transforms-python}}
|
||||
|
||||
enterprise/writable-connection
|
||||
{:team "Querying"
|
||||
:api #{}
|
||||
:uses #{premium-features}}}
|
||||
|
||||
;; 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.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
;; 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,
|
||||
#{;; 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!
|
||||
|
||||
@@ -75,6 +75,15 @@
|
||||
(contains? module-api-namespaces ns-symb)
|
||||
(contains? module-friends current-module))))
|
||||
|
||||
(defn- rest-module? [module]
|
||||
(str/ends-with? module "-rest"))
|
||||
|
||||
(defn- routes-module? [module]
|
||||
(str/ends-with? module "-routes"))
|
||||
|
||||
(defn- core-module? [module]
|
||||
(str/ends-with? module "core"))
|
||||
|
||||
(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."
|
||||
@@ -93,4 +102,15 @@
|
||||
(format "Namespace %s is not an allowed external API namespace for the %s module. [:metabase/modules %s :api]"
|
||||
required-namespace
|
||||
required-module
|
||||
required-module)))))
|
||||
required-module)
|
||||
|
||||
;; (for now) rest modules are allowed to use one another; `routes` is ok because it collects routes together
|
||||
;; and `core` is ok because [[metabase.core.init]] might need to init some of the `-routes` modules'
|
||||
;; namespaces
|
||||
(and (not ((some-fn rest-module? routes-module? core-module?) current-module))
|
||||
(rest-module? required-module))
|
||||
(format "Do not use -rest modules (%s) in non-rest modules (%s) -- move things from %s to %s if needed"
|
||||
required-module
|
||||
current-module
|
||||
required-module
|
||||
(symbol (str/replace required-module #"-rest$" "")))))))
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
help-link-custom-destination
|
||||
instance-creation
|
||||
is-hosted?
|
||||
is-metabot-enabled
|
||||
jdbc-data-warehouse-max-connection-pool-size
|
||||
jdbc-data-warehouse-unreturned-connection-timeout-seconds
|
||||
jwt-attribute-email
|
||||
|
||||
@@ -120,7 +120,10 @@
|
||||
metabase.dashboards.models.dashboard-test/with-dash-in-collection [[:inner 0]]
|
||||
metabase.driver-api.core/match [[:inner 0]]
|
||||
metabase.driver-api.core/match-one [[:inner 0]]
|
||||
metabase.driver-api.core/match-lite [[:inner 0]]
|
||||
metabase.driver-api.core/match-many [[:inner 0]]
|
||||
metabase.driver-api.core/replace [[:inner 0]]
|
||||
metabase.driver-api.core/replace-lite [[:inner 0]]
|
||||
metabase.driver-api.core/replace-in [[:inner 0]]
|
||||
metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]]
|
||||
metabase.driver.druid.client/DELETE [[:default]]
|
||||
@@ -156,7 +159,9 @@
|
||||
metabase.lib.util.match/match [[:inner 0]]
|
||||
metabase.lib.util.match/match-one [[:inner 0]]
|
||||
metabase.lib.util.match/match-lite [[:inner 0]]
|
||||
metabase.lib.util.match/match-many [[:inner 0]]
|
||||
metabase.lib.util.match/replace [[:inner 0]]
|
||||
metabase.lib.util.match/replace-lite [[:inner 0]]
|
||||
metabase.lib.util.match/replace-in [[:inner 0]]
|
||||
metabase.models.interface/define-batched-hydration-method [[:inner 0]]
|
||||
metabase.models.interface/define-simple-hydration-method [[:inner 0]]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/java:21
|
||||
|
||||
# Set up nodesource, install node, yarn, fontconfig for static viz, rlwrap for dev ergonomics
|
||||
# Set up nodesource, install node, bun, fontconfig for static viz, rlwrap for dev ergonomics
|
||||
|
||||
RUN ( curl -fsSL https://deb.nodesource.com/setup_22.x | bash ) \
|
||||
&& export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get update && apt-get -y install --no-install-recommends nodejs rlwrap fontconfig \
|
||||
&& npm install --global yarn
|
||||
&& npm install -g bun
|
||||
|
||||
# install Clojure
|
||||
RUN curl https://download.clojure.org/install/linux-install-1.12.0.1488.sh | bash
|
||||
|
||||
@@ -100,7 +100,7 @@ METASTORE_DEV_SERVER_URL="https://token-check.staging.metabase.com"
|
||||
#
|
||||
# The whole environment is already orchestrated via deps.edn using :e2e alias.
|
||||
# Treat it as a source of truth for JVM flags and ENVs whether you decide to run
|
||||
# backend on your own, or using `yarn test-cypress` (local test runner).
|
||||
# backend on your own, or using `bun run test-cypress` (local test runner).
|
||||
#
|
||||
# You don't _have to_ export any of these vars (except for the tokens below).
|
||||
# Things will still work.
|
||||
|
||||
8
.gitattributes
vendored
8
.gitattributes
vendored
@@ -1,7 +1,9 @@
|
||||
# Custom merge driver for YAML migration files
|
||||
# Treats each changeset as an atomic unit to avoid conflicts within changesets
|
||||
# Custom merge driver for per-release YAML migration files.
|
||||
# Treats each changeset as an atomic unit to avoid conflicts within changesets.
|
||||
# Only applies to per-release *_update_migrations.yaml files — directory-based
|
||||
# migrations (e.g. 060/*.yaml) have one file per migration and don't need this.
|
||||
# Execute this in your shell to enable custom merge driver:
|
||||
# echo '\n[include]\npath=../.gitconfig' >> .git/config
|
||||
#
|
||||
# TODO (Bryan 12/12/25) -- reenable this when we can completely verify correctness
|
||||
# resources/migrations/*.yaml merge=yaml-migrations
|
||||
# resources/migrations/*_update_migrations.yaml merge=yaml-migrations
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# execute this in your shell to enable custom merge driver:
|
||||
# echo '\n[include]\npath=../.gitconfig' >> .git/config
|
||||
[merge "yaml-migrations"]
|
||||
name = YAML migration changeset-aware merge
|
||||
name = Per-release YAML migration changeset-aware merge
|
||||
driver = bin/mage -merge-yaml-migrations %O %A %B %L %P
|
||||
|
||||
268
.github/CODEOWNERS
vendored
268
.github/CODEOWNERS
vendored
@@ -1,266 +1,30 @@
|
||||
# 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
|
||||
|
||||
# @metabase/core-backend-querying-platform
|
||||
src/metabase/lib @metabase/core-backend-querying-platform
|
||||
src/metabase/lib_be @metabase/core-backend-querying-platform
|
||||
src/metabase/query_processor @metabase/core-backend-querying-platform
|
||||
|
||||
# @metabase/graphy
|
||||
src/metabase/graph @metabase/graphy
|
||||
enterprise/backend/src/metabase_enterprise/dependencies @metabase/graphy
|
||||
enterprise/frontend/src/metabase-enterprise/dependencies @metabase/graphy
|
||||
|
||||
# @metabase/embedding
|
||||
enterprise/frontend/src/embedding-sdk-package @metabase/embedding
|
||||
enterprise/frontend/src/embedding-sdk-bundle @metabase/embedding
|
||||
enterprise/frontend/src/embedding-sdk-shared @metabase/embedding
|
||||
frontend/src/metabase/embedding-sdk @metabase/embedding
|
||||
frontend/src/metabase/public/containers/PublicOrEmbeddedQuestion @metabase/embedding
|
||||
frontend/src/metabase/public/containers/PublicOrEmbeddedDashboard @metabase/embedding
|
||||
frontend/src/metabase/ui @metabase/core-frontend-ux-west
|
||||
# snowplow/* @metabase/data
|
||||
src/metabase/analytics/prometheus.clj @metabase/cloud-ops
|
||||
.github @metabase/devexp
|
||||
.clj-kondo @metabase/devexp
|
||||
deps.edn @metabase/backend-developers
|
||||
|
||||
# @metabase/tech-writers
|
||||
docs @metabase/tech-writers
|
||||
docs/developers-guide # exclude from tech-writer ownership
|
||||
|
||||
#
|
||||
# Backend modules <=> teams that own them
|
||||
#
|
||||
# @metabase/devexp
|
||||
.github @metabase/devexp
|
||||
|
||||
# modules/drivers @metabase/drivers
|
||||
src/metabase/driver/sql_jdbc/actions.clj @metabase/core-ux-east
|
||||
test/metabase/driver/sql_jdbc/actions_test.clj @metabase/core-ux-east
|
||||
|
||||
# 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-ux-east
|
||||
# enterprise/backend/test/metabase_enterprise/dashboard_subscription_filters @metabase/core-ux-east
|
||||
|
||||
# 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 ...
|
||||
# enterprise/backend/test/metabase_enterprise/search ...
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/serialization @metabase/core-ux-east
|
||||
# enterprise/backend/test/metabase_enterprise/serialization @metabase/core-ux-east
|
||||
|
||||
# enterprise/backend/src/metabase_enterprise/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-ux-east
|
||||
test/metabase/actions @metabase/core-ux-east
|
||||
|
||||
# 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-ux-east
|
||||
# test/metabase/channel @metabase/core-ux-east
|
||||
|
||||
# src/metabase/cloud_migration ...
|
||||
# test/metabase/cloud_migration ...
|
||||
|
||||
# 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/embed @metabase/embedding
|
||||
# test/metabase/embed @metabase/embedding
|
||||
|
||||
# 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 ...
|
||||
# test/metabase/integrations ...
|
||||
|
||||
# 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/model_persistence @metabase/core-backend-querying
|
||||
# test/metabase/model_persistence @metabase/core-backend-querying
|
||||
|
||||
# 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-ux-east
|
||||
# test/metabase/notification @metabase/core-ux-east
|
||||
|
||||
# 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/public_sharing @metabase/core-backend-admin-webapp
|
||||
# test/metabase/public_sharing @metabase/core-backend-admin-webapp
|
||||
|
||||
# src/metabase/pulse @metabase/core-ux-east
|
||||
# test/metabase/pulse @metabase/core-ux-east
|
||||
|
||||
# src/metabase/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 ...
|
||||
# test/metabase/search ...
|
||||
|
||||
# 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/sso @metabase/core-backend-admin-webapp
|
||||
# test/metabase/sso @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
|
||||
# @metabase/cloud-ops
|
||||
src/metabase/analytics/prometheus.clj @metabase/cloud-ops
|
||||
|
||||
@@ -5,7 +5,7 @@ PENDING CI / PENDING REVIEW / READY _(choose one and update accordingly)_
|
||||
- Reproduces #XXXXX
|
||||
|
||||
### How to test this manually?
|
||||
- `yarn test-cypress`
|
||||
- `bun run test-cypress`
|
||||
- `relative/path/to/the/file`
|
||||
> _(For still unfixed bug)_
|
||||
- Replace `it.skip()` with `it.only()` to run the test in isolation
|
||||
|
||||
28
.github/actions/fetch-artifact/action.yml
vendored
28
.github/actions/fetch-artifact/action.yml
vendored
@@ -45,17 +45,31 @@ runs:
|
||||
});
|
||||
|
||||
if (!artifacts.data?.artifacts?.[0]?.id) {
|
||||
throw new Error(`No artifacts found for ${artifactName}`);
|
||||
core.setFailed(`No artifacts found for ${artifactName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
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',
|
||||
});
|
||||
let download;
|
||||
const maxAttempts = 3;
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: artifact_id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
break;
|
||||
} catch (err) {
|
||||
console.log(`Attempt ${attempt}/${maxAttempts} failed: ${err.message}`);
|
||||
if (attempt === maxAttempts) throw err;
|
||||
const delay = attempt * 15000; // 15s, 30s
|
||||
console.log(`Retrying in ${delay / 1000}s...`);
|
||||
await new Promise(r => setTimeout(r, delay));
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/mb.zip`, Buffer.from(download.data));
|
||||
|
||||
|
||||
8
.github/actions/get-cache-keys/action.yml
vendored
8
.github/actions/get-cache-keys/action.yml
vendored
@@ -7,9 +7,15 @@ outputs:
|
||||
m2-cache-key:
|
||||
description: "The cache key for the Maven cache."
|
||||
value: ${{ steps.keys-factory.outputs.m2-cache-key }}
|
||||
m2-restore-key:
|
||||
description: "The fallback restore key prefix for the Maven cache."
|
||||
value: ${{ steps.keys-factory.outputs.m2-restore-key }}
|
||||
node-cache-key:
|
||||
description: "The cache key for the Node.js cache."
|
||||
value: ${{ steps.keys-factory.outputs.node-cache-key }}
|
||||
node-restore-key:
|
||||
description: "The fallback restore key prefix for the Node.js cache."
|
||||
value: ${{ steps.keys-factory.outputs.node-restore-key }}
|
||||
eslint-cache-key:
|
||||
description: "The cache key for Eslint cache"
|
||||
value: ${{ steps.keys-factory.outputs.eslint-cache-key }}
|
||||
@@ -31,7 +37,9 @@ runs:
|
||||
CACHE_SUFFIX="${{ runner.os }}-$WEEK_OF_YEAR"
|
||||
|
||||
echo "m2-cache-key=$M2_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "m2-restore-key=$M2_CACHE_PREFIX-" >> $GITHUB_OUTPUT
|
||||
echo "node-cache-key=$NODE_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "node-restore-key=$NODE_CACHE_PREFIX-" >> $GITHUB_OUTPUT
|
||||
echo "eslint-cache-key=$ESLINT_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
echo "cypress-cache-key=$CYPRESS_CACHE_PREFIX-$CACHE_SUFFIX" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
30
.github/actions/prepare-backend/action.yml
vendored
30
.github/actions/prepare-backend/action.yml
vendored
@@ -1,14 +1,13 @@
|
||||
name: Prepare back-end environment
|
||||
description: |
|
||||
Prepare the backend. Installs jdk, Clojure, and fetches the cache for m2.
|
||||
The CI config fetches `ci-test-config.json` from master and lets us ignore tests at the var level if they are flaking.
|
||||
inputs:
|
||||
java-version:
|
||||
required: true
|
||||
default: '21'
|
||||
default: "21"
|
||||
clojure-version:
|
||||
required: true
|
||||
default: '1.12.0.1488'
|
||||
default: "1.12.0.1488"
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -17,18 +16,33 @@ runs:
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ inputs.java-version }}
|
||||
distribution: 'temurin'
|
||||
distribution: "temurin"
|
||||
- name: Install Clojure CLI
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
curl -O https://download.clojure.org/install/linux-install-${{ inputs.clojure-version }}.sh &&
|
||||
sudo bash ./linux-install-${{ inputs.clojure-version }}.sh
|
||||
for attempt in 1 2 3; do
|
||||
curl --retry 3 --retry-delay 5 --fail -O https://download.clojure.org/install/linux-install-${{ inputs.clojure-version }}.sh \
|
||||
&& sudo bash ./linux-install-${{ inputs.clojure-version }}.sh \
|
||||
&& break
|
||||
echo "::warning::Clojure install attempt $attempt failed, retrying in 10s..."
|
||||
sleep 10
|
||||
done
|
||||
if ! clojure --version; then
|
||||
echo "::error::Clojure CLI installation failed after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Get M2 cache key
|
||||
uses: ./.github/actions/get-cache-keys
|
||||
id: get-cache-keys
|
||||
|
||||
- name: Install uv (for Python package installation)
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
enable-cache: false
|
||||
version: "0.10.2" # keep in sync with mise.toml
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Restore M2 cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
@@ -36,3 +50,5 @@ runs:
|
||||
~/.m2
|
||||
~/.gitlibs
|
||||
key: ${{ steps.get-cache-keys.outputs.m2-cache-key }}
|
||||
restore-keys: |
|
||||
${{ steps.get-cache-keys.outputs.m2-restore-key }}
|
||||
|
||||
15
.github/actions/prepare-cypress/action.yml
vendored
15
.github/actions/prepare-cypress/action.yml
vendored
@@ -3,7 +3,7 @@ description: Cypress preparation steps
|
||||
|
||||
inputs:
|
||||
working-directory:
|
||||
description: "Optional working directory for yarn commands"
|
||||
description: "Optional working directory for bun commands"
|
||||
required: false
|
||||
default: "."
|
||||
|
||||
@@ -15,11 +15,10 @@ outputs:
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Install Chrome v111
|
||||
- name: Install Chrome v144
|
||||
uses: browser-actions/setup-chrome@v2
|
||||
with:
|
||||
# https://chromium.cypress.io/linux/stable/111.0.5563.146
|
||||
chrome-version: 1097615
|
||||
chrome-version: 144
|
||||
id: setup-chrome
|
||||
|
||||
- name: Get cache keys
|
||||
@@ -34,9 +33,9 @@ runs:
|
||||
|
||||
- name: Ensure that Cypress executable is ready
|
||||
run: |
|
||||
yarn cypress install
|
||||
yarn cypress cache path
|
||||
yarn cypress cache list
|
||||
yarn cypress verify
|
||||
bun run cypress install
|
||||
bun run cypress cache path
|
||||
bun run cypress cache list
|
||||
bun run cypress verify
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
27
.github/actions/prepare-frontend/action.yml
vendored
27
.github/actions/prepare-frontend/action.yml
vendored
@@ -11,11 +11,10 @@ inputs:
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Prepare Node.js
|
||||
uses: actions/setup-node@v4
|
||||
- name: Prepare Node + Bun environment
|
||||
uses: ./.github/actions/prepare-node-bun
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }} # this overrides, if present
|
||||
node-version-file: ".nvmrc"
|
||||
node-version: ${{ inputs.node-version }}
|
||||
|
||||
- name: Get cache keys
|
||||
uses: ./.github/actions/get-cache-keys
|
||||
@@ -28,13 +27,15 @@ runs:
|
||||
~/.m2
|
||||
~/.gitlibs
|
||||
key: ${{ steps.get-cache-keys.outputs.m2-cache-key }}
|
||||
restore-keys: |
|
||||
${{ steps.get-cache-keys.outputs.m2-restore-key }}
|
||||
|
||||
- name: Get yarn cache directory
|
||||
id: yarn-cache-dir
|
||||
- name: Get bun cache directory
|
||||
id: bun-cache
|
||||
run: |
|
||||
YARN_CACHE_DIR=$(yarn cache dir)
|
||||
echo "Yarn cache directory: $YARN_CACHE_DIR"
|
||||
echo "dir=$YARN_CACHE_DIR" >> $GITHUB_OUTPUT
|
||||
BUN_CACHE_DIR=$(bun pm cache)
|
||||
echo "Bun cache directory: $BUN_CACHE_DIR"
|
||||
echo "dir=$BUN_CACHE_DIR" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Restore node_modules cache
|
||||
@@ -42,17 +43,19 @@ runs:
|
||||
id: node-modules-cache
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir.outputs.dir }}
|
||||
${{ steps.bun-cache.outputs.dir }}
|
||||
node_modules
|
||||
key: ${{ steps.get-cache-keys.outputs.node-cache-key }}
|
||||
restore-keys: |
|
||||
${{ steps.get-cache-keys.outputs.node-restore-key }}
|
||||
|
||||
# postinstall lifecycle hook can be dangerous in CI so we only run it locally (see package.json)
|
||||
# If a job wants to patch packages, it does so explicitly in the CI. This setting is opt-out.
|
||||
# For example, we never want to cache node_modules with patches already applied so cache-generator.yml skips that step.
|
||||
- name: Clean install
|
||||
run: yarn install --frozen-lockfile --prefer-offline
|
||||
run: bun install --frozen-lockfile
|
||||
shell: bash
|
||||
- name: Patch frontend dependencies
|
||||
if: inputs.apply-patches == 'true'
|
||||
run: yarn patch-package
|
||||
run: bun run patch-package
|
||||
shell: bash
|
||||
|
||||
20
.github/actions/prepare-node-bun/action.yml
vendored
Normal file
20
.github/actions/prepare-node-bun/action.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Prepare Node + Bun environment
|
||||
description: "Set up Node.js and Bun for CI jobs"
|
||||
inputs:
|
||||
node-version:
|
||||
description: "The version of Node.js to use, overriding .nvmrc"
|
||||
required: false
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Prepare Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ github.token }}
|
||||
14
.github/actions/prepare-release-scripts/action.yml
vendored
Normal file
14
.github/actions/prepare-release-scripts/action.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: Prepare Release Scripts
|
||||
description: Set up bun and build release scripts
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ github.token }}
|
||||
|
||||
- name: Build release scripts
|
||||
shell: bash
|
||||
run: cd ${{ github.workspace }}/release && bun install --frozen-lockfile && bun run build
|
||||
132
.github/actions/prepare-shoppy-e2e/action.yml
vendored
Normal file
132
.github/actions/prepare-shoppy-e2e/action.yml
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
name: Prepare Shoppy E2E environment
|
||||
description: Shared setup steps for Shoppy E2E tests (regular and synthetic)
|
||||
|
||||
inputs:
|
||||
commit:
|
||||
description: "Commit SHA for artifact retrieval"
|
||||
required: true
|
||||
sample-app-branch-name:
|
||||
description: "Branch name for the sample app"
|
||||
required: true
|
||||
sample-app-environment:
|
||||
description: "Environment (production/development)"
|
||||
required: true
|
||||
staging-mb-all-features-token:
|
||||
description: "Staging MB all features token"
|
||||
required: true
|
||||
shoppy-iam-role:
|
||||
description: "Shoppy IAM role for AWS credentials"
|
||||
required: true
|
||||
shoppy-metabase-app-db-s3-bucket:
|
||||
description: "S3 bucket for Shoppy app DB dump"
|
||||
required: true
|
||||
shoppy-metabase-app-db-s3-key:
|
||||
description: "S3 key for Shoppy app DB dump"
|
||||
required: true
|
||||
shoppy-metabase-app-db-user:
|
||||
description: "Shoppy Metabase app DB user"
|
||||
required: true
|
||||
shoppy-metabase-app-db-database:
|
||||
description: "Shoppy Metabase app DB database"
|
||||
required: true
|
||||
metastore-dev-server-url:
|
||||
description: "Metastore dev server URL"
|
||||
required: true
|
||||
shoppy-datadog-application-id:
|
||||
description: "Shoppy Datadog application ID"
|
||||
required: true
|
||||
shoppy-datadog-client-token:
|
||||
description: "Shoppy Datadog client token"
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
chrome-path:
|
||||
description: "Path to Chrome executable"
|
||||
value: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
sample_app_branch_exists:
|
||||
description: "Whether the sample app branch exists"
|
||||
value: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: ./.github/actions/maximize-disk-space
|
||||
|
||||
# We have to use the latest version that does not have this issue https://github.com/docker/compose/pull/12752
|
||||
- name: Set up Docker Compose
|
||||
uses: docker/setup-compose-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Retrieve uberjar artifact for ee
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
commit: ${{ inputs.commit }}
|
||||
edition: ee
|
||||
extract-path: "target/uberjar"
|
||||
output-name: "metabase.jar"
|
||||
|
||||
- name: Retrieve Embedding SDK dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: embedding-sdk-${{ inputs.commit }}
|
||||
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
|
||||
shell: bash
|
||||
env:
|
||||
SAMPLE_APP_BRANCH_NAME: ${{ inputs.sample-app-branch-name }}
|
||||
run: |
|
||||
REPO_URL="https://github.com/metabase/shoppy.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: Configure AWS credentials
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ inputs.shoppy-iam-role }}
|
||||
role-session-name: GitHub_to_AWS_via_FederatedOIDC
|
||||
aws-region: us-east-1
|
||||
|
||||
- name: Download Shoppy's App DB PostgreSQL dump from S3
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
aws s3 cp s3://${{ inputs.shoppy-metabase-app-db-s3-bucket }}/${{ inputs.shoppy-metabase-app-db-s3-key }} ./e2e/tmp/db_dumps/shoppy_metabase_app_db_dump.sql
|
||||
|
||||
- name: Prepare and launch Shoppy containers
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
|
||||
shell: bash
|
||||
env:
|
||||
SAMPLE_APP_BRANCH_NAME: ${{ inputs.sample-app-branch-name }}
|
||||
SAMPLE_APP_ENVIRONMENT: ${{ inputs.sample-app-environment }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ inputs.staging-mb-all-features-token }}
|
||||
METASTORE_DEV_SERVER_URL: ${{ inputs.metastore-dev-server-url }}
|
||||
METABASE_APP_DB_USER: ${{ inputs.shoppy-metabase-app-db-user }}
|
||||
METABASE_APP_DB: ${{ inputs.shoppy-metabase-app-db-database }}
|
||||
SHOPPY_DATADOG_APPLICATION_ID: ${{ inputs.shoppy-datadog-application-id }}
|
||||
SHOPPY_DATADOG_CLIENT_TOKEN: ${{ inputs.shoppy-datadog-client-token }}
|
||||
SHOPPY_DATADOG_SITE: "us5.datadoghq.com"
|
||||
SHOPPY_DATADOG_ENV: "staging"
|
||||
run: npx tsx ./e2e/runner/embedding-sdk/sample-apps/start-ci.ts shoppy-e2e
|
||||
|
||||
- name: Prune build cache
|
||||
shell: bash
|
||||
run: docker builder prune -af
|
||||
|
||||
- name: Log free disk space
|
||||
shell: bash
|
||||
run: df -h /
|
||||
110
.github/actions/sdk-bundle-size-check/action.yml
vendored
Normal file
110
.github/actions/sdk-bundle-size-check/action.yml
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
name: Embedding SDK Bundle Size Check
|
||||
description: |
|
||||
Downloads the current branch's uberjar and builds the base ref's
|
||||
embedding-sdk.js from source, compares their sizes,
|
||||
and sets output if the size change exceeds the threshold.
|
||||
|
||||
inputs:
|
||||
edition:
|
||||
description: 'Metabase edition (ee or oss)'
|
||||
required: true
|
||||
base-ref-commit:
|
||||
description: 'Commit SHA of the base ref (e.g. merge base).'
|
||||
required: true
|
||||
threshold-percent:
|
||||
description: 'Percent threshold for bundle size change'
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
status:
|
||||
description: "Bundle size status: increased, decreased, or stable"
|
||||
value: ${{ steps.compare.outputs.status }}
|
||||
size_change_percent:
|
||||
description: "size change percent (positive = increase, negative = decrease)"
|
||||
value: ${{ steps.compare.outputs.size_change_percent }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Prepare backend
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Prepare frontend
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Download current uberjar
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-${{ inputs.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
path: temp/current-uberjar
|
||||
- name: Extract embedding-sdk bundles from current uberjar
|
||||
shell: bash
|
||||
run: |
|
||||
# Try new layout first (app/embedding-sdk/legacy/), fall back to old layout (app/embedding-sdk.js)
|
||||
unzip -j temp/current-uberjar/target/uberjar/metabase.jar \
|
||||
"frontend_client/app/embedding-sdk/legacy/embedding-sdk.js" \
|
||||
-d temp/current-bundle 2>/dev/null || \
|
||||
unzip -j temp/current-uberjar/target/uberjar/metabase.jar \
|
||||
"frontend_client/app/embedding-sdk.js" \
|
||||
-d temp/current-bundle
|
||||
|
||||
# Extract chunks directory if it exists (new layout only)
|
||||
mkdir -p temp/current-chunks
|
||||
unzip -j temp/current-uberjar/target/uberjar/metabase.jar \
|
||||
"frontend_client/app/embedding-sdk/chunks/*" \
|
||||
-d temp/current-chunks 2>/dev/null || true
|
||||
- name: Checkout base ref
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.base-ref-commit }}
|
||||
path: temp/base-ref-src
|
||||
- name: Build base-ref embedding-sdk.js
|
||||
shell: bash
|
||||
run: |
|
||||
cd temp/base-ref-src
|
||||
bun install
|
||||
MB_EDITION=${{ inputs.edition }} bun run build-release
|
||||
- name: Compare sizes and set output
|
||||
id: compare
|
||||
shell: bash
|
||||
run: |
|
||||
# --- Legacy (monolithic) bundle comparison ---
|
||||
current_size=$(wc -c < temp/current-bundle/embedding-sdk.js) || { echo "::error::Failed to read current bundle"; exit 1; }
|
||||
|
||||
# Base ref: try new layout first, fall back to old
|
||||
if [ -f "temp/base-ref-src/resources/frontend_client/app/embedding-sdk/legacy/embedding-sdk.js" ]; then
|
||||
base_ref_size=$(wc -c < "temp/base-ref-src/resources/frontend_client/app/embedding-sdk/legacy/embedding-sdk.js")
|
||||
else
|
||||
base_ref_size=$(wc -c < "temp/base-ref-src/resources/frontend_client/app/embedding-sdk.js")
|
||||
fi
|
||||
[ "$current_size" -gt 0 ] && [ "$base_ref_size" -gt 0 ] || { echo "::error::Bundle file is empty"; exit 1; }
|
||||
|
||||
diff=$((current_size - base_ref_size))
|
||||
percent=$(( diff * 100 / base_ref_size ))
|
||||
|
||||
echo "=== Legacy (monolithic) bundle ==="
|
||||
echo "Current size: $current_size bytes"
|
||||
echo "Base ref size: $base_ref_size bytes"
|
||||
echo "Difference: $diff bytes ($percent%)"
|
||||
|
||||
threshold="${{ inputs.threshold-percent }}"
|
||||
if [ "$percent" -gt "$threshold" ]; then
|
||||
status="increased"
|
||||
elif [ "$percent" -lt "$(( -threshold ))" ]; then
|
||||
status="decreased"
|
||||
else
|
||||
status="stable"
|
||||
fi
|
||||
|
||||
echo "status=$status" >> $GITHUB_OUTPUT
|
||||
echo "size_change_percent=$percent" >> $GITHUB_OUTPUT
|
||||
|
||||
# --- Chunked bundle (informational) ---
|
||||
if ls temp/current-chunks/*.js 1>/dev/null 2>&1; then
|
||||
chunks_size=$(cat temp/current-chunks/*.js | wc -c)
|
||||
echo ""
|
||||
echo "=== Chunked bundle (bootstrap + chunks) ==="
|
||||
echo "Total chunked size: $chunks_size bytes"
|
||||
echo "chunks_size=$chunks_size" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo ""
|
||||
echo "No chunked bundle found (expected on master)"
|
||||
fi
|
||||
7
.github/actions/test-driver/action.yml
vendored
7
.github/actions/test-driver/action.yml
vendored
@@ -17,6 +17,11 @@ inputs:
|
||||
default: 'ee'
|
||||
description: Edition to test (oss or ee)
|
||||
|
||||
outputs:
|
||||
test-outcome:
|
||||
description: "The outcome of the test run (success, failure, cancelled, skipped)"
|
||||
value: ${{ steps.run-test.outcome }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
@@ -27,7 +32,7 @@ runs:
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Build static viz frontend
|
||||
if: ${{ inputs.build-static-viz == 'true' }}
|
||||
run: yarn build-static-viz
|
||||
run: bun run build-static-viz
|
||||
shell: bash
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
|
||||
@@ -21,6 +21,8 @@ inputs:
|
||||
variant:
|
||||
required: false
|
||||
default: ""
|
||||
previous-step-outcome:
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -57,3 +59,4 @@ runs:
|
||||
dry-run: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == true }} # quarantine, but do not upload results
|
||||
token: ${{ inputs.trunk-api-token }}
|
||||
use-cache: true
|
||||
previous-step-outcome: ${{ inputs.previous-step-outcome }}
|
||||
|
||||
75
.github/copilot-instructions.md
vendored
75
.github/copilot-instructions.md
vendored
@@ -1,52 +1,55 @@
|
||||
# Metabase Developer Assistant Instructions
|
||||
|
||||
## Frontend Development
|
||||
For detailed coding standards and conventions, see `CLAUDE.md` and the skills in `.claude/skills/`.
|
||||
|
||||
### Repository Structure
|
||||
## Code Review Standards
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── metabase/ # Main application components and pages
|
||||
├── metabase-lib/ # Query building and data modeling utilities
|
||||
├── metabase-types/ # TypeScript type definitions
|
||||
└── metabase/ui/ # Design system components
|
||||
Review focus areas: Security → Performance → Testing → Documentation (in priority order)
|
||||
|
||||
e2e/ # Cypress end-to-end tests
|
||||
frontend/test/ # Jest unit tests
|
||||
```
|
||||
When reviewing code, use these emoji prefixes to categorize feedback:
|
||||
- 🔒 Security concerns
|
||||
- ⚡ Performance opportunities
|
||||
- 🧹 Cleanup needs
|
||||
- 📚 Documentation gaps
|
||||
- 🚨 Blocking issues
|
||||
- 💭 Clarification questions
|
||||
|
||||
### Technology Stack
|
||||
### Security Critical Issues
|
||||
|
||||
- **Framework**: React 18 with TypeScript
|
||||
- **State**: Redux Toolkit
|
||||
- **Styling**: CSS Modules (preferred) > Emotion styled-components
|
||||
- **UI**: `metabase/ui` components built with Mantine v8
|
||||
- **Build**: Rspack (primary), Webpack (legacy)
|
||||
- **Testing**: Jest + React Testing Library, Cypress
|
||||
- Check for hardcoded secrets, API keys, or credentials
|
||||
- Look for SQL injection and XSS vulnerabilities
|
||||
- Verify proper input validation and sanitization
|
||||
- Review authentication and authorization logic
|
||||
- Ensure enterprise features use the plugin system (no enterprise code in OSS)
|
||||
|
||||
### Coding Standards
|
||||
### Performance Red Flags
|
||||
|
||||
#### Component Preferences
|
||||
- Identify N+1 database query problems
|
||||
- Spot inefficient loops and algorithmic issues
|
||||
- Check for memory leaks and resource cleanup
|
||||
- Review caching opportunities for expensive operations
|
||||
|
||||
- Prefer `metabase/ui`components over `metabase/common/components`
|
||||
- Use `.tsx` for components, `.ts` for utilities
|
||||
### Code Quality Essentials
|
||||
|
||||
#### Styling
|
||||
- Functions should be focused and appropriately sized
|
||||
- Use clear, descriptive naming conventions
|
||||
- Ensure proper error handling throughout
|
||||
- Remove dead code and unused imports
|
||||
|
||||
ALWAYS prefer Mantine style props,then CSS modules. DO NOT suggest styled components, they are deprecated.
|
||||
### Review Style
|
||||
|
||||
#### TypeScript Migration
|
||||
- Only flag actual issues worth mentioning
|
||||
- Do not post "looks good" comments or congratulate following conventions
|
||||
- Be specific and actionable in feedback
|
||||
- Explain the "why" behind recommendations
|
||||
- Ask clarifying questions when code intent is unclear
|
||||
|
||||
When heavily editing `.js`/`.jsx` files, create a separate PR to convert to TypeScript first, then implement changes.
|
||||
### Review Format
|
||||
|
||||
#### Enterprise Features
|
||||
- Condense feedback into as few comments as possible
|
||||
- Prefer a single summary comment over dozens of inline comments
|
||||
- Only use inline comments for critical issues that require immediate attention
|
||||
- For non-critical issues, link to the specific line from the summary instead
|
||||
- Delete all stale comments and summaries on each new push or CI run
|
||||
|
||||
Enterprise functionality MUST use the plugin system. It is very important to not expose enterprise code in the OSS version.
|
||||
|
||||
#### Testing Requirements
|
||||
|
||||
All PRs should include tests. Prefer Unit tests over E2E tests.
|
||||
|
||||
#### Localization
|
||||
|
||||
All user-facing strings MUST be localized using the ttag library. Localized strings should be complete phrases, do not concatenate a few separately localized strings. You should add context to strings where the meaning of the string might not be obvious in isolation: e.g. "Home" might have different words in some languages depending on whether you're talking about a dwelling or the landing page for a website.
|
||||
Language-specific rules are in `.github/instructions/*.instructions.md`.
|
||||
|
||||
16
.github/file-paths.yaml
vendored
16
.github/file-paths.yaml
vendored
@@ -26,11 +26,11 @@ frontend_ci: &frontend_ci
|
||||
- ".github/workflows/frontend.yml"
|
||||
|
||||
frontend_configs: &frontend_configs
|
||||
- "yarn.lock"
|
||||
- "bun.lock"
|
||||
- "**/tsconfig*.json"
|
||||
- "package.json"
|
||||
- "babel.config.json"
|
||||
- ".eslintrc.js"
|
||||
- "eslint.config.mjs"
|
||||
- "postcss.config.js"
|
||||
- "rspack.config.js"
|
||||
- "rspack.main.config.js"
|
||||
@@ -199,3 +199,15 @@ cljs:
|
||||
- *shared_specs
|
||||
- ".github/workflows/cljs.yml"
|
||||
- "shadow-cljs.edn"
|
||||
|
||||
openapi:
|
||||
- ".github/workflows/openapi.yml"
|
||||
- "src/metabase/api/**"
|
||||
- "src/metabase/api_routes/**"
|
||||
- "src/metabase/*_rest/api.clj"
|
||||
- "src/metabase/*/api.clj"
|
||||
- "src/metabase/*/api/*.clj"
|
||||
- "enterprise/backend/src/metabase_enterprise/**/api.clj"
|
||||
- "enterprise/backend/src/metabase_enterprise/**/api/*.clj"
|
||||
- "resources/openapi/**"
|
||||
- "package.json"
|
||||
|
||||
9
.github/instructions/clojure.instructions.md
vendored
Normal file
9
.github/instructions/clojure.instructions.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
applyTo: "**/*.{clj,cljs,cljc}"
|
||||
---
|
||||
|
||||
# Clojure/ClojureScript Code Review
|
||||
|
||||
When reviewing Clojure or ClojureScript code, apply the guidelines in these files:
|
||||
- `.claude/skills/_shared/clojure-style-guide.md`
|
||||
- `.claude/skills/clojure-review/SKILL.md`
|
||||
9
.github/instructions/docs.instructions.md
vendored
Normal file
9
.github/instructions/docs.instructions.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
applyTo: "docs/**/*.md"
|
||||
---
|
||||
|
||||
# Documentation Review
|
||||
|
||||
When reviewing documentation, apply the guidelines in these files:
|
||||
- `.claude/skills/_shared/metabase-style-guide.md`
|
||||
- `.claude/skills/docs-review/SKILL.md`
|
||||
11
.github/instructions/typescript.instructions.md
vendored
Normal file
11
.github/instructions/typescript.instructions.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
applyTo: "**/*.{ts,tsx,js,jsx}"
|
||||
---
|
||||
|
||||
# TypeScript/JavaScript Code Review
|
||||
|
||||
When reviewing TypeScript or JavaScript code, apply the guidelines in these files:
|
||||
|
||||
- `.claude/skills/typescript-review/SKILL.md`
|
||||
- `docs/developers-guide/frontend.md`
|
||||
- `frontend/CLAUDE.md`
|
||||
52
.github/scripts/build-cross-version-matrix.js
vendored
Normal file
52
.github/scripts/build-cross-version-matrix.js
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// This script is used in .github/workflows/cross-version.yml
|
||||
// to generate a matrix for cross-version migration testing
|
||||
|
||||
/**
|
||||
* Generate a CI matrix for cross-version migration testing (EE only).
|
||||
*
|
||||
* Creates pairs of [source, target] for testing HEAD against the last N
|
||||
* minor versions in both upgrade and downgrade directions.
|
||||
*
|
||||
* @param {number} currentVersion - The current major version number (e.g., 59)
|
||||
* @param {number} count - Number of previous minor versions to test against (default: 3)
|
||||
* @returns {{ config: Array<{ source: string, target: string }> }}
|
||||
*
|
||||
* @example
|
||||
* buildMatrix(59)
|
||||
* // Returns:
|
||||
* // {
|
||||
* // config: [
|
||||
* // { source: "HEAD", target: "v1.59.x" },
|
||||
* // { source: "v1.59.x", target: "HEAD" },
|
||||
* // { source: "HEAD", target: "v1.58.x" },
|
||||
* // { source: "v1.58.x", target: "HEAD" },
|
||||
* // { source: "HEAD", target: "v1.57.x" },
|
||||
* // { source: "v1.57.x", target: "HEAD" },
|
||||
* // ]
|
||||
* // }
|
||||
*/
|
||||
function buildCrossVersionMatrix(currentVersion, count = 3) {
|
||||
if (!Number.isInteger(currentVersion) || currentVersion < 1) {
|
||||
throw new Error(
|
||||
`Invalid currentVersion: ${currentVersion}. Must be a positive integer.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!Number.isInteger(count) || count < 1) {
|
||||
throw new Error(`Invalid count: ${count}. Must be a positive integer.`);
|
||||
}
|
||||
|
||||
const config = Array.from({ length: count }, (_, i) => currentVersion - i)
|
||||
.filter((majorVersion) => majorVersion >= 1)
|
||||
.flatMap((majorVersion) => {
|
||||
const version = `v1.${majorVersion}.x`;
|
||||
return [
|
||||
{ source: "HEAD", target: version },
|
||||
{ source: version, target: "HEAD" },
|
||||
];
|
||||
});
|
||||
|
||||
return { config };
|
||||
}
|
||||
|
||||
module.exports = { buildCrossVersionMatrix };
|
||||
242
.github/scripts/build-cross-version-matrix.unit.spec.js
vendored
Normal file
242
.github/scripts/build-cross-version-matrix.unit.spec.js
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
const { buildCrossVersionMatrix } = require("./build-cross-version-matrix");
|
||||
|
||||
describe("buildCrossVersionMatrix", () => {
|
||||
describe("default behavior", () => {
|
||||
it("should generate matrix with 3 versions by default", () => {
|
||||
const result = buildCrossVersionMatrix(59);
|
||||
expect(result).toEqual({
|
||||
config: [
|
||||
{ source: "HEAD", target: "v1.59.x" },
|
||||
{ source: "v1.59.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.58.x" },
|
||||
{ source: "v1.58.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.57.x" },
|
||||
{ source: "v1.57.x", target: "HEAD" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should use EE prefix (v1)", () => {
|
||||
const result = buildCrossVersionMatrix(50);
|
||||
expect(result.config[0].target).toBe("v1.50.x");
|
||||
expect(result.config[1].source).toBe("v1.50.x");
|
||||
});
|
||||
|
||||
it("should create pairs in both directions (upgrade and downgrade)", () => {
|
||||
const result = buildCrossVersionMatrix(59);
|
||||
const entries = result.config;
|
||||
|
||||
// Check HEAD -> version (upgrade test)
|
||||
expect(entries.filter((e) => e.source === "HEAD")).toHaveLength(3);
|
||||
// Check version -> HEAD (downgrade test)
|
||||
expect(entries.filter((e) => e.target === "HEAD")).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("custom count", () => {
|
||||
it("should generate matrix with custom count", () => {
|
||||
const result = buildCrossVersionMatrix(59, 5);
|
||||
expect(result.config).toHaveLength(10); // 5 versions × 2 directions
|
||||
expect(result.config).toEqual([
|
||||
{ source: "HEAD", target: "v1.59.x" },
|
||||
{ source: "v1.59.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.58.x" },
|
||||
{ source: "v1.58.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.57.x" },
|
||||
{ source: "v1.57.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.56.x" },
|
||||
{ source: "v1.56.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.55.x" },
|
||||
{ source: "v1.55.x", target: "HEAD" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should generate matrix with count=1", () => {
|
||||
const result = buildCrossVersionMatrix(59, 1);
|
||||
expect(result.config).toHaveLength(2);
|
||||
expect(result.config).toEqual([
|
||||
{ source: "HEAD", target: "v1.59.x" },
|
||||
{ source: "v1.59.x", target: "HEAD" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should generate matrix with large count", () => {
|
||||
const result = buildCrossVersionMatrix(59, 10);
|
||||
expect(result.config).toHaveLength(20);
|
||||
|
||||
// Check first and last entries
|
||||
expect(result.config[0]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.59.x",
|
||||
});
|
||||
expect(result.config[18]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.50.x",
|
||||
});
|
||||
expect(result.config[19]).toEqual({
|
||||
source: "v1.50.x",
|
||||
target: "HEAD",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", () => {
|
||||
it("should handle version 1", () => {
|
||||
const result = buildCrossVersionMatrix(1);
|
||||
expect(result).toEqual({
|
||||
config: [
|
||||
{ source: "HEAD", target: "v1.1.x" },
|
||||
{ source: "v1.1.x", target: "HEAD" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should stop when version would go below 1", () => {
|
||||
const result = buildCrossVersionMatrix(2, 5);
|
||||
// Can only generate v1.2.x and v1.1.x (2 versions, not 5)
|
||||
expect(result.config).toHaveLength(4);
|
||||
expect(result).toEqual({
|
||||
config: [
|
||||
{ source: "HEAD", target: "v1.2.x" },
|
||||
{ source: "v1.2.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.1.x" },
|
||||
{ source: "v1.1.x", target: "HEAD" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle high version numbers", () => {
|
||||
const result = buildCrossVersionMatrix(100, 2);
|
||||
expect(result).toEqual({
|
||||
config: [
|
||||
{ source: "HEAD", target: "v1.100.x" },
|
||||
{ source: "v1.100.x", target: "HEAD" },
|
||||
{ source: "HEAD", target: "v1.99.x" },
|
||||
{ source: "v1.99.x", target: "HEAD" },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("error handling", () => {
|
||||
it("should throw for undefined currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix()).toThrow(
|
||||
"Invalid currentVersion: undefined. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for non-integer currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix(59.5)).toThrow(
|
||||
"Invalid currentVersion: 59.5. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for zero currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix(0)).toThrow(
|
||||
"Invalid currentVersion: 0. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for negative currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix(-1)).toThrow(
|
||||
"Invalid currentVersion: -1. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for NaN currentVersion", () => {
|
||||
expect(() => buildCrossVersionMatrix(NaN)).toThrow(
|
||||
"Invalid currentVersion: NaN. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for non-integer count", () => {
|
||||
expect(() => buildCrossVersionMatrix(59, 2.5)).toThrow(
|
||||
"Invalid count: 2.5. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for zero count", () => {
|
||||
expect(() => buildCrossVersionMatrix(59, 0)).toThrow(
|
||||
"Invalid count: 0. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw for negative count", () => {
|
||||
expect(() => buildCrossVersionMatrix(59, -1)).toThrow(
|
||||
"Invalid count: -1. Must be a positive integer.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("matrix structure for GitHub Actions", () => {
|
||||
it("should return object with config array", () => {
|
||||
const result = buildCrossVersionMatrix(59);
|
||||
expect(result).toHaveProperty("config");
|
||||
expect(Array.isArray(result.config)).toBe(true);
|
||||
});
|
||||
|
||||
it("should produce valid JSON for GitHub Actions", () => {
|
||||
const result = buildCrossVersionMatrix(59);
|
||||
const json = JSON.stringify(result);
|
||||
expect(() => JSON.parse(json)).not.toThrow();
|
||||
|
||||
const parsed = JSON.parse(json);
|
||||
expect(parsed.config).toHaveLength(6);
|
||||
expect(parsed.config[0]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.59.x",
|
||||
});
|
||||
});
|
||||
|
||||
it("should have consistent entry structure", () => {
|
||||
const result = buildCrossVersionMatrix(59, 5);
|
||||
|
||||
result.config.forEach((entry) => {
|
||||
expect(entry).toHaveProperty("source");
|
||||
expect(entry).toHaveProperty("target");
|
||||
expect(typeof entry.source).toBe("string");
|
||||
expect(typeof entry.target).toBe("string");
|
||||
// Either source or target should be HEAD
|
||||
expect(entry.source === "HEAD" || entry.target === "HEAD").toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("version ordering", () => {
|
||||
it("should generate versions in descending order", () => {
|
||||
const result = buildCrossVersionMatrix(59, 5);
|
||||
const versions = result.config
|
||||
.filter((e) => e.source === "HEAD")
|
||||
.map((e) => e.target);
|
||||
|
||||
expect(versions).toEqual([
|
||||
"v1.59.x",
|
||||
"v1.58.x",
|
||||
"v1.57.x",
|
||||
"v1.56.x",
|
||||
"v1.55.x",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should interleave upgrade and downgrade tests", () => {
|
||||
const result = buildCrossVersionMatrix(59, 2);
|
||||
|
||||
expect(result.config[0]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.59.x",
|
||||
});
|
||||
expect(result.config[1]).toEqual({
|
||||
source: "v1.59.x",
|
||||
target: "HEAD",
|
||||
});
|
||||
expect(result.config[2]).toEqual({
|
||||
source: "HEAD",
|
||||
target: "v1.58.x",
|
||||
});
|
||||
expect(result.config[3]).toEqual({
|
||||
source: "v1.58.x",
|
||||
target: "HEAD",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
2
.github/scripts/setup-python-runner.sh
vendored
2
.github/scripts/setup-python-runner.sh
vendored
@@ -19,7 +19,7 @@ while [[ $# -gt 0 ]]; do
|
||||
done
|
||||
|
||||
echo "Cloning python-runner repository..."
|
||||
git clone --depth 1 https://${METABASE_AUTOMATION_USER_TOKEN}@github.com/metabase/python-runner-container.git python-runner
|
||||
git clone --depth 1 -b extensible-resource-schema https://${METABASE_AUTOMATION_USER_TOKEN}@github.com/metabase/python-runner-container.git python-runner
|
||||
|
||||
if [ "$SKIP_START" = false ]; then
|
||||
echo "Starting python-runner..."
|
||||
|
||||
23
.github/team.json
vendored
23
.github/team.json
vendored
@@ -4,21 +4,23 @@
|
||||
"name": "Querying",
|
||||
"label": ".Team/Querying",
|
||||
"members": [
|
||||
"ranquild",
|
||||
"romeovs",
|
||||
"bshepherdson",
|
||||
"metamben",
|
||||
"wzimrin",
|
||||
"ericnormand",
|
||||
"galdre"
|
||||
"rileythomp",
|
||||
"camsual"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Drivers",
|
||||
"label": ".Team/Drivers",
|
||||
"name": "Graphy",
|
||||
"label": ".Team/Graphy",
|
||||
"members": [
|
||||
"snoe",
|
||||
"rileythomp"
|
||||
"ranquild",
|
||||
"romeovs",
|
||||
"ericnormand",
|
||||
"galdre",
|
||||
"dpsutton",
|
||||
"escherize",
|
||||
"bgrabow"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -43,9 +45,6 @@
|
||||
"name": "DevEx",
|
||||
"label": ".Team/DevEx",
|
||||
"members": [
|
||||
"camsaul",
|
||||
"dpsutton",
|
||||
"escherize",
|
||||
"fraserdrops",
|
||||
"nemanjaglumac"
|
||||
]
|
||||
|
||||
32
.github/workflows/app-db.yml
vendored
32
.github/workflows/app-db.yml
vendored
@@ -15,14 +15,14 @@ jobs:
|
||||
be-tests-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MariaDB 10.2
|
||||
junit-name: be-tests-mariadb-10-2-ee
|
||||
image: circleci/mariadb:10.2.23
|
||||
- name: MariaDB 10.6
|
||||
junit-name: be-tests-mariadb-10-6-ee
|
||||
image: circleci/mariadb:10.6
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MariaDB Latest
|
||||
@@ -100,11 +100,12 @@ jobs:
|
||||
MB_MYSQL_SSL_TEST_USER: metabase
|
||||
MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
# "be-tests-mariadb-10-6-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
@@ -123,18 +124,19 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-mysql:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MySQL 8.0
|
||||
junit-name: be-tests-mysql-8-0-ee
|
||||
image: cimg/mysql:8.0
|
||||
- name: MySQL 8.4
|
||||
junit-name: be-tests-mysql-8-4-ee
|
||||
image: cimg/mysql:8.4
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
@@ -222,11 +224,12 @@ jobs:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_AURORA_IAM_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_AURORA_IAM_SECRET_ACCESS_KEY }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
# "be-tests-mariadb-10-6-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
@@ -245,18 +248,19 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-postgres:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Postgres 12.x
|
||||
- name: Postgres 14.x
|
||||
junit-name: postgres-ee
|
||||
docker-image: postgres:12-alpine
|
||||
docker-image: postgres:14-alpine
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
@@ -336,6 +340,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }} (${{ matrix.job.name }})
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
@@ -354,6 +359,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
app-db-tests-result:
|
||||
needs:
|
||||
|
||||
9
.github/workflows/backend-cloverage.yml
vendored
9
.github/workflows/backend-cloverage.yml
vendored
@@ -11,8 +11,9 @@ name: Backend-Cloverage
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 10 * * 1-5' # every weekday at 5am US eastern time
|
||||
# re-enable once we figure out the correctness of the reports in DEV-1104
|
||||
# schedule:
|
||||
# - cron: '0 10 * * 1-5' # every weekday at 5am US eastern time
|
||||
|
||||
jobs:
|
||||
setup-oss:
|
||||
@@ -44,7 +45,7 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Build static viz frontend
|
||||
run: yarn build-static-viz
|
||||
run: bun run build-static-viz
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Collect the test coverage
|
||||
@@ -91,7 +92,7 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Build static viz frontend
|
||||
run: yarn build-static-viz
|
||||
run: bun run build-static-viz
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
- name: Collect the test coverage
|
||||
|
||||
18
.github/workflows/backend.yml
vendored
18
.github/workflows/backend.yml
vendored
@@ -36,10 +36,22 @@ jobs:
|
||||
- run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood
|
||||
name: Run Eastwood linter
|
||||
|
||||
be-linter-eastwood-test:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
name: Eastwood (test)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood/test
|
||||
name: Run Eastwood linter on tests
|
||||
|
||||
be-tests:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -104,7 +116,7 @@ jobs:
|
||||
with:
|
||||
java-version: "${{ matrix.java-version }}"
|
||||
- name: Build static-viz frontend
|
||||
run: yarn build-static-viz
|
||||
run: bun run build-static-viz
|
||||
env:
|
||||
MB_EDITION: ${{ matrix.job.edition }}
|
||||
|
||||
@@ -130,6 +142,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.run-java-tests.outcome }}
|
||||
|
||||
- name: Publish Test Report (JUnit)
|
||||
uses: dorny/test-reporter@v1
|
||||
@@ -183,6 +196,7 @@ jobs:
|
||||
be-tests-result:
|
||||
needs:
|
||||
- be-linter-eastwood
|
||||
- be-linter-eastwood-test
|
||||
- be-linter-clj-kondo
|
||||
- be-cljfmt
|
||||
- be-check
|
||||
|
||||
35
.github/workflows/build-for-release.yml
vendored
35
.github/workflows/build-for-release.yml
vendored
@@ -19,9 +19,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Send build start message
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
@@ -79,9 +81,11 @@ jobs:
|
||||
exit 1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get Release Version
|
||||
uses: actions/github-script@v7
|
||||
id: canonical_version
|
||||
@@ -121,9 +125,11 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # IMPORTANT! to get all the branches
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get Release Branch
|
||||
uses: actions/github-script@v7
|
||||
id: release_branch
|
||||
@@ -135,12 +141,13 @@ jobs:
|
||||
const releaseBranch = getReleaseBranch(version);
|
||||
return releaseBranch;
|
||||
- name: Ensure that the specified commit exists in the ${{ steps.release_branch.outputs.result }} release branch
|
||||
run: |
|
||||
run: | # bash
|
||||
RELEASE_BRANCH=${{ steps.release_branch.outputs.result }}
|
||||
git checkout $RELEASE_BRANCH
|
||||
git branch --contains ${{ inputs.commit }} | grep -q $RELEASE_BRANCH \
|
||||
&& echo "Commit found in correct release branch" \
|
||||
|| (echo "Commit not found in correct release branch" && exit 1)
|
||||
git checkout -
|
||||
|
||||
get-build-requirements:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -152,9 +159,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get build dependencies
|
||||
uses: actions/github-script@v7
|
||||
id: dependencies
|
||||
|
||||
28
.github/workflows/cache-generator.yml
vendored
28
.github/workflows/cache-generator.yml
vendored
@@ -82,7 +82,7 @@ jobs:
|
||||
|
||||
# Frontend preparation and caches
|
||||
# `prepare-frontend` action (1) prepares Node.js and (2) fetches the node_modules cache if it exists.
|
||||
# It then (3) runs `yarn install --frozen-lockfile --prefer-offline` which ensures the local cache is fully populated.
|
||||
# It then (3) runs `bun install --frozen-lockfile` which ensures the local cache is fully populated.
|
||||
# IMPORTANT: Caching node_modules after patches are already applied will break any PR that attempts to either:
|
||||
# - upgrade a patched library
|
||||
# - change a patch
|
||||
@@ -92,31 +92,31 @@ jobs:
|
||||
with:
|
||||
apply-patches: "false"
|
||||
|
||||
- name: Get yarn cache directory
|
||||
id: yarn-cache-dir
|
||||
- name: Get bun cache directory
|
||||
id: bun-cache
|
||||
run: |
|
||||
YARN_CACHE_DIR=$(yarn cache dir)
|
||||
echo "Yarn cache directory: $YARN_CACHE_DIR"
|
||||
echo "dir=$YARN_CACHE_DIR" >> $GITHUB_OUTPUT
|
||||
BUN_CACHE_DIR=$(bun pm cache)
|
||||
echo "Bun cache directory: $BUN_CACHE_DIR"
|
||||
echo "dir=$BUN_CACHE_DIR" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Ensure that Cypress executable is ready
|
||||
run: |
|
||||
yarn cypress install
|
||||
yarn cypress cache path
|
||||
yarn cypress cache list
|
||||
yarn cypress verify
|
||||
bun run cypress install
|
||||
bun run cypress cache path
|
||||
bun run cypress cache list
|
||||
bun run cypress verify
|
||||
shell: bash
|
||||
|
||||
# Run Prettier to populate its cache (stored in node_modules/.cache/prettier)
|
||||
# so it gets saved as part of the node_modules cache below
|
||||
- name: Run Prettier to populate cache
|
||||
run: yarn lint-prettier-pure
|
||||
run: bun run lint-prettier-pure
|
||||
|
||||
- name: Update node_modules cache
|
||||
uses: ./.github/actions/update-cache
|
||||
with:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir.outputs.dir }}
|
||||
${{ steps.bun-cache.outputs.dir }}
|
||||
node_modules
|
||||
key: ${{ env.NODE_CACHE_KEY }}
|
||||
|
||||
@@ -128,10 +128,10 @@ jobs:
|
||||
|
||||
# ESLint cache (requires both backend and frontend)
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
run: bun run build-pure:cljs
|
||||
|
||||
- name: Run ESLint to populate cache
|
||||
run: yarn lint-eslint-pure
|
||||
run: bun run lint-eslint-pure
|
||||
|
||||
- name: Update eslint cache
|
||||
uses: ./.github/actions/update-cache
|
||||
|
||||
8
.github/workflows/check-milestone.yml
vendored
8
.github/workflows/check-milestone.yml
vendored
@@ -26,11 +26,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
fetch-depth: ${{ env.isNewReleaseBranch == 'false' && 3 || 0 }} # we need the full history to get all the commit messages
|
||||
ref: master # we want the logic from master, even though we're triggering on a release branch
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- uses: actions/github-script@v7
|
||||
if: ${{ env.isNewReleaseBranch == 'false' }}
|
||||
name: Set milestone for single commit
|
||||
|
||||
2
.github/workflows/chromatic.yml
vendored
2
.github/workflows/chromatic.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Compile CLJS
|
||||
run: NODE_ENV=development yarn build-pure:cljs
|
||||
run: NODE_ENV=development bun run build-pure:cljs
|
||||
- name: Publish to Chromatic
|
||||
uses: chromaui/action@v10.2.0
|
||||
env:
|
||||
|
||||
8
.github/workflows/claude-code-bug-fix.yml
vendored
8
.github/workflows/claude-code-bug-fix.yml
vendored
@@ -230,6 +230,12 @@ jobs:
|
||||
mkdir -p .claude-scratchpad
|
||||
echo "Scratchpad directory created for Claude's temporary verification files"
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Generate GitHub App Token for Claude
|
||||
uses: actions/create-github-app-token@v1
|
||||
id: claude-app-token
|
||||
@@ -244,7 +250,7 @@ jobs:
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
prompt: ${{ env.BUG_FIX_PROMPT }}
|
||||
claude_args: |
|
||||
--allowedTools "Bash(yarn test-unit*),Bash(yarn lint-eslint-pure),Bash(./bin/mage*),Bash(clojure*),Bash(find*),Bash(ls*),Bash(grep*),Write,Read,Glob,Grep,Edit,MultiEdit"
|
||||
--allowedTools "Bash(bun run test-unit*),Bash(bun run lint-eslint-pure),Bash(./bin/mage*),Bash(clojure*),Bash(find*),Bash(ls*),Bash(grep*),Write,Read,Glob,Grep,Edit,MultiEdit"
|
||||
--disallowedTools "Bash(git commit*),Bash(git push*),Bash(gh*)"
|
||||
|
||||
- name: Clean up scratchpad directory
|
||||
|
||||
98
.github/workflows/claude-code-review.yaml
vendored
98
.github/workflows/claude-code-review.yaml
vendored
@@ -1,98 +0,0 @@
|
||||
name: "Claude Code Review"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
- synchronize
|
||||
- labeled
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-claude-code-review
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
review:
|
||||
if: |
|
||||
(
|
||||
github.event.action == 'closed' &&
|
||||
github.event.pull_request.merged == true
|
||||
) ||
|
||||
contains(github.event.pull_request.labels.*.name, 'Claude Code Review')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
# Make sure the value of GITHUB_TOKEN will not be persisted in repo's config
|
||||
persist-credentials: false
|
||||
|
||||
# Apparently comments on specific lines and top-level comments about the PR as a whole are TWO TOTALLY DIFFERENT
|
||||
# TYPES OF THINGS. So we have to fetch and delete separately.
|
||||
- name: Delete old Claude PR review comments
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
shell: bash
|
||||
run: |
|
||||
# Get old comment IDs from the bot
|
||||
old_line_comment_ids=$(gh api "/repos/$REPO/pulls/$PR_NUMBER/comments" --jq '.[] | select(.user.login=="metabase-bot[bot]") | .id')
|
||||
echo "Deleting line comments: $old_line_comment_ids"
|
||||
# Delete each comment
|
||||
for id in $old_line_comment_ids; do
|
||||
gh api -X DELETE "/repos/$REPO/pulls/comments/$id"
|
||||
echo "Deleted line comment $id"
|
||||
done
|
||||
# Get old top-level comment IDs (technically "issue" comments on the PR?)
|
||||
old_top_level_comment_ids=$(gh api "/repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login=="metabase-bot[bot]") | .id')
|
||||
echo "Deleting top-level comments: $old_top_level_comment_ids"
|
||||
# Delete each comment
|
||||
for id in $old_top_level_comment_ids; do
|
||||
gh api -X DELETE "/repos/$REPO/issues/comments/$id"
|
||||
echo "Deleted top-level comment $id"
|
||||
done
|
||||
|
||||
- name: Generate GitHub App Token for Claude
|
||||
uses: actions/create-github-app-token@v1
|
||||
id: claude-app-token
|
||||
with:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
permission-contents: read # Required for reading repository files and PR diffs
|
||||
permission-pull-requests: write # Required for creating inline comments and PR comments
|
||||
|
||||
- name: Fetch Community Clojure Style Guide
|
||||
run: >-
|
||||
curl https://raw.githubusercontent.com/bbatsov/clojure-style-guide/refs/heads/master/README.adoc -o CLOJURE_STYLE_GUIDE.adoc
|
||||
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
prompt: |
|
||||
REPO: ${{ github.repository }}
|
||||
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||
|
||||
Review this pull request for compliance with project coding standards and conventions.
|
||||
|
||||
**Use the appropriate review skill based on the file types changed:**
|
||||
- Use clojure-review for Clojure/ClojureScript files
|
||||
- Use typescript-review for TypeScript/JavaScript files
|
||||
- Follow the review guidelines and checklists in each skill
|
||||
|
||||
DO NOT post any comments confirming that something is correct - only post comments for style violations or concerns.
|
||||
|
||||
Note: The PR branch is already checked out in the current working directory.
|
||||
|
||||
Use `gh pr comment` for top-level feedback.
|
||||
Use `mcp__github_inline_comment__create_inline_comment` to highlight specific code issues.
|
||||
Only post GitHub comments - don't submit review text as messages.
|
||||
|
||||
claude_args: |
|
||||
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),ReadFile(CLOJURE_STYLE_GUIDE.adoc)"
|
||||
103
.github/workflows/claude-docs-review.yml
vendored
103
.github/workflows/claude-docs-review.yml
vendored
@@ -1,103 +0,0 @@
|
||||
name: "Claude Docs Review"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
paths:
|
||||
- 'docs/**/*.md'
|
||||
- 'docs/**/*.mdx'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to review'
|
||||
required: true
|
||||
type: number
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-claude-docs-review
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.draft == false
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
# Make sure the value of GITHUB_TOKEN will not be persisted in repo's config
|
||||
persist-credentials: false
|
||||
|
||||
- name: Generate GitHub App Token for Claude
|
||||
uses: actions/create-github-app-token@v1
|
||||
id: claude-app-token
|
||||
with:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
permission-contents: read # Required for reading repository files and PR diffs
|
||||
permission-pull-requests: write # Required for creating inline comments and PR comments
|
||||
|
||||
- name: Delete old Claude docs review comments
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.claude-app-token.outputs.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
|
||||
shell: bash
|
||||
run: |
|
||||
# Delete pending reviews first (these can be deleted entirely)
|
||||
pending_review_ids=$(gh api "/repos/$REPO/pulls/$PR_NUMBER/reviews" --jq '.[] | select(.user.login=="metabase-bot[bot]" and .state=="PENDING") | .id')
|
||||
if [ -n "$pending_review_ids" ]; then
|
||||
echo "Deleting pending reviews: $pending_review_ids"
|
||||
for id in $pending_review_ids; do
|
||||
gh api -X DELETE "/repos/$REPO/pulls/$PR_NUMBER/reviews/$id"
|
||||
echo "Deleted pending review $id"
|
||||
done
|
||||
fi
|
||||
|
||||
# Delete individual inline comments from submitted reviews (can't delete the review itself)
|
||||
old_line_comment_ids=$(gh api "/repos/$REPO/pulls/$PR_NUMBER/comments" --jq '.[] | select(.user.login=="metabase-bot[bot]") | .id')
|
||||
if [ -n "$old_line_comment_ids" ]; then
|
||||
echo "Deleting line comments: $old_line_comment_ids"
|
||||
for id in $old_line_comment_ids; do
|
||||
gh api -X DELETE "/repos/$REPO/pulls/comments/$id"
|
||||
echo "Deleted line comment $id"
|
||||
done
|
||||
fi
|
||||
|
||||
# Delete top-level review comments
|
||||
old_top_level_comment_ids=$(gh api "/repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login=="metabase-bot[bot]") | .id')
|
||||
if [ -n "$old_top_level_comment_ids" ]; then
|
||||
echo "Deleting top-level comments: $old_top_level_comment_ids"
|
||||
for id in $old_top_level_comment_ids; do
|
||||
gh api -X DELETE "/repos/$REPO/issues/comments/$id"
|
||||
echo "Deleted top-level comment $id"
|
||||
done
|
||||
fi
|
||||
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
show_full_output: true
|
||||
prompt: |
|
||||
REPO: ${{ github.repository }}
|
||||
PR NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
|
||||
|
||||
Review the documentation changes in this pull request.
|
||||
|
||||
Use the docs-review skill to review all changed documentation files for compliance with the Metabase writing style guide.
|
||||
|
||||
IMPORTANT: Only review markdown files (.md and .mdx). Skip any .txt files or other non-markdown files.
|
||||
|
||||
DO NOT post any comments confirming that something is correct - only post comments for style violations or concerns.
|
||||
|
||||
claude_args: |
|
||||
--allowedTools "Skill,mcp__github__get_pull_request,mcp__github__get_pull_request_files,mcp__github__create_pending_pull_request_review,mcp__github__get_pull_request_diff,mcp__github__add_comment_to_pending_review,mcp__github__add_pull_request_review_comment_to_pending_review,mcp__github__delete_pending_pull_request_review,mcp__github__submit_pending_pull_request_review"
|
||||
2
.github/workflows/cljs.yml
vendored
2
.github/workflows/cljs.yml
vendored
@@ -39,4 +39,4 @@ jobs:
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Run Cljs tests
|
||||
run: yarn run test-cljs
|
||||
run: bun run test-cljs
|
||||
|
||||
38
.github/workflows/codespell.yml
vendored
38
.github/workflows/codespell.yml
vendored
@@ -43,27 +43,35 @@ jobs:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Build Claude prompt
|
||||
if: steps.codespell_check.outcome == 'failure'
|
||||
run: |
|
||||
cat > /tmp/claude_prompt.txt << 'PROMPT_HEADER'
|
||||
You are tasked with fixing spelling errors detected by codespell in this repository.
|
||||
|
||||
## Codespell Output
|
||||
The following spelling errors were detected:
|
||||
```
|
||||
PROMPT_HEADER
|
||||
cat codespell_output.txt >> /tmp/claude_prompt.txt
|
||||
cat >> /tmp/claude_prompt.txt << 'PROMPT_FOOTER'
|
||||
```
|
||||
|
||||
## Instructions
|
||||
1. **Review the codespell output and identify reported spelling errors** - The usual format codespell uses is `typo ==> correction`
|
||||
2. **Fix every spelling error** - Use the Edit tool to fix each misspelling with the suggested correction
|
||||
- If codespell suggests multiple corrections, choose the most appropriate one based on context
|
||||
|
||||
Human reviewers will verify the changes before merging, so fix all errors reported by codespell.
|
||||
PROMPT_FOOTER
|
||||
|
||||
- name: Fix spelling errors with Claude Code
|
||||
if: steps.codespell_check.outcome == 'failure'
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
prompt: |
|
||||
You are tasked with fixing spelling errors detected by codespell in this repository.
|
||||
|
||||
## Codespell Output
|
||||
The following spelling errors were detected:
|
||||
```
|
||||
$(cat codespell_output.txt)
|
||||
```
|
||||
|
||||
## Instructions
|
||||
1. **Review the codespell output and identify reported spelling errors** - The usual format codespell uses is `typo ==> correction`
|
||||
2. **Fix every spelling error** - Use the Edit tool to fix each misspelling with the suggested correction
|
||||
- If codespell suggests multiple corrections, choose the most appropriate one based on context
|
||||
|
||||
Human reviewers will verify the changes before merging, so fix all errors reported by codespell.
|
||||
prompt_file: /tmp/claude_prompt.txt
|
||||
claude_args: |
|
||||
--allowedTools "Write,Read,Glob,Grep,Edit,MultiEdit,Bash(cat*),Bash(ls*)"
|
||||
--disallowedTools "Bash(git commit*),Bash(git push*),Bash(gh*)"
|
||||
|
||||
4
.github/workflows/containerize-jar.yml
vendored
4
.github/workflows/containerize-jar.yml
vendored
@@ -64,8 +64,8 @@ jobs:
|
||||
commit: ${{ inputs.commit }}
|
||||
edition: ${{ inputs.edition }}
|
||||
release: ${{ inputs.release }}
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get platforms
|
||||
id: platforms
|
||||
uses: actions/github-script@v7
|
||||
|
||||
6
.github/workflows/containerize-uberjar.yml
vendored
6
.github/workflows/containerize-uberjar.yml
vendored
@@ -56,12 +56,12 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
edition: ${{ matrix.edition }}
|
||||
commit: ${{ github.event.inputs.commit || github.sha }}
|
||||
commit: ${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}
|
||||
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
|
||||
tag: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).tag }}
|
||||
release: "false"
|
||||
build-args: |
|
||||
GIT_COMMIT_SHA=${{ github.event.inputs.commit || github.sha }}
|
||||
GIT_COMMIT_SHA=${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
run-trivy:
|
||||
needs: [get-container-info, containerize]
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit || github.sha }}
|
||||
ref: ${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
env:
|
||||
|
||||
2
.github/workflows/coverage.yml
vendored
2
.github/workflows/coverage.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Run frontend unit tests
|
||||
run: |
|
||||
yarn run test-unit \
|
||||
bun run test-unit \
|
||||
--silent \
|
||||
--coverage \
|
||||
--testTimeout=60000
|
||||
|
||||
12
.github/workflows/cross-branch-migrations.yml
vendored
12
.github/workflows/cross-branch-migrations.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
|
||||
migrations-postgres-12-ee:
|
||||
migrations-postgres-14-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
MB_DB_USER: circle_test
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:12-alpine
|
||||
image: postgres:14-alpine
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
|
||||
migrations-mysql-8-0-ee:
|
||||
migrations-mysql-8-4-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
MB_DB_USER: root
|
||||
services:
|
||||
mysql:
|
||||
image: cimg/mysql:8.0
|
||||
image: cimg/mysql:8.4
|
||||
ports:
|
||||
- "3306:3306"
|
||||
steps:
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
base-ref: ${{ env.BASE_REF }}
|
||||
head-ref: ${{ env.HEAD_REF }}
|
||||
|
||||
migrations-mariadb-10-2-ee:
|
||||
migrations-mariadb-10-6-ee:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
MB_DB_USER: root
|
||||
services:
|
||||
mariadb:
|
||||
image: circleci/mariadb:10.2.23
|
||||
image: circleci/mariadb:10.6
|
||||
ports:
|
||||
- "3306:3306"
|
||||
steps:
|
||||
|
||||
102
.github/workflows/cross-version.yml
vendored
Normal file
102
.github/workflows/cross-version.yml
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
name: Cross-Version Migration Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
source_version:
|
||||
description: "Source version (e.g., v1.58.6 or HEAD)"
|
||||
required: true
|
||||
type: string
|
||||
target_version:
|
||||
description: "Target version (e.g., v1.58.7 or HEAD)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- "cross-version/**"
|
||||
- ".github/workflows/cross-version.yml"
|
||||
- ".github/scripts/build-cross-version-matrix.js"
|
||||
|
||||
schedule:
|
||||
# Run nightly at 3 AM UTC
|
||||
- cron: "0 3 * * *"
|
||||
|
||||
jobs:
|
||||
# Generate matrix for scheduled/PR runs
|
||||
generate-matrix:
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github
|
||||
|
||||
- name: Generate version matrix
|
||||
id: set-matrix
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { buildCrossVersionMatrix } = require('${{ github.workspace }}/.github/scripts/build-cross-version-matrix');
|
||||
const { config } = buildCrossVersionMatrix(${{ vars.CURRENT_VERSION }});
|
||||
const matrix = JSON.stringify({ include: config });
|
||||
core.setOutput("matrix", matrix);
|
||||
console.log("matrix:", matrix);
|
||||
|
||||
# Matrix-based tests for scheduled/PR runs
|
||||
test-matrix:
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
needs: generate-matrix
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Run cross-version test
|
||||
env:
|
||||
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
|
||||
run: |
|
||||
cd cross-version
|
||||
./test.sh --source "${{ matrix.source }}" --target "${{ matrix.target }}"
|
||||
|
||||
- name: Show logs on failure
|
||||
if: failure()
|
||||
run: |
|
||||
cd cross-version
|
||||
docker compose logs metabase || true
|
||||
docker compose logs postgres || true
|
||||
|
||||
# Manual test for workflow_dispatch
|
||||
test-manual:
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Run cross-version test
|
||||
env:
|
||||
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
|
||||
run: |
|
||||
cd cross-version
|
||||
./test.sh --source "${{ inputs.source_version }}" --target "${{ inputs.target_version }}"
|
||||
|
||||
- name: Show logs on failure
|
||||
if: failure()
|
||||
run: |
|
||||
cd cross-version
|
||||
docker compose logs metabase || true
|
||||
docker compose logs postgres || true
|
||||
79
.github/workflows/disabled-drivers-slack-notification.yml
vendored
Normal file
79
.github/workflows/disabled-drivers-slack-notification.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Disabled Drivers Slack Notification
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 9 * * 1" # every Monday at 9:00 UTC
|
||||
|
||||
jobs:
|
||||
notify-disabled-drivers:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/Jod
|
||||
|
||||
- name: Install Slack SDK
|
||||
run: npm install @slack/web-api
|
||||
|
||||
- name: Check disabled drivers and notify Slack
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
with:
|
||||
script: |
|
||||
const { WebClient } = require('@slack/web-api');
|
||||
|
||||
// Fetch the config
|
||||
const response = await fetch('https://raw.githubusercontent.com/metabase/ci-test-config/refs/heads/master/ci-test-config.json');
|
||||
const config = await response.json();
|
||||
|
||||
const drivers = config.ignored?.drivers || [];
|
||||
const lastUpdated = config.metadata?.last_updated || 'unknown';
|
||||
const reason = config.metadata?.reason || 'No reason provided';
|
||||
|
||||
if (drivers.length === 0) {
|
||||
console.log('No drivers are currently disabled. Skipping Slack notification.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.SLACK_BOT_TOKEN) {
|
||||
throw new Error('SLACK_BOT_TOKEN is required');
|
||||
}
|
||||
|
||||
const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
|
||||
|
||||
await slack.chat.postMessage({
|
||||
channel: 'engineering-ci',
|
||||
text: 'CI Disabled Drivers Report',
|
||||
blocks: [
|
||||
{
|
||||
type: 'section',
|
||||
text: {
|
||||
type: 'mrkdwn',
|
||||
text: `:no_entry: *CI Disabled Drivers*\n${drivers.map(d => `\`${d}\``).join(' ')}`
|
||||
},
|
||||
accessory: {
|
||||
type: 'button',
|
||||
text: {
|
||||
type: 'plain_text',
|
||||
text: 'View Config',
|
||||
emoji: true
|
||||
},
|
||||
url: 'https://github.com/metabase/ci-test-config/blob/master/ci-test-config.json'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'context',
|
||||
elements: [
|
||||
{
|
||||
type: 'mrkdwn',
|
||||
text: `${reason} · :clock1: *${lastUpdated}*`
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
console.log('Slack notification sent successfully');
|
||||
4
.github/workflows/docs-release-generate.yml
vendored
4
.github/workflows/docs-release-generate.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
target_branch:
|
||||
description: "Target branch for documentation update"
|
||||
required: false
|
||||
default: "release-x.58.x"
|
||||
default: "release-x.59.x"
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
@@ -16,6 +16,6 @@ jobs:
|
||||
name: Call documentation update for release branch
|
||||
uses: ./.github/workflows/docs-generate-base.yml
|
||||
with:
|
||||
target_branch: ${{ inputs.target_branch || 'release-x.58.x' }}
|
||||
target_branch: ${{ inputs.target_branch || 'release-x.59.x' }}
|
||||
secrets:
|
||||
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
20
.github/workflows/drivers-stress-test.yml
vendored
20
.github/workflows/drivers-stress-test.yml
vendored
@@ -109,7 +109,7 @@ jobs:
|
||||
junit-name: 'be-tests-druid-ee'
|
||||
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
|
||||
|
||||
be-tests-mariadb-10-2-ee:
|
||||
be-tests-mariadb-10-6-ee:
|
||||
if: inputs.driver == 'Maria DB'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
@@ -124,15 +124,15 @@ jobs:
|
||||
MB_MYSQL_TEST_USER: root
|
||||
services:
|
||||
mariadb:
|
||||
image: circleci/mariadb:10.2.23
|
||||
image: circleci/mariadb:10.6
|
||||
ports:
|
||||
- "3306:3306"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test MariaDB driver (10.2)
|
||||
- name: Test MariaDB driver (10.6)
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mariadb-10-2-ee'
|
||||
junit-name: 'be-tests-mariadb-10-6-ee'
|
||||
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
|
||||
|
||||
be-tests-mariadb-latest-ee:
|
||||
@@ -298,7 +298,7 @@ jobs:
|
||||
junit-name: 'be-tests-mongo-latest-ee'
|
||||
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
|
||||
|
||||
be-tests-mysql-8-0-ee:
|
||||
be-tests-mysql-8-4-ee:
|
||||
if: inputs.driver == 'MySQL'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
@@ -313,15 +313,15 @@ jobs:
|
||||
MB_MYSQL_TEST_USER: root
|
||||
services:
|
||||
mysql:
|
||||
image: cimg/mysql:8.0
|
||||
image: cimg/mysql:8.4
|
||||
ports:
|
||||
- "3306:3306"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test MySQL driver (8.0)
|
||||
- name: Test MySQL driver (8.4)
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mysql-8-0-ee'
|
||||
junit-name: 'be-tests-mysql-8-4-ee'
|
||||
test-args: ":only ${{ inputs.test }} :times ${{ inputs.times }}"
|
||||
|
||||
be-tests-mysql-latest-ee:
|
||||
@@ -438,7 +438,7 @@ jobs:
|
||||
MB_POSTGRESQL_TEST_USER: mb_test
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:12-alpine
|
||||
image: postgres:14-alpine
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
@@ -447,7 +447,7 @@ jobs:
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Postgres driver (12)
|
||||
- name: Test Postgres driver (14)
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-postgres-ee'
|
||||
|
||||
102
.github/workflows/drivers.yml
vendored
102
.github/workflows/drivers.yml
vendored
@@ -72,11 +72,14 @@ jobs:
|
||||
# All decision logic is in mage - it analyzes module dependencies,
|
||||
# branch context, labels, and file changes to decide what runs.
|
||||
MAGE_ARGS=(
|
||||
--git-ref="${{ github.event.pull_request.base.ref || 'master' }}"
|
||||
--git-ref="${{ github.event.pull_request.base.ref || github.ref_name }}"
|
||||
--is-master-or-release="${{ github.ref_name == 'master' || startsWith(github.ref_name, 'release-') }}"
|
||||
--pr-labels="${{ join(github.event.pull_request.labels.*.name, ',') }}"
|
||||
--skip="${{ inputs.skip }}"
|
||||
)
|
||||
echo "Running:"
|
||||
echo "./bin/mage -driver-decisions ${MAGE_ARGS[@]}"
|
||||
echo ""
|
||||
# First call: show human-readable output for debugging
|
||||
./bin/mage -driver-decisions "${MAGE_ARGS[@]}"
|
||||
# Second call: output only key=value lines for GITHUB_OUTPUT
|
||||
@@ -87,7 +90,7 @@ jobs:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.athena-should-run == 'true' || needs.determine-driver-skips.outputs.athena-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: athena
|
||||
@@ -132,6 +135,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-bigquery-cloud-sdk-ee:
|
||||
needs: determine-driver-skips
|
||||
@@ -147,11 +151,10 @@ jobs:
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
:fail-fast? true
|
||||
# DS 2025-09-26 temporarily disabled while we diagnose flakes
|
||||
# - name: Transforms Python Tests
|
||||
# test-args: >-
|
||||
# :only-tags [:mb/transforms-python-test]
|
||||
# needs-python-runner: true
|
||||
- name: Transforms Python Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: bigquery-cloud-sdk
|
||||
@@ -198,6 +201,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
- name: Cleanup python-runner
|
||||
if: ${{ always() && matrix.job.needs-python-runner == true }}
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
@@ -209,7 +213,7 @@ jobs:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.clickhouse-should-run == 'true' || needs.determine-driver-skips.outputs.clickhouse-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -280,6 +284,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
- name: Cleanup python-runner
|
||||
if: ${{ always() && matrix.job.needs-python-runner == true }}
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
@@ -291,7 +296,7 @@ jobs:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.druid-should-run == 'true' || needs.determine-driver-skips.outputs.druid-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: druid
|
||||
@@ -334,12 +339,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-druid-jdbc-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.druid-jdbc-should-run == 'true' || needs.determine-driver-skips.outputs.druid-jdbc-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: druid-jdbc
|
||||
@@ -382,12 +388,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-databricks-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.databricks-should-run == 'true' || needs.determine-driver-skips.outputs.databricks-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: databricks
|
||||
@@ -430,12 +437,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-databricks-multi-catalog-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.databricks-should-run == 'true' || needs.determine-driver-skips.outputs.databricks-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: databricks
|
||||
@@ -479,12 +487,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-h2:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.h2-should-run == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
name: H2
|
||||
@@ -509,12 +518,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-h2-oss:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.h2-should-run == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
name: H2 (OSS)
|
||||
@@ -540,19 +550,20 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-mysql-mariadb:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.mysql-mariadb-should-run == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MariaDB 10.2
|
||||
junit-name: be-tests-mariadb-10-2-ee
|
||||
image: circleci/mariadb:10.2.23
|
||||
- name: MariaDB 10.6
|
||||
junit-name: be-tests-mariadb-10-6-ee
|
||||
image: circleci/mariadb:10.6
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
@@ -562,9 +573,9 @@ jobs:
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
- name: MySQL 8.0
|
||||
junit-name: be-tests-mysql-8-0-ee
|
||||
image: mysql:8.0
|
||||
- name: MySQL 8.4
|
||||
junit-name: be-tests-mysql-8-4-ee
|
||||
image: mysql:8.4
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
@@ -670,6 +681,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
@@ -681,7 +693,7 @@ jobs:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.mongo-should-run == 'true' || needs.determine-driver-skips.outputs.mongo-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -750,6 +762,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
- name: Cleanup python-runner
|
||||
if: ${{ always() && matrix.job.needs-python-runner == true }}
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
@@ -761,7 +774,7 @@ jobs:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.mongo-ssl-should-run == 'true' || needs.determine-driver-skips.outputs.mongo-ssl-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -832,12 +845,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-mongo-sharded-cluster-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.mongo-sharded-cluster-should-run == 'true' || needs.determine-driver-skips.outputs.mongo-sharded-cluster-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mongo
|
||||
@@ -877,12 +891,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-oracle:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.oracle-should-run == 'true' || needs.determine-driver-skips.outputs.oracle-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -962,19 +977,20 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-postgres:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.postgres-should-run == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Postgres 12.x
|
||||
- name: Postgres 14.x
|
||||
junit-name: postgres-ee
|
||||
docker-image: postgres:12-alpine
|
||||
docker-image: postgres:14-alpine
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
enable-aws-iam-tests: 'false'
|
||||
@@ -1066,6 +1082,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
@@ -1077,7 +1094,7 @@ jobs:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.presto-jdbc-should-run == 'true' || needs.determine-driver-skips.outputs.presto-jdbc-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: presto-jdbc
|
||||
@@ -1153,12 +1170,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-redshift-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.redshift-should-run == 'true' || needs.determine-driver-skips.outputs.redshift-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -1237,6 +1255,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
- name: Cleanup python-runner
|
||||
if: ${{ always() && matrix.job.needs-python-runner == true }}
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
@@ -1258,12 +1277,10 @@ jobs:
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
:fail-fast? true
|
||||
|
||||
# DS 2025-09-26 temporarily disabled while we diagnose flakes
|
||||
# - name: Transforms Python Tests
|
||||
# test-args: >-
|
||||
# :only-tags [:mb/transforms-python-test]
|
||||
# needs-python-runner: true
|
||||
- name: Transforms Python Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
needs-python-runner: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: snowflake
|
||||
@@ -1319,6 +1336,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
- name: Cleanup python-runner
|
||||
if: ${{ always() && matrix.job.needs-python-runner == true }}
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
@@ -1330,7 +1348,7 @@ jobs:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.sparksql-should-run == 'true' || needs.determine-driver-skips.outputs.sparksql-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: sparksql
|
||||
@@ -1371,12 +1389,13 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-sqlite-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ needs.determine-driver-skips.outputs.sqlite-should-run == 'true' || needs.determine-driver-skips.outputs.sqlite-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: sqlite
|
||||
@@ -1412,6 +1431,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
be-tests-sqlserver:
|
||||
needs: determine-driver-skips
|
||||
@@ -1488,6 +1508,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
- name: Cleanup python-runner
|
||||
if: ${{ always() && matrix.job.needs-python-runner == true }}
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
@@ -1500,7 +1521,7 @@ jobs:
|
||||
# needs: determine-driver-skips
|
||||
# if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
# runs-on: ubuntu-latest
|
||||
# timeout-minutes: 60
|
||||
# timeout-minutes: 40
|
||||
# env:
|
||||
# CI: 'true'
|
||||
# DRIVERS: starburst
|
||||
@@ -1568,7 +1589,7 @@ jobs:
|
||||
needs: [determine-driver-skips, login-to-ecr]
|
||||
if: ${{ needs.determine-driver-skips.outputs.vertica-should-run == 'true' || needs.determine-driver-skips.outputs.vertica-quarantine-conflict == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: vertica
|
||||
@@ -1614,6 +1635,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
drivers-tests-result:
|
||||
needs:
|
||||
|
||||
@@ -115,11 +115,15 @@ jobs:
|
||||
|
||||
- name: Build Embedding SDK package
|
||||
if: ${{ needs.determine-test-type.outputs.type == 'component' }}
|
||||
run: yarn build-embedding-sdk-package
|
||||
run: bun run build-embedding-sdk-package
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Compile CLJS
|
||||
run: bun run build-pure:cljs
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
|
||||
11
.github/workflows/e2e-test.yml
vendored
11
.github/workflows/e2e-test.yml
vendored
@@ -41,12 +41,13 @@ env:
|
||||
jobs:
|
||||
e2e-tests:
|
||||
runs-on: ${{ inputs.runner }}
|
||||
timeout-minutes: 45
|
||||
timeout-minutes: 40
|
||||
name: e2e-tests-${{ inputs.name }}-${{ inputs.edition }}
|
||||
env:
|
||||
MB_EDITION: ${{ inputs.edition }}
|
||||
# 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")`
|
||||
# Sensitive env vars (tokens, secrets) starting with `CYPRESS_` are accessed via async `cy.env()`
|
||||
# Example: `cy.env(["FOO"]).then(({ FOO }) => ...)`
|
||||
# For non-sensitive config, use `Cypress.expose()` in cypress config instead (synchronous)
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
@@ -105,6 +106,9 @@ jobs:
|
||||
if: github.run_attempt != '1'
|
||||
run: echo "specs=$(cat failed-specs)" >> $GITHUB_ENV
|
||||
|
||||
- name: Compile CLJS
|
||||
run: bun run build-pure:cljs
|
||||
|
||||
- name: Run Metabase
|
||||
run: node e2e/runner/run_cypress_ci.js start
|
||||
|
||||
@@ -168,6 +172,7 @@ jobs:
|
||||
# Related to Trunk uploader
|
||||
variant: e2e-tests
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ inputs.specs && steps.tagged-e2e.outcome || steps.split-e2e.outcome }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -19,8 +19,9 @@ jobs:
|
||||
react-version: [18, 19]
|
||||
fail-fast: false
|
||||
env:
|
||||
# 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")`
|
||||
# Sensitive env vars (tokens, secrets) starting with `CYPRESS_` are accessed via async `cy.env()`
|
||||
# Example: `cy.env(["FOO"]).then(({ FOO }) => ...)`
|
||||
# For non-sensitive config, use `Cypress.expose()` in cypress config instead (synchronous)
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
@@ -81,6 +82,7 @@ jobs:
|
||||
run: ./bin/embedding-sdk/change-react-version.bash ${{ matrix.react-version }}
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
id: run-tests
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: node e2e/runner/run_cypress_ci.js component
|
||||
@@ -96,6 +98,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.run-tests.outcome }}
|
||||
|
||||
- name: "SDK component tests > Upload Cypress Artifacts upon failure"
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -153,8 +156,9 @@ jobs:
|
||||
env:
|
||||
MB_RUN_MODE: "e2e"
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
# Sensitive env vars (tokens, secrets) starting with `CYPRESS_` are accessed via async `cy.env()`
|
||||
# Example: `cy.env(["FOO"]).then(({ FOO }) => ...)`
|
||||
# For non-sensitive config, use `Cypress.expose()` in cypress config instead (synchronous)
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
|
||||
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
|
||||
@@ -230,6 +234,7 @@ jobs:
|
||||
--spec "e2e/snapshot-creators/default.cy.snap.js"
|
||||
|
||||
- name: Run component tests for Embedding SDK
|
||||
id: run-tests
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
run: |
|
||||
@@ -247,6 +252,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.run-tests.outcome }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -9,7 +9,6 @@ env:
|
||||
TZ: US/Pacific # to make Node match the instance tz
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
HASH: ${{ github.event.pull_request.head.sha || github.sha }}-${{ github.run_attempt }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
|
||||
MB_EDITION: ee
|
||||
jobs:
|
||||
get-sample-apps-data:
|
||||
@@ -21,12 +20,7 @@ jobs:
|
||||
TEST_SUITES: |
|
||||
[
|
||||
"metabase-nodejs-react-sdk-embedding-sample-e2e",
|
||||
"metabase-nextjs-sdk-embedding-sample-e2e",
|
||||
"shoppy-e2e"
|
||||
]
|
||||
TEST_SUITES_TO_RUN_IN_MASTER_ONLY: |
|
||||
[
|
||||
"shoppy-e2e"
|
||||
"metabase-nextjs-sdk-embedding-sample-e2e"
|
||||
]
|
||||
SAMPLE_APP_MAIN_BRANCH_NAME: "main"
|
||||
SAMPLE_APP_STABLE_RELEASE_BRANCH_NAME_SUFFIX: "stable"
|
||||
@@ -36,8 +30,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Build sample app compatibility data
|
||||
uses: actions/github-script@v7
|
||||
@@ -50,7 +44,6 @@ jobs:
|
||||
} = 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\//, '');
|
||||
@@ -74,19 +67,16 @@ jobs:
|
||||
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
|
||||
: []
|
||||
matrix: shouldRunTestsForVersion ? testSuites : [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
branch_name: '',
|
||||
matrix: []
|
||||
matrix: [],
|
||||
};
|
||||
|
||||
# In order to check compatibility between Sample Apps and Embedding SDK, the job for each sample app:
|
||||
@@ -98,6 +88,7 @@ jobs:
|
||||
e2e-sample-apps-tests:
|
||||
needs: [get-sample-apps-data]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test_suite: ${{ fromJSON(needs.get-sample-apps-data.outputs.matrix) }}
|
||||
environment: ["production", "development"]
|
||||
@@ -168,28 +159,8 @@ jobs:
|
||||
echo "sample_app_branch_exists=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Configure AWS credentials
|
||||
if: ${{ matrix.test_suite == 'shoppy-e2e' }}
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.SHOPPY_IAM_ROLE }}
|
||||
role-session-name: GitHub_to_AWS_via_FederatedOIDC
|
||||
aws-region: us-east-1
|
||||
|
||||
- name: Download Shoppy's App DB PostgreSQL dump from S3
|
||||
if: ${{ matrix.test_suite == 'shoppy-e2e' }}
|
||||
run: |
|
||||
aws s3 cp s3://${{ secrets.SHOPPY_METABASE_APP_DB_S3_BUCKET }}/${{ secrets.SHOPPY_METABASE_APP_DB_S3_KEY }} ./e2e/tmp/db_dumps/shoppy_metabase_app_db_dump.sql
|
||||
|
||||
- name: Prepare and launch Sample Apps for shoppy-e2e test suite
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' && matrix.test_suite == 'shoppy-e2e' }}
|
||||
env:
|
||||
METABASE_APP_DB_USER: ${{ secrets.SHOPPY_METABASE_APP_DB_USER }}
|
||||
METABASE_APP_DB: ${{ secrets.SHOPPY_METABASE_APP_DB_DATABASE }}
|
||||
run: npx tsx ./e2e/runner/embedding-sdk/sample-apps/start-ci.ts shoppy-e2e
|
||||
|
||||
- name: Prepare and launch Sample Apps for other test suites
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' && matrix.test_suite != 'shoppy-e2e' }}
|
||||
- name: Prepare and launch Sample Apps
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' }}
|
||||
run: npx tsx ./e2e/runner/embedding-sdk/sample-apps/start-ci.ts ${{ matrix.test_suite }}
|
||||
|
||||
- name: Prune build cache
|
||||
@@ -210,7 +181,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ steps.check-sample-app-branch.outputs.sample_app_branch_exists == 'true' && steps.run-e2e-tests.outcome != 'success' }}
|
||||
with:
|
||||
name: cypress-recording-sample-app-${{ matrix.test_suite }}-latest
|
||||
name: cypress-recording-sample-app-${{ matrix.test_suite }}-${{ matrix.environment }}-latest
|
||||
path: |
|
||||
./e2e/tmp/${{ steps.get-sample-app-name.outputs.sample_app_name }}/cypress
|
||||
if-no-files-found: ignore
|
||||
@@ -220,6 +191,150 @@ jobs:
|
||||
run: |
|
||||
exit 1
|
||||
|
||||
# Shoppy e2e tests run separately using the shared composite action for setup.
|
||||
e2e-shoppy-tests:
|
||||
needs: [get-sample-apps-data]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
environment: ["production", "development"]
|
||||
if: |
|
||||
!cancelled() && needs.get-sample-apps-data.result == 'success' &&
|
||||
(github.ref == 'refs/heads/master' || github.base_ref == 'master')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-sample-app-shoppy-e2e-tests-${{ matrix.environment }}-bundle
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
MB_RUN_MODE: e2e
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
SAMPLE_APP_BRANCH_NAME: ${{ needs.get-sample-apps-data.outputs.branch_name }}
|
||||
SAMPLE_APP_ENVIRONMENT: ${{ matrix.environment }}
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Shoppy E2E environment
|
||||
uses: ./.github/actions/prepare-shoppy-e2e
|
||||
id: shoppy-prep
|
||||
with:
|
||||
commit: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
sample-app-branch-name: ${{ needs.get-sample-apps-data.outputs.branch_name }}
|
||||
sample-app-environment: ${{ matrix.environment }}
|
||||
staging-mb-all-features-token: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
shoppy-iam-role: ${{ secrets.SHOPPY_IAM_ROLE }}
|
||||
shoppy-metabase-app-db-s3-bucket: ${{ secrets.SHOPPY_METABASE_APP_DB_S3_BUCKET }}
|
||||
shoppy-metabase-app-db-s3-key: ${{ secrets.SHOPPY_METABASE_APP_DB_S3_KEY }}
|
||||
shoppy-metabase-app-db-user: ${{ secrets.SHOPPY_METABASE_APP_DB_USER }}
|
||||
shoppy-metabase-app-db-database: ${{ secrets.SHOPPY_METABASE_APP_DB_DATABASE }}
|
||||
metastore-dev-server-url: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
shoppy-datadog-application-id: ${{ secrets.SHOPPY_METABASE_APP_DATADOG_APPLICATION_ID }}
|
||||
shoppy-datadog-client-token: ${{ secrets.SHOPPY_METABASE_APP_DATADOG_CLIENT_TOKEN }}
|
||||
|
||||
- name: Run e2e tests for Shoppy
|
||||
id: run-e2e-tests
|
||||
if: ${{ steps.shoppy-prep.outputs.sample_app_branch_exists == 'true' }}
|
||||
continue-on-error: true
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.shoppy-prep.outputs.chrome-path }}
|
||||
run: node e2e/runner/run_cypress_ci.js shoppy-e2e
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ steps.shoppy-prep.outputs.sample_app_branch_exists == 'true' && steps.run-e2e-tests.outcome != 'success' }}
|
||||
with:
|
||||
name: cypress-recording-sample-app-shoppy-e2e-${{ matrix.environment }}-latest
|
||||
path: |
|
||||
./e2e/tmp/shoppy/cypress
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Fail job if tests failed
|
||||
if: ${{ steps.shoppy-prep.outputs.sample_app_branch_exists == 'true' && steps.run-e2e-tests.outcome != 'success' }}
|
||||
run: |
|
||||
exit 1
|
||||
|
||||
# Synthetic monitoring tests run only on master merges, in parallel with the regular e2e tests.
|
||||
# They use the shoppy production environment and include performance measurement tests.
|
||||
e2e-shoppy-synthetic-tests:
|
||||
needs: [get-sample-apps-data]
|
||||
if: |
|
||||
!cancelled() && needs.get-sample-apps-data.result == 'success' &&
|
||||
github.event_name == 'push' &&
|
||||
github.ref == 'refs/heads/master'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 45
|
||||
name: e2e-shoppy-synthetic-monitoring-tests
|
||||
env:
|
||||
MB_EDITION: ee
|
||||
MB_RUN_MODE: e2e
|
||||
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
SAMPLE_APP_BRANCH_NAME: ${{ needs.get-sample-apps-data.outputs.branch_name }}
|
||||
SAMPLE_APP_ENVIRONMENT: production
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Shoppy E2E environment
|
||||
uses: ./.github/actions/prepare-shoppy-e2e
|
||||
id: shoppy-prep
|
||||
with:
|
||||
commit: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
sample-app-branch-name: ${{ needs.get-sample-apps-data.outputs.branch_name }}
|
||||
sample-app-environment: production
|
||||
staging-mb-all-features-token: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
|
||||
shoppy-iam-role: ${{ secrets.SHOPPY_IAM_ROLE }}
|
||||
shoppy-metabase-app-db-s3-bucket: ${{ secrets.SHOPPY_METABASE_APP_DB_S3_BUCKET }}
|
||||
shoppy-metabase-app-db-s3-key: ${{ secrets.SHOPPY_METABASE_APP_DB_S3_KEY }}
|
||||
shoppy-metabase-app-db-user: ${{ secrets.SHOPPY_METABASE_APP_DB_USER }}
|
||||
shoppy-metabase-app-db-database: ${{ secrets.SHOPPY_METABASE_APP_DB_DATABASE }}
|
||||
metastore-dev-server-url: ${{ secrets.METASTORE_DEV_SERVER_URL }}
|
||||
shoppy-datadog-application-id: ${{ secrets.SHOPPY_METABASE_APP_DATADOG_APPLICATION_ID }}
|
||||
shoppy-datadog-client-token: ${{ secrets.SHOPPY_METABASE_APP_DATADOG_CLIENT_TOKEN }}
|
||||
|
||||
- name: Get merged PR number
|
||||
if: ${{ steps.shoppy-prep.outputs.sample_app_branch_exists == 'true' }}
|
||||
id: get-merged-pr-number
|
||||
run: |
|
||||
PR=$(gh api repos/${{ github.repository }}/commits/${{ github.sha }}/pulls --jq '.[0].number' 2>/dev/null || echo "")
|
||||
if [ -z "$PR" ] || [ "$PR" = "null" ]; then
|
||||
echo "number=" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "number=$PR" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run synthetic monitoring tests
|
||||
id: run-e2e-tests
|
||||
if: ${{ steps.shoppy-prep.outputs.sample_app_branch_exists == 'true' }}
|
||||
continue-on-error: true
|
||||
env:
|
||||
CYPRESS_BROWSER: ${{ steps.shoppy-prep.outputs.chrome-path }}
|
||||
PR_NUMBER: ${{ steps.get-merged-pr-number.outputs.number }}
|
||||
INCLUDE_SYNTHETIC_MONITORING_TESTS: "true"
|
||||
run: node e2e/runner/run_cypress_ci.js shoppy-e2e
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ steps.shoppy-prep.outputs.sample_app_branch_exists == 'true' && steps.run-e2e-tests.outcome != 'success' }}
|
||||
with:
|
||||
name: cypress-recording-shoppy-synthetic-tests-latest
|
||||
path: |
|
||||
./e2e/tmp/shoppy/cypress
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Fail job if tests failed
|
||||
if: ${{ steps.shoppy-prep.outputs.sample_app_branch_exists == 'true' && steps.run-e2e-tests.outcome != 'success' }}
|
||||
run: |
|
||||
exit 1
|
||||
|
||||
# The job checks integration of the Embedding SDK with consumer's apps that use different frameworks/bundlers,
|
||||
# or when we want to test some tricky integration cases like conflicting types.
|
||||
# The job
|
||||
@@ -229,6 +344,7 @@ jobs:
|
||||
# For more details see `Embedding SDK integration tests with Host Apps` in `enterprise/frontend/src/embedding-sdk-package/dev.md`
|
||||
e2e-host-apps-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test_suite:
|
||||
[
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
# Remove the matchers to not report false positives on every PR
|
||||
# unrelated to embedding (EMB-765)
|
||||
echo "::remove-matcher owner=tsc::"
|
||||
yarn build-embedding-sdk-package
|
||||
bun run build-embedding-sdk-package
|
||||
- name: Prepare Embedding SDK artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
65
.github/workflows/embedding-sdk.yml
vendored
65
.github/workflows/embedding-sdk.yml
vendored
@@ -23,6 +23,10 @@ on:
|
||||
description: "Whether to run host and sample apps compatibility tests."
|
||||
required: true
|
||||
type: boolean
|
||||
bundle-size-check:
|
||||
description: "Whether to run bundle size check."
|
||||
required: true
|
||||
type: boolean
|
||||
skip:
|
||||
description: Whether to skip running tests due to one of the artifacts failing to build.
|
||||
required: true
|
||||
@@ -47,7 +51,7 @@ jobs:
|
||||
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
|
||||
run: bun run embedding-sdk:cli-snippets:type-check
|
||||
|
||||
embedding-sdk-docs-snippets-type-check:
|
||||
if: inputs.docs-snippets-type-check && !inputs.skip
|
||||
@@ -63,7 +67,7 @@ jobs:
|
||||
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
|
||||
run: bun run embedding-sdk:docs-snippets:type-check
|
||||
|
||||
embedding-sdk-api-documentation-generation-validation:
|
||||
if: inputs.documentation-validation && !inputs.skip
|
||||
@@ -86,7 +90,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -o pipefail
|
||||
yarn embedding-sdk:docs:generate:pure --emit none 2>&1 \
|
||||
bun run embedding-sdk:docs:generate:pure --emit none 2>&1 \
|
||||
| tee /dev/stdout \
|
||||
| tee >( tail -n 10 \
|
||||
| sed '1s/^/output<<EOF\n/' \
|
||||
@@ -98,14 +102,14 @@ jobs:
|
||||
with:
|
||||
remove-previous-comment: true
|
||||
add-new-comment: ${{ steps.docs-generation.outcome == 'failure' && contains(steps.docs-generation.outputs.output, 'not included in the documentation') }}
|
||||
message: "📝 Some exported types from `@metabase/embedding-sdk-react` reference other types that are not exported, i.e. internal types.<br><br>The internal type reference breaks API documentation generation for the SDK and also may unexpectedly increase the output size of the type declaration file.<br><br>Please check the CI logs to find the name of such type.<br><br>- If a type should be publicly available in the SDK, add a description to it or a TypeDoc annotation and export it from SDK.<br>- If a type is internal and should _not_ be publicly exposed, refactor types to prevent referencing other types that are not exported from SDK.<br><br>You can run `yarn embedding-sdk:docs:generate` locally to verify that the issue has been resolved."
|
||||
message: "📝 Some exported types from `@metabase/embedding-sdk-react` reference other types that are not exported, i.e. internal types.<br><br>The internal type reference breaks API documentation generation for the SDK and also may unexpectedly increase the output size of the type declaration file.<br><br>Please check the CI logs to find the name of such type.<br><br>- If a type should be publicly available in the SDK, add a description to it or a TypeDoc annotation and export it from SDK.<br>- If a type is internal and should _not_ be publicly exposed, refactor types to prevent referencing other types that are not exported from SDK.<br><br>You can run `bun run embedding-sdk:docs:generate` locally to verify that the issue has been resolved."
|
||||
include-log: true
|
||||
- name: Comment on PR if 'Failed to resolve link' error happens
|
||||
uses: ./.github/actions/notify-pull-request
|
||||
with:
|
||||
remove-previous-comment: true
|
||||
add-new-comment: ${{ steps.docs-generation.outcome == 'failure' && contains(steps.docs-generation.outputs.output, 'Failed to resolve link') }}
|
||||
message: "📝 A typedoc `{@link <TYPE_NAME>}` annotation is found that can't be resolved.<br><br>Most likely it happens when the referenced type is not exported. Please check the CI logs to find the name of such type.<br><br>- If a referenced in `{@link}` type should be publicly available in the SDK, add a description to it or a TypeDoc annotation and export it from SDK.<br>- If a type is internal and should _not_ be publicly exposed, change types to prevent unexported type reference in `{@link}` annotation.<br><br>You can run `yarn embedding-sdk:docs:generate` locally to verify that the issue has been resolved."
|
||||
message: "📝 A typedoc `{@link <TYPE_NAME>}` annotation is found that can't be resolved.<br><br>Most likely it happens when the referenced type is not exported. Please check the CI logs to find the name of such type.<br><br>- If a referenced in `{@link}` type should be publicly available in the SDK, add a description to it or a TypeDoc annotation and export it from SDK.<br>- If a type is internal and should _not_ be publicly exposed, change types to prevent unexported type reference in `{@link}` annotation.<br><br>You can run `bun run embedding-sdk:docs:generate` locally to verify that the issue has been resolved."
|
||||
include-log: true
|
||||
- name: Fail Job
|
||||
if: ${{ steps.docs-generation.outcome == 'failure' }}
|
||||
@@ -124,8 +128,59 @@ jobs:
|
||||
uses: ./.github/workflows/embedding-sdk-host-sample-apps-tests.yml
|
||||
secrets: inherit
|
||||
|
||||
embedding-sdk-bundle-size-check:
|
||||
if: inputs.bundle-size-check
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
BUNDLE_SIZE_THRESHOLD_PERCENT: 2
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sdk-bundle-size-check
|
||||
id: compare
|
||||
with:
|
||||
edition: ee
|
||||
base-ref-commit: ${{ github.event.pull_request.base.sha }}
|
||||
threshold-percent: ${{ env.BUNDLE_SIZE_THRESHOLD_PERCENT }}
|
||||
|
||||
- name: Comment on PR if bundle size has increased
|
||||
uses: ./.github/actions/notify-pull-request
|
||||
if: success() && steps.compare.outputs.status == 'increased'
|
||||
with:
|
||||
remove-previous-comment: true
|
||||
add-new-comment: true
|
||||
message: "⚠️ Embedding SDK bundle size increased by ${{ steps.compare.outputs.size_change_percent }}% compared to the ${{ github.base_ref }} branch (threshold: ${{ env.BUNDLE_SIZE_THRESHOLD_PERCENT }}%). Please contact Embedding team."
|
||||
include-log: false
|
||||
|
||||
- name: Send notification to Slack via Zapier
|
||||
if: success() && steps.compare.outputs.status == 'increased'
|
||||
shell: bash
|
||||
env:
|
||||
ZAPIER_WEBHOOK_URL: ${{ secrets.EMBEDDING_ZAPIER_WEBHOOK_URL }}
|
||||
SIZE_CHANGE_PERCENT: ${{ steps.compare.outputs.size_change_percent }}
|
||||
BUNDLE_SIZE_THRESHOLD_PERCENT: ${{ env.BUNDLE_SIZE_THRESHOLD_PERCENT }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
curl -s -X POST "$ZAPIER_WEBHOOK_URL" \
|
||||
-d "⚠️ Embedding SDK bundle size increased by ${SIZE_CHANGE_PERCENT}% (threshold: ${BUNDLE_SIZE_THRESHOLD_PERCENT}%)
|
||||
|
||||
PR #${PR_NUMBER} ${PR_TITLE} (${PR_URL})"
|
||||
|
||||
- name: Comment on PR if bundle size has decreased
|
||||
uses: ./.github/actions/notify-pull-request
|
||||
if: success() && steps.compare.outputs.status == 'decreased'
|
||||
with:
|
||||
remove-previous-comment: true
|
||||
add-new-comment: true
|
||||
message: "🎉 Embedding SDK bundle size decreased by ${{ steps.compare.outputs.size_change_percent }}% compared to the ${{ github.base_ref }} branch. Nice work!"
|
||||
include-log: false
|
||||
|
||||
embedding-sdk-tests-result:
|
||||
needs:
|
||||
- embedding-sdk-bundle-size-check
|
||||
- embedding-sdk-cli-snippets-type-check
|
||||
- embedding-sdk-docs-snippets-type-check
|
||||
- embedding-sdk-api-documentation-generation-validation
|
||||
|
||||
@@ -44,12 +44,12 @@ jobs:
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Build cljs
|
||||
run: yarn build:cljs
|
||||
run: bun run build:cljs
|
||||
- name: Stress-test ${{ github.event.inputs.spec }} ${{ github.event.inputs.burn_in }} times
|
||||
run: |
|
||||
for i in {1..${{ github.event.inputs.burn_in }}}; do
|
||||
yarn jest --runInBand --ci --silent --no-cache \
|
||||
--testPathPattern '${{ github.event.inputs.spec }}' \
|
||||
bun run jest --runInBand --ci --silent --no-cache \
|
||||
--testPathPatterns '${{ github.event.inputs.spec }}' \
|
||||
--testNamePattern '${{ github.event.inputs.grep }}'
|
||||
if [[ "$?" != 0 ]]; then
|
||||
echo "Failed after $i attempts" && break;
|
||||
|
||||
20
.github/workflows/frontend.yml
vendored
20
.github/workflows/frontend.yml
vendored
@@ -46,15 +46,15 @@ jobs:
|
||||
key: ${{ steps.get-cache-keys.outputs.eslint-cache-key }}
|
||||
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
run: bun run build-pure:cljs
|
||||
- name: Find CSS variables that are used but never defined
|
||||
run: yarn find-never-defined-css-variables
|
||||
run: bun run find-never-defined-css-variables
|
||||
- name: Run Prettier formatting linter
|
||||
run: yarn run lint-prettier-pure
|
||||
run: bun run lint-prettier-pure
|
||||
- name: Run ESLint linter
|
||||
run: yarn run lint-eslint-pure
|
||||
run: bun run lint-eslint-pure
|
||||
- name: Run CSS linter
|
||||
run: yarn run lint-css
|
||||
run: bun run lint-css
|
||||
|
||||
fe-type-check:
|
||||
if: ${{ !inputs.skip }}
|
||||
@@ -66,9 +66,9 @@ jobs:
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- run: yarn build-pure:cljs
|
||||
- run: bun run build-pure:cljs
|
||||
name: Compile CLJS
|
||||
- run: yarn type-check-pure
|
||||
- run: bun run type-check-pure
|
||||
name: Check types
|
||||
|
||||
fe-tests-unit:
|
||||
@@ -90,7 +90,8 @@ jobs:
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Run frontend unit tests
|
||||
run: yarn run test-unit --silent --shard=${{ matrix.shard }}/${{ env.SHARDS }}
|
||||
id: fe-tests
|
||||
run: bun run test-unit --silent --shard=${{ matrix.shard }}/${{ env.SHARDS }}
|
||||
# Trunk Quarantine controls the final state of the run in the CI!
|
||||
# Even if this step fails we let it pass, and then `trunk-io/analytics-uploader` determines
|
||||
# whether to fail or to pass the whole job, based on the quarantine list.
|
||||
@@ -106,6 +107,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.fe-tests.outcome }}
|
||||
|
||||
fe-tests-timezones:
|
||||
if: ${{ !inputs.skip }}
|
||||
@@ -118,7 +120,7 @@ jobs:
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Run frontend timezones tests
|
||||
run: yarn run test-timezones
|
||||
run: bun run test-timezones
|
||||
|
||||
fe-tests-result:
|
||||
needs:
|
||||
|
||||
120
.github/workflows/loki-stress-test-flake-fix.yml
vendored
120
.github/workflows/loki-stress-test-flake-fix.yml
vendored
@@ -1,10 +1,15 @@
|
||||
name: Loki Visual Stress Test Flake Fix
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- 'frontend/**/*.stories.tsx'
|
||||
- 'enterprise/frontend/**/*.stories.tsx'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stories_filter:
|
||||
description: "Regex pattern to filter stories (e.g. 'DataGrid' or 'static-viz|viz')"
|
||||
story_files:
|
||||
description: "Comma-separated story file paths relative to repo root"
|
||||
type: string
|
||||
required: true
|
||||
burn_in:
|
||||
@@ -14,22 +19,96 @@ on:
|
||||
default: "50"
|
||||
|
||||
jobs:
|
||||
detect-changed-stories:
|
||||
name: Detect changed stories
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
story_files: ${{ steps.detect.outputs.story_files }}
|
||||
should_run: ${{ steps.detect.outputs.should_run }}
|
||||
steps:
|
||||
- name: Detect changed story files
|
||||
id: detect
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
validate_paths() {
|
||||
local files="$1"
|
||||
for file in $(echo "$files" | tr ',' '\n'); do
|
||||
file=$(echo "$file" | xargs) # trim whitespace
|
||||
if [[ -z "$file" ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ ! "$file" =~ ^(frontend/|enterprise/frontend/).+\.stories\. ]]; then
|
||||
echo "::error::Invalid story path: $file"
|
||||
echo "Paths must start with 'frontend/' or 'enterprise/frontend/' and contain '.stories.'"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
FILES=$(echo "${{ inputs.story_files }}" | tr ',' '\n' | xargs -n1 | paste -sd',')
|
||||
if ! validate_paths "$FILES"; then
|
||||
exit 1
|
||||
fi
|
||||
echo "story_files=$FILES" >> $GITHUB_OUTPUT
|
||||
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
FILES=$( \
|
||||
gh pr view ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --json files --jq '.files[].path' \
|
||||
| grep '\.stories\.tsx$' \
|
||||
| xargs -n1 \
|
||||
| sort -u \
|
||||
| paste -sd',' \
|
||||
)
|
||||
|
||||
if [[ -z "$FILES" ]]; then
|
||||
echo "No changed story files found"
|
||||
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! validate_paths "$FILES"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Changed story files: $FILES"
|
||||
echo "story_files=$FILES" >> $GITHUB_OUTPUT
|
||||
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||
|
||||
workflow-summary:
|
||||
name: Stress test inputs
|
||||
name: Summary
|
||||
needs: detect-changed-stories
|
||||
if: needs.detect-changed-stories.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Generate workflow summary
|
||||
run: |
|
||||
echo '**Inputs:**' >> $GITHUB_STEP_SUMMARY
|
||||
echo '## Loki Stress Test' >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `branch`: ${{ github.ref_name }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `stories_filter`: ${{ inputs.stories_filter }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `burn_in`: ${{ inputs.burn_in }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '**Branch:** `${{ github.head_ref || github.ref_name }}`' >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo 'triggered by: @${{ github.event.sender.login }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo '**Story files:**' >> $GITHUB_STEP_SUMMARY
|
||||
echo '${{ needs.detect-changed-stories.outputs.story_files }}' | tr ',' '\n' | while read -r file; do
|
||||
echo "- \`$file\`" >> $GITHUB_STEP_SUMMARY
|
||||
done
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '**Burn-in:** ${{ inputs.burn_in || 50 }} runs' >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo 'Triggered by PR #${{ github.event.pull_request.number }} from @${{ github.event.pull_request.user.login }}' >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo 'Triggered manually by @${{ github.actor }}' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
stress-test-loki:
|
||||
needs: detect-changed-stories
|
||||
if: needs.detect-changed-stories.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
name: Stress test Loki visual tests
|
||||
@@ -56,17 +135,20 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Compile CLJS
|
||||
run: NODE_ENV=development yarn build-pure:cljs
|
||||
run: NODE_ENV=development bun run build-pure:cljs
|
||||
|
||||
- name: Build Storybook (once)
|
||||
run: yarn build-storybook
|
||||
- name: Build Storybook
|
||||
run: bun run build-storybook
|
||||
env:
|
||||
STORYBOOK_STORIES_FILTER: ${{ needs.detect-changed-stories.outputs.story_files }}
|
||||
|
||||
- name: Stress-test Loki "${{ inputs.stories_filter }}" ${{ inputs.burn_in }} times
|
||||
- name: Stress-test Loki
|
||||
run: |
|
||||
BURN_IN=${{ inputs.burn_in }}
|
||||
STORIES_FILTER='${{ inputs.stories_filter }}'
|
||||
BURN_IN=${{ inputs.burn_in || '50' }}
|
||||
STORY_FILES='${{ needs.detect-changed-stories.outputs.story_files }}'
|
||||
|
||||
echo "Running Loki visual tests $BURN_IN times for stories matching: $STORIES_FILTER"
|
||||
echo "Story files: $STORY_FILES"
|
||||
echo "Running Loki $BURN_IN times"
|
||||
echo ""
|
||||
|
||||
FAILED=0
|
||||
@@ -75,10 +157,9 @@ jobs:
|
||||
echo "Run $i of $BURN_IN"
|
||||
echo "=========================================="
|
||||
|
||||
if yarn test-visual:loki \
|
||||
if bun run test-visual:loki \
|
||||
--reactUri file:./storybook-static \
|
||||
--verboseRenderer \
|
||||
--storiesFilter "$STORIES_FILTER"; then
|
||||
--verboseRenderer; then
|
||||
echo "✅ Run $i passed"
|
||||
else
|
||||
echo "❌ Run $i FAILED"
|
||||
@@ -101,10 +182,9 @@ jobs:
|
||||
|
||||
- name: Generate Visual Report on Failure
|
||||
if: failure()
|
||||
run: yarn test-visual:loki-report
|
||||
run: bun run test-visual:loki-report
|
||||
|
||||
- name: Upload Artifact
|
||||
id: artifact-upload-step
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
4
.github/workflows/loki-update.yml
vendored
4
.github/workflows/loki-update.yml
vendored
@@ -22,10 +22,10 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Compile CLJS
|
||||
run: yarn build-pure:cljs
|
||||
run: bun run build-pure:cljs
|
||||
|
||||
- name: Update Loki Snapshots
|
||||
run: yarn test-visual:loki:ci || yarn test-visual:loki-approve-diff && yarn test-visual:loki-prune
|
||||
run: bun run test-visual:loki:ci || bun run test-visual:loki-approve-diff && bun run test-visual:loki-prune
|
||||
|
||||
- name: Check for changes
|
||||
id: check-changes
|
||||
|
||||
6
.github/workflows/loki.yml
vendored
6
.github/workflows/loki.yml
vendored
@@ -49,14 +49,14 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Compile CLJS
|
||||
run: NODE_ENV=development yarn build-pure:cljs
|
||||
run: NODE_ENV=development bun run build-pure:cljs
|
||||
|
||||
- name: Run Loki Visual Tests
|
||||
run: yarn test-visual:loki:ci
|
||||
run: bun run test-visual:loki:ci
|
||||
|
||||
- name: Generate Visual Report on Failure
|
||||
if: failure()
|
||||
run: yarn test-visual:loki-report
|
||||
run: bun run test-visual:loki-report
|
||||
|
||||
- name: Upload Artifact
|
||||
id: artifact-upload-step
|
||||
|
||||
3
.github/workflows/mage.yml
vendored
3
.github/workflows/mage.yml
vendored
@@ -2,9 +2,6 @@ name: Mage Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "mage/**"
|
||||
- "bb.edn"
|
||||
|
||||
jobs:
|
||||
# run mage tests even if the rest of be isnt needed
|
||||
|
||||
20
.github/workflows/minimal-tests.yml
vendored
20
.github/workflows/minimal-tests.yml
vendored
@@ -30,9 +30,9 @@ jobs:
|
||||
needs: check-label
|
||||
if: needs.check-label.outputs.should-run == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
CI: "true"
|
||||
DRIVERS: postgres
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5432
|
||||
@@ -53,10 +53,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Postgres App DB
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: true
|
||||
junit-name: 'minimal-be-tests-postgres-ee'
|
||||
junit-name: "minimal-be-tests-postgres-ee"
|
||||
test-args: >-
|
||||
:only '["test" "enterprise/backend/test"]'
|
||||
:exclude-tags '[:mb/driver-tests :mb/transforms-python-test]'
|
||||
@@ -73,15 +74,16 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
# Postgres driver tests
|
||||
postgres-driver-test:
|
||||
needs: check-label
|
||||
if: needs.check-label.outputs.should-run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CI: 'true'
|
||||
CI: "true"
|
||||
DRIVERS: postgres
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5432
|
||||
@@ -102,9 +104,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Postgres Driver
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'minimal-be-tests-postgres-driver-ee'
|
||||
junit-name: "minimal-be-tests-postgres-driver-ee"
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/transforms-python-test]
|
||||
@@ -119,6 +122,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
# Frontend unit tests
|
||||
frontend-unit-tests:
|
||||
@@ -137,7 +141,8 @@ jobs:
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Run frontend unit tests
|
||||
run: yarn run test-unit --silent
|
||||
id: run-tests
|
||||
run: bun run test-unit --silent
|
||||
continue-on-error: true
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
@@ -150,6 +155,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.run-tests.outcome }}
|
||||
|
||||
# Summary job
|
||||
minimal-tests-result:
|
||||
|
||||
78
.github/workflows/module-boundaries-stats.yml
vendored
Normal file
78
.github/workflows/module-boundaries-stats.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
name: Module Boundaries Stats
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 40 1 * * 5 # every Friday at 1:40
|
||||
|
||||
jobs:
|
||||
module-boundaries-stats:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
- name: get module boundary stats
|
||||
id: get-stats
|
||||
run: |
|
||||
NAMED_MODULES=$(node --input-type=module -e "
|
||||
import { elements } from './frontend/lint/module-boundaries.mjs';
|
||||
const named = elements.filter(e => !['shared/other', 'app/misc', 'other'].includes(e.type));
|
||||
")
|
||||
|
||||
NAMED_DIRS=$(node --input-type=module -e "
|
||||
import { elements } from './frontend/lint/module-boundaries.mjs';
|
||||
const dirs = new Set();
|
||||
for (const el of elements) {
|
||||
const m = el.pattern.match(/^frontend\/src\/metabase\/(\w[\w-]*)\//)
|
||||
if (m) dirs.add(m[1]);
|
||||
}
|
||||
")
|
||||
TOTAL_DIRS=$(ls -d frontend/src/metabase/*/ | xargs -I{} basename {} | wc -l | tr -d ' ')
|
||||
NAMED_COUNT=$(echo "$NAMED_DIRS" | grep -c . || true)
|
||||
UNMODULED=$(($TOTAL_DIRS - $NAMED_COUNT))
|
||||
|
||||
VIOLATIONS=$(bun run module-boundaries 2>&1 | grep -o '✖ [0-9]* problems' | grep -o '[0-9]*' || echo "0")
|
||||
|
||||
echo "$NAMED_MODULES modules, $UNMODULED unmoduled folders, $VIOLATIONS violations"
|
||||
|
||||
echo "NAMED_MODULES=$NAMED_MODULES" >> $GITHUB_OUTPUT
|
||||
echo "UNMODULED=$UNMODULED" >> $GITHUB_OUTPUT
|
||||
echo "VIOLATIONS=$VIOLATIONS" >> $GITHUB_OUTPUT
|
||||
- name: Upload stats
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
NAMED_MODULES: ${{ steps.get-stats.outputs.NAMED_MODULES }}
|
||||
UNMODULED: ${{ steps.get-stats.outputs.UNMODULED }}
|
||||
VIOLATIONS: ${{ steps.get-stats.outputs.VIOLATIONS }}
|
||||
API_KEY: ${{ secrets.STATS_API_KEY }}
|
||||
with:
|
||||
script: | # js
|
||||
const { uploadCsvToMb } = require('${{ github.workspace }}/.github/scripts/csv-to-mb.js');
|
||||
|
||||
console.log("NAMED_MODULES", Number(process.env.NAMED_MODULES));
|
||||
console.log("UNMODULED", Number(process.env.UNMODULED));
|
||||
console.log("VIOLATIONS", Number(process.env.VIOLATIONS));
|
||||
|
||||
const data = [
|
||||
{
|
||||
'Date': new Date().toISOString().slice(0, 10), // YYYY-MM-DD
|
||||
'Modules': Number(process.env.NAMED_MODULES),
|
||||
'Unmoduled folders': Number(process.env.UNMODULED),
|
||||
'Violations': Number(process.env.VIOLATIONS),
|
||||
}
|
||||
];
|
||||
|
||||
uploadCsvToMb({
|
||||
baseUrl: 'https://stats.metabase.com',
|
||||
tableId: 75146,
|
||||
jsonData: data,
|
||||
mode: 'append',
|
||||
}).then(() => {
|
||||
console.log('Data uploaded successfully');
|
||||
}).catch((error) => {
|
||||
console.error('Error uploading data:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
6
.github/workflows/openapi-check.yml
vendored
6
.github/workflows/openapi-check.yml
vendored
@@ -22,10 +22,8 @@ jobs:
|
||||
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
# fetch full git history to allow fetching the latest base branch commit and comparing changes
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js 22
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.x
|
||||
- name: Prepare Node + Bun environment
|
||||
uses: ./.github/actions/prepare-node-bun
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Generate version.properties
|
||||
|
||||
44
.github/workflows/openapi.yml
vendored
Normal file
44
.github/workflows/openapi.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Validate OpenAPI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-**'
|
||||
pull_request:
|
||||
|
||||
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:
|
||||
openapi: ${{ steps.changes.outputs.openapi }}
|
||||
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
|
||||
|
||||
validate-openapi:
|
||||
needs: files-changed
|
||||
if: needs.files-changed.outputs.openapi == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Generate OpenAPI spec
|
||||
run: bun run generate-openapi
|
||||
- name: Validate OpenAPI spec
|
||||
run: bun run lint-openapi
|
||||
4
.github/workflows/pr-env-close-unlabel.yml
vendored
4
.github/workflows/pr-env-close-unlabel.yml
vendored
@@ -5,8 +5,8 @@ on:
|
||||
types: [ closed, unlabeled ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
pr-env:
|
||||
|
||||
29
.github/workflows/pr-env-destroy-manual.yml
vendored
Normal file
29
.github/workflows/pr-env-destroy-manual.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: PR Env Destroy (Manual)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to destroy environment for'
|
||||
required: true
|
||||
type: string
|
||||
self_hosting:
|
||||
description: 'Self-hosting stack'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
approve:
|
||||
runs-on: ubuntu-latest
|
||||
environment: pr-env-admin
|
||||
steps:
|
||||
- name: Approved
|
||||
run: echo "Destroying PR environment for PR #${{ inputs.pr_number }}"
|
||||
|
||||
destroy:
|
||||
needs: approve
|
||||
uses: ./.github/workflows/pr-env-destroy.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
self_hosting: ${{ inputs.self_hosting }}
|
||||
pr_number: ${{ inputs.pr_number }}
|
||||
17
.github/workflows/pr-env-destroy.yml
vendored
17
.github/workflows/pr-env-destroy.yml
vendored
@@ -9,6 +9,13 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
pr_number:
|
||||
description: 'PR number (required for manual dispatch)'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
env:
|
||||
PR_NUMBER: ${{ inputs.pr_number || github.event.number }}
|
||||
|
||||
jobs:
|
||||
destroy_pr:
|
||||
@@ -62,8 +69,8 @@ jobs:
|
||||
- name: Destroy PR Review ENV
|
||||
if: ${{ !inputs.self_hosting }}
|
||||
run: |
|
||||
kubectl delete metabase -n hosting-pr${{ github.event.number }} hosting-pr${{ github.event.number }}
|
||||
kubectl delete ns hosting-pr${{ github.event.number }}
|
||||
kubectl delete metabase -n hosting-pr${{ env.PR_NUMBER }} hosting-pr${{ env.PR_NUMBER }}
|
||||
kubectl delete ns hosting-pr${{ env.PR_NUMBER }}
|
||||
- name: Setup Helm
|
||||
if: ${{ inputs.self_hosting }}
|
||||
uses: azure/setup-helm@v4
|
||||
@@ -72,8 +79,8 @@ jobs:
|
||||
- name: Destroy PR Review ENV (self-hosting)
|
||||
if: ${{ inputs.self_hosting }}
|
||||
run: |
|
||||
helm uninstall hosting-pr${{ github.event.number }} --namespace hosting-pr${{ github.event.number }} || true
|
||||
kubectl delete ns hosting-pr${{ github.event.number }} || true
|
||||
helm uninstall hosting-pr${{ env.PR_NUMBER }} --namespace hosting-pr${{ env.PR_NUMBER }} || true
|
||||
kubectl delete ns hosting-pr${{ env.PR_NUMBER }} || true
|
||||
- name: Setup psql client
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -85,4 +92,4 @@ jobs:
|
||||
-h ${{ secrets.PR_ENV_DB_HOST }} \
|
||||
-U ${{ secrets.PR_ENV_DB_USER }} \
|
||||
-d ${{ secrets.PR_ENV_DB_NAME }} \
|
||||
-c "DROP DATABASE IF EXISTS hosting_pr${{ github.event.number }};"
|
||||
-c "DROP DATABASE IF EXISTS hosting_pr${{ env.PR_NUMBER }};"
|
||||
|
||||
33
.github/workflows/pre-release.yml
vendored
33
.github/workflows/pre-release.yml
vendored
@@ -30,12 +30,15 @@ jobs:
|
||||
outputs:
|
||||
run_mode: ${{ fromJson(steps.config.outputs.result).runMode }}
|
||||
dev_server: ${{ fromJson(steps.config.outputs.result).metastoreDevServer }}
|
||||
package_manager: ${{ fromJson(steps.config.outputs.result).packageManager }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
# note: this block can be removed after we stop needing to be able to test versions before v55
|
||||
- name: Get test mode config
|
||||
id: config
|
||||
@@ -48,13 +51,17 @@ jobs:
|
||||
// with v55 and later we use enterprise tokens that mirror actual Metabase plans
|
||||
const MULTIPLE_TEST_TOKEN_THRESHOLD = 55;
|
||||
|
||||
// Versions before v59 used yarn, v59 and later use bun
|
||||
const BUN_PACKAGE_MANAGER_THRESHOLD = 59;
|
||||
|
||||
const version = '${{ inputs.version }}';
|
||||
const majorVersion = Number(getMajorVersion(version));
|
||||
const oldTestToken = majorVersion < MULTIPLE_TEST_TOKEN_THRESHOLD;
|
||||
|
||||
return ({
|
||||
runMode: oldTestToken ? 'prod' : 'e2e',
|
||||
metastoreDevServer: oldTestToken ? '' : '${{ vars.METASTORE_DEV_SERVER_URL }}'
|
||||
metastoreDevServer: oldTestToken ? '' : '${{ vars.METASTORE_DEV_SERVER_URL }}',
|
||||
packageManager: majorVersion < BUN_PACKAGE_MANAGER_THRESHOLD ? 'yarn' : 'bun',
|
||||
});
|
||||
|
||||
release-artifact:
|
||||
@@ -170,6 +177,8 @@ jobs:
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- name: Compile CLJS
|
||||
run: ${{ needs.test-mode-config.outputs.package_manager }} run build-pure:cljs
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
@@ -250,14 +259,22 @@ jobs:
|
||||
tests-complete-message:
|
||||
if: always()
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [run-sanity-check, check-uberjar-health, containerize-ee, containerize-oss]
|
||||
needs:
|
||||
[
|
||||
run-sanity-check,
|
||||
check-uberjar-health,
|
||||
containerize-ee,
|
||||
containerize-oss,
|
||||
]
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Send tests complete message
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
|
||||
18
.github/workflows/release-embedding-sdk.yml
vendored
18
.github/workflows/release-embedding-sdk.yml
vendored
@@ -44,10 +44,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Cache build scripts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -178,8 +180,8 @@ jobs:
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
|
||||
- name: Prepare build scripts
|
||||
run: yarn --cwd release --frozen-lockfile && yarn --cwd release build
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Get current SDK version
|
||||
id: current-sdk-version
|
||||
@@ -256,7 +258,7 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Run unit tests
|
||||
run: yarn embedding-sdk:test-unit
|
||||
run: bun run embedding-sdk:test-unit
|
||||
|
||||
build-sdk:
|
||||
needs: [test, determine-sdk-version]
|
||||
@@ -282,10 +284,10 @@ jobs:
|
||||
./bin/embedding-sdk/release_utils.bash update_package_json_template ${{ needs.determine-sdk-version.outputs.sdk_version }}
|
||||
|
||||
- name: Build SDK package
|
||||
run: yarn run build-embedding-sdk-package
|
||||
run: bun run build-embedding-sdk-package
|
||||
|
||||
- name: Generate SDK package.json in the build directory
|
||||
run: yarn run embedding-sdk:generate-package
|
||||
run: bun run embedding-sdk:generate-package
|
||||
|
||||
- name: Upload built SDK package as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
8
.github/workflows/release-log.yml
vendored
8
.github/workflows/release-log.yml
vendored
@@ -38,10 +38,10 @@ jobs:
|
||||
with:
|
||||
ref: master # this only works on master
|
||||
fetch-depth: 0 # we want all branches and tags
|
||||
- name: Install Dependencies
|
||||
run: yarn --cwd release --frozen-lockfile && npm i -g tsx
|
||||
- name: Build release scripts
|
||||
run: yarn --cwd release build
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Install tsx
|
||||
run: npm i -g tsx
|
||||
- name: Get version number
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
|
||||
@@ -21,8 +21,13 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Prepare build scripts
|
||||
run: yarn --cwd release --frozen-lockfile && yarn --cwd release build
|
||||
run: bun install --cwd release --frozen-lockfile && bun run --cwd release build
|
||||
- uses: actions/github-script@v7
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
8
.github/workflows/release-notes-preview.yml
vendored
8
.github/workflows/release-notes-preview.yml
vendored
@@ -23,9 +23,11 @@ jobs:
|
||||
exit 1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get Release Version
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
|
||||
7
.github/workflows/release-patch.yml
vendored
7
.github/workflows/release-patch.yml
vendored
@@ -26,8 +26,13 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Prepare build scripts
|
||||
run: yarn --cwd release --frozen-lockfile && yarn --cwd release build
|
||||
run: bun install --cwd release --frozen-lockfile && bun run --cwd release build
|
||||
- name: Trigger auto-patch
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
|
||||
16
.github/workflows/release-status.yml
vendored
16
.github/workflows/release-status.yml
vendored
@@ -14,9 +14,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn --frozen-lockfile && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Check release status and post to Slack
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
@@ -47,9 +49,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn --frozen-lockfile && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Check backport status and post to Slack
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
|
||||
16
.github/workflows/release-tag.yml
vendored
16
.github/workflows/release-tag.yml
vendored
@@ -79,9 +79,11 @@ jobs:
|
||||
exit 1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get Release Version
|
||||
uses: actions/github-script@v7
|
||||
id: canonical_version
|
||||
@@ -304,9 +306,11 @@ jobs:
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Generate new version info
|
||||
uses: actions/github-script@v7
|
||||
id: new_version_info
|
||||
|
||||
27
.github/workflows/release-tests.yml
vendored
27
.github/workflows/release-tests.yml
vendored
@@ -36,10 +36,15 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: "release"
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: prepare dependencies in release directory
|
||||
run: yarn --cwd release --frozen-lockfile
|
||||
run: bun install --cwd release --frozen-lockfile
|
||||
- name: Run frontend type-check
|
||||
run: yarn --cwd release type-check
|
||||
run: bun run --cwd release type-check
|
||||
|
||||
release-tests-unit:
|
||||
needs: files-changed
|
||||
@@ -50,10 +55,15 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: "release"
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: prepare dependencies in release directory
|
||||
run: yarn --cwd release --frozen-lockfile
|
||||
run: bun install --cwd release --frozen-lockfile
|
||||
- name: Run release unit tests
|
||||
run: yarn --cwd release test:unit
|
||||
run: bun run --cwd release test:unit
|
||||
|
||||
release-test-build:
|
||||
needs: files-changed
|
||||
@@ -64,9 +74,14 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: "release"
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: prepare dependencies in release directory
|
||||
run: yarn --cwd release --frozen-lockfile
|
||||
run: bun install --cwd release --frozen-lockfile
|
||||
- name: Build release bundle
|
||||
run: yarn --cwd release build
|
||||
run: bun run --cwd release build
|
||||
- name: Smoke test release bundle
|
||||
run: node release/dist/index.cjs
|
||||
|
||||
80
.github/workflows/release.yml
vendored
80
.github/workflows/release.yml
vendored
@@ -63,9 +63,11 @@ jobs:
|
||||
exit 1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get Release Version
|
||||
uses: actions/github-script@v7
|
||||
id: canonical_version
|
||||
@@ -108,9 +110,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Send publish start message
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
@@ -189,9 +193,11 @@ jobs:
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: prepare release scripts
|
||||
run: cd release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Retrieve uberjar artifact
|
||||
with:
|
||||
@@ -286,9 +292,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: prepare release scripts
|
||||
run: cd release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Tag Release
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
@@ -318,9 +326,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: prepare release scripts
|
||||
run: cd release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get extra tags
|
||||
id: taglist
|
||||
uses: actions/github-script@v7
|
||||
@@ -366,9 +376,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Find milestone
|
||||
id: find-milestone
|
||||
uses: actions/github-script@v7
|
||||
@@ -444,9 +456,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: prepare release scripts
|
||||
run: cd release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Publish draft release notes to github
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
@@ -523,9 +537,11 @@ jobs:
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Publish version info
|
||||
uses: actions/github-script@v7
|
||||
id: new_version_info
|
||||
@@ -580,9 +596,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Send publish complete message
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
@@ -611,9 +629,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Update milestones
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
|
||||
266
.github/workflows/repro-bot.yml
vendored
Normal file
266
.github/workflows/repro-bot.yml
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
name: "Repro Bot"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: "Issue number to reproduce"
|
||||
required: true
|
||||
type: string
|
||||
repro_bot_branch:
|
||||
description: "Branch of repro-bot to use"
|
||||
required: false
|
||||
default: "main"
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
repro-bot:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
id-token: write
|
||||
if: |
|
||||
(github.event_name == 'push') ||
|
||||
(github.event_name == 'issues' && github.event.label.name == '.Run Repro Bot' && github.event.issue.state == 'open') ||
|
||||
(github.event_name == 'workflow_dispatch')
|
||||
|
||||
steps:
|
||||
- name: Get issue number
|
||||
id: issue
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "number=${{ github.event.inputs.issue_number }}" >> $GITHUB_OUTPUT
|
||||
elif [ "${{ github.event_name }}" = "push" ]; then
|
||||
echo "number=70292" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/prepare-backend
|
||||
- uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Checkout repro-bot
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: metabase/repro-bot
|
||||
ref: ${{ github.event.inputs.repro_bot_branch || 'main' }}
|
||||
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
path: repro-bot
|
||||
|
||||
- name: Pre-clone metabase bare git repo
|
||||
run: git clone --bare .git repro-bot/git/metabase
|
||||
|
||||
- name: Capture Node.js path for MCP subprocesses
|
||||
run: echo "NODE_BIN_DIR=$(dirname $(which node))" >> $GITHUB_ENV
|
||||
|
||||
- name: Install playwright-cli
|
||||
run: |
|
||||
npm install -g @playwright/cli@latest
|
||||
playwright-cli install --skills
|
||||
|
||||
- name: Install bbin and clj-nrepl-eval
|
||||
run: |
|
||||
# Install babashka
|
||||
curl -sLO https://raw.githubusercontent.com/babashka/babashka/master/install
|
||||
chmod +x install
|
||||
sudo ./install
|
||||
rm install
|
||||
|
||||
# Install bbin
|
||||
mkdir -p ~/.bbin/bin
|
||||
curl -sL https://raw.githubusercontent.com/babashka/bbin/main/bbin > ~/.bbin/bin/bbin
|
||||
chmod +x ~/.bbin/bin/bbin
|
||||
export PATH="$HOME/.bbin/bin:$PATH"
|
||||
export BBIN_HOME="$HOME/.bbin"
|
||||
|
||||
# Install clj-nrepl-eval via bbin
|
||||
bbin install https://github.com/bhauman/clojure-mcp-light.git \
|
||||
--tag v0.2.1 \
|
||||
--as clj-nrepl-eval \
|
||||
--main-opts '["-m" "clojure-mcp-light.nrepl-eval"]'
|
||||
|
||||
# Add to PATH for the rest of the workflow
|
||||
echo "$HOME/.bbin/bin" >> $GITHUB_PATH
|
||||
|
||||
# Verify
|
||||
clj-nrepl-eval --help | head -3
|
||||
|
||||
- name: Register MCP servers
|
||||
env:
|
||||
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY_REPRO_BOT }}
|
||||
run: |
|
||||
UV_PATH="$(which uv)"
|
||||
jq -n \
|
||||
--arg uv "$UV_PATH" \
|
||||
--arg key "$LINEAR_API_KEY" \
|
||||
'{
|
||||
mcpServers: {
|
||||
"metabase-repro": {
|
||||
command: $uv,
|
||||
args: ["run", "--directory", "mcp", "--with", "fastmcp", "python", "-m", "metabase_repro"]
|
||||
},
|
||||
"linear-server": {
|
||||
url: "https://mcp.linear.app/mcp",
|
||||
type: "http",
|
||||
headers: {
|
||||
Authorization: ("Bearer " + $key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}' > ~/.claude.json
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
uses: actions/create-github-app-token@v2
|
||||
id: claude-app-token
|
||||
with:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Run repro-bot with Claude Code
|
||||
id: claude
|
||||
continue-on-error: true
|
||||
uses: anthropics/claude-code-action@v1
|
||||
env:
|
||||
CLAUDE_CODE_DEBUG_LOGS_DIR: /tmp/claude-debug-logs
|
||||
CLAUDE_WORKING_DIR: ${{ github.workspace }}/repro-bot
|
||||
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.PR_ENV_MB_PREMIUM_EMBEDDING_TOKEN }}
|
||||
GITHUB_ACTIONS_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
timeout-minutes: 30
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_REPRO_BOT }}
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
prompt: |
|
||||
Read and follow the instructions in .claude/commands/repro.md to investigate this GitHub issue:
|
||||
https://github.com/${{ github.repository }}/issues/${{ steps.issue.outputs.number }}
|
||||
|
||||
## CI Environment Notes
|
||||
- The bare git repo has been pre-cloned at git/metabase. The master worktree still needs setup via setup_master_worktree().
|
||||
- The GITHUB_ACTIONS_RUN_URL environment variable is set. If any phase fails catastrophically, follow the CI error handling in repro.md to post the error to Linear with the action run URL.
|
||||
- Docker is available for appdb (Postgres/MySQL/MariaDB) if needed.
|
||||
- Java and Python/uv are pre-installed.
|
||||
claude_args: >-
|
||||
--allowedTools
|
||||
"Glob,
|
||||
Grep,
|
||||
Read,
|
||||
ToolSearch,
|
||||
Skill(*),
|
||||
WebSearch,
|
||||
WebFetch(domain:docs.metabase.com),
|
||||
WebFetch(domain:metabase.com),
|
||||
WebFetch(domain:www.metabase.com),
|
||||
Edit(//./**),
|
||||
Edit(repro/**),
|
||||
Edit(/tmp/**),
|
||||
Write(//./**),
|
||||
Write(repro/**),
|
||||
Write(/tmp/**),
|
||||
Bash(base64 *),
|
||||
Bash(cat *),
|
||||
Bash(cd *),
|
||||
Bash(clj-nrepl-eval *),
|
||||
Bash(clojure *),
|
||||
Bash(cp *),
|
||||
Bash(diff *),
|
||||
Bash(docker *),
|
||||
Bash(echo *),
|
||||
Bash(find *),
|
||||
Bash(gh api repos/metabase/metabase/*),
|
||||
Bash(gh issue list * --repo metabase/metabase *),
|
||||
Bash(gh issue view * --repo metabase/metabase *),
|
||||
Bash(gh release *),
|
||||
Bash(gh search *),
|
||||
Bash(git *),
|
||||
Bash(grep *),
|
||||
Bash(head *),
|
||||
Bash(jar *),
|
||||
Bash(jq *),
|
||||
Bash(kill *),
|
||||
Bash(ls *),
|
||||
Bash(mkdir *),
|
||||
Bash(mv *),
|
||||
Bash(npx playwright-cli:*),
|
||||
Bash(pgrep *),
|
||||
Bash(playwright-cli:*),
|
||||
Bash(printf *),
|
||||
Bash(ps *),
|
||||
Bash(psql *),
|
||||
Bash(PGPASSWORD=*),
|
||||
Bash(python -m mcp.metabase_repro *),
|
||||
Bash(python3 *),
|
||||
Bash(sed *),
|
||||
Bash(sort *),
|
||||
Bash(strings *),
|
||||
Bash(tail *),
|
||||
Bash(tee *),
|
||||
Bash(touch *),
|
||||
Bash(unzip *),
|
||||
Bash(wc *),
|
||||
Bash(which *),
|
||||
Bash(xargs *),
|
||||
mcp__metabase-repro__api_call,
|
||||
mcp__metabase-repro__browser_snapshot,
|
||||
mcp__metabase-repro__check_setup,
|
||||
mcp__metabase-repro__check_setup_token,
|
||||
mcp__metabase-repro__cleanup,
|
||||
mcp__metabase-repro__create_env,
|
||||
mcp__metabase-repro__get_session_token,
|
||||
mcp__metabase-repro__get_status,
|
||||
mcp__metabase-repro__jar_download,
|
||||
mcp__metabase-repro__repl_eval,
|
||||
mcp__metabase-repro__reset_metabase,
|
||||
mcp__metabase-repro__run_test,
|
||||
mcp__metabase-repro__save_test_patch,
|
||||
mcp__metabase-repro__setup_master_worktree,
|
||||
mcp__metabase-repro__setup_worktree,
|
||||
mcp__metabase-repro__start_appdb,
|
||||
mcp__metabase-repro__start_metabase,
|
||||
mcp__metabase-repro__stop_appdb_tool,
|
||||
mcp__metabase-repro__stop_metabase,
|
||||
mcp__linear-server__create_attachment,
|
||||
mcp__linear-server__get_issue,
|
||||
mcp__linear-server__list_comments,
|
||||
mcp__linear-server__list_issues,
|
||||
mcp__linear-server__list_issue_labels,
|
||||
mcp__linear-server__save_comment,
|
||||
mcp__linear-server__save_issue"
|
||||
--disallowedTools "Bash(git push*),Bash(rm -rf /*)"
|
||||
|
||||
- name: Verify repro-bot completed successfully
|
||||
run: |
|
||||
if [ "${{ steps.claude.outcome }}" = "failure" ]; then
|
||||
echo "::error::Claude Code crashed or exited with an error"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXEC_FILE="${{ steps.claude.outputs.execution_file }}"
|
||||
if [ -z "$EXEC_FILE" ] || [ ! -f "$EXEC_FILE" ]; then
|
||||
echo "::error::No execution file found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "mcp__linear-server__" "$EXEC_FILE"; then
|
||||
echo "::error::Claude did not post a comment on Linear"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Repro-bot completed successfully and posted to Linear"
|
||||
|
||||
- name: Upload repro-bot artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
if: always()
|
||||
with:
|
||||
name: repro-bot-${{ steps.issue.outputs.number }}
|
||||
if-no-files-found: ignore
|
||||
path: |
|
||||
repro/*/SUGGESTIONS.md
|
||||
repro/*/*.log
|
||||
repro/*/*.patch
|
||||
repro/*/playwright/**
|
||||
${{ steps.claude.outputs.execution_file }}
|
||||
18
.github/workflows/rerun-workflows.yml
vendored
18
.github/workflows/rerun-workflows.yml
vendored
@@ -40,11 +40,19 @@ jobs:
|
||||
const BRANCH = process.env.BRANCH_NAME;
|
||||
|
||||
if (ATTEMPT <= MAX_ATTEMPTS) {
|
||||
github.rest.actions.reRunWorkflowFailedJobs({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{ github.event.workflow_run.id }},
|
||||
});
|
||||
try {
|
||||
await github.rest.actions.reRunWorkflowFailedJobs({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{ github.event.workflow_run.id }},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status === 403) {
|
||||
console.log(`Skipping re-run: ${error.message}`);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} else if (!BRANCH.includes('backport-')) {
|
||||
// notify slack of repeated failure
|
||||
const { WebClient } = require('@slack/web-api');
|
||||
|
||||
63
.github/workflows/reset-cloud-test-dataset.yml
vendored
Normal file
63
.github/workflows/reset-cloud-test-dataset.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Reset Cloud Test Dataset
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
driver:
|
||||
description: 'Driver to reset dataset for'
|
||||
required: true
|
||||
default: 'snowflake'
|
||||
type: choice
|
||||
options:
|
||||
- snowflake
|
||||
- bigquery-cloud-sdk
|
||||
- redshift
|
||||
- databricks
|
||||
dataset-name:
|
||||
description: 'Dataset name to drop (e.g., test-data)'
|
||||
required: true
|
||||
default: 'test-data'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
reset-dataset:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: ${{ inputs.driver }}
|
||||
# Snowflake
|
||||
MB_SNOWFLAKE_TEST_USER: METABASE CI
|
||||
MB_SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.MB_SNOWFLAKE_TEST_ACCOUNT }}
|
||||
MB_SNOWFLAKE_TEST_PASSWORD: ${{ secrets.MB_SNOWFLAKE_TEST_PASSWORD }}
|
||||
MB_SNOWFLAKE_TEST_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PRIVATE_KEY }}
|
||||
MB_SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.MB_SNOWFLAKE_TEST_WAREHOUSE }}
|
||||
# BigQuery
|
||||
MB_BIGQUERY_TEST_PROJECT_ID: ${{ secrets.BIGQUERY_TEST_PROJECT_ID }}
|
||||
MB_BIGQUERY_TEST_CLIENT_ID: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_ID }}
|
||||
MB_BIGQUERY_TEST_CLIENT_SECRET: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_SECRET }}
|
||||
MB_BIGQUERY_TEST_ACCESS_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_ACCESS_TOKEN }}
|
||||
MB_BIGQUERY_TEST_REFRESH_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_REFRESH_TOKEN }}
|
||||
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON }}
|
||||
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING }}
|
||||
# Redshift
|
||||
MB_REDSHIFT_TEST_USER: metabase_ci
|
||||
MB_REDSHIFT_TEST_DB: testdb
|
||||
MB_REDSHIFT_TEST_HOST: ${{ secrets.MB_REDSHIFT_TEST_HOST }}
|
||||
MB_REDSHIFT_TEST_PASSWORD: ${{ secrets.MB_REDSHIFT_TEST_PASSWORD }}
|
||||
# Databricks
|
||||
MB_DATABRICKS_TEST_HOST: ${{ secrets.MB_DATABRICKS_JDBC_TEST_HOST }}
|
||||
MB_DATABRICKS_TEST_HTTP_PATH: ${{ secrets.MB_DATABRICKS_JDBC_TEST_HTTP_PATH }}
|
||||
MB_DATABRICKS_TEST_TOKEN: ${{ secrets.MB_DATABRICKS_JDBC_TEST_TOKEN }}
|
||||
MB_DATABRICKS_TEST_CATALOG: metabase_ci
|
||||
MB_DATABRICKS_TEST_CLIENT_ID: ${{ secrets.MB_DATABRICKS_JDBC_TEST_CLIENT_ID }}
|
||||
MB_DATABRICKS_TEST_OAUTH_SECRET: ${{ secrets.MB_DATABRICKS_JDBC_TEST_OAUTH_SECRET }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/prepare-backend
|
||||
- name: Drop dataset
|
||||
run: >-
|
||||
clojure -X:dev:drivers:drivers-dev:test
|
||||
metabase.test.data.impl/drop-dataset!
|
||||
:driver '"${{ inputs.driver }}"'
|
||||
:dataset-name '"${{ inputs.dataset-name }}"'
|
||||
6
.github/workflows/run-tests.yml
vendored
6
.github/workflows/run-tests.yml
vendored
@@ -81,7 +81,7 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.frontend_all != 'true' }}
|
||||
skip-lint: ${{ needs.files-changed.outputs.frontend_all != 'true' && needs.files-changed.outputs.e2e_specs != 'true' }}
|
||||
skip-lint: ${{ needs.files-changed.outputs.frontend_all != 'true' && needs.files-changed.outputs.e2e_all != 'true' }}
|
||||
|
||||
e2e-tests:
|
||||
needs: [files-changed, uberjar]
|
||||
@@ -103,7 +103,8 @@ jobs:
|
||||
(needs.files-changed.outputs.embedding_sdk_components == 'true' ||
|
||||
needs.files-changed.outputs.frontend_sources == 'true' ||
|
||||
needs.files-changed.outputs.embedding_documentation == 'true' ||
|
||||
needs.files-changed.outputs.embedding_sdk_ci == 'true')
|
||||
needs.files-changed.outputs.embedding_sdk_ci == 'true' ||
|
||||
needs.files-changed.outputs.embedding_sdk_host_sample_apps == 'true')
|
||||
uses: ./.github/workflows/embedding-sdk-package-build.yml
|
||||
secrets: inherit
|
||||
|
||||
@@ -113,6 +114,7 @@ jobs:
|
||||
uses: ./.github/workflows/embedding-sdk.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
bundle-size-check: ${{ github.event_name == 'pull_request' && needs.uberjar.result == 'success' && needs.files-changed.outputs.frontend_sources == 'true' }}
|
||||
cli-snippets-type-check: ${{ needs.files-changed.outputs.frontend_sources == 'true' }}
|
||||
docs-snippets-type-check: ${{ needs.files-changed.outputs.embedding_documentation == 'true' }}
|
||||
documentation-validation: ${{ needs.files-changed.outputs.frontend_sources == 'true' }}
|
||||
|
||||
34
.github/workflows/semantic-search.yml
vendored
34
.github/workflows/semantic-search.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
semantic-search-tests-h2:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -46,6 +46,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
@@ -64,18 +65,19 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
semantic-search-tests-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MariaDB 10.2 with pgvector pg17
|
||||
junit-name: semantic-search-tests-mariadb-10-2
|
||||
mariadb-image: circleci/mariadb:10.2.23
|
||||
- name: MariaDB 10.6 with pgvector pg17
|
||||
junit-name: semantic-search-tests-mariadb-10-6
|
||||
mariadb-image: circleci/mariadb:10.6
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
- name: MariaDB Latest with pgvector pg17
|
||||
junit-name: semantic-search-tests-mariadb-latest
|
||||
@@ -118,6 +120,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
@@ -136,18 +139,19 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
semantic-search-tests-mysql:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MySQL 8.0 with pgvector pg17
|
||||
junit-name: semantic-search-tests-mysql-8-0
|
||||
mysql-image: cimg/mysql:8.0
|
||||
- name: MySQL 8.4 with pgvector pg17
|
||||
junit-name: semantic-search-tests-mysql-8-4
|
||||
mysql-image: cimg/mysql:8.4
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
- name: MySQL Latest with pgvector pg17
|
||||
junit-name: semantic-search-tests-mysql-latest
|
||||
@@ -190,6 +194,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
@@ -208,18 +213,19 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
semantic-search-tests-postgres:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 40
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Postgres 12.x with pgvector pg17
|
||||
junit-name: semantic-search-tests-postgres-12
|
||||
postgres-image: postgres:12-alpine
|
||||
- name: Postgres 14.x with pgvector pg17
|
||||
junit-name: semantic-search-tests-postgres-14
|
||||
postgres-image: postgres:14-alpine
|
||||
pgvector-image: pgvector/pgvector:pg17
|
||||
- name: Postgres Latest with pgvector pg17
|
||||
junit-name: semantic-search-tests-postgres-latest
|
||||
@@ -263,6 +269,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
build-static-viz: ${{ matrix.job.build-static-viz }}
|
||||
@@ -281,6 +288,7 @@ jobs:
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
|
||||
|
||||
semantic-search-tests-result:
|
||||
needs:
|
||||
|
||||
24
.github/workflows/snyk.yml
vendored
24
.github/workflows/snyk.yml
vendored
@@ -41,16 +41,36 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
project:
|
||||
- name: Main Frontend (yarn)
|
||||
- name: Main Frontend (bun w/ yarn.lock)
|
||||
file: yarn.lock
|
||||
package-manager: yarn
|
||||
category: main-frontend-yarn
|
||||
category: main-frontend-bun
|
||||
- name: Release Helper
|
||||
file: release/yarn.lock
|
||||
package-manager: yarn
|
||||
category: release-helper
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.7"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Snyk doesn't support bun.lock yet, so we generate yarn.lock and scan that.
|
||||
# Bun has a bug where it normalizes "< 3.0.0" to "< 3" in alias entries
|
||||
# but not in dependency specs, causing Snyk to fail matching dependencies.
|
||||
# The sed commands fix this inconsistency. They continue on error to let
|
||||
# Snyk report parsing issues directly rather than failing on the workaround.
|
||||
- name: Generate yarn.lock
|
||||
if: matrix.project.file == 'yarn.lock'
|
||||
run: |
|
||||
bun install --yarn
|
||||
sed -i -E 's/< ([0-9]+)\.0\.0"/< \1"/g; s/< ([0-9]+)\.0"/< \1"/g' yarn.lock || true
|
||||
- name: Generate release/yarn.lock
|
||||
if: matrix.project.file == 'release/yarn.lock'
|
||||
run: |
|
||||
bun install --yarn
|
||||
sed -i -E 's/< ([0-9]+)\.0\.0"/< \1"/g; s/< ([0-9]+)\.0"/< \1"/g' yarn.lock || true
|
||||
working-directory: release
|
||||
- uses: snyk/actions/setup@0.4.0
|
||||
- name: Run snyk test
|
||||
env:
|
||||
|
||||
25
.github/workflows/tag-for-release.yml
vendored
25
.github/workflows/tag-for-release.yml
vendored
@@ -23,9 +23,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Send build start message
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
@@ -62,9 +64,11 @@ jobs:
|
||||
exit 1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get Release Version
|
||||
uses: actions/github-script@v7
|
||||
id: canonical_version
|
||||
@@ -104,9 +108,11 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # IMPORTANT! to get all the branches
|
||||
sparse-checkout: release
|
||||
- name: Prepare build scripts
|
||||
run: cd ${{ github.workspace }}/release && yarn && yarn build
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
- name: Get Release Branch
|
||||
uses: actions/github-script@v7
|
||||
id: release_branch
|
||||
@@ -124,6 +130,7 @@ jobs:
|
||||
git branch --contains ${{ inputs.commit }} | grep -q $RELEASE_BRANCH \
|
||||
&& echo "Commit found in correct release branch" \
|
||||
|| (echo "Commit not found in correct release branch" && exit 1)
|
||||
git checkout -
|
||||
|
||||
tag-uberjar-for-release:
|
||||
needs: [check-version, check-commit]
|
||||
|
||||
495
.github/workflows/transforms-python-stress-test.yml
vendored
Normal file
495
.github/workflows/transforms-python-stress-test.yml
vendored
Normal file
@@ -0,0 +1,495 @@
|
||||
name: Transforms Python Stress Test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Branch name or commit SHA to checkout'
|
||||
type: string
|
||||
required: false
|
||||
default: ''
|
||||
driver:
|
||||
description: 'Which driver(s) to test'
|
||||
required: true
|
||||
default: 'all'
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
- bigquery
|
||||
- clickhouse
|
||||
- mongo
|
||||
- mysql
|
||||
- postgres
|
||||
- redshift
|
||||
- snowflake
|
||||
- sqlserver
|
||||
|
||||
jobs:
|
||||
|
||||
be-tests-bigquery-transforms:
|
||||
# inputs.driver is empty on push triggers, so treat '' as 'all'
|
||||
if: ${{ inputs.driver == 'all' || inputs.driver == '' || inputs.driver == 'bigquery' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: bigquery-cloud-sdk
|
||||
MB_BIGQUERY_TEST_PROJECT_ID: ${{ secrets.BIGQUERY_TEST_PROJECT_ID }}
|
||||
MB_BIGQUERY_TEST_CLIENT_ID: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_ID }}
|
||||
MB_BIGQUERY_TEST_CLIENT_SECRET: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_SECRET }}
|
||||
MB_BIGQUERY_TEST_ACCESS_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_ACCESS_TOKEN }}
|
||||
MB_BIGQUERY_TEST_REFRESH_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_REFRESH_TOKEN }}
|
||||
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON }}
|
||||
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING }}
|
||||
name: BigQuery Transforms
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test BigQuery transforms
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-bigquery-transforms'
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: BigQuery Transforms
|
||||
|
||||
be-tests-clickhouse-transforms:
|
||||
if: ${{ inputs.driver == 'all' || inputs.driver == '' || inputs.driver == 'clickhouse' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: clickhouse
|
||||
MB_CLICKHOUSE_TEST_HOST: localhost
|
||||
MB_CLICKHOUSE_TEST_PORT: 8123
|
||||
MB_CLICKHOUSE_TEST_OLD_PORT: 8124
|
||||
MB_CLICKHOUSE_TEST_NGINX_PORT: 8127
|
||||
name: ClickHouse Transforms
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Add ClickHouse TLS instance to /etc/hosts
|
||||
run: |
|
||||
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
|
||||
- name: Start ClickHouse in Docker
|
||||
uses: hoverkraft-tech/compose-action@v2.0.0
|
||||
with:
|
||||
compose-file: "modules/drivers/clickhouse/docker-compose.yml"
|
||||
down-flags: "--volumes"
|
||||
services: |
|
||||
clickhouse
|
||||
clickhouse_tls
|
||||
clickhouse_older_version
|
||||
clickhouse_cluster_node1
|
||||
clickhouse_cluster_node2
|
||||
nginx
|
||||
- name: Test ClickHouse transforms
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-clickhouse-transforms'
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: ClickHouse Transforms
|
||||
|
||||
be-tests-mongo-transforms:
|
||||
if: ${{ inputs.driver == 'all' || inputs.driver == '' || inputs.driver == 'mongo' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mongo
|
||||
MB_MONGO_TEST_USER: metabase
|
||||
MB_MONGO_TEST_PASSWORD: metasample123
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:latest
|
||||
ports:
|
||||
- "27017:27017"
|
||||
env:
|
||||
MONGO_INITDB_ROOT_USERNAME: metabase
|
||||
MONGO_INITDB_ROOT_PASSWORD: metasample123
|
||||
name: MongoDB Transforms
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test MongoDB transforms
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mongo-transforms'
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: MongoDB Transforms
|
||||
|
||||
be-tests-mysql-transforms:
|
||||
if: ${{ inputs.driver == 'all' || inputs.driver == '' || inputs.driver == 'mysql' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:latest
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
name: MySQL Transforms
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test MySQL transforms
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mysql-transforms'
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: MySQL Transforms
|
||||
|
||||
be-tests-postgres-transforms:
|
||||
if: ${{ inputs.driver == 'all' || inputs.driver == '' || inputs.driver == 'postgres' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: postgres
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_USER: mb_test
|
||||
MB_POSTGRESQL_TEST_USER: mb_test
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
name: Postgres Transforms
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test Postgres transforms
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-postgres-transforms'
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: Postgres Transforms
|
||||
|
||||
be-tests-redshift-transforms:
|
||||
if: ${{ inputs.driver == 'all' || inputs.driver == '' || inputs.driver == 'redshift' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: redshift
|
||||
MB_REDSHIFT_TEST_USER: metabase_ci
|
||||
MB_REDSHIFT_TEST_DB: testdb
|
||||
MB_REDSHIFT_TEST_DB_ROUTING: dev
|
||||
MB_REDSHIFT_TEST_HOST: ${{ secrets.MB_REDSHIFT_TEST_HOST }}
|
||||
MB_REDSHIFT_TEST_PASSWORD: ${{ secrets.MB_REDSHIFT_TEST_PASSWORD }}
|
||||
name: Redshift Transforms
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test Redshift transforms
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-redshift-transforms'
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: Redshift Transforms
|
||||
|
||||
be-tests-snowflake-transforms:
|
||||
if: ${{ inputs.driver == 'all' || inputs.driver == '' || inputs.driver == 'snowflake' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: snowflake
|
||||
MB_SNOWFLAKE_TEST_USER: METABASE CI
|
||||
MB_SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.MB_SNOWFLAKE_TEST_ACCOUNT }}
|
||||
MB_SNOWFLAKE_TEST_PASSWORD: ${{ secrets.MB_SNOWFLAKE_TEST_PASSWORD }}
|
||||
MB_SNOWFLAKE_TEST_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PRIVATE_KEY }}
|
||||
MB_SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.MB_SNOWFLAKE_TEST_WAREHOUSE }}
|
||||
MB_SNOWFLAKE_TEST_PK_USER: METABASE PK
|
||||
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY }}
|
||||
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2 }}
|
||||
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY }}
|
||||
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2 }}
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DEFAULT_USER: RSA_ROLE_TEST_DEFAULT_USER
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_CUSTOM_USER: RSA_ROLE_TEST_CUSTOM_USER
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_ROLE: RSA_ROLE_TEST_ROLE
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DB: RSA_ROLE_TEST_DB
|
||||
name: Snowflake Transforms
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test Snowflake transforms
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-snowflake-transforms'
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: Snowflake Transforms
|
||||
|
||||
be-tests-sqlserver-transforms:
|
||||
if: ${{ inputs.driver == 'all' || inputs.driver == '' || inputs.driver == 'sqlserver' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: sqlserver
|
||||
MB_SQLSERVER_TEST_HOST: localhost
|
||||
MB_SQLSERVER_TEST_PASSWORD: 'P@ssw0rd'
|
||||
MB_SQLSERVER_TEST_USER: SA
|
||||
services:
|
||||
sqlserver:
|
||||
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||
ports:
|
||||
- "1433:1433"
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: 'P@ssw0rd'
|
||||
MSSQL_MEMORY_LIMIT_MB: 1024
|
||||
name: SQL Server Transforms
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
- name: Setup python-runner
|
||||
uses: ./.github/actions/setup-python-runner
|
||||
with:
|
||||
metabase-automation-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
- name: Test SQL Server transforms
|
||||
id: test-driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-sqlserver-transforms'
|
||||
test-args: >-
|
||||
:only-tags [:mb/transforms-python-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
- name: Cleanup python-runner
|
||||
if: always()
|
||||
uses: ./.github/actions/cleanup-python-runner
|
||||
with:
|
||||
upload-logs: ${{ steps.test-driver.outcome == 'failure' }}
|
||||
logs-artifact-suffix: SQL Server Transforms
|
||||
|
||||
transforms-stress-test-result:
|
||||
needs:
|
||||
- be-tests-bigquery-transforms
|
||||
- be-tests-clickhouse-transforms
|
||||
- be-tests-mongo-transforms
|
||||
- be-tests-mysql-transforms
|
||||
- be-tests-postgres-transforms
|
||||
- be-tests-redshift-transforms
|
||||
- be-tests-snowflake-transforms
|
||||
- be-tests-sqlserver-transforms
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: transforms-stress-test-result
|
||||
if: always() && !cancelled()
|
||||
steps:
|
||||
- name: Check job status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
with:
|
||||
script: |
|
||||
const needs = JSON.parse(process.env.needs);
|
||||
|
||||
const jobs = Object.entries(needs).map(
|
||||
([jobName, jobValues]) => ({
|
||||
name: jobName,
|
||||
result: jobValues.result
|
||||
}));
|
||||
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
||||
console.log("All transforms tests have passed (or been skipped).");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log("Some transforms tests have failed:");
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success' && job.result !== 'skipped') {
|
||||
console.log(` ${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
|
||||
process.exit(1);
|
||||
50
.github/workflows/translation-context.yml
vendored
Normal file
50
.github/workflows/translation-context.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: Crowdin Context Harvester
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "30 6 * * 2" # Tuesday at 06:30 UTC, one hour after translation-update.yml
|
||||
|
||||
jobs:
|
||||
harvest:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Harvest and write AI context to Crowdin
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
OPENAI_KEY: ${{ secrets.CROWDIN_OPENAI_API_KEY }}
|
||||
run: |
|
||||
npx --yes crowdin-context-harvester@latest harvest \
|
||||
--project="$CROWDIN_PROJECT_ID" \
|
||||
--ai="openai" \
|
||||
--model="gpt-5.1" \
|
||||
--croql='not (context contains "✨ AI Context")' \
|
||||
--output="crowdin" \
|
||||
--concurrency=10
|
||||
|
||||
# We only want to trigger pre-translations, not wait for them to finish running upstream.
|
||||
# Weirdly, Crowdin keeps this step running as long as it runs pre-translations on their servers.
|
||||
# Hence, the three minute timeout and continue-on-error.
|
||||
- name: Trigger pre-translations
|
||||
timeout-minutes: 3
|
||||
uses: crowdin/github-action@v2
|
||||
# https://crowdin.github.io/crowdin-cli/commands/crowdin-pre-translate
|
||||
with:
|
||||
command: "pre-translate"
|
||||
command_args: >-
|
||||
--method ai
|
||||
--ai-prompt ${{ secrets.CROWDIN_AI_PROMPT_ID }}
|
||||
--translate-untranslated-only
|
||||
--token ${{ secrets.CROWDIN_TOKEN }}
|
||||
--project-id ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
--config=locales/crowdin.yml
|
||||
--no-progress
|
||||
continue-on-error: true
|
||||
70
.github/workflows/tusk-sanity-check.yml
vendored
70
.github/workflows/tusk-sanity-check.yml
vendored
@@ -1,70 +0,0 @@
|
||||
# This allows Tusk to manually run workflows to check lint/build/tests before creating a PR.
|
||||
# If you have any questions, please reach out to support@usetusk.ai.
|
||||
|
||||
name: Tusk Sanity Check
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
taskId:
|
||||
description: "Tusk Task ID"
|
||||
required: true
|
||||
runType:
|
||||
description: "Tusk Run Type"
|
||||
required: true
|
||||
runId:
|
||||
description: "Tusk Run ID"
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
sanity_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Log inputs
|
||||
run: |
|
||||
echo "Tusk Task ID: ${{ github.event.inputs.taskId }}"
|
||||
echo "Tusk Run Type: ${{ github.event.inputs.runType }}"
|
||||
echo "Tusk Run ID: ${{ github.event.inputs.runId }}"
|
||||
echo "Current Branch: ${{ github.ref }}"
|
||||
echo "Repository default branch: ${{ github.event.repository.default_branch }}"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check which files changed
|
||||
uses: dorny/paths-filter@v3.0.0
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
# Similar to .github/workflows/frontend.yml
|
||||
- name: Prepare front-end environment
|
||||
if: steps.changes.outputs.frontend_all == 'true'
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- run: yarn build-pure:cljs
|
||||
name: Compile CLJS
|
||||
|
||||
- name: Run lint-staged
|
||||
if: steps.changes.outputs.frontend_all == 'true'
|
||||
run: |
|
||||
BASE_COMMIT=$(git merge-base origin/${{ github.event.repository.default_branch }} HEAD)
|
||||
npx lint-staged --shell --no-stash --diff="$BASE_COMMIT...HEAD"
|
||||
|
||||
# You must include this step after running all auto-fixing steps
|
||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "fix(${{ github.run_id }}): auto linting" # The commit message MUST start with "fix(${{ github.run_id }}):"
|
||||
commit_options: "--no-verify" # No verify because we already ran it above
|
||||
skip_fetch: true
|
||||
skip_checkout: true
|
||||
status_options: "--untracked-files=no"
|
||||
add_options: "--update"
|
||||
|
||||
- name: Check types
|
||||
if: steps.changes.outputs.frontend_all == 'true'
|
||||
run: yarn type-check-pure
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user