mirror of
https://github.com/mikefarah/yq.git
synced 2026-08-24 08:22:13 +08:00
Compare commits
886
Commits
xml-comments
...
v4.49.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ccaf8e700 | ||
|
|
a1a27b8536 | ||
|
|
1b91fc63ea | ||
|
|
9e0c5fd3c9 | ||
|
|
5d0481c0d2 | ||
|
|
f91176a204 | ||
|
|
8e86bdb876 | ||
|
|
fc164ca9c3 | ||
|
|
810e9d921e | ||
|
|
45be35c063 | ||
|
|
39fbf01fa8 | ||
|
|
306dc931a5 | ||
|
|
f00852bc6c | ||
|
|
c716d157f2 | ||
|
|
e49e588ab5 | ||
|
|
389486829d | ||
|
|
d32e71f25b | ||
|
|
796317b885 | ||
|
|
258b84a05e | ||
|
|
e056b91a00 | ||
|
|
85b0985a60 | ||
|
|
874cbc4d3c | ||
|
|
f6c780e793 | ||
|
|
8c25f33df4 | ||
|
|
2869919cb4 | ||
|
|
458d02f3ab | ||
|
|
877a47cb19 | ||
|
|
3050ca5303 | ||
|
|
49b6477c49 | ||
|
|
78bc9baffd | ||
|
|
1f2b0fe76b | ||
|
|
1228bcfa75 | ||
|
|
7f72595a12 | ||
|
|
ff2c1c930c | ||
|
|
36d410b348 | ||
|
|
6dfe002058 | ||
|
|
ed4f468c97 | ||
|
|
8b2ba41c6c | ||
|
|
0ecdce24e8 | ||
|
|
01ac615e67 | ||
|
|
6629924dea | ||
|
|
386935470d | ||
|
|
d5dd338707 | ||
|
|
201542b522 | ||
|
|
f3538850f2 | ||
|
|
df92decbe0 | ||
|
|
23060cb8af | ||
|
|
4532346e13 | ||
|
|
ff40a023cc | ||
|
|
7d49d408ef | ||
|
|
02b28073bf | ||
|
|
6957399dc0 | ||
|
|
6251e95af8 | ||
|
|
b2155ebb00 | ||
|
|
911e3942e3 | ||
|
|
35d338cc24 | ||
|
|
6d7aa384c4 | ||
|
|
e89001156a | ||
|
|
c34edcf983 | ||
|
|
a46a5766fa | ||
|
|
af2f86828c | ||
|
|
76c817009f | ||
|
|
f03c9dc599 | ||
|
|
023c85e1e2 | ||
|
|
a3d9e0172f | ||
|
|
ab3be228dc | ||
|
|
93fed3fd7a | ||
|
|
f42534ea0f | ||
|
|
b968963ed4 | ||
|
|
92309b17a4 | ||
|
|
d5757fc82b | ||
|
|
db2a4550e5 | ||
|
|
3018396ed2 | ||
|
|
84b095bbc4 | ||
|
|
f35e57d901 | ||
|
|
70ac3d6c7a | ||
|
|
904215ef4d | ||
|
|
41cc4fb4ac | ||
|
|
9c95a9f379 | ||
|
|
a4720c089a | ||
|
|
3431aebb2c | ||
|
|
ae87394f4a | ||
|
|
23a7b173bf | ||
|
|
5e75db824b | ||
|
|
08ecd39a1e | ||
|
|
b7aa711d94 | ||
|
|
a5b8ef6cb1 | ||
|
|
a47e882c8f | ||
|
|
128bf80eed | ||
|
|
4019d42d60 | ||
|
|
55daf6d93c | ||
|
|
8e731ac13c | ||
|
|
d0c897f5e6 | ||
|
|
6e8cc00030 | ||
|
|
b9d9e2fbad | ||
|
|
a98921213f | ||
|
|
0153cccda9 | ||
|
|
9b299649f7 | ||
|
|
369fe56e2d | ||
|
|
8c06478ade | ||
|
|
fa6dc5c9fb | ||
|
|
422300457c | ||
|
|
544bd9ff6f | ||
|
|
1187c954ec | ||
|
|
08d3789daa | ||
|
|
6a5e95ac84 | ||
|
|
221a5b1106 | ||
|
|
c10bfe0602 | ||
|
|
99f88df21e | ||
|
|
7696723d5c | ||
|
|
a9f7cc1ebb | ||
|
|
384d227efe | ||
|
|
25365a0f0b | ||
|
|
78c096fa8f | ||
|
|
ce9a4af0df | ||
|
|
4734be9a4d | ||
|
|
4d88d51b1b | ||
|
|
31628e7324 | ||
|
|
bfcb3fc6b7 | ||
|
|
c3782799c5 | ||
|
|
162ea5437c | ||
|
|
40808838ba | ||
|
|
176873e93a | ||
|
|
fd7e750040 | ||
|
|
ff9d2acd4a | ||
|
|
88bfbec97b | ||
|
|
b15ce77cad | ||
|
|
b84fd47934 | ||
|
|
58e0cd2600 | ||
|
|
c58d9e7da4 | ||
|
|
3ac203ebb8 | ||
|
|
b534aa9ee5 | ||
|
|
39a65b62d2 | ||
|
|
1e3006e951 | ||
|
|
22949df0fd | ||
|
|
734e2cd254 | ||
|
|
082b76affa | ||
|
|
5bc2cd03da | ||
|
|
20407a07a5 | ||
|
|
0a83da6b38 | ||
|
|
77da8b7d32 | ||
|
|
a27db3aacc | ||
|
|
ed7fd54dac | ||
|
|
2c2487c0bd | ||
|
|
2c7cce0878 | ||
|
|
2bd08ea4e8 | ||
|
|
72fa3cca98 | ||
|
|
f89605e3c0 | ||
|
|
c59fa8de59 | ||
|
|
ef8b520f89 | ||
|
|
20b5129120 | ||
|
|
89518a09b8 | ||
|
|
d01ac7801d | ||
|
|
67520b5e7b | ||
|
|
7b6e61ba2c | ||
|
|
cf8621d9e3 | ||
|
|
340b897252 | ||
|
|
4155c99a12 | ||
|
|
b26de94758 | ||
|
|
7dc5efee62 | ||
|
|
2cb7498444 | ||
|
|
649c9a083a | ||
|
|
074d785396 | ||
|
|
de2f77b49c | ||
|
|
fe06096514 | ||
|
|
40f2c2381b | ||
|
|
64bee0fbe3 | ||
|
|
6e6575d5b0 | ||
|
|
ce2ce51236 | ||
|
|
c9766c1cab | ||
|
|
337960a6d1 | ||
|
|
1395d6e230 | ||
|
|
ba1b9737b6 | ||
|
|
d74e70104f | ||
|
|
15071ba393 | ||
|
|
a9495ff395 | ||
|
|
e176ec649f | ||
|
|
c34d3f7805 | ||
|
|
99e24956b3 | ||
|
|
2bce6b2608 | ||
|
|
f202d06d82 | ||
|
|
1508f1fd5f | ||
|
|
8bf425b4d1 | ||
|
|
f755755bb0 | ||
|
|
0f390b27aa | ||
|
|
31ad7fbaa2 | ||
|
|
566cf82d08 | ||
|
|
2c9f8331c4 | ||
|
|
c02d44de63 | ||
|
|
f73c862cc5 | ||
|
|
294a1709ad | ||
|
|
342efb23ff | ||
|
|
2201381235 | ||
|
|
5273715428 | ||
|
|
e204677ff8 | ||
|
|
f76815959a | ||
|
|
c16b1800a0 | ||
|
|
3bca3a23c0 | ||
|
|
70bef5b9d2 | ||
|
|
ea2ba18ef4 | ||
|
|
80310eaaac | ||
|
|
8391193732 | ||
|
|
d858ab1644 | ||
|
|
f1c7e5ca79 | ||
|
|
bc5b54cb1d | ||
|
|
129e597346 | ||
|
|
42db154e2e | ||
|
|
f06c7c6b52 | ||
|
|
1ae6ac18f9 | ||
|
|
b710fadfc0 | ||
|
|
6a43f17d02 | ||
|
|
1a4b2485ff | ||
|
|
58cc9db721 | ||
|
|
4af292f076 | ||
|
|
f238f01856 | ||
|
|
58a4d2401f | ||
|
|
887b4a560f | ||
|
|
f2cc2ea5a8 | ||
|
|
95e7b35f74 | ||
|
|
f071a25fa9 | ||
|
|
b8607bacb9 | ||
|
|
6908161f1f | ||
|
|
f168172bf4 | ||
|
|
3ce266bfa0 | ||
|
|
7eef1a454e | ||
|
|
072491c656 | ||
|
|
18972b290a | ||
|
|
9d60c79609 | ||
|
|
9caeb429ac | ||
|
|
39a81da170 | ||
|
|
b84e23e1ec | ||
|
|
f654fbf459 | ||
|
|
5df71072f5 | ||
|
|
cc7738bb8d | ||
|
|
302ce1291f | ||
|
|
f57de74cfe | ||
|
|
383cd0f182 | ||
|
|
3727b3e664 | ||
|
|
c477fc08cf | ||
|
|
1846006082 | ||
|
|
dff0122481 | ||
|
|
42b92aff13 | ||
|
|
c76e432de3 | ||
|
|
19e2591b1e | ||
|
|
6eb2ae757a | ||
|
|
82fca30efa | ||
|
|
c46c1a7128 | ||
|
|
bbdd97482f | ||
|
|
9940668729 | ||
|
|
95aaccee46 | ||
|
|
b9c3ff6f0a | ||
|
|
b80e1cb35e | ||
|
|
0ff8415244 | ||
|
|
e8dcf3f1a4 | ||
|
|
208302cf6b | ||
|
|
ef6fb92e7f | ||
|
|
28646c7b50 | ||
|
|
0b7d4b799c | ||
|
|
7e1722510a | ||
|
|
f15500b20a | ||
|
|
b8aec71e66 | ||
|
|
68aafb6a53 | ||
|
|
d58870b056 | ||
|
|
f1964dece0 | ||
|
|
45eda8dbae | ||
|
|
d9af2dd976 | ||
|
|
7bad2f4f06 | ||
|
|
3ea19ad2ed | ||
|
|
42120e1341 | ||
|
|
1147113fd3 | ||
|
|
2edfa834d5 | ||
|
|
8c7cfb720c | ||
|
|
356c60f40c | ||
|
|
4cc646b16b | ||
|
|
2e44ab4512 | ||
|
|
557dcb87b8 | ||
|
|
2daa39e306 | ||
|
|
0c15cf35a7 | ||
|
|
d9adcdb668 | ||
|
|
9010809d89 | ||
|
|
8250f0c2bd | ||
|
|
8ac9f605f6 | ||
|
|
1748171463 | ||
|
|
21ba506edb | ||
|
|
b54d7eb21c | ||
|
|
db6f2dc6a2 | ||
|
|
1411f45055 | ||
|
|
c7baa8bc34 | ||
|
|
a4b9c3049b | ||
|
|
fe84f62288 | ||
|
|
28c406706a | ||
|
|
101cf14b8c | ||
|
|
3283c65dc4 | ||
|
|
2ee7508b76 | ||
|
|
2ecd8b6092 | ||
|
|
021d5f05f9 | ||
|
|
19a0e6dd8b | ||
|
|
fbd4c8428a | ||
|
|
d9ef03a2d1 | ||
|
|
f5bfe5a248 | ||
|
|
3b85cef340 | ||
|
|
76a0da3937 | ||
|
|
2b2c285415 | ||
|
|
8a07e3da3d | ||
|
|
9e9cb65ec0 | ||
|
|
29056ee87f | ||
|
|
8f4e971829 | ||
|
|
b4463e29e8 | ||
|
|
93ed666000 | ||
|
|
b5e3a01452 | ||
|
|
b6ea54ba93 | ||
|
|
e092329bf3 | ||
|
|
152b158411 | ||
|
|
72a78472af | ||
|
|
706424a459 | ||
|
|
9adde1ac14 | ||
|
|
ef24680ee9 | ||
|
|
447bf28cd8 | ||
|
|
8f6d642012 | ||
|
|
1d371b712f | ||
|
|
55f6a3a49d | ||
|
|
7a01e216c4 | ||
|
|
f44d47a204 | ||
|
|
2866e90c49 | ||
|
|
571caa696a | ||
|
|
8a538394a5 | ||
|
|
d0bbda20c3 | ||
|
|
359c4a11b2 | ||
|
|
3b84aaabe4 | ||
|
|
311c9afe2f | ||
|
|
abef92d7d9 | ||
|
|
9f3822fbaa | ||
|
|
b24b484efc | ||
|
|
2865022cf8 | ||
|
|
9a8151d316 | ||
|
|
42439b7d00 | ||
|
|
796b4a0955 | ||
|
|
86bb90f989 | ||
|
|
047694546c | ||
|
|
3c3f1180d9 | ||
|
|
9142c93d1b | ||
|
|
d9e1a789ea | ||
|
|
c32a9ceab8 | ||
|
|
e81b600744 | ||
|
|
8cde0c837c | ||
|
|
b55f52f377 | ||
|
|
1d06d8b482 | ||
|
|
2871fa058c | ||
|
|
2dda0a203d | ||
|
|
24bd563680 | ||
|
|
bf2bc29e47 | ||
|
|
7781eddad2 | ||
|
|
c9722c22bc | ||
|
|
bb66c9c872 | ||
|
|
279d9a691d | ||
|
|
1b17de134b | ||
|
|
e19046273e | ||
|
|
5513ac8a7d | ||
|
|
d4e16a413e | ||
|
|
6e21c9f77f | ||
|
|
2473068a1a | ||
|
|
6183350182 | ||
|
|
33a3bb3bdf | ||
|
|
c06e160480 | ||
|
|
010750daf6 | ||
|
|
8af3e46fcb | ||
|
|
84890ae34d | ||
|
|
a69c1b5230 | ||
|
|
aaef27147f | ||
|
|
16bab88946 | ||
|
|
dea8f95670 | ||
|
|
eb4278784a | ||
|
|
cbd03f8a93 | ||
|
|
4cf123fed5 | ||
|
|
eff7d99e2b | ||
|
|
dd64899434 | ||
|
|
755becdde3 | ||
|
|
7f26d91cd0 | ||
|
|
fa322aec33 | ||
|
|
c7b14eacbd | ||
|
|
2fd1537b35 | ||
|
|
1db4b52c3a | ||
|
|
d474bb8ba4 | ||
|
|
1c3d551060 | ||
|
|
d16b6fbb03 | ||
|
|
54a6978e97 | ||
|
|
a988cd9005 | ||
|
|
779b26310e | ||
|
|
638deed03e | ||
|
|
730f240d24 | ||
|
|
c11a53322b | ||
|
|
430a6d5237 | ||
|
|
eecc106529 | ||
|
|
998b4edb34 | ||
|
|
26effddb8c | ||
|
|
1cf9ecc79d | ||
|
|
8024470b51 | ||
|
|
e5cc57bedf | ||
|
|
3d64bdadc1 | ||
|
|
4b8c8506a5 | ||
|
|
f789740f7e | ||
|
|
0523c9df87 | ||
|
|
c7ef946031 | ||
|
|
79a50b9c20 | ||
|
|
b75dc0782b | ||
|
|
fc940689bf | ||
|
|
7892d38fca | ||
|
|
5f4c1fbd0a | ||
|
|
8122c9edf2 | ||
|
|
d6e099f7ff | ||
|
|
bbe196ba11 | ||
|
|
a60d27975a | ||
|
|
c8f4ba7f45 | ||
|
|
13d1bbb45f | ||
|
|
7430419413 | ||
|
|
5ac95e543e | ||
|
|
aaa81ac45a | ||
|
|
7c78a15b23 | ||
|
|
610896e5ed | ||
|
|
d113344abf | ||
|
|
6e65d44a98 | ||
|
|
5fa41624c9 | ||
|
|
ee900ec997 | ||
|
|
a198f72367 | ||
|
|
9f355b93a2 | ||
|
|
0cf3adf5dd | ||
|
|
05ad1dad90 | ||
|
|
c750f1a48c | ||
|
|
cdb9b56066 | ||
|
|
980b0e6daa | ||
|
|
0667500b1b | ||
|
|
fa8cfd0400 | ||
|
|
974ded5add | ||
|
|
e126344c00 | ||
|
|
e09779e004 | ||
|
|
0a0182da13 | ||
|
|
594a0be856 | ||
|
|
95f61c6f69 | ||
|
|
de55fb3988 | ||
|
|
62b25b1bf3 | ||
|
|
c66480448d | ||
|
|
cda69bff5e | ||
|
|
e5564c18fe | ||
|
|
55c0d485ba | ||
|
|
71f95ab87b | ||
|
|
1e84d7ef1f | ||
|
|
d18a5e3adc | ||
|
|
596dda8c53 | ||
|
|
6609ed76ec | ||
|
|
44c7f3aec3 | ||
|
|
aaf805d893 | ||
|
|
ba1d5c1a84 | ||
|
|
1c4f92ecd1 | ||
|
|
804b204f3c | ||
|
|
d302d75c77 | ||
|
|
9b4082919b | ||
|
|
1d611f04fb | ||
|
|
1dee08aa38 | ||
|
|
a68be7debf | ||
|
|
0b34c9a00d | ||
|
|
9cb0918206 | ||
|
|
dde183386c | ||
|
|
b762da38f0 | ||
|
|
cb521fd05a | ||
|
|
22ad68d142 | ||
|
|
845d4ae389 | ||
|
|
fcc04823ca | ||
|
|
5950329e2c | ||
|
|
5ef537f3fd | ||
|
|
f7fc4edce2 | ||
|
|
af177f9605 | ||
|
|
4d50b001d1 | ||
|
|
d14eaa9b49 | ||
|
|
91f47ff307 | ||
|
|
80b42b81fd | ||
|
|
bbe3055006 | ||
|
|
d2f1ae9db7 | ||
|
|
3aa4c83337 | ||
|
|
385921a3e2 | ||
|
|
9d3c402923 | ||
|
|
cb9076479c | ||
|
|
a793468048 | ||
|
|
f58b1e5263 | ||
|
|
eba075294a | ||
|
|
a389bb64b8 | ||
|
|
496035c75a | ||
|
|
d00ffd9ce8 | ||
|
|
d5bbe5bea5 | ||
|
|
6a52760415 | ||
|
|
1fa8804107 | ||
|
|
4cbede618e | ||
|
|
5fd2890d1b | ||
|
|
311622d14b | ||
|
|
8d516ce535 | ||
|
|
1b0a62d08d | ||
|
|
ac5346b138 | ||
|
|
ebd1548e96 | ||
|
|
aebdbb4113 | ||
|
|
7103b78d38 | ||
|
|
47f4ddc910 | ||
|
|
4ee4b09e12 | ||
|
|
df5737ab46 | ||
|
|
48b481b68d | ||
|
|
7305b50ffe | ||
|
|
875cfdc945 | ||
|
|
95aafdd8d5 | ||
|
|
fdce8fce13 | ||
|
|
d8324dee0d | ||
|
|
dccba10997 | ||
|
|
69d3e3f3f8 | ||
|
|
99b2ab610a | ||
|
|
cbe84232a3 | ||
|
|
dd4306b00a | ||
|
|
ac8ffa287e | ||
|
|
fa6fac1a76 | ||
|
|
a6f0495b1f | ||
|
|
e7f4c6ca8d | ||
|
|
f2dd29995b | ||
|
|
3404cc0d29 | ||
|
|
9d2210b152 | ||
|
|
a466821e8f | ||
|
|
360a47fddc | ||
|
|
08a6cb65fe | ||
|
|
2c14c98408 | ||
|
|
eafcc3bafa | ||
|
|
9539877ff6 | ||
|
|
d30941b575 | ||
|
|
fed96f67ea | ||
|
|
2340ce6a8c | ||
|
|
e5260d855f | ||
|
|
47f4f8c793 | ||
|
|
2195df9e7a | ||
|
|
fdb14875c0 | ||
|
|
3f1f66a8ee | ||
|
|
cf8cfbd865 | ||
|
|
62d167c141 | ||
|
|
cb27444e54 | ||
|
|
ce3d8378e7 | ||
|
|
9949a23724 | ||
|
|
b86fb0aea0 | ||
|
|
b369de6662 | ||
|
|
75483fe908 | ||
|
|
f4e7203a55 | ||
|
|
d17fd9424e | ||
|
|
a1698b740a | ||
|
|
5cb3c876fc | ||
|
|
0f2a84d270 | ||
|
|
bbb149b31e | ||
|
|
18a51ca5b8 | ||
|
|
93b7c999be | ||
|
|
f64f73a0ce | ||
|
|
88a6b20ba5 | ||
|
|
915ab69922 | ||
|
|
f9f340b6bf | ||
|
|
75920481b1 | ||
|
|
d21bb920d6 | ||
|
|
d119dbc239 | ||
|
|
7eda4a5100 | ||
|
|
3b1bcac5b3 | ||
|
|
6d7d76a3f1 | ||
|
|
dd6cf3df14 | ||
|
|
473be23153 | ||
|
|
ec40a1a08a | ||
|
|
729ac285da | ||
|
|
3b84c03131 | ||
|
|
d7da0cca3c | ||
|
|
fcda053d73 | ||
|
|
e23c989b78 | ||
|
|
95d14cb0af | ||
|
|
00c2be541d | ||
|
|
9af55d555b | ||
|
|
ef2064c753 | ||
|
|
18cdea3f88 | ||
|
|
36e0194e42 | ||
|
|
87cba2ecbe | ||
|
|
8d8e7c3b2c | ||
|
|
48d00f807e | ||
|
|
481ea64ccc | ||
|
|
f6a48b6899 | ||
|
|
8e5290d7c5 | ||
|
|
83c5e1bc83 | ||
|
|
8df8d89c6d | ||
|
|
226f1c1a9b | ||
|
|
bb3f3e541d | ||
|
|
42e7c3cdf0 | ||
|
|
f9a8ba91e4 | ||
|
|
1617e5de19 | ||
|
|
77998d1bb3 | ||
|
|
68f47c02c8 | ||
|
|
02be2b2918 | ||
|
|
43233ce62e | ||
|
|
51043770bf | ||
|
|
4478bd14c9 | ||
|
|
91dc315fdb | ||
|
|
a6d1a52e33 | ||
|
|
fd35530f35 | ||
|
|
cb609a1886 | ||
|
|
b202ccc5dc | ||
|
|
ee226b47fc | ||
|
|
9edff1f22c | ||
|
|
67864ffdab | ||
|
|
b55381f34e | ||
|
|
b6ad314dbb | ||
|
|
762f46ed54 | ||
|
|
63db5de4e2 | ||
|
|
9606957a0e | ||
|
|
e02bb71948 | ||
|
|
1fd96e168e | ||
|
|
ffc20f7e2c | ||
|
|
3e795d020d | ||
|
|
88ce6ffcbe | ||
|
|
83ef7ee5cf | ||
|
|
817287ec90 | ||
|
|
c5994a8b28 | ||
|
|
3435fee1f9 | ||
|
|
ececd00fbd | ||
|
|
46dbd0c859 | ||
|
|
cad809515c | ||
|
|
1d35134310 | ||
|
|
fae1dbf0be | ||
|
|
04847502bf | ||
|
|
af7e36bd47 | ||
|
|
fdad478684 | ||
|
|
22f376bbfd | ||
|
|
688fe55bb9 | ||
|
|
91b3fb2af3 | ||
|
|
bd5e5dc965 | ||
|
|
c915113120 | ||
|
|
cf02b90624 | ||
|
|
7f5dda93c6 | ||
|
|
a0be871a9d | ||
|
|
5af062a86f | ||
|
|
ed551bf339 | ||
|
|
5e490527de | ||
|
|
c887042a1b | ||
|
|
0cc5e75432 | ||
|
|
6d6cd43255 | ||
|
|
d99614f55a | ||
|
|
880397d549 | ||
|
|
46c32f4c79 | ||
|
|
b25114c1fc | ||
|
|
44552c151b | ||
|
|
108cfeb4bb | ||
|
|
24bbffd71e | ||
|
|
c62e18f9b2 | ||
|
|
c1640fb10d | ||
|
|
0c33690596 | ||
|
|
6bf3defe85 | ||
|
|
6d6b693fb3 | ||
|
|
14f827b9b7 | ||
|
|
eac988abdc | ||
|
|
ea4a50f6e9 | ||
|
|
6c94869329 | ||
|
|
5ef9101913 | ||
|
|
1f0881fb5f | ||
|
|
600e132fb6 | ||
|
|
f571b163e5 | ||
|
|
0b235b367d | ||
|
|
a264833c06 | ||
|
|
22c44d3ecb | ||
|
|
8ed817916f | ||
|
|
cb4c41eebf | ||
|
|
ecc490a0fa | ||
|
|
8a45e02cbb | ||
|
|
28a6c9c532 | ||
|
|
dee0c94346 | ||
|
|
780ae29c2b | ||
|
|
9ca20451b7 | ||
|
|
6db7acbf69 | ||
|
|
d16ee386d2 | ||
|
|
c3f4bb8156 | ||
|
|
c640888133 | ||
|
|
33ec66cfdd | ||
|
|
1b8d399de4 | ||
|
|
48c36efe07 | ||
|
|
f27a3c5c92 | ||
|
|
ffe635f943 | ||
|
|
b5b81abb90 | ||
|
|
da3f3b93b4 | ||
|
|
7f4c8e1c78 | ||
|
|
60f30f8a48 | ||
|
|
2362451fda | ||
|
|
55a7fdfd8a | ||
|
|
3dd5f0c80c | ||
|
|
be304a1102 | ||
|
|
a4b60039ca | ||
|
|
b20477210f | ||
|
|
9b6961875e | ||
|
|
51b64e6cf3 | ||
|
|
851d93e5a8 | ||
|
|
9291865eed | ||
|
|
fb330ebcb0 | ||
|
|
ae7a48df2d | ||
|
|
2e3c50c535 | ||
|
|
a64eea3b1a | ||
|
|
a12c1a6610 | ||
|
|
bf6b4d3941 | ||
|
|
3d11b0545d | ||
|
|
b4ca184108 | ||
|
|
b1a40a9fb7 | ||
|
|
187d9523c6 | ||
|
|
a44938101d | ||
|
|
a4634cdc86 | ||
|
|
a696dceea4 | ||
|
|
989dabac5a | ||
|
|
8eac186a35 | ||
|
|
1c7dc0e88a | ||
|
|
2c9b5be408 | ||
|
|
a91a8ccc66 | ||
|
|
6dfef716d7 | ||
|
|
06a2b9d426 | ||
|
|
6cd0892d59 | ||
|
|
51b3985aa0 | ||
|
|
c8815f5ab9 | ||
|
|
3c222d8707 | ||
|
|
4508bc2dc2 | ||
|
|
8c3a8c085e | ||
|
|
b9a1ef89fe | ||
|
|
c2c48bbe1d | ||
|
|
97f89c0edb | ||
|
|
03ba38eaff | ||
|
|
f393cf37c2 | ||
|
|
f3a8c4b2ec | ||
|
|
86c2b03630 | ||
|
|
98b411f82e | ||
|
|
06d2aaad80 | ||
|
|
9b47a29864 | ||
|
|
b669844ef7 | ||
|
|
ad4e7c9d8a | ||
|
|
c5f83e4730 | ||
|
|
be05df03b5 | ||
|
|
c7c11ef68b | ||
|
|
7fe937c61e | ||
|
|
dfd396b480 | ||
|
|
f63d76f006 | ||
|
|
2bc7dcfcca | ||
|
|
60c037f57e | ||
|
|
b9309a42a4 | ||
|
|
98193a7a9d | ||
|
|
8d3be1a23c | ||
|
|
70403375d7 | ||
|
|
9eaec6f0f5 | ||
|
|
439bcaad25 | ||
|
|
294da55012 | ||
|
|
8d32e6a82c | ||
|
|
1e27e39927 | ||
|
|
5126f3dfee | ||
|
|
6bce23d9c2 | ||
|
|
dee592eb3b | ||
|
|
451d8643ee | ||
|
|
fbe49c3700 | ||
|
|
7d28e4e0de | ||
|
|
9f0f29f0fc | ||
|
|
3c46c9ff1e | ||
|
|
c05995b301 | ||
|
|
72cdde78f6 | ||
|
|
1aeb8e3025 | ||
|
|
689c535a29 | ||
|
|
1425d0720e | ||
|
|
55c5497fab | ||
|
|
67c79b7cb1 | ||
|
|
26529fae94 | ||
|
|
70ad7a35a8 | ||
|
|
90261a2fdd | ||
|
|
3ad5355777 | ||
|
|
ed5b811f37 | ||
|
|
22a2ccfdf4 | ||
|
|
691e52e408 | ||
|
|
b11075ec98 | ||
|
|
ea66a73f43 | ||
|
|
6f9f80cc48 | ||
|
|
13a27e8b2d | ||
|
|
bcff465f11 | ||
|
|
270d8bd51a | ||
|
|
2ef367b2b0 | ||
|
|
454e563a0e | ||
|
|
217744bc9a | ||
|
|
34ffb94d98 | ||
|
|
6f4702a230 | ||
|
|
b1f77d975e | ||
|
|
523b8335d8 | ||
|
|
5d225b9454 | ||
|
|
0956eefb1a | ||
|
|
a9ed08a5f8 | ||
|
|
ae71824adc | ||
|
|
186b319093 | ||
|
|
bc2118736b | ||
|
|
1780027421 | ||
|
|
3cd999bb87 | ||
|
|
bbeae229ca | ||
|
|
3a1e2c7518 | ||
|
|
fb148c305a | ||
|
|
897604142f | ||
|
|
03f57b7034 | ||
|
|
005b097cee | ||
|
|
e08b6803f5 | ||
|
|
78e9cc7998 | ||
|
|
1a964c5055 | ||
|
|
0ffee92175 | ||
|
|
99cd9e8aae | ||
|
|
8cb2422b57 | ||
|
|
4bb2fa1652 | ||
|
|
d27fb0ec78 | ||
|
|
406bbb8ca4 | ||
|
|
3924ed8a35 | ||
|
|
b1fee26a7f | ||
|
|
58be9829f9 | ||
|
|
33a29817d7 | ||
|
|
edbdb16ab7 | ||
|
|
2e731c310f | ||
|
|
79f2e1558c | ||
|
|
f0b37ea06d | ||
|
|
8698433d44 | ||
|
|
ff047d0748 | ||
|
|
6c220d4562 | ||
|
|
0e0183b9ea | ||
|
|
61978b34df | ||
|
|
d9bca65626 | ||
|
|
d7b158f855 | ||
|
|
71706af3d4 | ||
|
|
8142e94349 | ||
|
|
2752544bc8 | ||
|
|
558a18029a | ||
|
|
665f6b0267 | ||
|
|
fc447b46ce | ||
|
|
304fc462a4 | ||
|
|
75fffc17e1 | ||
|
|
e1b5e90adf | ||
|
|
6374098cdc | ||
|
|
b35893d783 | ||
|
|
4b2b47af48 | ||
|
|
a5f6a80cf6 | ||
|
|
a9c3617b4f | ||
|
|
4d4bd5114d | ||
|
|
bfaafa66f9 | ||
|
|
26356ff4be | ||
|
|
06e944dcb6 | ||
|
|
2891c6948d | ||
|
|
703418d0c4 | ||
|
|
75960c6484 | ||
|
|
5ac3c57510 | ||
|
|
926a68181b | ||
|
|
1f367ac200 | ||
|
|
de38abb121 | ||
|
|
d00153de71 | ||
|
|
992fe066aa | ||
|
|
b80080a26d | ||
|
|
0afb59c65e | ||
|
|
535799462f | ||
|
|
77204551ab | ||
|
|
772d369058 | ||
|
|
9b7c80a76b | ||
|
|
a6fc7aa881 | ||
|
|
6f27cb93d1 | ||
|
|
b2a93930d0 | ||
|
|
dedd1ca892 | ||
|
|
01b4917f26 | ||
|
|
25e374f4ee | ||
|
|
3ba728d40c | ||
|
|
8761920f58 | ||
|
|
394be5c65a | ||
|
|
4ec533bc1b | ||
|
|
fc276ff450 | ||
|
|
c3d815998a | ||
|
|
71242b0c8e | ||
|
|
a9e7f669d0 | ||
|
|
8195ff8b9b | ||
|
|
9b1a7bf451 | ||
|
|
9b66e0a094 | ||
|
|
6e33132de9 | ||
|
|
a6c79f3410 | ||
|
|
50df792e49 | ||
|
|
6f24e878aa | ||
|
|
a0ba208669 | ||
|
|
d5b0154747 | ||
|
|
7d2ddc1cde | ||
|
|
02252e8e69 | ||
|
|
2526b03d67 | ||
|
|
e347090571 | ||
|
|
f8bcaca4c9 | ||
|
|
de0716e875 | ||
|
|
21cdbab0d7 | ||
|
|
1fba46f2f7 | ||
|
|
dfe4308304 | ||
|
|
8c94a96ee0 | ||
|
|
ec8ef312ef |
@@ -1,51 +0,0 @@
|
||||
---
|
||||
name: Bug report - V3
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug, v3
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
Note that any how to questions should be posted in the discussion board and not raised as an issue.
|
||||
|
||||
Version of yq: 3.X.X
|
||||
Operating system: mac/linux/windows/....
|
||||
Installed via: docker/binary release/homebrew/snap/...
|
||||
|
||||
**Input Yaml**
|
||||
Concise yaml document(s) (as simple as possible to show the bug, please keep it to 10 lines or less)
|
||||
data1.yml:
|
||||
```yaml
|
||||
this: should really work
|
||||
```
|
||||
|
||||
data2.yml:
|
||||
```yaml
|
||||
but: it strangely didn't
|
||||
```
|
||||
|
||||
**Command**
|
||||
The command you ran:
|
||||
```
|
||||
yq merge data1.yml data2.yml
|
||||
```
|
||||
|
||||
**Actual behavior**
|
||||
|
||||
```yaml
|
||||
cat: meow
|
||||
```
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
```yaml
|
||||
this: should really work
|
||||
but: it strangely didn't
|
||||
```
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -26,7 +26,7 @@ country: Australia
|
||||
And we run a command:
|
||||
|
||||
```bash
|
||||
yq eval 'predictWeatherOf(.country)'
|
||||
yq 'predictWeatherOf(.country)'
|
||||
```
|
||||
|
||||
it could output
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '24 3 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
@@ -0,0 +1,100 @@
|
||||
name: Release Docker
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publishDocker:
|
||||
environment: dockerhub
|
||||
env:
|
||||
IMAGE_NAME: mikefarah/yq
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Available platforms
|
||||
run: echo ${{ steps.buildx.outputs.platforms }} && docker version
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push image
|
||||
run: |
|
||||
echo "GithubRef: ${GITHUB_REF}"
|
||||
VERSION=${GITHUB_REF##*/}
|
||||
echo "VERSION: ${VERSION}"
|
||||
IMAGE_VERSION=${VERSION:1}
|
||||
echo "IMAGE_VERSION: ${IMAGE_VERSION}"
|
||||
|
||||
PLATFORMS="linux/amd64,linux/ppc64le,linux/arm64,linux/arm/v7,linux/s390x"
|
||||
|
||||
echo "Building and pushing version ${IMAGE_VERSION} of image ${IMAGE_NAME}"
|
||||
docker buildx build \
|
||||
--label "org.opencontainers.image.authors=https://github.com/mikefarah/yq/graphs/contributors" \
|
||||
--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \
|
||||
--label "org.opencontainers.image.description=yq is a portable command-line data file processor" \
|
||||
--label "org.opencontainers.image.documentation=https://mikefarah.gitbook.io/yq/" \
|
||||
--label "org.opencontainers.image.licenses=MIT" \
|
||||
--label "org.opencontainers.image.revision=$(git rev-parse HEAD)" \
|
||||
--label "org.opencontainers.image.source=https://github.com/mikefarah/yq" \
|
||||
--label "org.opencontainers.image.title=yq" \
|
||||
--label "org.opencontainers.image.url=https://mikefarah.gitbook.io/yq/" \
|
||||
--label "org.opencontainers.image.version=${IMAGE_VERSION}" \
|
||||
--platform "${PLATFORMS}" \
|
||||
--pull \
|
||||
--push \
|
||||
-t "${IMAGE_NAME}:${IMAGE_VERSION}" \
|
||||
-t "${IMAGE_NAME}:4" \
|
||||
-t "${IMAGE_NAME}:latest" \
|
||||
-t "ghcr.io/${IMAGE_NAME}:${IMAGE_VERSION}" \
|
||||
-t "ghcr.io/${IMAGE_NAME}:4" \
|
||||
-t "ghcr.io/${IMAGE_NAME}:latest" \
|
||||
.
|
||||
|
||||
cd github-action
|
||||
docker buildx build \
|
||||
--label "org.opencontainers.image.authors=https://github.com/mikefarah/yq/graphs/contributors" \
|
||||
--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \
|
||||
--label "org.opencontainers.image.description=yq is a portable command-line data file processor" \
|
||||
--label "org.opencontainers.image.documentation=https://mikefarah.gitbook.io/yq/" \
|
||||
--label "org.opencontainers.image.licenses=MIT" \
|
||||
--label "org.opencontainers.image.revision=$(git rev-parse HEAD)" \
|
||||
--label "org.opencontainers.image.source=https://github.com/mikefarah/yq" \
|
||||
--label "org.opencontainers.image.title=yq" \
|
||||
--label "org.opencontainers.image.url=https://mikefarah.gitbook.io/yq/" \
|
||||
--label "org.opencontainers.image.version=${IMAGE_VERSION}" \
|
||||
--platform "${PLATFORMS}" \
|
||||
--pull \
|
||||
--push \
|
||||
-t "${IMAGE_NAME}:${IMAGE_VERSION}-githubaction" \
|
||||
-t "${IMAGE_NAME}:4-githubaction" \
|
||||
-t "${IMAGE_NAME}:latest-githubaction" \
|
||||
-t "ghcr.io/${IMAGE_NAME}:${IMAGE_VERSION}-githubaction" \
|
||||
-t "ghcr.io/${IMAGE_NAME}:4-githubaction" \
|
||||
-t "ghcr.io/${IMAGE_NAME}:latest-githubaction" \
|
||||
.
|
||||
@@ -1,5 +1,8 @@
|
||||
name: Build
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
@@ -7,14 +10,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: '^1.20'
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
@@ -25,6 +28,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Check the build
|
||||
shell: bash -l {0}
|
||||
run: |
|
||||
export PATH=${PATH}:`go env GOPATH`/bin
|
||||
scripts/devtools.sh
|
||||
|
||||
@@ -2,25 +2,26 @@ name: Release YQ
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- 'v4.*'
|
||||
- 'draft-*'
|
||||
|
||||
jobs:
|
||||
publishGitRelease:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '^1.17.5'
|
||||
go-version: '^1.20'
|
||||
check-latest: true
|
||||
- name: Compile man page markup
|
||||
id: gen-man-page-md
|
||||
run: |
|
||||
./scripts/generate-man-page-md.sh
|
||||
echo "::set-output name=man-page-md::man.md"
|
||||
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF##*/}
|
||||
run: echo "VERSION=${GITHUB_REF##*/}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Generate man page
|
||||
uses: docker://pandoc/core:2.14.2
|
||||
@@ -34,77 +35,17 @@ jobs:
|
||||
--variable=header:"yq (https://github.com/mikefarah/yq/) version ${{ steps.get_version.outputs.VERSION }}"
|
||||
--variable=author:"Mike Farah"
|
||||
--output=yq.1
|
||||
${{ steps.gen-man-page-md.outputs.man-page-md }}
|
||||
man.md
|
||||
|
||||
- name: Cross compile
|
||||
run: |
|
||||
sudo apt-get install rhash -y
|
||||
go get github.com/mitchellh/gox
|
||||
mkdir -p build
|
||||
cp yq.1 build/yq.1
|
||||
go install github.com/goreleaser/goreleaser/v2@latest
|
||||
./scripts/xcompile.sh
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1.1.4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
files: build/*
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
- uses: shogo82148/actions-upload-release-asset@v1
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: build/*
|
||||
|
||||
publishDocker:
|
||||
environment: dockerhub
|
||||
env:
|
||||
IMAGE_NAME: mikefarah/yq
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Available platforms
|
||||
run: echo ${{ steps.buildx.outputs.platforms }} && docker version
|
||||
|
||||
- name: Build and push image
|
||||
run: |
|
||||
IMAGE_V_VERSION="$(git describe --tags --abbrev=0)"
|
||||
IMAGE_VERSION=${IMAGE_V_VERSION:1}
|
||||
PLATFORMS="linux/amd64,linux/ppc64le,linux/arm64"
|
||||
|
||||
echo "Building and pushing version ${IMAGE_VERSION} of image ${IMAGE_NAME}"
|
||||
echo '${{ secrets.DOCKER_PASSWORD }}' | docker login -u '${{ secrets.DOCKER_USERNAME }}' --password-stdin
|
||||
docker buildx build \
|
||||
--label "org.opencontainers.image.authors=https://github.com/mikefarah/yq/graphs/contributors" \
|
||||
--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \
|
||||
--label "org.opencontainers.image.description=yq is a portable command-line YAML processor" \
|
||||
--label "org.opencontainers.image.documentation=https://mikefarah.gitbook.io/yq/" \
|
||||
--label "org.opencontainers.image.licenses=MIT" \
|
||||
--label "org.opencontainers.image.revision=$(git rev-parse HEAD)" \
|
||||
--label "org.opencontainers.image.source=https://github.com/mikefarah/yq" \
|
||||
--label "org.opencontainers.image.title=yq" \
|
||||
--label "org.opencontainers.image.url=https://mikefarah.gitbook.io/yq/" \
|
||||
--label "org.opencontainers.image.version=${IMAGE_VERSION}" \
|
||||
--platform "${PLATFORMS}" \
|
||||
--pull \
|
||||
--push \
|
||||
-t "${IMAGE_NAME}:${IMAGE_VERSION}" \
|
||||
-t "${IMAGE_NAME}:4" \
|
||||
-t "${IMAGE_NAME}:latest" \
|
||||
.
|
||||
fail_on_unmatched_files: true
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Release Snap
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
buildSnap:
|
||||
environment: snap
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: snapcore/action-build@v1
|
||||
id: build
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }}
|
||||
with:
|
||||
snapcraft-args: "remote-build"
|
||||
- uses: snapcore/action-publish@v1
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }}
|
||||
with:
|
||||
snap: ${{ steps.build.outputs.snap }}
|
||||
release: stable
|
||||
@@ -1,5 +1,11 @@
|
||||
name: Test Yq Action
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
@@ -7,13 +13,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v5
|
||||
- name: Get test
|
||||
id: get_value
|
||||
uses: mikefarah/yq@master
|
||||
with:
|
||||
cmd: yq eval '.a' examples/sample.yaml
|
||||
cmd: yq '.test' examples/multiline-text.yaml
|
||||
- name: Multiline test
|
||||
run: echo "### It was [${{ steps.get_value.outputs.result }}]" >> $GITHUB_STEP_SUMMARY
|
||||
- name: Write inplace test
|
||||
id: lookupSdkVersion
|
||||
uses: mikefarah/yq@master
|
||||
with:
|
||||
cmd: yq eval -i '.b.c = 5' examples/sample.yaml
|
||||
cmd: yq -i '.b.c = 5' examples/sample.yaml
|
||||
+15
@@ -22,8 +22,10 @@ _cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
cover.out
|
||||
coverage.out
|
||||
coverage.html
|
||||
coverage_sorted.txt
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
@@ -41,6 +43,11 @@ yq*.snap
|
||||
|
||||
test.yml
|
||||
test*.yml
|
||||
test*.xml
|
||||
test*.toml
|
||||
test*.yaml
|
||||
test_dir1/
|
||||
test_dir2/
|
||||
0.yml
|
||||
1.yml
|
||||
2.yml
|
||||
@@ -53,3 +60,11 @@ yq.1
|
||||
# debian pkg
|
||||
_build
|
||||
debian/files
|
||||
|
||||
# intellij
|
||||
/.idea
|
||||
|
||||
# vscode
|
||||
.vscode
|
||||
|
||||
yq3
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck
|
||||
- depguard
|
||||
- errorlint
|
||||
- gci
|
||||
- gochecknoinits
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- staticcheck
|
||||
- unused
|
||||
- misspell
|
||||
- nakedret
|
||||
- nolintlint
|
||||
- predeclared
|
||||
- revive
|
||||
- unconvert
|
||||
- unparam
|
||||
linters-settings:
|
||||
depguard:
|
||||
rules:
|
||||
prevent_unmaintained_packages:
|
||||
list-mode: lax
|
||||
files:
|
||||
- $all
|
||||
- "!$test"
|
||||
deny:
|
||||
- pkg: io/ioutil
|
||||
desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil"
|
||||
issues:
|
||||
exclude-rules:
|
||||
- linters:
|
||||
- revive
|
||||
text: "var-naming"
|
||||
+38
-18
@@ -1,16 +1,11 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
version: "2"
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck
|
||||
- depguard
|
||||
- errorlint
|
||||
- gci
|
||||
- gochecknoinits
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosec
|
||||
- megacheck
|
||||
- misspell
|
||||
- nakedret
|
||||
- nolintlint
|
||||
@@ -18,15 +13,40 @@ linters:
|
||||
- revive
|
||||
- unconvert
|
||||
- unparam
|
||||
issues:
|
||||
exclude-rules:
|
||||
- linters:
|
||||
- gosec
|
||||
text: "Implicit memory aliasing in for loop."
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- revive
|
||||
text: "unexported-return"
|
||||
- linters:
|
||||
- revive
|
||||
text: "var-naming"
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
prevent_unmaintained_packages:
|
||||
list-mode: lax
|
||||
files:
|
||||
- $all
|
||||
- '!$test'
|
||||
deny:
|
||||
- pkg: io/ioutil
|
||||
desc: 'replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil'
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- revive
|
||||
text: var-naming
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofmt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
version: 2
|
||||
|
||||
dist: build
|
||||
|
||||
builds:
|
||||
- id: yq
|
||||
|
||||
binary: yq_{{ .Os }}_{{ .Arch }}
|
||||
|
||||
ldflags:
|
||||
- -s -w
|
||||
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
|
||||
targets:
|
||||
- darwin_amd64
|
||||
- darwin_arm64
|
||||
- freebsd_386
|
||||
- freebsd_amd64
|
||||
- freebsd_arm
|
||||
- linux_386
|
||||
- linux_amd64
|
||||
- linux_arm
|
||||
- linux_arm64
|
||||
- linux_loong64
|
||||
- linux_mips
|
||||
- linux_mips64
|
||||
- linux_mips64le
|
||||
- linux_mipsle
|
||||
- linux_ppc64
|
||||
- linux_ppc64le
|
||||
- linux_riscv64
|
||||
- linux_s390x
|
||||
- netbsd_386
|
||||
- netbsd_amd64
|
||||
- netbsd_arm
|
||||
- openbsd_386
|
||||
- openbsd_amd64
|
||||
- windows_386
|
||||
- windows_amd64
|
||||
- windows_arm
|
||||
- windows_arm64
|
||||
|
||||
no_unique_dist_dir: true
|
||||
|
||||
release:
|
||||
disable: true
|
||||
skip_upload: true
|
||||
+214
-8
@@ -1,8 +1,214 @@
|
||||
1. Install (golang)[https://golang.org/]
|
||||
1. Run `scripts/devtools.sh` to install the required devtools
|
||||
2. Run `make [local] vendor` to install the vendor dependencies
|
||||
2. Run `make [local] test` to ensure you can run the existing tests
|
||||
3. Write unit tests - (see existing examples). Changes will not be accepted without corresponding unit tests.
|
||||
4. Make the code changes.
|
||||
5. `make [local] test` to lint code and run tests
|
||||
6. Profit! ok no profit, but raise a PR and get kudos :)
|
||||
# Before you begin
|
||||
Not all new PRs will be merged in
|
||||
|
||||
It's recommended to check with the owner first (e.g. raise an issue) to discuss a new feature before developing, to ensure your hard efforts don't go to waste.
|
||||
|
||||
PRs to fix bugs and issues are almost always welcome :pray: please ensure you write tests as well.
|
||||
|
||||
The following types of PRs will _not_ be accepted:
|
||||
- **Significant refactors** take a lot of time to understand and can have all sorts of unintended side effects. If you think there's a better way to do things (that requires significant changes) raise an issue for discussion first :)
|
||||
- **Release pipeline PRs** are a security risk - it's too easy for a serious vulnerability to sneak in (either intended or not). If there is a new cool way of releasing things, raise an issue for discussion first - it will need to be gone over with a fine tooth comb.
|
||||
- **Version bumps** are handled by dependabot, the bot will auto-raise PRs and they will be regularly merged in.
|
||||
- **New release platforms** At this stage, yq is not going to maintain any other release platforms other than GitHub and Docker - that said, I'm more than happy to put in other community maintained methods in the README for visibility :heart:
|
||||
|
||||
|
||||
# Development
|
||||
|
||||
## Initial Setup
|
||||
|
||||
1. Install [Golang](https://golang.org/) (version 1.24.0 or later)
|
||||
2. Run `scripts/devtools.sh` to install required development tools:
|
||||
- golangci-lint for code linting
|
||||
- gosec for security analysis
|
||||
3. Run `make [local] vendor` to install vendor dependencies
|
||||
4. Run `make [local] test` to ensure you can run the existing tests
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Write unit tests first** - Changes will not be accepted without corresponding unit tests (see Testing section below)
|
||||
2. **Make your code changes**
|
||||
3. **Run tests and linting**: `make [local] test` (this runs formatting, linting, security checks, and tests)
|
||||
4. **Create your PR** and get kudos! :)
|
||||
|
||||
## Make Commands
|
||||
|
||||
- Use `make [local] <command>` for local development (runs in Docker container)
|
||||
- Use `make <command>` for CI/CD environments
|
||||
- Common commands:
|
||||
- `make [local] vendor` - Install dependencies
|
||||
- `make [local] test` - Run all checks and tests
|
||||
- `make [local] build` - Build the yq binary
|
||||
- `make [local] format` - Format code
|
||||
- `make [local] check` - Run linting and security checks
|
||||
|
||||
# Code Quality
|
||||
|
||||
## Linting and Formatting
|
||||
|
||||
The project uses strict linting rules defined in `.golangci.yml`. All code must pass:
|
||||
|
||||
- **Code formatting**: gofmt, goimports, gci
|
||||
- **Linting**: revive, errorlint, gosec, misspell, and others
|
||||
- **Security checks**: gosec security analysis
|
||||
- **Spelling checks**: misspell detection
|
||||
|
||||
Run `make [local] check` to verify your code meets all quality standards.
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
- Follow standard Go conventions
|
||||
- Use meaningful variable names
|
||||
- Add comments for public functions and complex logic
|
||||
- Keep functions focused and reasonably sized
|
||||
- Use the project's existing patterns and conventions
|
||||
|
||||
# Testing
|
||||
|
||||
## Test Structure
|
||||
|
||||
Tests in yq use the `expressionScenario` pattern. Each test scenario includes:
|
||||
- `expression`: The yq expression to test
|
||||
- `document`: Input YAML/JSON (optional)
|
||||
- `expected`: Expected output
|
||||
- `skipDoc`: Whether to skip documentation generation
|
||||
|
||||
## Writing Tests
|
||||
|
||||
1. **Find the appropriate test file** (e.g., `operator_add_test.go` for addition operations)
|
||||
2. **Add your test scenario** to the `*OperatorScenarios` slice
|
||||
3. **Run the specific test**: `go test -run TestAddOperatorScenarios` (replace with appropriate test name)
|
||||
4. **Verify documentation generation** (see Documentation section)
|
||||
|
||||
## Test Examples
|
||||
|
||||
```go
|
||||
var addOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
skipDoc: true,
|
||||
expression: `"foo" + "bar"`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::foobar\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: "apples: 3",
|
||||
expression: `.apples + 3`,
|
||||
expected: []string{
|
||||
"D0, P[apples], (!!int)::6\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
- **All tests**: `make [local] test`
|
||||
- **Specific test**: `go test -run TestName`
|
||||
- **With coverage**: `make [local] cover`
|
||||
|
||||
# Documentation
|
||||
|
||||
## Documentation Generation
|
||||
|
||||
The project uses a documentation system that combines static headers with dynamically generated content from tests.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Static headers** are defined in `pkg/yqlib/doc/operators/headers/*.md`
|
||||
2. **Dynamic content** is generated from test scenarios in `*_test.go` files
|
||||
3. **Generated docs** are created in `pkg/yqlib/doc/*.md` by concatenating headers with test-generated content
|
||||
4. **Documentation is synced** to the gitbook branch for the website
|
||||
|
||||
### Updating Operator Documentation
|
||||
|
||||
#### For Test-Generated Documentation
|
||||
|
||||
Most operator documentation is generated from tests. To update:
|
||||
|
||||
1. **Find the test file** (e.g., `operator_add_test.go`)
|
||||
2. **Update test scenarios** - each `expressionScenario` with `skipDoc: false` becomes documentation
|
||||
3. **Run the test** to regenerate docs:
|
||||
```bash
|
||||
cd pkg/yqlib
|
||||
go test -run TestAddOperatorScenarios
|
||||
```
|
||||
4. **Verify the generated documentation** in `pkg/yqlib/doc/add.md`
|
||||
5. **Create a PR** with your changes
|
||||
|
||||
#### For Header-Only Documentation
|
||||
|
||||
If documentation exists only in `headers/*.md` files:
|
||||
1. **Update the header file directly** (e.g., `pkg/yqlib/doc/operators/headers/add.md`)
|
||||
2. **Create a PR** with your changes
|
||||
|
||||
### Updating Static Documentation
|
||||
|
||||
For documentation not in the master branch:
|
||||
|
||||
1. **Check the gitbook branch** for additional pages
|
||||
2. **Update the `*.md` files** directly
|
||||
3. **Create a PR** to the gitbook branch
|
||||
|
||||
### Documentation Best Practices
|
||||
|
||||
- **Write clear, concise examples** in test scenarios
|
||||
- **Use meaningful variable names** in examples
|
||||
- **Include edge cases** and error conditions
|
||||
- **Test your documentation changes** by running the specific test
|
||||
- **Verify generated output** matches expectations
|
||||
|
||||
Note: PRs with small changes (e.g. minor typos) may not be merged (see https://joel.net/how-one-guy-ruined-hacktoberfest2020-drama).
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## Common Setup Issues
|
||||
|
||||
### Docker/Podman Issues
|
||||
- **Problem**: `make` commands fail with Docker errors
|
||||
- **Solution**: Ensure Docker or Podman is running and accessible
|
||||
- **Alternative**: Use `make local <command>` to run in containers
|
||||
|
||||
### Go Version Issues
|
||||
- **Problem**: Build fails with Go version errors
|
||||
- **Solution**: Ensure you have Go 1.24.0 or later installed
|
||||
- **Check**: Run `go version` to verify
|
||||
|
||||
### Vendor Dependencies
|
||||
- **Problem**: `make vendor` fails or dependencies are outdated
|
||||
- **Solution**:
|
||||
```bash
|
||||
go mod tidy
|
||||
make [local] vendor
|
||||
```
|
||||
|
||||
### Linting Failures
|
||||
- **Problem**: `make check` fails with linting errors
|
||||
- **Solution**:
|
||||
```bash
|
||||
make [local] format # Auto-fix formatting
|
||||
# Manually fix remaining linting issues
|
||||
make [local] check # Verify fixes
|
||||
```
|
||||
|
||||
### Test Failures
|
||||
- **Problem**: Tests fail locally but pass in CI
|
||||
- **Solution**:
|
||||
```bash
|
||||
make [local] test # Run in Docker container
|
||||
```
|
||||
|
||||
### Documentation Generation Issues
|
||||
- **Problem**: Generated docs don't update after test changes
|
||||
- **Solution**:
|
||||
```bash
|
||||
cd pkg/yqlib
|
||||
go test -run TestSpecificOperatorScenarios
|
||||
# Check if generated file updated in pkg/yqlib/doc/
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **Check existing issues**: Search GitHub issues for similar problems
|
||||
- **Create an issue**: If you can't find a solution, create a detailed issue
|
||||
- **Ask questions**: Use GitHub Discussions for general questions
|
||||
- **Join the community**: Check the project's community channels
|
||||
+3
-3
@@ -1,16 +1,16 @@
|
||||
FROM golang:1.17 as builder
|
||||
FROM golang:1.25.4 AS builder
|
||||
|
||||
WORKDIR /go/src/mikefarah/yq
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN CGO_ENABLED=0 go build .
|
||||
RUN CGO_ENABLED=0 go build -ldflags "-s -w" .
|
||||
# RUN ./scripts/test.sh -- this too often times out in the github pipeline.
|
||||
RUN ./scripts/acceptance.sh
|
||||
|
||||
# Choose alpine as a base image to make this useful for CI, as many
|
||||
# CI tools expect an interactive shell inside the container
|
||||
FROM alpine:3 as production
|
||||
FROM alpine:3 AS production
|
||||
LABEL maintainer="Mike Farah <mikefarah@users.noreply.github.com>"
|
||||
|
||||
COPY --from=builder /go/src/mikefarah/yq/yq /usr/bin/yq
|
||||
|
||||
+8
-3
@@ -1,9 +1,14 @@
|
||||
FROM golang:1.17
|
||||
FROM golang:1.25.4
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y npm && \
|
||||
npm install -g npx cspell@latest
|
||||
|
||||
COPY scripts/devtools.sh /opt/devtools.sh
|
||||
|
||||
RUN set -e -x \
|
||||
&& /opt/devtools.sh
|
||||
RUN set -e -x && \
|
||||
/opt/devtools.sh
|
||||
|
||||
ENV PATH=/go/bin:$PATH
|
||||
|
||||
ENV CGO_ENABLED 0
|
||||
|
||||
@@ -2,7 +2,7 @@ MAKEFLAGS += --warn-undefined-variables
|
||||
SHELL := /bin/bash
|
||||
.SHELLFLAGS := -o pipefail -euc
|
||||
.DEFAULT_GOAL := install
|
||||
ENGINE := $(shell { command -v podman || command -v docker; } 2>/dev/null)
|
||||
ENGINE := $(shell { (podman version > /dev/null 2>&1 && command -v podman) || command -v docker; } 2>/dev/null)
|
||||
|
||||
include Makefile.variables
|
||||
|
||||
@@ -84,8 +84,12 @@ format: vendor
|
||||
${ENGINERUN} bash ./scripts/format.sh
|
||||
|
||||
|
||||
.PHONY: spelling
|
||||
spelling: format
|
||||
${ENGINERUN} bash ./scripts/spelling.sh
|
||||
|
||||
.PHONY: secure
|
||||
secure: format
|
||||
secure: spelling
|
||||
${ENGINERUN} bash ./scripts/secure.sh
|
||||
|
||||
.PHONY: check
|
||||
|
||||
+4
-2
@@ -7,6 +7,7 @@ export GIT_DESCRIBE = $(shell git describe --tags --always)
|
||||
LDFLAGS :=
|
||||
LDFLAGS += -X main.GitCommit=${GIT_COMMIT}${GIT_DIRTY}
|
||||
LDFLAGS += -X main.GitDescribe=${GIT_DESCRIBE}
|
||||
LDFLAGS += -w -s
|
||||
|
||||
GITHUB_TOKEN ?=
|
||||
|
||||
@@ -25,6 +26,7 @@ ifeq ($(CYG_CHECK),1)
|
||||
else
|
||||
# all non-windows environments
|
||||
ROOT := $(shell pwd)
|
||||
SELINUX := $(shell which getenforce 2>&1 >/dev/null && echo :z)
|
||||
endif
|
||||
|
||||
DEV_IMAGE := ${PROJECT}_dev
|
||||
@@ -32,7 +34,7 @@ DEV_IMAGE := ${PROJECT}_dev
|
||||
ENGINERUN := ${ENGINE} run --rm \
|
||||
-e LDFLAGS="${LDFLAGS}" \
|
||||
-e GITHUB_TOKEN="${GITHUB_TOKEN}" \
|
||||
-v ${ROOT}/vendor:/go/src \
|
||||
-v ${ROOT}:/${PROJECT}/src/${IMPORT_PATH} \
|
||||
-v ${ROOT}/vendor:/go/src${SELINUX} \
|
||||
-v ${ROOT}:/${PROJECT}/src/${IMPORT_PATH}${SELINUX} \
|
||||
-w /${PROJECT}/src/${IMPORT_PATH} \
|
||||
${DEV_IMAGE}
|
||||
|
||||
@@ -1,71 +1,107 @@
|
||||
# yq
|
||||
|
||||
   
|
||||
    
|
||||
|
||||
|
||||
a lightweight and portable command-line YAML processor. `yq` uses [jq](https://github.com/stedolan/jq) like syntax but works with yaml files as well as json. It doesn't yet support everything `jq` does - but it does support the most common operations and functions, and more is being added continuously.
|
||||
A lightweight and portable command-line YAML, JSON, INI and XML processor. `yq` uses [jq](https://github.com/stedolan/jq) (a popular JSON processor) like syntax but works with yaml files as well as json, xml, ini, properties, csv and tsv. It doesn't yet support everything `jq` does - but it does support the most common operations and functions, and more is being added continuously.
|
||||
|
||||
yq is written in go - so you can download a dependency free binary for your platform and you are good to go! If you prefer there are a variety of package managers that can be used as well as Docker and Podman, all listed below.
|
||||
yq is written in Go - so you can download a dependency free binary for your platform and you are good to go! If you prefer there are a variety of package managers that can be used as well as Docker and Podman, all listed below.
|
||||
|
||||
## Quick Usage Guide
|
||||
|
||||
Read a value:
|
||||
### Basic Operations
|
||||
|
||||
**Read a value:**
|
||||
```bash
|
||||
yq e '.a.b[0].c' file.yaml
|
||||
yq '.a.b[0].c' file.yaml
|
||||
```
|
||||
|
||||
Pipe from STDIN:
|
||||
**Pipe from STDIN:**
|
||||
```bash
|
||||
cat file.yaml | yq e '.a.b[0].c' -
|
||||
yq '.a.b[0].c' < file.yaml
|
||||
```
|
||||
|
||||
Update a yaml file, inplace
|
||||
**Update a yaml file in place:**
|
||||
```bash
|
||||
yq e -i '.a.b[0].c = "cool"' file.yaml
|
||||
yq -i '.a.b[0].c = "cool"' file.yaml
|
||||
```
|
||||
|
||||
Update using environment variables
|
||||
**Update using environment variables:**
|
||||
```bash
|
||||
NAME=mike yq e -i '.a.b[0].c = strenv(NAME)' file.yaml
|
||||
NAME=mike yq -i '.a.b[0].c = strenv(NAME)' file.yaml
|
||||
```
|
||||
|
||||
Merge multiple files
|
||||
```
|
||||
### Advanced Operations
|
||||
|
||||
**Merge multiple files:**
|
||||
```bash
|
||||
# merge two files
|
||||
yq -n 'load("file1.yaml") * load("file2.yaml")'
|
||||
|
||||
# merge using globs (note: `ea` evaluates all files at once instead of in sequence)
|
||||
yq ea '. as $item ireduce ({}; . * $item )' path/to/*.yml
|
||||
```
|
||||
|
||||
Multiple updates to a yaml file
|
||||
**Multiple updates to a yaml file:**
|
||||
```bash
|
||||
yq e -i '
|
||||
yq -i '
|
||||
.a.b[0].c = "cool" |
|
||||
.x.y.z = "foobar" |
|
||||
.person.name = strenv(NAME)
|
||||
' file.yaml
|
||||
```
|
||||
|
||||
See the [documentation](https://mikefarah.gitbook.io/yq/) for more.
|
||||
**Find and update an item in an array:**
|
||||
```bash
|
||||
# Note: requires input file - add your file at the end
|
||||
yq -i '(.[] | select(.name == "foo") | .address) = "12 cat st"' data.yaml
|
||||
```
|
||||
|
||||
**Convert between formats:**
|
||||
```bash
|
||||
# Convert JSON to YAML (pretty print)
|
||||
yq -Poy sample.json
|
||||
|
||||
# Convert YAML to JSON
|
||||
yq -o json file.yaml
|
||||
|
||||
# Convert XML to YAML
|
||||
yq -o yaml file.xml
|
||||
```
|
||||
|
||||
See [recipes](https://mikefarah.gitbook.io/yq/recipes) for more examples and the [documentation](https://mikefarah.gitbook.io/yq/) for more information.
|
||||
|
||||
Take a look at the discussions for [common questions](https://github.com/mikefarah/yq/discussions/categories/q-a), and [cool ideas](https://github.com/mikefarah/yq/discussions/categories/show-and-tell)
|
||||
|
||||
## Install
|
||||
|
||||
### [Download the latest binary](https://github.com/mikefarah/yq/releases/latest)
|
||||
|
||||
### wget
|
||||
Use wget to download the pre-compiled binaries:
|
||||
Use wget to download pre-compiled binaries. Choose your platform and architecture:
|
||||
|
||||
#### Compressed via tar.gz
|
||||
**For Linux (example):**
|
||||
```bash
|
||||
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY}.tar.gz -O - |\
|
||||
tar xz && mv ${BINARY} /usr/bin/yq
|
||||
# Set your platform variables (adjust as needed)
|
||||
VERSION=v4.2.0
|
||||
PLATFORM=linux_amd64
|
||||
|
||||
# Download compressed binary
|
||||
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_${PLATFORM}.tar.gz -O - |\
|
||||
tar xz && sudo mv yq_${PLATFORM} /usr/local/bin/yq
|
||||
|
||||
# Or download plain binary
|
||||
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_${PLATFORM} -O /usr/local/bin/yq &&\
|
||||
chmod +x /usr/local/bin/yq
|
||||
```
|
||||
|
||||
#### Plain binary
|
||||
|
||||
**Latest version (Linux AMD64):**
|
||||
```bash
|
||||
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O /usr/bin/yq &&\
|
||||
chmod +x /usr/bin/yq
|
||||
wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq &&\
|
||||
chmod +x /usr/local/bin/yq
|
||||
```
|
||||
|
||||
For instance, VERSION=v4.2.0 and BINARY=yq_linux_amd64
|
||||
**Available platforms:** `linux_amd64`, `linux_arm64`, `linux_arm`, `linux_386`, `darwin_amd64`, `darwin_arm64`, `windows_amd64`, `windows_386`, etc.
|
||||
|
||||
### MacOS / Linux via Homebrew:
|
||||
Using [Homebrew](https://brew.sh/)
|
||||
@@ -82,42 +118,47 @@ snap install yq
|
||||
`yq` installs with [_strict confinement_](https://docs.snapcraft.io/snap-confinement/6233) in snap, this means it doesn't have direct access to root files. To read root files you can:
|
||||
|
||||
```
|
||||
sudo cat /etc/myfile | yq e '.a.path' -
|
||||
sudo cat /etc/myfile | yq '.a.path'
|
||||
```
|
||||
|
||||
And to write to a root file you can either use [sponge](https://linux.die.net/man/1/sponge):
|
||||
```
|
||||
sudo cat /etc/myfile | yq e '.a.path = "value"' - | sudo sponge /etc/myfile
|
||||
sudo cat /etc/myfile | yq '.a.path = "value"' | sudo sponge /etc/myfile
|
||||
```
|
||||
or write to a temporary file:
|
||||
```
|
||||
sudo cat /etc/myfile | yq e '.a.path = "value"' | sudo tee /etc/myfile.tmp
|
||||
sudo cat /etc/myfile | yq '.a.path = "value"' | sudo tee /etc/myfile.tmp
|
||||
sudo mv /etc/myfile.tmp /etc/myfile
|
||||
rm /etc/myfile.tmp
|
||||
```
|
||||
|
||||
### Run with Docker or Podman
|
||||
|
||||
#### Oneshot use:
|
||||
|
||||
#### One-time use:
|
||||
```bash
|
||||
docker run --rm -v "${PWD}":/workdir mikefarah/yq <command> [flags] [expression ]FILE...
|
||||
# Docker - process files in current directory
|
||||
docker run --rm -v "${PWD}":/workdir mikefarah/yq '.a.b[0].c' file.yaml
|
||||
|
||||
# Podman - same usage as Docker
|
||||
podman run --rm -v "${PWD}":/workdir mikefarah/yq '.a.b[0].c' file.yaml
|
||||
```
|
||||
|
||||
**Security note:** You can run `yq` in Docker with restricted privileges:
|
||||
```bash
|
||||
podman run --rm -v "${PWD}":/workdir mikefarah/yq <command> [flags] [expression ]FILE...
|
||||
docker run --rm --security-opt=no-new-privileges --cap-drop all --network none \
|
||||
-v "${PWD}":/workdir mikefarah/yq '.a.b[0].c' file.yaml
|
||||
```
|
||||
|
||||
#### Pipe in via STDIN:
|
||||
#### Pipe data via STDIN:
|
||||
|
||||
You'll need to pass the `-i\--interactive` flag to docker:
|
||||
You'll need to pass the `-i --interactive` flag to Docker/Podman:
|
||||
|
||||
```bash
|
||||
cat myfile.yml | docker run -i --rm mikefarah/yq e . -
|
||||
```
|
||||
# Process piped data
|
||||
docker run -i --rm mikefarah/yq '.this.thing' < myfile.yml
|
||||
|
||||
```bash
|
||||
cat myfile.yml | podman run -i --rm mikefarah/yq e . -
|
||||
# Same with Podman
|
||||
podman run -i --rm mikefarah/yq '.this.thing' < myfile.yml
|
||||
```
|
||||
|
||||
#### Run commands interactively:
|
||||
@@ -143,8 +184,6 @@ yq() {
|
||||
podman run --rm -i -v "${PWD}":/workdir mikefarah/yq "$@"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Running as root:
|
||||
|
||||
`yq`'s container image no longer runs under root (https://github.com/mikefarah/yq/pull/860). If you'd like to install more things in the container image, or you're having permissions issues when attempting to read/write files you'll need to either:
|
||||
@@ -164,29 +203,75 @@ Or, in your Dockerfile:
|
||||
FROM mikefarah/yq
|
||||
|
||||
USER root
|
||||
RUN apk add bash
|
||||
RUN apk add --no-cache bash
|
||||
USER yq
|
||||
```
|
||||
|
||||
#### Missing timezone data
|
||||
By default, the alpine image yq uses does not include timezone data. If you'd like to use the `tz` operator, you'll need to include this data:
|
||||
|
||||
```
|
||||
FROM mikefarah/yq
|
||||
|
||||
USER root
|
||||
RUN apk add --no-cache tzdata
|
||||
USER yq
|
||||
```
|
||||
|
||||
#### Podman with SELinux
|
||||
|
||||
If you are using podman with SELinux, you will need to set the shared volume flag `:z` on the volume mount:
|
||||
|
||||
```
|
||||
-v "${PWD}":/workdir:z
|
||||
```
|
||||
|
||||
### GitHub Action
|
||||
```
|
||||
- name: Set foobar to cool
|
||||
uses: mikefarah/yq@master
|
||||
with:
|
||||
cmd: yq eval -i '.foo.bar = "cool"' 'config.yml'
|
||||
cmd: yq -i '.foo.bar = "cool"' 'config.yml'
|
||||
- name: Get an entry with a variable that might contain dots or spaces
|
||||
id: get_username
|
||||
uses: mikefarah/yq@master
|
||||
with:
|
||||
cmd: yq '.all.children.["${{ matrix.ip_address }}"].username' ops/inventories/production.yml
|
||||
- name: Reuse a variable obtained in another step
|
||||
run: echo ${{ steps.get_username.outputs.result }}
|
||||
```
|
||||
|
||||
See https://mikefarah.gitbook.io/yq/usage/github-action for more.
|
||||
|
||||
### Go Get:
|
||||
### Go Install:
|
||||
```
|
||||
GO111MODULE=on go get github.com/mikefarah/yq/v4
|
||||
go install github.com/mikefarah/yq/v4@latest
|
||||
```
|
||||
|
||||
## Community Supported Installation methods
|
||||
As these are supported by the community :heart: - however, they may be out of date with the officially supported releases.
|
||||
|
||||
# Webi
|
||||
_Please note that the Debian package (previously supported by @rmescandon) is no longer maintained. Please use an alternative installation method._
|
||||
|
||||
|
||||
### X-CMD
|
||||
Checkout `yq` on x-cmd: https://x-cmd.com/mod/yq
|
||||
|
||||
- Instant Results: See the output of your yq filter in real-time.
|
||||
- Error Handling: Encounter a syntax error? It will display the error message and the results of the closest valid filter
|
||||
|
||||
Thanks @edwinjhlee!
|
||||
|
||||
### Nix
|
||||
|
||||
```
|
||||
nix profile install nixpkgs#yq-go
|
||||
```
|
||||
|
||||
See [here](https://search.nixos.org/packages?channel=unstable&show=yq-go&from=0&size=50&sort=relevance&type=packages&query=yq-go)
|
||||
|
||||
|
||||
### Webi
|
||||
|
||||
```
|
||||
webi yq
|
||||
@@ -202,6 +287,9 @@ pacman -S go-yq
|
||||
```
|
||||
|
||||
### Windows:
|
||||
|
||||
Using [Chocolatey](https://chocolatey.org)
|
||||
|
||||
[](https://chocolatey.org/packages/yq)
|
||||
[](https://chocolatey.org/packages/yq)
|
||||
```
|
||||
@@ -209,7 +297,17 @@ choco install yq
|
||||
```
|
||||
Supported by @chillum (https://chocolatey.org/packages/yq)
|
||||
|
||||
### Mac:
|
||||
Using [scoop](https://scoop.sh/)
|
||||
```
|
||||
scoop install main/yq
|
||||
```
|
||||
|
||||
Using [winget](https://learn.microsoft.com/en-us/windows/package-manager/)
|
||||
```
|
||||
winget install --id MikeFarah.yq
|
||||
```
|
||||
|
||||
### MacPorts:
|
||||
Using [MacPorts](https://www.macports.org/)
|
||||
```
|
||||
sudo port selfupdate
|
||||
@@ -218,40 +316,55 @@ sudo port install yq
|
||||
Supported by @herbygillot (https://ports.macports.org/maintainer/github/herbygillot)
|
||||
|
||||
### Alpine Linux
|
||||
- Enable edge/community repo by adding ```$MIRROR/alpine/edge/community``` to ```/etc/apk/repositories```
|
||||
- Update database index with ```apk update```
|
||||
- Install yq with ```apk add yq```
|
||||
|
||||
Supported by Tuan Hoang
|
||||
https://pkgs.alpinelinux.org/package/edge/community/x86/yq
|
||||
Alpine Linux v3.20+ (and Edge):
|
||||
```
|
||||
apk add yq-go
|
||||
```
|
||||
|
||||
Alpine Linux up to v3.19:
|
||||
```
|
||||
apk add yq
|
||||
```
|
||||
|
||||
### On Ubuntu 16.04 or higher from Debian package:
|
||||
```sh
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CC86BB64
|
||||
sudo add-apt-repository ppa:rmescandon/yq
|
||||
sudo apt update
|
||||
sudo apt install yq -y
|
||||
Supported by Tuan Hoang (https://pkgs.alpinelinux.org/packages?name=yq-go)
|
||||
|
||||
### Flox:
|
||||
|
||||
Flox can be used to install yq on Linux, MacOS, and Windows through WSL.
|
||||
|
||||
```
|
||||
flox install yq
|
||||
```
|
||||
|
||||
|
||||
### MacOS / Linux via gah:
|
||||
Using [gah](https://github.com/marverix/gah)
|
||||
|
||||
```
|
||||
gah install yq
|
||||
```
|
||||
Supported by @rmescandon (https://launchpad.net/~rmescandon/+archive/ubuntu/yq)
|
||||
|
||||
## Features
|
||||
- [Detailed documentation with many examples](https://mikefarah.gitbook.io/yq/)
|
||||
- Written in portable go, so you can download a lovely dependency free binary
|
||||
- Uses similar syntax as `jq` but works with YAML and JSON files
|
||||
- Uses similar syntax as `jq` but works with YAML, INI, [JSON](https://mikefarah.gitbook.io/yq/usage/convert) and [XML](https://mikefarah.gitbook.io/yq/usage/xml) files
|
||||
- Fully supports multi document yaml files
|
||||
- Supports yaml [front matter](https://mikefarah.gitbook.io/yq/usage/front-matter) blocks (e.g. jekyll/assemble)
|
||||
- Colorized yaml output
|
||||
- [Deeply traverse yaml](https://mikefarah.gitbook.io/yq/operators/traverse-read)
|
||||
- [Sort yaml by keys](https://mikefarah.gitbook.io/yq/operators/sort-keys)
|
||||
- [Date/Time manipulation and formatting with TZ](https://mikefarah.gitbook.io/yq/operators/datetime)
|
||||
- [Deep data structures](https://mikefarah.gitbook.io/yq/operators/traverse-read)
|
||||
- [Sort keys](https://mikefarah.gitbook.io/yq/operators/sort-keys)
|
||||
- Manipulate yaml [comments](https://mikefarah.gitbook.io/yq/operators/comment-operators), [styling](https://mikefarah.gitbook.io/yq/operators/style), [tags](https://mikefarah.gitbook.io/yq/operators/tag) and [anchors and aliases](https://mikefarah.gitbook.io/yq/operators/anchor-and-alias-operators).
|
||||
- [Update yaml inplace](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate#flags)
|
||||
- [Update in place](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate#flags)
|
||||
- [Complex expressions to select and update](https://mikefarah.gitbook.io/yq/operators/select#select-and-update-matching-values-in-map)
|
||||
- Keeps yaml formatting and comments when updating (though there are issues with whitespace)
|
||||
- [Decode/Encode base64 data](https://mikefarah.gitbook.io/yq/operators/encode-decode)
|
||||
- [Load content from other files](https://mikefarah.gitbook.io/yq/operators/load)
|
||||
- [Convert to/from json](https://mikefarah.gitbook.io/yq/v/v4.x/usage/convert)
|
||||
- [Convert to properties](https://mikefarah.gitbook.io/yq/v/v4.x/usage/properties)
|
||||
- [Pipe data in by using '-'](https://mikefarah.gitbook.io/yq/v/v4.x/commands/evaluate)
|
||||
- [Convert to/from json/ndjson](https://mikefarah.gitbook.io/yq/v/v4.x/usage/convert)
|
||||
- [Convert to/from xml](https://mikefarah.gitbook.io/yq/v/v4.x/usage/xml)
|
||||
- [Convert to/from properties](https://mikefarah.gitbook.io/yq/v/v4.x/usage/properties)
|
||||
- [Convert to/from csv/tsv](https://mikefarah.gitbook.io/yq/usage/csv-tsv)
|
||||
- [General shell completion scripts (bash/zsh/fish/powershell)](https://mikefarah.gitbook.io/yq/v/v4.x/commands/shell-completion)
|
||||
- [Reduce](https://mikefarah.gitbook.io/yq/operators/reduce) to merge multiple files or sum an array or other fancy things.
|
||||
- [Github Action](https://mikefarah.gitbook.io/yq/usage/github-action) to use in your automated pipeline (thanks @devorbitus)
|
||||
@@ -265,33 +378,101 @@ Usage:
|
||||
yq [flags]
|
||||
yq [command]
|
||||
|
||||
Examples:
|
||||
|
||||
# yq tries to auto-detect the file format based off the extension, and defaults to YAML if it's unknown (or piping through STDIN)
|
||||
# Use the '-p/--input-format' flag to specify a format type.
|
||||
cat file.xml | yq -p xml
|
||||
|
||||
# read the "stuff" node from "myfile.yml"
|
||||
yq '.stuff' < myfile.yml
|
||||
|
||||
# update myfile.yml in place
|
||||
yq -i '.stuff = "foo"' myfile.yml
|
||||
|
||||
# print contents of sample.json as idiomatic YAML
|
||||
yq -P -oy sample.json
|
||||
|
||||
|
||||
Available Commands:
|
||||
eval Apply the expression to each document in each yaml file in sequence
|
||||
eval-all Loads _all_ yaml documents of _all_ yaml files and runs expression once
|
||||
help Help about any command
|
||||
shell-completion Generate completion script
|
||||
completion Generate the autocompletion script for the specified shell
|
||||
eval (default) Apply the expression to each document in each yaml file in sequence
|
||||
eval-all Loads _all_ yaml documents of _all_ yaml files and runs expression once
|
||||
help Help about any command
|
||||
|
||||
Flags:
|
||||
-C, --colors force print with colors
|
||||
-e, --exit-status set exit status if there are no matches or null or false is returned
|
||||
-f, --front-matter string (extract|process) first input as yaml front-matter. Extract will pull out the yaml content, process will run the expression against the yaml content, leaving the remaining data intact
|
||||
--header-preprocess Slurp any header comments and seperators before processing expression. This is a workaround for go-yaml to persist header content (default true)
|
||||
-h, --help help for yq
|
||||
-I, --indent int sets indent level for output (default 2)
|
||||
-i, --inplace update the yaml file inplace of first yaml file given.
|
||||
-M, --no-colors force print with no colors
|
||||
-N, --no-doc Don't print document separators (---)
|
||||
-n, --null-input Don't read input, simply evaluate the expression given. Useful for creating yaml docs from scratch.
|
||||
-o, --output-format string [yaml|y|json|j|props|p] output format type. (default "yaml")
|
||||
-P, --prettyPrint pretty print, shorthand for '... style = ""'
|
||||
--unwrapScalar unwrap scalar, print the value with no quotes, colors or comments (default true)
|
||||
-v, --verbose verbose mode
|
||||
-V, --version Print version information and quit
|
||||
-C, --colors force print with colors
|
||||
--csv-auto-parse parse CSV YAML/JSON values (default true)
|
||||
--csv-separator char CSV Separator character (default ,)
|
||||
--debug-node-info debug node info
|
||||
-e, --exit-status set exit status if there are no matches or null or false is returned
|
||||
--expression string forcibly set the expression argument. Useful when yq argument detection thinks your expression is a file.
|
||||
--from-file string Load expression from specified file.
|
||||
-f, --front-matter string (extract|process) first input as yaml front-matter. Extract will pull out the yaml content, process will run the expression against the yaml content, leaving the remaining data intact
|
||||
--header-preprocess Slurp any header comments and separators before processing expression. (default true)
|
||||
-h, --help help for yq
|
||||
-I, --indent int sets indent level for output (default 2)
|
||||
-i, --inplace update the file in place of first file given.
|
||||
-p, --input-format string [auto|a|yaml|y|json|j|props|p|csv|c|tsv|t|xml|x|base64|uri|toml|lua|l|ini|i] parse format for input. (default "auto")
|
||||
--lua-globals output keys as top-level global variables
|
||||
--lua-prefix string prefix (default "return ")
|
||||
--lua-suffix string suffix (default ";\n")
|
||||
--lua-unquoted output unquoted string keys (e.g. {foo="bar"})
|
||||
-M, --no-colors force print with no colors
|
||||
-N, --no-doc Don't print document separators (---)
|
||||
-0, --nul-output Use NUL char to separate values. If unwrap scalar is also set, fail if unwrapped scalar contains NUL char.
|
||||
-n, --null-input Don't read input, simply evaluate the expression given. Useful for creating docs from scratch.
|
||||
-o, --output-format string [auto|a|yaml|y|json|j|props|p|csv|c|tsv|t|xml|x|base64|uri|toml|shell|s|lua|l|ini|i] output format type. (default "auto")
|
||||
-P, --prettyPrint pretty print, shorthand for '... style = ""'
|
||||
--properties-array-brackets use [x] in array paths (e.g. for SpringBoot)
|
||||
--properties-separator string separator to use between keys and values (default " = ")
|
||||
--security-disable-env-ops Disable env related operations.
|
||||
--security-disable-file-ops Disable file related operations (e.g. load)
|
||||
--shell-key-separator string separator for shell variable key paths (default "_")
|
||||
-s, --split-exp string print each result (or doc) into a file named (exp). [exp] argument must return a string. You can use $index in the expression as the result counter. The necessary directories will be created.
|
||||
--split-exp-file string Use a file to specify the split-exp expression.
|
||||
--string-interpolation Toggles strings interpolation of \(exp) (default true)
|
||||
--tsv-auto-parse parse TSV YAML/JSON values (default true)
|
||||
-r, --unwrapScalar unwrap scalar, print the value with no quotes, colors or comments. Defaults to true for yaml (default true)
|
||||
-v, --verbose verbose mode
|
||||
-V, --version Print version information and quit
|
||||
--xml-attribute-prefix string prefix for xml attributes (default "+@")
|
||||
--xml-content-name string name for xml content (if no attribute name is present). (default "+content")
|
||||
--xml-directive-name string name for xml directives (e.g. <!DOCTYPE thing cat>) (default "+directive")
|
||||
--xml-keep-namespace enables keeping namespace after parsing attributes (default true)
|
||||
--xml-proc-inst-prefix string prefix for xml processing instructions (e.g. <?xml version="1"?>) (default "+p_")
|
||||
--xml-raw-token enables using RawToken method instead Token. Commonly disables namespace translations. See https://pkg.go.dev/encoding/xml#Decoder.RawToken for details. (default true)
|
||||
--xml-skip-directives skip over directives (e.g. <!DOCTYPE thing cat>)
|
||||
--xml-skip-proc-inst skip over process instructions (e.g. <?xml version="1"?>)
|
||||
--xml-strict-mode enables strict parsing of XML. See https://pkg.go.dev/encoding/xml for more details.
|
||||
--yaml-fix-merge-anchor-to-spec Fix merge anchor to match YAML spec. Will default to true in late 2025
|
||||
|
||||
Use "yq [command] --help" for more information about a command.
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**PowerShell quoting issues:**
|
||||
```powershell
|
||||
# Use single quotes for expressions
|
||||
yq '.a.b[0].c' file.yaml
|
||||
|
||||
# Or escape double quotes
|
||||
yq ".a.b[0].c = \"value\"" file.yaml
|
||||
```
|
||||
|
||||
### Getting Help
|
||||
|
||||
- **Check existing issues**: [GitHub Issues](https://github.com/mikefarah/yq/issues)
|
||||
- **Ask questions**: [GitHub Discussions](https://github.com/mikefarah/yq/discussions)
|
||||
- **Documentation**: [Complete documentation](https://mikefarah.gitbook.io/yq/)
|
||||
- **Examples**: [Recipes and examples](https://mikefarah.gitbook.io/yq/recipes)
|
||||
|
||||
## Known Issues / Missing Features
|
||||
- `yq` attempts to preserve comment positions and whitespace as much as possible, but it does not handle all scenarios (see https://github.com/go-yaml/yaml/tree/v3 for details)
|
||||
- Powershell has its own...[opinions on quoting yq](https://mikefarah.gitbook.io/yq/usage/tips-and-tricks#quotes-in-windows-powershell)
|
||||
- "yes", "no" were dropped as boolean values in the yaml 1.2 standard - which is the standard yq assumes.
|
||||
|
||||
See [tips and tricks](https://mikefarah.gitbook.io/yq/usage/tips-and-tricks) for more common problems and solutions.
|
||||
|
||||
@@ -3,25 +3,25 @@
|
||||
testWriteInPlacePipeIn() {
|
||||
result=$(./yq e -i -n '.a' 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: write inplace flag only applicable when giving an expression and at least one file" "$result"
|
||||
assertEquals "Error: write in place flag only applicable when giving an expression and at least one file" "$result"
|
||||
}
|
||||
|
||||
testWriteInPlacePipeInEvalall() {
|
||||
result=$(./yq ea -i -n '.a' 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: write inplace flag only applicable when giving an expression and at least one file" "$result"
|
||||
assertEquals "Error: write in place flag only applicable when giving an expression and at least one file" "$result"
|
||||
}
|
||||
|
||||
testWriteInPlaceWithSplit() {
|
||||
result=$(./yq e -s "cat" -i '.a = "thing"' test.yml 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: write inplace cannot be used with split file" "$result"
|
||||
assertEquals "Error: write in place cannot be used with split file" "$result"
|
||||
}
|
||||
|
||||
testWriteInPlaceWithSplitEvalAll() {
|
||||
result=$(./yq ea -s "cat" -i '.a = "thing"' test.yml 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: write inplace cannot be used with split file" "$result"
|
||||
assertEquals "Error: write in place cannot be used with split file" "$result"
|
||||
}
|
||||
|
||||
testNullWithFiles() {
|
||||
|
||||
+305
-4
@@ -1,12 +1,236 @@
|
||||
#!/bin/bash
|
||||
|
||||
setUp() {
|
||||
rm test*.yml || true
|
||||
rm test*.yml 2>/dev/null || true
|
||||
rm .xyz 2>/dev/null || true
|
||||
rm instructions.txt 2>/dev/null || true
|
||||
}
|
||||
|
||||
testBasicEvalRoundTrip() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
X=$(./yq '.a' test.yml)
|
||||
assertEquals 123 "$X"
|
||||
}
|
||||
|
||||
testBasicTrailingContent() {
|
||||
cat >test-trailing.yml <<EOL
|
||||
test:
|
||||
# this comment will be removed
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
test:
|
||||
# this comment will be removed
|
||||
EOM
|
||||
X=$(./yq test-trailing.yml -P)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testBasicTrailingContent() {
|
||||
cat >test-trailing.yml <<EOL
|
||||
test:
|
||||
# this comment will be removed
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
test:
|
||||
# hi
|
||||
EOM
|
||||
X=$(./yq '. footComment = "hi"' test-trailing.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testBasicTrailingContentEvalAll() {
|
||||
cat >test-trailing.yml <<EOL
|
||||
test:
|
||||
# this comment will be removed
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
test:
|
||||
# this comment will be removed
|
||||
EOM
|
||||
X=$(./yq ea test-trailing.yml -P)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testBasicTrailingContentEvalAll() {
|
||||
cat >test-trailing.yml <<EOL
|
||||
test:
|
||||
# this comment will be removed
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
test:
|
||||
# hi
|
||||
EOM
|
||||
X=$(./yq ea '. footComment = "hi"' test-trailing.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testBasicPipeWithDot() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
X=$(cat test.yml | ./yq '.')
|
||||
assertEquals "a: 123" "$X"
|
||||
}
|
||||
|
||||
testBasicExpressionMatchesFileName() {
|
||||
./yq -n ".xyz = 123" > test.yml
|
||||
touch .xyz
|
||||
|
||||
X=$(./yq --expression '.xyz' test.yml)
|
||||
assertEquals "123" "$X"
|
||||
|
||||
X=$(./yq ea --expression '.xyz' test.yml)
|
||||
assertEquals "123" "$X"
|
||||
}
|
||||
|
||||
testBasicExpressionFromFile() {
|
||||
./yq -n ".xyz = 123" > test.yml
|
||||
echo '.xyz = "meow" | .cool = "frog"' > instructions.txt
|
||||
|
||||
X=$(./yq --from-file instructions.txt test.yml -o=j -I=0)
|
||||
assertEquals '{"xyz":"meow","cool":"frog"}' "$X"
|
||||
|
||||
X=$(./yq ea --from-file instructions.txt test.yml -o=j -I=0)
|
||||
assertEquals '{"xyz":"meow","cool":"frog"}' "$X"
|
||||
}
|
||||
|
||||
testBasicExpressionFromFileDos() {
|
||||
./yq -n ".xyz = 123" > test.yml
|
||||
echo '.xyz = "meow" | .cool = "frog"' | sed 's/$'"/`echo \\\r`/" > instructions.txt
|
||||
|
||||
X=$(./yq --from-file instructions.txt test.yml -o=j -I=0)
|
||||
assertEquals '{"xyz":"meow","cool":"frog"}' "$X"
|
||||
|
||||
X=$(./yq ea --from-file instructions.txt test.yml -o=j -I=0)
|
||||
assertEquals '{"xyz":"meow","cool":"frog"}' "$X"
|
||||
}
|
||||
|
||||
testBasicGitHubAction() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
X=$(cat /dev/null | ./yq test.yml)
|
||||
assertEquals "a: 123" "$X"
|
||||
|
||||
X=$(cat /dev/null | ./yq e test.yml)
|
||||
assertEquals "a: 123" "$X"
|
||||
|
||||
X=$(cat /dev/null | ./yq ea test.yml)
|
||||
assertEquals "a: 123" "$X"
|
||||
}
|
||||
|
||||
testBasicGitHubActionWithExpression() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
X=$(cat /dev/null | ./yq '.a' test.yml)
|
||||
assertEquals "123" "$X"
|
||||
|
||||
X=$(cat /dev/null | ./yq e '.a' test.yml)
|
||||
assertEquals "123" "$X"
|
||||
|
||||
X=$(cat /dev/null | ./yq ea '.a' test.yml)
|
||||
assertEquals "123" "$X"
|
||||
}
|
||||
|
||||
|
||||
testBasicEvalAllAllFiles() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(./yq ea test.yml test2.yml)
|
||||
Y=$(./yq e '.' test.yml test2.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
# when given a file, don't read STDIN
|
||||
# otherwise strange things start happening
|
||||
# in scripts
|
||||
# https://github.com/mikefarah/yq/issues/1115
|
||||
|
||||
testBasicCatWithFilesNoDash() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(cat test.yml | ./yq test2.yml)
|
||||
Y=$(./yq e '.' test2.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
# when the nullinput flag is used
|
||||
# don't automatically read STDIN (this breaks github actions)
|
||||
testBasicCreateFileGithubAction() {
|
||||
cat /dev/null | ./yq -n ".a = 123" > test.yml
|
||||
}
|
||||
|
||||
testBasicEvalAllCatWithFilesNoDash() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(cat test.yml | ./yq ea test2.yml)
|
||||
Y=$(./yq e '.' test2.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
testBasicCatWithFilesNoDashWithExp() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(cat test.yml | ./yq '.a' test2.yml)
|
||||
Y=$(./yq e '.a' test2.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
testBasicEvalAllCatWithFilesNoDashWithExp() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(cat test.yml | ./yq ea '.a' test2.yml)
|
||||
Y=$(./yq e '.a' test2.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
|
||||
testBasicStdInWithFiles() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(cat test.yml | ./yq - test2.yml)
|
||||
Y=$(./yq e '.' test.yml test2.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
testBasicEvalAllStdInWithFiles() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(cat test.yml | ./yq ea - test2.yml)
|
||||
Y=$(./yq e '.' test.yml test2.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
testBasicStdInWithFilesReverse() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(cat test.yml | ./yq test2.yml -)
|
||||
Y=$(./yq e '.' test2.yml test.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
testBasicEvalAllStdInWithFilesReverse() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
./yq -n ".a = 124" > test2.yml
|
||||
X=$(cat test.yml | ./yq ea test2.yml -)
|
||||
Y=$(./yq e '.' test2.yml test.yml)
|
||||
assertEquals "$Y" "$X"
|
||||
}
|
||||
|
||||
testBasicEvalRoundTripNoEval() {
|
||||
./yq -n ".a = 123" > test.yml
|
||||
X=$(./yq '.a' test.yml)
|
||||
assertEquals 123 "$X"
|
||||
}
|
||||
|
||||
testBasicStdInWithOneArg() {
|
||||
./yq e -n ".a = 123" > test.yml
|
||||
X=$(./yq e '.a' test.yml)
|
||||
X=$(cat test.yml | ./yq e ".a")
|
||||
assertEquals 123 "$X"
|
||||
|
||||
X=$(cat test.yml | ./yq ea ".a")
|
||||
assertEquals 123 "$X"
|
||||
|
||||
X=$(cat test.yml | ./yq ".a")
|
||||
assertEquals 123 "$X"
|
||||
}
|
||||
|
||||
@@ -19,6 +243,15 @@ EOL
|
||||
assertEquals "10" "$X"
|
||||
}
|
||||
|
||||
testBasicUpdateInPlaceSequenceNoEval() {
|
||||
cat >test.yml <<EOL
|
||||
a: 0
|
||||
EOL
|
||||
./yq -i ".a = 10" test.yml
|
||||
X=$(./yq '.a' test.yml)
|
||||
assertEquals "10" "$X"
|
||||
}
|
||||
|
||||
testBasicUpdateInPlaceSequenceEvalAll() {
|
||||
cat >test.yml <<EOL
|
||||
a: 0
|
||||
@@ -28,6 +261,40 @@ EOL
|
||||
assertEquals "10" "$X"
|
||||
}
|
||||
|
||||
testBasicUpdateInPlaceMultipleFilesNoExpressionEval() {
|
||||
cat >test.yml <<EOL
|
||||
a: 0
|
||||
EOL
|
||||
cat >test2.yml <<EOL
|
||||
a: 1
|
||||
EOL
|
||||
read -r -d '' expected << EOM
|
||||
0
|
||||
---
|
||||
1
|
||||
EOM
|
||||
./yq -i test.yml test2.yml
|
||||
X=$(./yq e '.a' test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testBasicUpdateInPlaceMultipleFilesNoExpressionEvalAll() {
|
||||
cat >test.yml <<EOL
|
||||
a: 0
|
||||
EOL
|
||||
cat >test2.yml <<EOL
|
||||
a: 1
|
||||
EOL
|
||||
read -r -d '' expected << EOM
|
||||
0
|
||||
---
|
||||
1
|
||||
EOM
|
||||
./yq -i ea test.yml test2.yml
|
||||
X=$(./yq e '.a' test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testBasicNoExitStatus() {
|
||||
echo "a: cat" > test.yml
|
||||
X=$(./yq e '.z' test.yml)
|
||||
@@ -40,7 +307,13 @@ testBasicExitStatus() {
|
||||
assertEquals 1 "$?"
|
||||
}
|
||||
|
||||
testBasicExtractFieldWithSeperator() {
|
||||
testBasicExitStatusNoEval() {
|
||||
echo "a: cat" > test.yml
|
||||
X=$(./yq -e '.z' test.yml 2&>/dev/null)
|
||||
assertEquals 1 "$?"
|
||||
}
|
||||
|
||||
testBasicExtractFieldWithSeparator() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
name: chart-name
|
||||
@@ -50,7 +323,7 @@ EOL
|
||||
assertEquals "chart-name" "$X"
|
||||
}
|
||||
|
||||
testBasicExtractMultipleFieldWithSeperator() {
|
||||
testBasicExtractMultipleFieldWithSeparator() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
name: chart-name
|
||||
@@ -69,4 +342,32 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testBasicMultiplyAssignMultiDoc() {
|
||||
cat >test.yml <<EOL
|
||||
a: 1
|
||||
---
|
||||
b: 2
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
a: 1
|
||||
c: 3
|
||||
---
|
||||
b: 2
|
||||
c: 3
|
||||
EOM
|
||||
|
||||
|
||||
X=$(./yq '. *= {"c":3}' test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testBasicClosedStdIn() {
|
||||
cat >test.yml <<EOL
|
||||
a: 1
|
||||
EOL
|
||||
X=$(./yq e '.a' test.yml <&-)
|
||||
assertEquals "1" "$X"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
testCompletionRuns() {
|
||||
result=$(./yq __complete "" 2>&1)
|
||||
assertEquals 0 $?
|
||||
assertContains "$result" "Completion ended with directive:"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
@@ -9,7 +9,7 @@ EOL
|
||||
|
||||
testEmptyEval() {
|
||||
X=$(./yq e test.yml)
|
||||
expected=$(cat test.yml)
|
||||
expected="# comment"
|
||||
assertEquals 0 $?
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
setUp() {
|
||||
rm test*.yml || true
|
||||
cat >test.yml <<EOL
|
||||
# comment
|
||||
EOL
|
||||
}
|
||||
|
||||
testStringInterpolation() {
|
||||
X=$(./yq -n '"Mike \(3 + 4)"')
|
||||
assertEquals "Mike 7" "$X"
|
||||
}
|
||||
|
||||
testNoStringInterpolation() {
|
||||
X=$(./yq --string-interpolation=f -n '"Mike \(3 + 4)"')
|
||||
assertEquals "Mike \(3 + 4)" "$X"
|
||||
}
|
||||
|
||||
|
||||
source ./scripts/shunit2
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
setUp() {
|
||||
rm test*.yml || true
|
||||
|
||||
}
|
||||
|
||||
testLineCountFirstLineComment() {
|
||||
cat >test.yml <<EOL
|
||||
#test123
|
||||
abc: 123
|
||||
test123: 123123
|
||||
#comment
|
||||
lalilu: lalilu
|
||||
EOL
|
||||
|
||||
X=$(./yq '.lalilu | line' --header-preprocess=false < test.yml)
|
||||
assertEquals "5" "$X"
|
||||
}
|
||||
|
||||
testArrayOfDocs() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# leading comment doc 1
|
||||
a: 1
|
||||
---
|
||||
# leading comment doc 2
|
||||
a: 2
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
- # leading comment doc 1
|
||||
a: 1
|
||||
- # leading comment doc 2
|
||||
a: 2
|
||||
EOM
|
||||
|
||||
X=$(./yq ea '[.]' --header-preprocess=false < test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
Executable
+249
@@ -0,0 +1,249 @@
|
||||
#!/bin/bash
|
||||
|
||||
setUp() {
|
||||
rm test*.yml 2>/dev/null || true
|
||||
rm test*.toml 2>/dev/null || true
|
||||
rm test*.tfstate 2>/dev/null || true
|
||||
rm test*.json 2>/dev/null || true
|
||||
rm test*.properties 2>/dev/null || true
|
||||
rm test*.csv 2>/dev/null || true
|
||||
rm test*.tsv 2>/dev/null || true
|
||||
rm test*.xml 2>/dev/null || true
|
||||
}
|
||||
|
||||
testInputJson() {
|
||||
cat >test.json <<EOL
|
||||
{ "mike" : { "things": "cool" } }
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
{
|
||||
"mike": {
|
||||
"things": "cool"
|
||||
}
|
||||
}
|
||||
EOM
|
||||
|
||||
X=$(./yq test.json)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea test.json)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputToml() {
|
||||
cat >test.toml <<EOL
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
dob = 1979-05-27T07:32:00-08:00
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
owner:
|
||||
name: Tom Preston-Werner
|
||||
dob: 1979-05-27T07:32:00-08:00
|
||||
EOM
|
||||
|
||||
X=$(./yq -oy test.toml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -oy test.toml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputTfstate() {
|
||||
cat >test.tfstate <<EOL
|
||||
{ "mike" : { "things": "cool" } }
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
{"mike": {"things": "cool"}}
|
||||
EOM
|
||||
|
||||
X=$(./yq test.tfstate)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea test.tfstate)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputJsonOutputYaml() {
|
||||
cat >test.json <<EOL
|
||||
{ "mike" : { "things": "cool" } }
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
mike:
|
||||
things: cool
|
||||
EOM
|
||||
|
||||
X=$(./yq test.json -oy)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea test.json -oy)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputProperties() {
|
||||
cat >test.properties <<EOL
|
||||
mike.things = hello
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
mike.things = hello
|
||||
EOM
|
||||
|
||||
X=$(./yq e test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputPropertiesGitHubAction() {
|
||||
cat >test.properties <<EOL
|
||||
mike.things = hello
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
mike.things = hello
|
||||
EOM
|
||||
|
||||
X=$(cat /dev/null | ./yq e test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(cat /dev/null | ./yq ea test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputCSV() {
|
||||
cat >test.csv <<EOL
|
||||
fruit,yumLevel
|
||||
apple,5
|
||||
banana,4
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
fruit,yumLevel
|
||||
apple,5
|
||||
banana,4
|
||||
EOM
|
||||
|
||||
X=$(./yq e test.csv)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea test.csv)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputCSVUTF8() {
|
||||
read -r -d '' expected << EOM
|
||||
id,first,last
|
||||
1,john,smith
|
||||
1,jane,smith
|
||||
EOM
|
||||
|
||||
X=$(./yq utf8.csv)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputTSV() {
|
||||
cat >test.tsv <<EOL
|
||||
fruit yumLevel
|
||||
apple 5
|
||||
banana 4
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
fruit yumLevel
|
||||
apple 5
|
||||
banana 4
|
||||
EOM
|
||||
|
||||
X=$(./yq e test.tsv)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea test.tsv)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
testInputXml() {
|
||||
cat >test.xml <<EOL
|
||||
<cat legs="4">BiBi</cat>
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
<cat legs="4">BiBi</cat>
|
||||
EOM
|
||||
|
||||
X=$(./yq e test.xml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea test.xml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputXmlNamespaces() {
|
||||
cat >test.xml <<EOL
|
||||
<?xml version="1.0"?>
|
||||
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url">
|
||||
</map>
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
<?xml version="1.0"?>
|
||||
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url"></map>
|
||||
EOM
|
||||
|
||||
X=$(./yq e test.xml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea test.xml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
|
||||
|
||||
testInputXmlStrict() {
|
||||
cat >test.xml <<EOL
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE root [
|
||||
<!ENTITY writer "Catherine.">
|
||||
<!ENTITY copyright "(r) Great">
|
||||
]>
|
||||
<root>
|
||||
<item>&writer;©right;</item>
|
||||
</root>
|
||||
EOL
|
||||
|
||||
X=$(./yq --xml-strict-mode test.xml 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: bad file 'test.xml': XML syntax error on line 7: invalid character entity &writer;" "$X"
|
||||
|
||||
X=$(./yq ea --xml-strict-mode test.xml 2>&1)
|
||||
assertEquals "Error: bad file 'test.xml': XML syntax error on line 7: invalid character entity &writer;" "$X"
|
||||
}
|
||||
|
||||
testInputXmlGithubAction() {
|
||||
cat >test.xml <<EOL
|
||||
<cat legs="4">BiBi</cat>
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
<cat legs="4">BiBi</cat>
|
||||
EOM
|
||||
|
||||
X=$(cat /dev/null | ./yq e test.xml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(cat /dev/null | ./yq ea test.xml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
@@ -1,9 +1,161 @@
|
||||
#!/bin/bash
|
||||
|
||||
setUp() {
|
||||
rm test*.yml || true
|
||||
rm test*.yml 2>/dev/null || true
|
||||
rm test*.properties 2>/dev/null || true
|
||||
rm test*.csv 2>/dev/null || true
|
||||
rm test*.tsv 2>/dev/null || true
|
||||
rm test*.xml 2>/dev/null || true
|
||||
}
|
||||
|
||||
testInputProperties() {
|
||||
cat >test.properties <<EOL
|
||||
mike.things = hello
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
mike:
|
||||
things: hello
|
||||
EOM
|
||||
|
||||
X=$(./yq e -p=props test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -p=props test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputPropertiesGitHubAction() {
|
||||
cat >test.properties <<EOL
|
||||
mike.things = hello
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
mike:
|
||||
things: hello
|
||||
EOM
|
||||
|
||||
X=$(cat /dev/null | ./yq e -p=props test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(cat /dev/null | ./yq ea -p=props test.properties)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputCSV() {
|
||||
cat >test.csv <<EOL
|
||||
fruit,yumLevel
|
||||
apple,5
|
||||
banana,4
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
- fruit: apple
|
||||
yumLevel: 5
|
||||
- fruit: banana
|
||||
yumLevel: 4
|
||||
EOM
|
||||
|
||||
X=$(./yq e -p=csv test.csv)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -p=csv test.csv)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputCSVCustomSeparator() {
|
||||
cat >test.csv <<EOL
|
||||
fruit;yumLevel
|
||||
apple;5
|
||||
banana;4
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
- fruit: apple
|
||||
yumLevel: 5
|
||||
- fruit: banana
|
||||
yumLevel: 4
|
||||
EOM
|
||||
|
||||
X=$(./yq -p=csv --csv-separator ";" test.csv)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -p=csv --csv-separator ";" test.csv)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputCSVNoAuto() {
|
||||
cat >test.csv <<EOL
|
||||
thing1
|
||||
name: cat
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
- thing1: 'name: cat'
|
||||
EOM
|
||||
|
||||
X=$(./yq --csv-auto-parse=f test.csv -oy)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea --csv-auto-parse=f test.csv -oy)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputTSVNoAuto() {
|
||||
cat >test.tsv <<EOL
|
||||
thing1
|
||||
name: cat
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
- thing1: 'name: cat'
|
||||
EOM
|
||||
|
||||
X=$(./yq --tsv-auto-parse=f test.tsv -oy)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea --tsv-auto-parse=f test.tsv -oy)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputCSVUTF8() {
|
||||
read -r -d '' expected << EOM
|
||||
- id: 1
|
||||
first: john
|
||||
last: smith
|
||||
- id: 1
|
||||
first: jane
|
||||
last: smith
|
||||
EOM
|
||||
|
||||
X=$(./yq -p=csv utf8.csv)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputTSV() {
|
||||
cat >test.tsv <<EOL
|
||||
fruit yumLevel
|
||||
apple 5
|
||||
banana 4
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
- fruit: apple
|
||||
yumLevel: 5
|
||||
- fruit: banana
|
||||
yumLevel: 4
|
||||
EOM
|
||||
|
||||
X=$(./yq e -p=t test.tsv)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -p=t test.tsv)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
testInputXml() {
|
||||
cat >test.yml <<EOL
|
||||
<cat legs="4">BiBi</cat>
|
||||
@@ -12,7 +164,7 @@ EOL
|
||||
read -r -d '' expected << EOM
|
||||
cat:
|
||||
+content: BiBi
|
||||
+legs: "4"
|
||||
+@legs: "4"
|
||||
EOM
|
||||
|
||||
X=$(./yq e -p=xml test.yml)
|
||||
@@ -22,4 +174,85 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputXmlNamespaces() {
|
||||
cat >test.xml <<EOL
|
||||
<?xml version="1.0"?>
|
||||
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url">
|
||||
</map>
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
+p_xml: version="1.0"
|
||||
map:
|
||||
+@xmlns: some-namespace
|
||||
+@xmlns:xsi: some-instance
|
||||
+@xsi:schemaLocation: some-url
|
||||
EOM
|
||||
|
||||
X=$(./yq e -p=xml test.xml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -p=xml test.xml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testInputXmlRoundtrip() {
|
||||
cat >test.yml <<EOL
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >
|
||||
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url">Meow</map>
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >
|
||||
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url">Meow</map>
|
||||
EOM
|
||||
|
||||
X=$(./yq -p=xml -o=xml test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -p=xml -o=xml test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
|
||||
testInputXmlStrict() {
|
||||
cat >test.yml <<EOL
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE root [
|
||||
<!ENTITY writer "Catherine.">
|
||||
<!ENTITY copyright "(r) Great">
|
||||
]>
|
||||
<root>
|
||||
<item>&writer;©right;</item>
|
||||
</root>
|
||||
EOL
|
||||
|
||||
X=$(./yq -p=xml --xml-strict-mode test.yml -o=xml 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: bad file 'test.yml': XML syntax error on line 7: invalid character entity &writer;" "$X"
|
||||
|
||||
X=$(./yq ea -p=xml --xml-strict-mode test.yml -o=xml 2>&1)
|
||||
assertEquals "Error: bad file 'test.yml': XML syntax error on line 7: invalid character entity &writer;" "$X"
|
||||
}
|
||||
|
||||
testInputXmlGithubAction() {
|
||||
cat >test.yml <<EOL
|
||||
<cat legs="4">BiBi</cat>
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
cat:
|
||||
+content: BiBi
|
||||
+@legs: "4"
|
||||
EOM
|
||||
|
||||
X=$(cat /dev/null | ./yq e -p=xml test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(cat /dev/null | ./yq ea -p=xml test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
# examples where header-preprocess is required
|
||||
|
||||
setUp() {
|
||||
rm test*.yml || true
|
||||
cat >test.yml <<EOL
|
||||
@@ -8,7 +11,7 @@ a: test
|
||||
EOL
|
||||
}
|
||||
|
||||
testLeadingSeperatorWithDoc() {
|
||||
testLeadingSeparatorWithDoc() {
|
||||
cat >test.yml <<EOL
|
||||
# hi peeps
|
||||
# cool
|
||||
@@ -31,18 +34,94 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorPipeIntoEvalSeq() {
|
||||
|
||||
testLeadingSeparatorWithNewlinesNewDoc() {
|
||||
cat >test.yml <<EOL
|
||||
# hi peeps
|
||||
# cool
|
||||
|
||||
|
||||
---
|
||||
a: test
|
||||
---
|
||||
b: cool
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
# hi peeps
|
||||
# cool
|
||||
|
||||
|
||||
---
|
||||
a: thing
|
||||
---
|
||||
b: cool
|
||||
EOM
|
||||
|
||||
X=$(./yq e '(select(di == 0) | .a) = "thing"' - < test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeparatorWithNewlinesMoreComments() {
|
||||
cat >test.yml <<EOL
|
||||
# hi peeps
|
||||
# cool
|
||||
|
||||
---
|
||||
# great
|
||||
|
||||
a: test
|
||||
---
|
||||
b: cool
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
# hi peeps
|
||||
# cool
|
||||
|
||||
---
|
||||
# great
|
||||
|
||||
a: thing
|
||||
---
|
||||
b: cool
|
||||
EOM
|
||||
|
||||
X=$(./yq e '(select(di == 0) | .a) = "thing"' - < test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
|
||||
testLeadingSeparatorWithDirective() {
|
||||
cat >test.yml <<EOL
|
||||
%YAML 1.1
|
||||
---
|
||||
this: should really work
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
%YAML 1.1
|
||||
---
|
||||
this: should really work
|
||||
EOM
|
||||
|
||||
X=$(./yq < test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
|
||||
testLeadingSeparatorPipeIntoEvalSeq() {
|
||||
X=$(./yq e - < test.yml)
|
||||
expected=$(cat test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorExtractField() {
|
||||
testLeadingSeparatorExtractField() {
|
||||
X=$(./yq e '.a' - < test.yml)
|
||||
assertEquals "test" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorExtractFieldWithCommentsAfterSep() {
|
||||
testLeadingSeparatorExtractFieldWithCommentsAfterSep() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# hi peeps
|
||||
@@ -53,7 +132,7 @@ EOL
|
||||
assertEquals "test" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorExtractFieldWithCommentsBeforeSep() {
|
||||
testLeadingSeparatorExtractFieldWithCommentsBeforeSep() {
|
||||
cat >test.yml <<EOL
|
||||
# hi peeps
|
||||
# cool
|
||||
@@ -65,7 +144,7 @@ EOL
|
||||
}
|
||||
|
||||
|
||||
testLeadingSeperatorExtractFieldMultiDoc() {
|
||||
testLeadingSeparatorExtractFieldMultiDoc() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
a: test
|
||||
@@ -82,13 +161,13 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorExtractFieldMultiDocWithComments() {
|
||||
testLeadingSeparatorExtractFieldMultiDocWithComments() {
|
||||
cat >test.yml <<EOL
|
||||
# here
|
||||
---
|
||||
# there
|
||||
a: test
|
||||
# whereever
|
||||
# wherever
|
||||
---
|
||||
# you are
|
||||
a: test2
|
||||
@@ -105,26 +184,26 @@ EOM
|
||||
}
|
||||
|
||||
|
||||
testLeadingSeperatorEvalSeq() {
|
||||
testLeadingSeparatorEvalSeq() {
|
||||
X=$(./yq e test.yml)
|
||||
expected=$(cat test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorPipeIntoEvalAll() {
|
||||
testLeadingSeparatorPipeIntoEvalAll() {
|
||||
X=$(./yq ea - < test.yml)
|
||||
expected=$(cat test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
|
||||
testLeadingSeperatorEvalAll() {
|
||||
testLeadingSeparatorEvalAll() {
|
||||
X=$(./yq ea test.yml)
|
||||
expected=$(cat test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalSimple() {
|
||||
testLeadingSeparatorMultiDocEvalSimple() {
|
||||
read -r -d '' expected << EOM
|
||||
---
|
||||
a: test
|
||||
@@ -138,7 +217,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocInOneFile() {
|
||||
testLeadingSeparatorMultiDocInOneFile() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# hi peeps
|
||||
@@ -152,7 +231,7 @@ EOL
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocInOneFileEvalAll() {
|
||||
testLeadingSeparatorMultiDocInOneFileEvalAll() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# hi peeps
|
||||
@@ -166,7 +245,7 @@ EOL
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalComments() {
|
||||
testLeadingSeparatorMultiDocEvalComments() {
|
||||
cat >test.yml <<EOL
|
||||
# hi peeps
|
||||
# cool
|
||||
@@ -194,7 +273,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalCommentsTrailingSep() {
|
||||
testLeadingSeparatorMultiDocEvalCommentsTrailingSep() {
|
||||
cat >test.yml <<EOL
|
||||
# hi peeps
|
||||
# cool
|
||||
@@ -226,7 +305,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiMultiDocEvalCommentsTrailingSep() {
|
||||
testLeadingSeparatorMultiMultiDocEvalCommentsTrailingSep() {
|
||||
cat >test.yml <<EOL
|
||||
# hi peeps
|
||||
# cool
|
||||
@@ -266,7 +345,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalCommentsLeadingSep() {
|
||||
testLeadingSeparatorMultiDocEvalCommentsLeadingSep() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# hi peeps
|
||||
@@ -326,7 +405,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalCommentsStripComments() {
|
||||
testLeadingSeparatorMultiDocEvalCommentsStripComments() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# hi peeps
|
||||
@@ -349,7 +428,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalCommentsLeadingSepNoDocFlag() {
|
||||
testLeadingSeparatorMultiDocEvalCommentsLeadingSepNoDocFlag() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# hi peeps
|
||||
@@ -375,7 +454,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalJsonFlag() {
|
||||
testLeadingSeparatorMultiDocEvalJsonFlag() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# hi peeps
|
||||
@@ -404,7 +483,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalAllJsonFlag() {
|
||||
testLeadingSeparatorMultiDocEvalAllJsonFlag() {
|
||||
cat >test.yml <<EOL
|
||||
---
|
||||
# hi peeps
|
||||
@@ -433,7 +512,7 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLeadingSeperatorMultiDocEvalAll() {
|
||||
testLeadingSeparatorMultiDocEvalAll() {
|
||||
read -r -d '' expected << EOM
|
||||
---
|
||||
a: test
|
||||
@@ -3,25 +3,25 @@
|
||||
testLoadFileNotExist() {
|
||||
result=$(./yq e -n 'load("cat.yml")' 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: Failed to load cat.yml: open cat.yml: no such file or directory" "$result"
|
||||
assertEquals "Error: failed to load cat.yml: open cat.yml: no such file or directory" "$result"
|
||||
}
|
||||
|
||||
testLoadFileExpNotExist() {
|
||||
result=$(./yq e -n 'load(.a)' 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: Filename expression returned nil" "$result"
|
||||
assertEquals "Error: filename expression returned nil" "$result"
|
||||
}
|
||||
|
||||
testStrLoadFileNotExist() {
|
||||
result=$(./yq e -n 'strload("cat.yml")' 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: Failed to load cat.yml: open cat.yml: no such file or directory" "$result"
|
||||
assertEquals "Error: failed to load cat.yml: open cat.yml: no such file or directory" "$result"
|
||||
}
|
||||
|
||||
testStrLoadFileExpNotExist() {
|
||||
result=$(./yq e -n 'strload(.a)' 2>&1)
|
||||
assertEquals 1 $?
|
||||
assertEquals "Error: Filename expression returned nil" "$result"
|
||||
assertEquals "Error: filename expression returned nil" "$result"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
Executable
+286
@@ -0,0 +1,286 @@
|
||||
#!/bin/bash
|
||||
|
||||
setUp() {
|
||||
rm test*.yml || true
|
||||
}
|
||||
|
||||
## Convenient bash shortcut to read records of NUL separated values
|
||||
## from stdin the safe way. See example usage in the next tests.
|
||||
read-0() {
|
||||
local eof="" IFS=''
|
||||
while [ "$1" ]; do
|
||||
## - The `-r` avoids bad surprise with '\n' and other interpreted
|
||||
## sequences that can be read.
|
||||
## - The `-d ''` is the (strange?) way to refer to NUL delimiter.
|
||||
## - The `--` is how to avoid unpleasant surprises if your
|
||||
## "$1" starts with "-" (minus) sign. This protection also
|
||||
## will produce a readable error if you want to try to start
|
||||
## your variable names with a "-".
|
||||
read -r -d '' -- "$1" || eof=1
|
||||
shift
|
||||
done
|
||||
[ -z "$eof" ] ## fail on EOF
|
||||
}
|
||||
|
||||
## Convenient bash shortcut to be used with the next function `p-err`
|
||||
## to read NUL separated values the safe way AND catch any errors from
|
||||
## the process creating the stream of NUL separated data. See example
|
||||
## usage in the tests.
|
||||
read-0-err() {
|
||||
local ret="$1" eof="" idx=0 last=
|
||||
read -r -- "${ret?}" <<<"0"
|
||||
shift
|
||||
while [ "$1" ]; do
|
||||
last=$idx
|
||||
read -r -d '' -- "$1" || {
|
||||
## Put this last value in ${!ret}
|
||||
eof="$1"
|
||||
read -r -- "$ret" <<<"${!eof}"
|
||||
break
|
||||
}
|
||||
((idx++))
|
||||
shift
|
||||
done
|
||||
[ -z "$eof" ] || {
|
||||
if [ "$last" != 0 ]; then
|
||||
## Uhoh, we have no idea if the errorlevel of the internal
|
||||
## command was properly delimited with a NUL char, and
|
||||
## anyway something went really wrong at least about the
|
||||
## number of fields separated by NUL char and the one
|
||||
## expected.
|
||||
echo "Error: read-0-err couldn't fill all value $ret = '${!ret}', '$eof', '${!eof}'" >&2
|
||||
read -r -- "$ret" <<<"not-enough-values"
|
||||
else
|
||||
if ! [[ "${!ret}" =~ ^[0-9]+$ && "${!ret}" -ge 0 && "${!ret}" -le 127 ]]; then
|
||||
## This could happen if you don't use `p-err` wrapper,
|
||||
## or used stdout in unexpected ways in your inner
|
||||
## command.
|
||||
echo "Error: last value is not a number, did you finish with an errorlevel ?" >&2
|
||||
read -r -- "$ret" <<<"last-value-not-a-number"
|
||||
fi
|
||||
fi
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
## Simply runs command given as argument and adds errorlevel in the
|
||||
## standard output. Is expected to be used in tandem with
|
||||
## `read-0-err`.
|
||||
p-err() {
|
||||
local exp="$1"
|
||||
"$@"
|
||||
printf "%s" "$?"
|
||||
}
|
||||
|
||||
wyq-r() {
|
||||
local exp="$1"
|
||||
./yq e -0 -r=false "$1"
|
||||
printf "%s" "$?"
|
||||
}
|
||||
|
||||
testBasicUsageRaw() {
|
||||
cat >test.yml <<EOL
|
||||
a: foo
|
||||
b: bar
|
||||
EOL
|
||||
|
||||
printf "foo\0bar\0" > expected.out
|
||||
|
||||
## We need to compare binary content here. We have to filter the compared
|
||||
## content through a representation that gets rid of NUL chars but accurately
|
||||
## transcribe the content.
|
||||
## Also as it would be nice to have a pretty output in case the test fails,
|
||||
## we use here 'hd': a widely available shortcut to 'hexdump' that will
|
||||
## pretty-print any binary to it's hexadecimal representation.
|
||||
##
|
||||
## Note that the standard `assertEquals` compare its arguments
|
||||
## value, but they can't hold NUL characters (this comes from the
|
||||
## limitation of the C API of `exec*(..)` functions that requires
|
||||
## `const char *arv[]`). And these are NUL terminated strings. As a
|
||||
## consequence, the NUL characters gets removed in bash arguments.
|
||||
assertEquals "$(hd expected.out)" \
|
||||
"$(./yq e -0 '.a, .b' test.yml | hd)"
|
||||
|
||||
rm expected.out
|
||||
}
|
||||
|
||||
testBasicUsage() {
|
||||
local a b
|
||||
cat >test.yml <<EOL
|
||||
a: foo
|
||||
b: bar
|
||||
EOL
|
||||
|
||||
## We provide 2 values, and ask to fill 2 variables.
|
||||
read-0 a b < <(./yq e -0 '.a, .b' test.yml)
|
||||
assertEquals "$?" "0" ## Everything is fine
|
||||
assertEquals "foo" "$a" ## Values are correctly parsed
|
||||
assertEquals "bar" "$b"
|
||||
|
||||
a=YYY ; b=XXX
|
||||
## Not enough values provided to fill `a` and `b`.
|
||||
read-0 a b < <(./yq e -0 '.a' test.yml)
|
||||
assertEquals "$?" "1" ## An error was emitted
|
||||
assertEquals "foo" "$a" ## First value was correctly parsed
|
||||
assertEquals "" "$b" ## Second was still reset
|
||||
|
||||
## Error from inner command are not catchable !. Use
|
||||
## `read-0-err`/`p-err` for that.
|
||||
read-0 a < <(printf "\0"; ./yq e -0 'xxx' test.yml; )
|
||||
assertEquals "$?" "0"
|
||||
|
||||
}
|
||||
|
||||
testBasicUsageJson() {
|
||||
cat >test.yml <<EOL
|
||||
a:
|
||||
x: foo
|
||||
b: bar
|
||||
EOL
|
||||
|
||||
read-0 a b < <(./yq e -0 -o=json '.a, .b' test.yml)
|
||||
|
||||
assertEquals '{
|
||||
"x": "foo"
|
||||
}' "$a"
|
||||
assertEquals '"bar"' "$b"
|
||||
|
||||
}
|
||||
|
||||
testFailWithValueContainingNUL() {
|
||||
local a b c
|
||||
## Note that value of field 'a' actually contains a NUL char !
|
||||
cat >test.yml <<EOL
|
||||
a: "foo\u0000bar"
|
||||
b: 1
|
||||
c: |
|
||||
wiz
|
||||
boom
|
||||
EOL
|
||||
|
||||
## We are looking for trouble with asking to separated fields with NUL
|
||||
## char and requested value `.a` actually contains itself a NUL char !
|
||||
read-0 a b c < <(./yq e -0 '.a, .b, .c' test.yml)
|
||||
assertNotEquals "0" "$?" ## read-0 failed to fill all values
|
||||
|
||||
## But here, we can request for one value, even if `./yq` fails
|
||||
read-0 b < <(./yq e -0 '.b, .a' test.yml)
|
||||
assertEquals "0" "$?" ## read-0 succeeds at feeding the first value
|
||||
## Note: to catch the failure of `yq`, see in the next tests the usage
|
||||
## of `read-0-err`.
|
||||
|
||||
## using -r=false solves any NUL containing value issues, but keeps
|
||||
## all in YAML representation:
|
||||
read-0 a b c < <(./yq e -0 -r=false '.a, .b, .c' test.yml)
|
||||
assertEquals "0" "$?" ## All goes well despite asking for `a` value
|
||||
|
||||
assertEquals '"foo\0bar"' "$a" ## This is a YAML string representation
|
||||
assertEquals '1' "$b"
|
||||
assertEquals '|
|
||||
wiz
|
||||
boom' "$c"
|
||||
}
|
||||
|
||||
testStandardLoop() {
|
||||
local E a b res
|
||||
|
||||
## Here everything is normal: 4 values, that will be paired
|
||||
## in key/values.
|
||||
cat >test.yml <<EOL
|
||||
- yay
|
||||
- wiz
|
||||
- hop
|
||||
- pow
|
||||
EOL
|
||||
|
||||
res=""
|
||||
while read-0-err E a b; do
|
||||
res+="$a: $b;"
|
||||
done < <(p-err ./yq -0 '.[]' test.yml)
|
||||
|
||||
assertEquals "0" "$E" ## errorlevel of internal command
|
||||
assertEquals "yay: wiz;hop: pow;" "$res" ## expected result
|
||||
}
|
||||
|
||||
testStandardLoopWithoutEnoughValues() {
|
||||
local E a b res
|
||||
|
||||
## Here 5 values, there will be a missing value when reading
|
||||
## pairs of value.
|
||||
cat >test.yml <<EOL
|
||||
- yay
|
||||
- wiz
|
||||
- hop
|
||||
- pow
|
||||
- kwak
|
||||
EOL
|
||||
|
||||
res=""
|
||||
## The loop will succeed 2 times then fail
|
||||
while read-0-err E a b; do
|
||||
res+="$a: $b;"
|
||||
done < <(p-err ./yq -0 '.[]' test.yml)
|
||||
|
||||
assertEquals "not-enough-values" "$E" ## Not enough value error
|
||||
assertEquals "yay: wiz;hop: pow;" "$res" ## the 2 full key/value pairs
|
||||
|
||||
}
|
||||
|
||||
testStandardLoopWithInternalCmdError() {
|
||||
local E a b res
|
||||
|
||||
## Note the third value contains a NUL char !
|
||||
cat >test.yml <<EOL
|
||||
- yay
|
||||
- wiz
|
||||
- "foo\0bar"
|
||||
- hop
|
||||
- pow
|
||||
EOL
|
||||
|
||||
res=""
|
||||
## It should be only upon the second pass in the loop that
|
||||
## read-0-err will catch the fact that there is an error !
|
||||
while read-0-err E a b; do
|
||||
res+="$a: $b;"
|
||||
done < <(p-err ./yq -0 '.[]' test.yml)
|
||||
assertEquals "1" "$E" ## Internal command errorlevel (from `./yq`)
|
||||
assertEquals "yay: wiz;" "$res" ## first 2 values were ok at least
|
||||
|
||||
}
|
||||
|
||||
testStandardLoopNotEnoughErrorEatsCmdError() {
|
||||
local E a b res
|
||||
|
||||
## Because of possible edge cases where the internal errorlevel
|
||||
## reported by `p-err` in the standard output might be mangled
|
||||
## with the unfinished record, `read-0-err E ...` will NOT report
|
||||
## the internal command error in the variable E and instead will
|
||||
## store the value 'not-enough-values'. In real world, anyway, you
|
||||
## will want to react the same if the internal command failed
|
||||
## and/or you didn't get as much values as expected while
|
||||
## reading. Keep in mind also that standard error is not
|
||||
## swallowed, so you can read reports from the inner command AND
|
||||
## from `read-0-err`.
|
||||
|
||||
## Here, note that the fourth value contains a NUL char !
|
||||
cat >test.yml <<EOL
|
||||
- yay
|
||||
- wiz
|
||||
- hop
|
||||
- "foo\0bar"
|
||||
- pow
|
||||
EOL
|
||||
|
||||
res=""
|
||||
## It should be only upon the second loop that read-0-err will catch
|
||||
## the fact that there are not enough data to fill the requested variables
|
||||
while read-0-err E a b; do
|
||||
res+="$a: $b;"
|
||||
done < <(p-err ./yq -0 '.[]' test.yml)
|
||||
assertEquals "not-enough-values" "$E" ## Not enough values error eats internal error !
|
||||
assertEquals "yay: wiz;" "$res" ## first 2 values were ok at least
|
||||
}
|
||||
|
||||
|
||||
source ./scripts/shunit2
|
||||
@@ -48,6 +48,64 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testOutputYamlRawDefault() {
|
||||
cat >test.yml <<EOL
|
||||
a: "cat"
|
||||
EOL
|
||||
|
||||
X=$(./yq e '.a' test.yml)
|
||||
assertEquals "cat" "$X"
|
||||
|
||||
X=$(./yq ea '.a' test.yml)
|
||||
assertEquals "cat" "$X"
|
||||
}
|
||||
|
||||
testOutputYamlRawOff() {
|
||||
cat >test.yml <<EOL
|
||||
a: "cat"
|
||||
EOL
|
||||
|
||||
X=$(./yq e -r=false '.a' test.yml)
|
||||
assertEquals "\"cat\"" "$X"
|
||||
|
||||
X=$(./yq ea -r=false '.a' test.yml)
|
||||
assertEquals "\"cat\"" "$X"
|
||||
}
|
||||
|
||||
testOutputYamlRawOnRoot() {
|
||||
cat >test.yml <<EOL
|
||||
'a'
|
||||
EOL
|
||||
|
||||
X=$(./yq e -r '.' test.yml)
|
||||
assertEquals "a" "$X"
|
||||
}
|
||||
|
||||
testOutputJsonRaw() {
|
||||
cat >test.yml <<EOL
|
||||
a: cat
|
||||
EOL
|
||||
|
||||
X=$(./yq e -r --output-format=json '.a' test.yml)
|
||||
assertEquals "cat" "$X"
|
||||
|
||||
X=$(./yq ea -r --output-format=json '.a' test.yml)
|
||||
assertEquals "cat" "$X"
|
||||
}
|
||||
|
||||
testOutputJsonDefault() {
|
||||
cat >test.yml <<EOL
|
||||
a: cat
|
||||
EOL
|
||||
|
||||
X=$(./yq e --output-format=json '.a' test.yml)
|
||||
assertEquals "\"cat\"" "$X"
|
||||
|
||||
X=$(./yq ea --output-format=json '.a' test.yml)
|
||||
assertEquals "\"cat\"" "$X"
|
||||
}
|
||||
|
||||
|
||||
testOutputJsonShort() {
|
||||
cat >test.yml <<EOL
|
||||
a: {b: ["cat"]}
|
||||
@@ -72,11 +130,11 @@ EOM
|
||||
|
||||
testOutputProperties() {
|
||||
cat >test.yml <<EOL
|
||||
a: {b: {c: ["cat"]}}
|
||||
a: {b: {c: ["cat cat"]}}
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
a.b.c.0 = cat
|
||||
a.b.c.0 = cat cat
|
||||
EOM
|
||||
|
||||
X=$(./yq e --output-format=props test.yml)
|
||||
@@ -86,13 +144,30 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testOutputPropertiesShort() {
|
||||
testOutputPropertiesDontUnwrap() {
|
||||
cat >test.yml <<EOL
|
||||
a: {b: {c: ["cat"]}}
|
||||
a: {b: {c: ["cat cat"]}}
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
a.b.c.0 = cat
|
||||
a.b.c.0 = "cat cat"
|
||||
EOM
|
||||
|
||||
X=$(./yq e -r=false --output-format=props test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -r=false --output-format=props test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
|
||||
testOutputPropertiesShort() {
|
||||
cat >test.yml <<EOL
|
||||
a: {b: {c: ["cat cat"]}}
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
a.b.c.0 = cat cat
|
||||
EOM
|
||||
|
||||
X=$(./yq e -o=p test.yml)
|
||||
@@ -102,6 +177,69 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testOutputCSV() {
|
||||
cat >test.yml <<EOL
|
||||
- fruit: apple
|
||||
yumLevel: 5
|
||||
- fruit: banana
|
||||
yumLevel: 4
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
fruit,yumLevel
|
||||
apple,5
|
||||
banana,4
|
||||
EOM
|
||||
|
||||
X=$(./yq -o=c test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -o=csv test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testOutputCSVCustomSeparator() {
|
||||
cat >test.yml <<EOL
|
||||
- fruit: apple
|
||||
yumLevel: 5
|
||||
- fruit: banana
|
||||
yumLevel: 4
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
fruit;yumLevel
|
||||
apple;5
|
||||
banana;4
|
||||
EOM
|
||||
|
||||
X=$(./yq -oc --csv-separator ";" test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -o=csv --csv-separator ";" test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testOutputTSV() {
|
||||
cat >test.yml <<EOL
|
||||
- fruit: apple
|
||||
yumLevel: 5
|
||||
- fruit: banana
|
||||
yumLevel: 4
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
fruit yumLevel
|
||||
apple 5
|
||||
banana 4
|
||||
EOM
|
||||
|
||||
X=$(./yq -o=t test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea -o=tsv test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testOutputXml() {
|
||||
cat >test.yml <<EOL
|
||||
a: {b: {c: ["cat"]}}
|
||||
@@ -142,4 +280,64 @@ EOM
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
testOutputXmComplex() {
|
||||
cat >test.yml <<EOL
|
||||
a: {b: {c: ["cat", "dog"], +@f: meow}}
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
<a>
|
||||
<b f="meow">
|
||||
<c>cat</c>
|
||||
<c>dog</c>
|
||||
</b>
|
||||
</a>
|
||||
EOM
|
||||
|
||||
X=$(./yq e --output-format=x test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq ea --output-format=x test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
testLuaOutputPretty() {
|
||||
cat >test.yml <<EOL
|
||||
animals:
|
||||
cat: meow
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
return {
|
||||
["animals"] = {
|
||||
["cat"] = "meow";
|
||||
};
|
||||
};
|
||||
EOM
|
||||
|
||||
X=$(./yq e --output-format=lua test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
X=$(./yq e --output-format=lua --prettyPrint test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
}
|
||||
|
||||
testLuaOutputSubset() {
|
||||
cat >test.yml <<EOL
|
||||
animals:
|
||||
cat: meow
|
||||
EOL
|
||||
|
||||
read -r -d '' expected << EOM
|
||||
return {
|
||||
["cat"] = "meow";
|
||||
};
|
||||
EOM
|
||||
|
||||
X=$(./yq e --output-format=lua '.animals' test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
setUp() {
|
||||
rm test*.yml || true
|
||||
cat >test.yml <<EOL
|
||||
a: frog
|
||||
EOL
|
||||
}
|
||||
|
||||
testPipeViaCatWithParam() {
|
||||
X=$(cat test.yml | ./yq '.a')
|
||||
assertEquals "frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaCatWithParamEval() {
|
||||
X=$(cat test.yml | ./yq e '.a')
|
||||
assertEquals "frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaCatWithParamEvalAll() {
|
||||
X=$(cat test.yml | ./yq ea '.a')
|
||||
assertEquals "frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaCatNoParam() {
|
||||
X=$(cat test.yml | ./yq)
|
||||
assertEquals "a: frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaCatNoParamEval() {
|
||||
X=$(cat test.yml | ./yq e)
|
||||
assertEquals "a: frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaCatNoParamEvalAll() {
|
||||
X=$(cat test.yml | ./yq ea)
|
||||
assertEquals "a: frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaFileishWithParam() {
|
||||
X=$(./yq '.a' < test.yml)
|
||||
assertEquals "frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaFileishWithParamEval() {
|
||||
X=$(./yq e '.a' < test.yml)
|
||||
assertEquals "frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaFileishWithParamEvalAll() {
|
||||
X=$(./yq ea '.a' < test.yml)
|
||||
assertEquals "frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaFileishNoParam() {
|
||||
X=$(./yq < test.yml)
|
||||
assertEquals "a: frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaFileishNoParamEval() {
|
||||
X=$(./yq e < test.yml)
|
||||
assertEquals "a: frog" "$X"
|
||||
}
|
||||
|
||||
testPipeViaFileishNoParamEvalAll() {
|
||||
X=$(./yq ea < test.yml)
|
||||
assertEquals "a: frog" "$X"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
setUp() {
|
||||
rm test*.yq || true
|
||||
cat >test.yq <<EOL
|
||||
#!./yq
|
||||
.a.b
|
||||
EOL
|
||||
chmod +x test.yq
|
||||
|
||||
rm test*.yml || true
|
||||
cat >test.yml <<EOL
|
||||
a: {b: apple}
|
||||
EOL
|
||||
}
|
||||
|
||||
testCanExecYqFile() {
|
||||
read -r -d '' expected << EOM
|
||||
apple
|
||||
EOM
|
||||
X=$(./test.yq test.yml)
|
||||
assertEquals "$expected" "$X"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
setUp() {
|
||||
rm test*.yml || true
|
||||
rm -rf test_dir* || true
|
||||
}
|
||||
|
||||
testBasicSplitWithName() {
|
||||
@@ -25,6 +26,56 @@ EOM
|
||||
assertEquals "$expectedDoc2" "$doc2"
|
||||
}
|
||||
|
||||
testBasicSplitWithNameCustomExtension() {
|
||||
rm test*.yaml || true
|
||||
cat >test.yml <<EOL
|
||||
a: test_doc1
|
||||
---
|
||||
a: test_doc2
|
||||
EOL
|
||||
|
||||
./yq e test.yml -s '.a + ".yaml"'
|
||||
|
||||
doc1=$(cat test_doc1.yaml)
|
||||
|
||||
assertEquals "a: test_doc1" "$doc1"
|
||||
|
||||
doc2=$(cat test_doc2.yaml)
|
||||
read -r -d '' expectedDoc2 << EOM
|
||||
---
|
||||
a: test_doc2
|
||||
EOM
|
||||
assertEquals "$expectedDoc2" "$doc2"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
testSplitFromFile() {
|
||||
cat >test.yml <<EOL
|
||||
a: test_doc1
|
||||
---
|
||||
a: test_doc2
|
||||
EOL
|
||||
|
||||
cat >test_splitExp.yml <<EOL
|
||||
.a
|
||||
EOL
|
||||
|
||||
./yq test.yml --split-exp-file test_splitExp.yml
|
||||
|
||||
doc1=$(cat test_doc1.yml)
|
||||
|
||||
assertEquals "a: test_doc1" "$doc1"
|
||||
|
||||
doc2=$(cat test_doc2.yml)
|
||||
read -r -d '' expectedDoc2 << EOM
|
||||
---
|
||||
a: test_doc2
|
||||
EOM
|
||||
assertEquals "$expectedDoc2" "$doc2"
|
||||
}
|
||||
|
||||
testBasicSplitWithNameEvalAll() {
|
||||
cat >test.yml <<EOL
|
||||
a: test_doc1
|
||||
@@ -154,4 +205,23 @@ EOM
|
||||
assertEquals "$expectedDoc3" "$doc3"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
testSplitWithDirectories() {
|
||||
cat >test.yml <<EOL
|
||||
f: test_dir1/test_file1
|
||||
---
|
||||
f: test_dir2/dir22/test_file2
|
||||
---
|
||||
f: test_file3
|
||||
EOL
|
||||
|
||||
./yq e --no-doc -s ".f" test.yml
|
||||
|
||||
doc1=$(cat test_dir1/test_file1.yml)
|
||||
assertEquals "f: test_dir1/test_file1" "$doc1"
|
||||
doc2=$(cat test_dir2/dir22/test_file2.yml)
|
||||
assertEquals "f: test_dir2/dir22/test_file2" "$doc2"
|
||||
doc3=$(cat test_file3.yml)
|
||||
assertEquals "f: test_file3" "$doc3"
|
||||
}
|
||||
|
||||
source ./scripts/shunit2
|
||||
|
||||
+1
-1
@@ -12,6 +12,6 @@ outputs:
|
||||
description: "The complete result from the yq command being run"
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'github-action/Dockerfile'
|
||||
image: 'docker://mikefarah/yq:4-githubaction'
|
||||
args:
|
||||
- ${{ inputs.cmd }}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package cmd
|
||||
|
||||
// import (
|
||||
// "strings"
|
||||
// "testing"
|
||||
|
||||
// "github.com/mikefarah/yq/v3/test"
|
||||
// "github.com/spf13/cobra"
|
||||
// )
|
||||
|
||||
// func getRootCommand() *cobra.Command {
|
||||
// return New()
|
||||
// }
|
||||
|
||||
// func TestRootCmd(t *testing.T) {
|
||||
// cmd := getRootCommand()
|
||||
// result := test.RunCmd(cmd, "")
|
||||
// if result.Error != nil {
|
||||
// t.Error(result.Error)
|
||||
// }
|
||||
|
||||
// if !strings.Contains(result.Output, "Usage:") {
|
||||
// t.Error("Expected usage message to be printed out, but the usage message was not found.")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestRootCmd_Help(t *testing.T) {
|
||||
// cmd := getRootCommand()
|
||||
// result := test.RunCmd(cmd, "--help")
|
||||
// if result.Error != nil {
|
||||
// t.Error(result.Error)
|
||||
// }
|
||||
|
||||
// if !strings.Contains(result.Output, "yq is a lightweight and portable command-line YAML processor. It aims to be the jq or sed of yaml files.") {
|
||||
// t.Error("Expected usage message to be printed out, but the usage message was not found.")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestRootCmd_VerboseLong(t *testing.T) {
|
||||
// cmd := getRootCommand()
|
||||
// result := test.RunCmd(cmd, "--verbose")
|
||||
// if result.Error != nil {
|
||||
// t.Error(result.Error)
|
||||
// }
|
||||
|
||||
// if !verbose {
|
||||
// t.Error("Expected verbose to be true")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestRootCmd_VerboseShort(t *testing.T) {
|
||||
// cmd := getRootCommand()
|
||||
// result := test.RunCmd(cmd, "-v")
|
||||
// if result.Error != nil {
|
||||
// t.Error(result.Error)
|
||||
// }
|
||||
|
||||
// if !verbose {
|
||||
// t.Error("Expected verbose to be true")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestRootCmd_VersionShort(t *testing.T) {
|
||||
// cmd := getRootCommand()
|
||||
// result := test.RunCmd(cmd, "-V")
|
||||
// if result.Error != nil {
|
||||
// t.Error(result.Error)
|
||||
// }
|
||||
// if !strings.Contains(result.Output, "yq version") {
|
||||
// t.Error("expected version message to be printed out, but the message was not found.")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestRootCmd_VersionLong(t *testing.T) {
|
||||
// cmd := getRootCommand()
|
||||
// result := test.RunCmd(cmd, "--version")
|
||||
// if result.Error != nil {
|
||||
// t.Error(result.Error)
|
||||
// }
|
||||
// if !strings.Contains(result.Output, "yq version") {
|
||||
// t.Error("expected version message to be printed out, but the message was not found.")
|
||||
// }
|
||||
// }
|
||||
@@ -7,19 +7,20 @@ import (
|
||||
)
|
||||
|
||||
var completionCmd = &cobra.Command{
|
||||
Use: "shell-completion [bash|zsh|fish|powershell]",
|
||||
Short: "Generate completion script",
|
||||
Use: "completion [bash|zsh|fish|powershell]",
|
||||
Aliases: []string{"shell-completion"},
|
||||
Short: "Generate the autocompletion script for the specified shell",
|
||||
Long: `To load completions:
|
||||
|
||||
Bash:
|
||||
|
||||
$ source <(yq shell-completion bash)
|
||||
$ source <(yq completion bash)
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
Linux:
|
||||
$ yq shell-completion bash > /etc/bash_completion.d/yq
|
||||
$ yq completion bash > /etc/bash_completion.d/yq
|
||||
MacOS:
|
||||
$ yq shell-completion bash > /usr/local/etc/bash_completion.d/yq
|
||||
$ yq completion bash > /usr/local/etc/bash_completion.d/yq
|
||||
|
||||
Zsh:
|
||||
|
||||
@@ -29,25 +30,25 @@ Zsh:
|
||||
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
$ yq shell-completion zsh > "${fpath[1]}/_yq"
|
||||
$ yq completion zsh > "${fpath[1]}/_yq"
|
||||
|
||||
# You will need to start a new shell for this setup to take effect.
|
||||
|
||||
Fish:
|
||||
|
||||
$ yq shell-completion fish | source
|
||||
$ yq completion fish | source
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
$ yq shell-completion fish > ~/.config/fish/completions/yq.fish
|
||||
$ yq completion fish > ~/.config/fish/completions/yq.fish
|
||||
`,
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var err error = nil
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
err = cmd.Root().GenBashCompletion(os.Stdout)
|
||||
err = cmd.Root().GenBashCompletionV2(os.Stdout, true)
|
||||
case "zsh":
|
||||
err = cmd.Root().GenZshCompletion(os.Stdout)
|
||||
case "fish":
|
||||
+18
-9
@@ -1,30 +1,39 @@
|
||||
package cmd
|
||||
|
||||
var leadingContentPreProcessing = true
|
||||
var unwrapScalar = true
|
||||
var unwrapScalarFlag = newUnwrapFlag()
|
||||
|
||||
var printNodeInfo = false
|
||||
|
||||
var unwrapScalar = false
|
||||
|
||||
var writeInplace = false
|
||||
var outputToJSON = false
|
||||
var outputFormat = "yaml"
|
||||
var inputFormat = "yaml"
|
||||
|
||||
var xmlAttributePrefix = "+"
|
||||
var xmlContentName = "+content"
|
||||
var outputFormat = ""
|
||||
|
||||
var inputFormat = ""
|
||||
|
||||
var exitStatus = false
|
||||
var forceColor = false
|
||||
var forceNoColor = false
|
||||
var colorsEnabled = false
|
||||
var indent = 2
|
||||
var noDocSeparators = false
|
||||
var nullInput = false
|
||||
var nulSepOutput = false
|
||||
var verbose = false
|
||||
var version = false
|
||||
var prettyPrint = false
|
||||
|
||||
var forceColor = false
|
||||
var forceNoColor = false
|
||||
var colorsEnabled = false
|
||||
|
||||
// can be either "" (off), "extract" or "process"
|
||||
var frontMatter = ""
|
||||
|
||||
var splitFileExp = ""
|
||||
var splitFileExpFile = ""
|
||||
|
||||
var completedSuccessfully = false
|
||||
|
||||
var forceExpression = ""
|
||||
|
||||
var expressionFile = ""
|
||||
|
||||
+30
-23
@@ -2,7 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/mikefarah/yq/v4/pkg/yqlib"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -13,8 +12,14 @@ func createEvaluateAllCommand() *cobra.Command {
|
||||
Use: "eval-all [expression] [yaml_file1]...",
|
||||
Aliases: []string{"ea"},
|
||||
Short: "Loads _all_ yaml documents of _all_ yaml files and runs expression once",
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
},
|
||||
Example: `
|
||||
# Merge f2.yml into f1.yml (inplace)
|
||||
# Merge f2.yml into f1.yml (in place)
|
||||
yq eval-all --inplace 'select(fileIndex == 0) * select(fileIndex == 1)' f1.yml f2.yml
|
||||
## the same command and expression using shortened names:
|
||||
yq ea -i 'select(fi == 0) * select(fi == 1)' f1.yml f2.yml
|
||||
@@ -27,7 +32,7 @@ yq ea '. as $item ireduce ({}; . * $item )' file1.yml file2.yml ...
|
||||
## use '-' as a filename to pipe from STDIN
|
||||
cat file2.yml | yq ea '.a.b' file1.yml - file3.yml
|
||||
`,
|
||||
Long: `yq is a portable command-line YAML processor (https://github.com/mikefarah/yq/)
|
||||
Long: `yq is a portable command-line data file processor (https://github.com/mikefarah/yq/)
|
||||
See https://mikefarah.gitbook.io/yq/ for detailed documentation and examples.
|
||||
|
||||
## Evaluate All ##
|
||||
@@ -47,20 +52,17 @@ func evaluateAll(cmd *cobra.Command, args []string) (cmdError error) {
|
||||
|
||||
var err error
|
||||
|
||||
firstFileIndex, err := initCommand(cmd, args)
|
||||
expression, args, err := initCommand(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, _ := os.Stdin.Stat()
|
||||
pipingStdIn := (stat.Mode() & os.ModeCharDevice) == 0
|
||||
|
||||
out := cmd.OutOrStdout()
|
||||
|
||||
if writeInplace {
|
||||
// only use colors if its forced
|
||||
colorsEnabled = forceColor
|
||||
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[firstFileIndex])
|
||||
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[0])
|
||||
out, err = writeInPlaceHandler.CreateTempFile()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -74,27 +76,37 @@ func evaluateAll(cmd *cobra.Command, args []string) (cmdError error) {
|
||||
}()
|
||||
}
|
||||
|
||||
format, err := yqlib.OutputFormatFromString(outputFormat)
|
||||
format, err := yqlib.FormatFromString(outputFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decoder, err := configureDecoder()
|
||||
decoder, err := configureDecoder(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printerWriter := configurePrinterWriter(format, out)
|
||||
printerWriter, err := configurePrinterWriter(format, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encoder, err := configureEncoder()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printer := yqlib.NewPrinter(printerWriter, format, unwrapScalar, colorsEnabled, indent, !noDocSeparators)
|
||||
printer := yqlib.NewPrinter(encoder, printerWriter)
|
||||
if nulSepOutput {
|
||||
printer.SetNulSepOutput(true)
|
||||
}
|
||||
|
||||
if frontMatter != "" {
|
||||
frontMatterHandler := yqlib.NewFrontMatterHandler(args[firstFileIndex])
|
||||
frontMatterHandler := yqlib.NewFrontMatterHandler(args[0])
|
||||
err = frontMatterHandler.Split()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args[firstFileIndex] = frontMatterHandler.GetYamlFrontMatterFilename()
|
||||
args[0] = frontMatterHandler.GetYamlFrontMatterFilename()
|
||||
|
||||
if frontMatter == "process" {
|
||||
reader := frontMatterHandler.GetContentReader()
|
||||
@@ -105,22 +117,17 @@ func evaluateAll(cmd *cobra.Command, args []string) (cmdError error) {
|
||||
}
|
||||
|
||||
allAtOnceEvaluator := yqlib.NewAllAtOnceEvaluator()
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
if pipingStdIn {
|
||||
err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer, leadingContentPreProcessing, decoder)
|
||||
if nullInput {
|
||||
err = yqlib.NewStreamEvaluator().EvaluateNew(processExpression(expression), printer)
|
||||
} else {
|
||||
cmd.Println(cmd.UsageString())
|
||||
return nil
|
||||
}
|
||||
case 1:
|
||||
if nullInput {
|
||||
err = yqlib.NewStreamEvaluator().EvaluateNew(processExpression(args[0]), printer, "")
|
||||
} else {
|
||||
err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer, leadingContentPreProcessing, decoder)
|
||||
}
|
||||
default:
|
||||
err = allAtOnceEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer, leadingContentPreProcessing, decoder)
|
||||
err = allAtOnceEvaluator.EvaluateFiles(processExpression(expression), args, printer, decoder)
|
||||
}
|
||||
|
||||
completedSuccessfully = err == nil
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateEvaluateAllCommand(t *testing.T) {
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
if cmd == nil {
|
||||
t.Fatal("createEvaluateAllCommand returned nil")
|
||||
}
|
||||
|
||||
// Test basic command properties
|
||||
if cmd.Use != "eval-all [expression] [yaml_file1]..." {
|
||||
t.Errorf("Expected Use to be 'eval-all [expression] [yaml_file1]...', got %q", cmd.Use)
|
||||
}
|
||||
|
||||
if cmd.Short == "" {
|
||||
t.Error("Expected Short description to be non-empty")
|
||||
}
|
||||
|
||||
if cmd.Long == "" {
|
||||
t.Error("Expected Long description to be non-empty")
|
||||
}
|
||||
|
||||
// Test aliases
|
||||
expectedAliases := []string{"ea"}
|
||||
if len(cmd.Aliases) != len(expectedAliases) {
|
||||
t.Errorf("Expected %d aliases, got %d", len(expectedAliases), len(cmd.Aliases))
|
||||
}
|
||||
|
||||
for i, expected := range expectedAliases {
|
||||
if i >= len(cmd.Aliases) || cmd.Aliases[i] != expected {
|
||||
t.Errorf("Expected alias %d to be %q, got %q", i, expected, cmd.Aliases[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_NoArgs(t *testing.T) {
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with no arguments and no null input
|
||||
nullInput = false
|
||||
defer func() { nullInput = false }()
|
||||
|
||||
err := evaluateAll(cmd, []string{})
|
||||
|
||||
// Should not error, but should print usage
|
||||
if err != nil {
|
||||
t.Errorf("evaluateAll with no args should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have printed usage information
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected usage information to be printed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_NullInput(t *testing.T) {
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with null input
|
||||
nullInput = true
|
||||
defer func() { nullInput = false }()
|
||||
|
||||
err := evaluateAll(cmd, []string{})
|
||||
|
||||
// Should not error when using null input
|
||||
if err != nil {
|
||||
t.Errorf("evaluateAll with null input should not error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_WithSingleFile(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with a single file
|
||||
err = evaluateAll(cmd, []string{yamlFile})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateAll with single file should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have some output
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected output from evaluateAll with single file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_WithMultipleFiles(t *testing.T) {
|
||||
// Create temporary YAML files
|
||||
tempDir := t.TempDir()
|
||||
|
||||
yamlFile1 := filepath.Join(tempDir, "test1.yaml")
|
||||
yamlContent1 := []byte("name: test1\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile1, yamlContent1, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file 1: %v", err)
|
||||
}
|
||||
|
||||
yamlFile2 := filepath.Join(tempDir, "test2.yaml")
|
||||
yamlContent2 := []byte("name: test2\nage: 30\n")
|
||||
err = os.WriteFile(yamlFile2, yamlContent2, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file 2: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with multiple files
|
||||
err = evaluateAll(cmd, []string{yamlFile1, yamlFile2})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateAll with multiple files should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have output
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected output from evaluateAll with multiple files")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_WithExpression(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with expression
|
||||
err = evaluateAll(cmd, []string{".name", yamlFile})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateAll with expression should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have output
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected output from evaluateAll with expression")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_WriteInPlace(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Enable write in place
|
||||
originalWriteInplace := writeInplace
|
||||
writeInplace = true
|
||||
defer func() { writeInplace = originalWriteInplace }()
|
||||
|
||||
// Test with write in place
|
||||
err = evaluateAll(cmd, []string{".name = \"updated\"", yamlFile})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateAll with write in place should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Verify the file was updated
|
||||
updatedContent, err := os.ReadFile(yamlFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read updated file: %v", err)
|
||||
}
|
||||
|
||||
// Should contain the updated content
|
||||
if !strings.Contains(string(updatedContent), "updated") {
|
||||
t.Errorf("Expected file to contain 'updated', got: %s", string(updatedContent))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_ExitStatus(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Enable exit status
|
||||
originalExitStatus := exitStatus
|
||||
exitStatus = true
|
||||
defer func() { exitStatus = originalExitStatus }()
|
||||
|
||||
// Test with expression that should find no matches
|
||||
err = evaluateAll(cmd, []string{".nonexistent", yamlFile})
|
||||
|
||||
// Should error when no matches found and exit status is enabled
|
||||
if err == nil {
|
||||
t.Error("Expected error when no matches found and exit status is enabled")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_WithMultipleDocuments(t *testing.T) {
|
||||
// Create a temporary YAML file with multiple documents
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("---\nname: doc1\nage: 25\n---\nname: doc2\nage: 30\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with multiple documents
|
||||
err = evaluateAll(cmd, []string{".", yamlFile})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateAll with multiple documents should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have output
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected output from evaluateAll with multiple documents")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateAll_NulSepOutput(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateAllCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Enable nul separator output
|
||||
originalNulSepOutput := nulSepOutput
|
||||
nulSepOutput = true
|
||||
defer func() { nulSepOutput = originalNulSepOutput }()
|
||||
|
||||
// Test with nul separator output
|
||||
err = evaluateAll(cmd, []string{".name", yamlFile})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateAll with nul separator output should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have output
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected output from evaluateAll with nul separator output")
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package cmd
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/mikefarah/yq/v4/pkg/yqlib"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -13,7 +12,13 @@ func createEvaluateSequenceCommand() *cobra.Command {
|
||||
var cmdEvalSequence = &cobra.Command{
|
||||
Use: "eval [expression] [yaml_file1]...",
|
||||
Aliases: []string{"e"},
|
||||
Short: "Apply the expression to each document in each yaml file in sequence",
|
||||
Short: "(default) Apply the expression to each document in each yaml file in sequence",
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
},
|
||||
Example: `
|
||||
# Reads field under the given path for each file
|
||||
yq e '.a.b' f1.yml f2.yml
|
||||
@@ -29,10 +34,10 @@ cat file2.yml | yq e '.a.b' file1.yml - file3.yml
|
||||
## Note that editing an empty file does not work.
|
||||
yq e -n '.a.b.c = "cat"'
|
||||
|
||||
# Update a file inplace
|
||||
# Update a file in place
|
||||
yq e '.a.b = "cool"' -i file.yaml
|
||||
`,
|
||||
Long: `yq is a portable command-line YAML processor (https://github.com/mikefarah/yq/)
|
||||
Long: `yq is a portable command-line data file processor (https://github.com/mikefarah/yq/)
|
||||
See https://mikefarah.gitbook.io/yq/ for detailed documentation and examples.
|
||||
|
||||
## Evaluate Sequence ##
|
||||
@@ -44,11 +49,11 @@ expression and prints the result in sequence.`,
|
||||
}
|
||||
|
||||
func processExpression(expression string) string {
|
||||
var prettyPrintExp = `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`
|
||||
|
||||
if prettyPrint && expression == "" {
|
||||
return prettyPrintExp
|
||||
return yqlib.PrettyPrintExp
|
||||
} else if prettyPrint {
|
||||
return fmt.Sprintf("%v | %v", expression, prettyPrintExp)
|
||||
return fmt.Sprintf("%v | %v", expression, yqlib.PrettyPrintExp)
|
||||
}
|
||||
return expression
|
||||
}
|
||||
@@ -59,21 +64,19 @@ func evaluateSequence(cmd *cobra.Command, args []string) (cmdError error) {
|
||||
// 1 arg, read file in sequence
|
||||
// 2+ args, [0] = expression, file the rest
|
||||
|
||||
out := cmd.OutOrStdout()
|
||||
|
||||
var err error
|
||||
firstFileIndex, err := initCommand(cmd, args)
|
||||
|
||||
expression, args, err := initCommand(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, _ := os.Stdin.Stat()
|
||||
pipingStdIn := (stat.Mode() & os.ModeCharDevice) == 0
|
||||
|
||||
out := cmd.OutOrStdout()
|
||||
|
||||
if writeInplace {
|
||||
// only use colors if its forced
|
||||
colorsEnabled = forceColor
|
||||
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[firstFileIndex])
|
||||
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[0])
|
||||
out, err = writeInPlaceHandler.CreateTempFile()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -87,29 +90,44 @@ func evaluateSequence(cmd *cobra.Command, args []string) (cmdError error) {
|
||||
}()
|
||||
}
|
||||
|
||||
format, err := yqlib.OutputFormatFromString(outputFormat)
|
||||
format, err := yqlib.FormatFromString(outputFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printerWriter := configurePrinterWriter(format, out)
|
||||
|
||||
printer := yqlib.NewPrinter(printerWriter, format, unwrapScalar, colorsEnabled, indent, !noDocSeparators)
|
||||
|
||||
decoder, err := configureDecoder()
|
||||
printerWriter, err := configurePrinterWriter(format, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encoder, err := configureEncoder()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printer := yqlib.NewPrinter(encoder, printerWriter)
|
||||
|
||||
if printNodeInfo {
|
||||
printer = yqlib.NewNodeInfoPrinter(printerWriter)
|
||||
}
|
||||
|
||||
if nulSepOutput {
|
||||
printer.SetNulSepOutput(true)
|
||||
}
|
||||
|
||||
decoder, err := configureDecoder(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
streamEvaluator := yqlib.NewStreamEvaluator()
|
||||
|
||||
if frontMatter != "" {
|
||||
frontMatterHandler := yqlib.NewFrontMatterHandler(args[firstFileIndex])
|
||||
yqlib.GetLogger().Debug("using front matter handler")
|
||||
frontMatterHandler := yqlib.NewFrontMatterHandler(args[0])
|
||||
err = frontMatterHandler.Split()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args[firstFileIndex] = frontMatterHandler.GetYamlFrontMatterFilename()
|
||||
args[0] = frontMatterHandler.GetYamlFrontMatterFilename()
|
||||
|
||||
if frontMatter == "process" {
|
||||
reader := frontMatterHandler.GetContentReader()
|
||||
@@ -121,20 +139,14 @@ func evaluateSequence(cmd *cobra.Command, args []string) (cmdError error) {
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
if pipingStdIn {
|
||||
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer, leadingContentPreProcessing, decoder)
|
||||
if nullInput {
|
||||
err = streamEvaluator.EvaluateNew(processExpression(expression), printer)
|
||||
} else {
|
||||
cmd.Println(cmd.UsageString())
|
||||
return nil
|
||||
}
|
||||
case 1:
|
||||
if nullInput {
|
||||
err = streamEvaluator.EvaluateNew(processExpression(args[0]), printer, "")
|
||||
} else {
|
||||
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer, leadingContentPreProcessing, decoder)
|
||||
}
|
||||
default:
|
||||
err = streamEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer, leadingContentPreProcessing, decoder)
|
||||
err = streamEvaluator.EvaluateFiles(processExpression(expression), args, printer, decoder)
|
||||
}
|
||||
completedSuccessfully = err == nil
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateEvaluateSequenceCommand(t *testing.T) {
|
||||
cmd := createEvaluateSequenceCommand()
|
||||
|
||||
if cmd == nil {
|
||||
t.Fatal("createEvaluateSequenceCommand returned nil")
|
||||
}
|
||||
|
||||
// Test basic command properties
|
||||
if cmd.Use != "eval [expression] [yaml_file1]..." {
|
||||
t.Errorf("Expected Use to be 'eval [expression] [yaml_file1]...', got %q", cmd.Use)
|
||||
}
|
||||
|
||||
if cmd.Short == "" {
|
||||
t.Error("Expected Short description to be non-empty")
|
||||
}
|
||||
|
||||
if cmd.Long == "" {
|
||||
t.Error("Expected Long description to be non-empty")
|
||||
}
|
||||
|
||||
// Test aliases
|
||||
expectedAliases := []string{"e"}
|
||||
if len(cmd.Aliases) != len(expectedAliases) {
|
||||
t.Errorf("Expected %d aliases, got %d", len(expectedAliases), len(cmd.Aliases))
|
||||
}
|
||||
|
||||
for i, expected := range expectedAliases {
|
||||
if i >= len(cmd.Aliases) || cmd.Aliases[i] != expected {
|
||||
t.Errorf("Expected alias %d to be %q, got %q", i, expected, cmd.Aliases[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessExpression(t *testing.T) {
|
||||
// Reset global variables
|
||||
originalPrettyPrint := prettyPrint
|
||||
defer func() { prettyPrint = originalPrettyPrint }()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
prettyPrint bool
|
||||
expression string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "empty expression without pretty print",
|
||||
prettyPrint: false,
|
||||
expression: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "empty expression with pretty print",
|
||||
prettyPrint: true,
|
||||
expression: "",
|
||||
expected: `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`,
|
||||
},
|
||||
{
|
||||
name: "simple expression without pretty print",
|
||||
prettyPrint: false,
|
||||
expression: ".a.b",
|
||||
expected: ".a.b",
|
||||
},
|
||||
{
|
||||
name: "simple expression with pretty print",
|
||||
prettyPrint: true,
|
||||
expression: ".a.b",
|
||||
expected: `.a.b | (... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`,
|
||||
},
|
||||
{
|
||||
name: "complex expression with pretty print",
|
||||
prettyPrint: true,
|
||||
expression: ".items[] | select(.active == true)",
|
||||
expected: `.items[] | select(.active == true) | (... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
prettyPrint = tt.prettyPrint
|
||||
result := processExpression(tt.expression)
|
||||
if result != tt.expected {
|
||||
t.Errorf("processExpression(%q) = %q, want %q", tt.expression, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateSequence_NoArgs(t *testing.T) {
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateSequenceCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with no arguments and no null input
|
||||
nullInput = false
|
||||
defer func() { nullInput = false }()
|
||||
|
||||
err := evaluateSequence(cmd, []string{})
|
||||
|
||||
// Should not error, but should print usage
|
||||
if err != nil {
|
||||
t.Errorf("evaluateSequence with no args should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have printed usage information
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected usage information to be printed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateSequence_NullInput(t *testing.T) {
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateSequenceCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with null input
|
||||
nullInput = true
|
||||
defer func() { nullInput = false }()
|
||||
|
||||
err := evaluateSequence(cmd, []string{})
|
||||
|
||||
// Should not error when using null input
|
||||
if err != nil {
|
||||
t.Errorf("evaluateSequence with null input should not error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateSequence_WithFile(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateSequenceCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with a file
|
||||
err = evaluateSequence(cmd, []string{yamlFile})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateSequence with file should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have some output
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected output from evaluateSequence with file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateSequence_WithExpressionAndFile(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateSequenceCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Test with expression and file
|
||||
err = evaluateSequence(cmd, []string{".name", yamlFile})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateSequence with expression and file should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Should have output
|
||||
if output.Len() == 0 {
|
||||
t.Error("Expected output from evaluateSequence with expression and file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateSequence_WriteInPlace(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateSequenceCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Enable write in place
|
||||
originalWriteInplace := writeInplace
|
||||
writeInplace = true
|
||||
defer func() { writeInplace = originalWriteInplace }()
|
||||
|
||||
// Test with write in place
|
||||
err = evaluateSequence(cmd, []string{".name = \"updated\"", yamlFile})
|
||||
|
||||
// Should not error
|
||||
if err != nil {
|
||||
t.Errorf("evaluateSequence with write in place should not error, got: %v", err)
|
||||
}
|
||||
|
||||
// Verify the file was updated
|
||||
updatedContent, err := os.ReadFile(yamlFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read updated file: %v", err)
|
||||
}
|
||||
|
||||
// Should contain the updated content
|
||||
if !strings.Contains(string(updatedContent), "updated") {
|
||||
t.Errorf("Expected file to contain 'updated', got: %s", string(updatedContent))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateSequence_ExitStatus(t *testing.T) {
|
||||
// Create a temporary YAML file
|
||||
tempDir := t.TempDir()
|
||||
yamlFile := filepath.Join(tempDir, "test.yaml")
|
||||
yamlContent := []byte("name: test\nage: 25\n")
|
||||
err := os.WriteFile(yamlFile, yamlContent, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test YAML file: %v", err)
|
||||
}
|
||||
|
||||
// Create a temporary command
|
||||
cmd := createEvaluateSequenceCommand()
|
||||
|
||||
// Set up command to capture output
|
||||
var output bytes.Buffer
|
||||
cmd.SetOut(&output)
|
||||
|
||||
// Enable exit status
|
||||
originalExitStatus := exitStatus
|
||||
exitStatus = true
|
||||
defer func() { exitStatus = originalExitStatus }()
|
||||
|
||||
// Test with expression that should find no matches
|
||||
err = evaluateSequence(cmd, []string{".nonexistent", yamlFile})
|
||||
|
||||
// Should error when no matches found and exit status is enabled
|
||||
if err == nil {
|
||||
t.Error("Expected error when no matches found and exit status is enabled")
|
||||
}
|
||||
}
|
||||
+171
-24
@@ -1,49 +1,105 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mikefarah/yq/v4/pkg/yqlib"
|
||||
"github.com/spf13/cobra"
|
||||
logging "gopkg.in/op/go-logging.v1"
|
||||
)
|
||||
|
||||
type runeValue rune
|
||||
|
||||
func newRuneVar(p *rune) *runeValue {
|
||||
return (*runeValue)(p)
|
||||
}
|
||||
|
||||
func (r *runeValue) String() string {
|
||||
return string(*r)
|
||||
}
|
||||
|
||||
func (r *runeValue) Set(rawVal string) error {
|
||||
val := strings.ReplaceAll(rawVal, "\\n", "\n")
|
||||
val = strings.ReplaceAll(val, "\\t", "\t")
|
||||
val = strings.ReplaceAll(val, "\\r", "\r")
|
||||
val = strings.ReplaceAll(val, "\\f", "\f")
|
||||
val = strings.ReplaceAll(val, "\\v", "\v")
|
||||
if len(val) != 1 {
|
||||
return fmt.Errorf("[%v] is not a valid character. Must be length 1 was %v", val, len(val))
|
||||
}
|
||||
*r = runeValue(rune(val[0]))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *runeValue) Type() string {
|
||||
return "char"
|
||||
}
|
||||
|
||||
func New() *cobra.Command {
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "yq",
|
||||
Short: "yq is a lightweight and portable command-line YAML processor.",
|
||||
Long: `yq is a portable command-line YAML processor (https://github.com/mikefarah/yq/)
|
||||
Short: "yq is a lightweight and portable command-line data file processor.",
|
||||
Long: `yq is a portable command-line data file processor (https://github.com/mikefarah/yq/)
|
||||
See https://mikefarah.gitbook.io/yq/ for detailed documentation and examples.`,
|
||||
Example: `
|
||||
# yq tries to auto-detect the file format based off the extension, and defaults to YAML if it's unknown (or piping through STDIN)
|
||||
# Use the '-p/--input-format' flag to specify a format type.
|
||||
cat file.xml | yq -p xml
|
||||
|
||||
# read the "stuff" node from "myfile.yml"
|
||||
yq '.stuff' < myfile.yml
|
||||
|
||||
# update myfile.yml in place
|
||||
yq -i '.stuff = "foo"' myfile.yml
|
||||
|
||||
# print contents of sample.json as idiomatic YAML
|
||||
yq -P -oy sample.json
|
||||
`,
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if version {
|
||||
cmd.Print(GetVersionDisplay())
|
||||
return nil
|
||||
}
|
||||
cmd.Println(cmd.UsageString())
|
||||
return nil
|
||||
return evaluateSequence(cmd, args)
|
||||
|
||||
},
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
cmd.SetOut(cmd.OutOrStdout())
|
||||
var format = logging.MustStringFormatter(
|
||||
`%{color}%{time:15:04:05} %{shortfunc} [%{level:.4s}]%{color:reset} %{message}`,
|
||||
)
|
||||
level := logging.WARNING
|
||||
stringFormat := `[%{level}] %{color}%{time:15:04:05}%{color:reset} %{message}`
|
||||
|
||||
// when NO_COLOR environment variable presents and not an empty string the coloured output should be disabled;
|
||||
// refer to no-color.org
|
||||
forceNoColor = forceNoColor || os.Getenv("NO_COLOR") != ""
|
||||
|
||||
if verbose && forceNoColor {
|
||||
level = logging.DEBUG
|
||||
stringFormat = `[%{level:5.5s}] %{time:15:04:05} %{shortfile:-33s} %{shortfunc:-25s} %{message}`
|
||||
} else if verbose {
|
||||
level = logging.DEBUG
|
||||
stringFormat = `[%{level:5.5s}] %{color}%{time:15:04:05}%{color:bold} %{shortfile:-33s} %{shortfunc:-25s}%{color:reset} %{message}`
|
||||
} else if forceNoColor {
|
||||
stringFormat = `[%{level}] %{time:15:04:05} %{message}`
|
||||
}
|
||||
|
||||
var format = logging.MustStringFormatter(stringFormat)
|
||||
var backend = logging.AddModuleLevel(
|
||||
logging.NewBackendFormatter(logging.NewLogBackend(os.Stderr, "", 0), format))
|
||||
|
||||
if verbose {
|
||||
backend.SetLevel(logging.DEBUG, "")
|
||||
} else {
|
||||
backend.SetLevel(logging.ERROR, "")
|
||||
}
|
||||
backend.SetLevel(level, "")
|
||||
|
||||
logging.SetBackend(backend)
|
||||
yqlib.XmlPreferences.AttributePrefix = xmlAttributePrefix
|
||||
yqlib.XmlPreferences.ContentName = xmlContentName
|
||||
yqlib.InitExpressionParser()
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode")
|
||||
rootCmd.PersistentFlags().BoolVarP(&printNodeInfo, "debug-node-info", "", false, "debug node info")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "(deprecated) output as json. Set indent to 0 to print json in one line.")
|
||||
err := rootCmd.PersistentFlags().MarkDeprecated("tojson", "please use -o=json instead")
|
||||
@@ -51,28 +107,119 @@ See https://mikefarah.gitbook.io/yq/ for detailed documentation and examples.`,
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&outputFormat, "output-format", "o", "yaml", "[yaml|y|json|j|props|p|xml|x] output format type.")
|
||||
rootCmd.PersistentFlags().StringVarP(&inputFormat, "input-format", "p", "yaml", "[yaml|y|xml|x] input format type. Note that json is a subset of yaml.")
|
||||
rootCmd.PersistentFlags().StringVarP(&outputFormat, "output-format", "o", "auto", fmt.Sprintf("[auto|a|%v] output format type.", yqlib.GetAvailableOutputFormatString()))
|
||||
var outputCompletions = []string{"auto"}
|
||||
for _, formats := range yqlib.GetAvailableOutputFormats() {
|
||||
outputCompletions = append(outputCompletions, formats.FormalName)
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&xmlAttributePrefix, "xml-attribute-prefix", "+", "prefix for xml attributes")
|
||||
rootCmd.PersistentFlags().StringVar(&xmlContentName, "xml-content-name", "+content", "name for xml content (if no attribute name is present).")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("output-format", cobra.FixedCompletions(outputCompletions, cobra.ShellCompDirectiveNoFileComp)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().StringVarP(&inputFormat, "input-format", "p", "auto", fmt.Sprintf("[auto|a|%v] parse format for input.", yqlib.GetAvailableInputFormatString()))
|
||||
|
||||
var inputCompletions = []string{"auto"}
|
||||
for _, formats := range yqlib.GetAvailableInputFormats() {
|
||||
inputCompletions = append(inputCompletions, formats.FormalName)
|
||||
}
|
||||
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("input-format", cobra.FixedCompletions(inputCompletions, cobra.ShellCompDirectiveNoFileComp)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredXMLPreferences.AttributePrefix, "xml-attribute-prefix", yqlib.ConfiguredXMLPreferences.AttributePrefix, "prefix for xml attributes")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("xml-attribute-prefix", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredXMLPreferences.ContentName, "xml-content-name", yqlib.ConfiguredXMLPreferences.ContentName, "name for xml content (if no attribute name is present).")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("xml-content-name", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.StrictMode, "xml-strict-mode", yqlib.ConfiguredXMLPreferences.StrictMode, "enables strict parsing of XML. See https://pkg.go.dev/encoding/xml for more details.")
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.KeepNamespace, "xml-keep-namespace", yqlib.ConfiguredXMLPreferences.KeepNamespace, "enables keeping namespace after parsing attributes")
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.UseRawToken, "xml-raw-token", yqlib.ConfiguredXMLPreferences.UseRawToken, "enables using RawToken method instead Token. Commonly disables namespace translations. See https://pkg.go.dev/encoding/xml#Decoder.RawToken for details.")
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredXMLPreferences.ProcInstPrefix, "xml-proc-inst-prefix", yqlib.ConfiguredXMLPreferences.ProcInstPrefix, "prefix for xml processing instructions (e.g. <?xml version=\"1\"?>)")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("xml-proc-inst-prefix", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredXMLPreferences.DirectiveName, "xml-directive-name", yqlib.ConfiguredXMLPreferences.DirectiveName, "name for xml directives (e.g. <!DOCTYPE thing cat>)")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("xml-directive-name", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.SkipProcInst, "xml-skip-proc-inst", yqlib.ConfiguredXMLPreferences.SkipProcInst, "skip over process instructions (e.g. <?xml version=\"1\"?>)")
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.SkipDirectives, "xml-skip-directives", yqlib.ConfiguredXMLPreferences.SkipDirectives, "skip over directives (e.g. <!DOCTYPE thing cat>)")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredCsvPreferences.AutoParse, "csv-auto-parse", yqlib.ConfiguredCsvPreferences.AutoParse, "parse CSV YAML/JSON values")
|
||||
rootCmd.PersistentFlags().Var(newRuneVar(&yqlib.ConfiguredCsvPreferences.Separator), "csv-separator", "CSV Separator character")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredTsvPreferences.AutoParse, "tsv-auto-parse", yqlib.ConfiguredTsvPreferences.AutoParse, "parse TSV YAML/JSON values")
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredLuaPreferences.DocPrefix, "lua-prefix", yqlib.ConfiguredLuaPreferences.DocPrefix, "prefix")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("lua-prefix", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredLuaPreferences.DocSuffix, "lua-suffix", yqlib.ConfiguredLuaPreferences.DocSuffix, "suffix")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("lua-suffix", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredLuaPreferences.UnquotedKeys, "lua-unquoted", yqlib.ConfiguredLuaPreferences.UnquotedKeys, "output unquoted string keys (e.g. {foo=\"bar\"})")
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredLuaPreferences.Globals, "lua-globals", yqlib.ConfiguredLuaPreferences.Globals, "output keys as top-level global variables")
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredPropertiesPreferences.KeyValueSeparator, "properties-separator", yqlib.ConfiguredPropertiesPreferences.KeyValueSeparator, "separator to use between keys and values")
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredPropertiesPreferences.UseArrayBrackets, "properties-array-brackets", yqlib.ConfiguredPropertiesPreferences.UseArrayBrackets, "use [x] in array paths (e.g. for SpringBoot)")
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredShellVariablesPreferences.KeySeparator, "shell-key-separator", yqlib.ConfiguredShellVariablesPreferences.KeySeparator, "separator for shell variable key paths")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("shell-key-separator", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().BoolVar(&yqlib.StringInterpolationEnabled, "string-interpolation", yqlib.StringInterpolationEnabled, "Toggles strings interpolation of \\(exp)")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&nullInput, "null-input", "n", false, "Don't read input, simply evaluate the expression given. Useful for creating docs from scratch.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&noDocSeparators, "no-doc", "N", false, "Don't print document separators (---)")
|
||||
|
||||
rootCmd.PersistentFlags().IntVarP(&indent, "indent", "I", 2, "sets indent level for output")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("indent", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit")
|
||||
rootCmd.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the file inplace of first file given.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&unwrapScalar, "unwrapScalar", "", true, "unwrap scalar, print the value with no quotes, colors or comments")
|
||||
rootCmd.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the file in place of first file given.")
|
||||
rootCmd.PersistentFlags().VarP(unwrapScalarFlag, "unwrapScalar", "r", "unwrap scalar, print the value with no quotes, colors or comments. Defaults to true for yaml")
|
||||
rootCmd.PersistentFlags().Lookup("unwrapScalar").NoOptDefVal = "true"
|
||||
rootCmd.PersistentFlags().BoolVarP(&nulSepOutput, "nul-output", "0", false, "Use NUL char to separate values. If unwrap scalar is also set, fail if unwrapped scalar contains NUL char.")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&prettyPrint, "prettyPrint", "P", false, "pretty print, shorthand for '... style = \"\"'")
|
||||
rootCmd.PersistentFlags().BoolVarP(&exitStatus, "exit-status", "e", false, "set exit status if there are no matches or null or false is returned")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&forceColor, "colors", "C", false, "force print with colors")
|
||||
rootCmd.PersistentFlags().BoolVarP(&forceNoColor, "no-colors", "M", false, "force print with no colors")
|
||||
rootCmd.PersistentFlags().BoolVarP(&forceNoColor, "no-colors", "M", forceNoColor, "force print with no colors")
|
||||
rootCmd.PersistentFlags().StringVarP(&frontMatter, "front-matter", "f", "", "(extract|process) first input as yaml front-matter. Extract will pull out the yaml content, process will run the expression against the yaml content, leaving the remaining data intact")
|
||||
rootCmd.PersistentFlags().BoolVarP(&leadingContentPreProcessing, "header-preprocess", "", true, "Slurp any header comments and separators before processing expression.")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("front-matter", cobra.FixedCompletions([]string{"extract", "process"}, cobra.ShellCompDirectiveNoFileComp)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().StringVarP(&forceExpression, "expression", "", "", "forcibly set the expression argument. Useful when yq argument detection thinks your expression is a file.")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("expression", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredYamlPreferences.LeadingContentPreProcessing, "header-preprocess", "", true, "Slurp any header comments and separators before processing expression.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredYamlPreferences.FixMergeAnchorToSpec, "yaml-fix-merge-anchor-to-spec", "", false, "Fix merge anchor to match YAML spec. Will default to true in late 2025")
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&splitFileExp, "split-exp", "s", "", "print each result (or doc) into a file named (exp). [exp] argument must return a string. You can use $index in the expression as the result counter.")
|
||||
rootCmd.PersistentFlags().StringVarP(&splitFileExp, "split-exp", "s", "", "print each result (or doc) into a file named (exp). [exp] argument must return a string. You can use $index in the expression as the result counter. The necessary directories will be created.")
|
||||
if err = rootCmd.RegisterFlagCompletionFunc("split-exp", cobra.NoFileCompletions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rootCmd.PersistentFlags().StringVarP(&splitFileExpFile, "split-exp-file", "", "", "Use a file to specify the split-exp expression.")
|
||||
if err = rootCmd.MarkPersistentFlagFilename("split-exp-file"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&expressionFile, "from-file", "", "", "Load expression from specified file.")
|
||||
if err = rootCmd.MarkPersistentFlagFilename("from-file"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredSecurityPreferences.DisableEnvOps, "security-disable-env-ops", "", false, "Disable env related operations.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&yqlib.ConfiguredSecurityPreferences.DisableFileOps, "security-disable-file-ops", "", false, "Disable file related operations (e.g. load)")
|
||||
|
||||
rootCmd.AddCommand(
|
||||
createEvaluateSequenceCommand(),
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewRuneVar(t *testing.T) {
|
||||
var r rune
|
||||
runeVar := newRuneVar(&r)
|
||||
|
||||
if runeVar == nil {
|
||||
t.Fatal("newRuneVar returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuneValue_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
runeVal rune
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "simple character",
|
||||
runeVal: 'a',
|
||||
expected: "a",
|
||||
},
|
||||
{
|
||||
name: "special character",
|
||||
runeVal: '\n',
|
||||
expected: "\n",
|
||||
},
|
||||
{
|
||||
name: "unicode character",
|
||||
runeVal: 'ñ',
|
||||
expected: "ñ",
|
||||
},
|
||||
{
|
||||
name: "zero rune",
|
||||
runeVal: 0,
|
||||
expected: string(rune(0)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
runeVal := runeValue(tt.runeVal)
|
||||
result := runeVal.String()
|
||||
if result != tt.expected {
|
||||
t.Errorf("runeValue.String() = %q, want %q", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuneValue_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected rune
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "simple character",
|
||||
input: "a",
|
||||
expected: 'a',
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "newline escape",
|
||||
input: "\\n",
|
||||
expected: '\n',
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "tab escape",
|
||||
input: "\\t",
|
||||
expected: '\t',
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "carriage return escape",
|
||||
input: "\\r",
|
||||
expected: '\r',
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "form feed escape",
|
||||
input: "\\f",
|
||||
expected: '\f',
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "vertical tab escape",
|
||||
input: "\\v",
|
||||
expected: '\v',
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
input: "",
|
||||
expected: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "multiple characters",
|
||||
input: "ab",
|
||||
expected: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "special character",
|
||||
input: "ñ",
|
||||
expected: 'ñ',
|
||||
expectError: true, // This will fail because the Set function checks len(val) != 1
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var r rune
|
||||
runeVal := newRuneVar(&r)
|
||||
|
||||
err := runeVal.Set(tt.input)
|
||||
|
||||
if tt.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for input %q, but got none", tt.input)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for input %q: %v", tt.input, err)
|
||||
}
|
||||
if r != tt.expected {
|
||||
t.Errorf("Expected rune %q (%d), got %q (%d)",
|
||||
string(tt.expected), tt.expected, string(r), r)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuneValue_Set_ErrorMessages(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "empty string error",
|
||||
input: "",
|
||||
expectedError: "[] is not a valid character. Must be length 1 was 0",
|
||||
},
|
||||
{
|
||||
name: "multiple characters error",
|
||||
input: "abc",
|
||||
expectedError: "[abc] is not a valid character. Must be length 1 was 3",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var r rune
|
||||
runeVal := newRuneVar(&r)
|
||||
|
||||
err := runeVal.Set(tt.input)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for input %q, but got none", tt.input)
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), tt.expectedError) {
|
||||
t.Errorf("Expected error message to contain %q, got %q",
|
||||
tt.expectedError, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuneValue_Type(t *testing.T) {
|
||||
var r rune
|
||||
runeVal := newRuneVar(&r)
|
||||
|
||||
result := runeVal.Type()
|
||||
expected := "char"
|
||||
|
||||
if result != expected {
|
||||
t.Errorf("runeValue.Type() = %q, want %q", result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
rootCmd := New()
|
||||
|
||||
if rootCmd == nil {
|
||||
t.Fatal("New() returned nil")
|
||||
}
|
||||
|
||||
// Test basic command properties
|
||||
if rootCmd.Use != "yq" {
|
||||
t.Errorf("Expected Use to be 'yq', got %q", rootCmd.Use)
|
||||
}
|
||||
|
||||
if rootCmd.Short == "" {
|
||||
t.Error("Expected Short description to be non-empty")
|
||||
}
|
||||
|
||||
if rootCmd.Long == "" {
|
||||
t.Error("Expected Long description to be non-empty")
|
||||
}
|
||||
|
||||
// Test that the command has the expected subcommands
|
||||
expectedCommands := []string{"eval", "eval-all", "completion"}
|
||||
actualCommands := make([]string, 0, len(rootCmd.Commands()))
|
||||
|
||||
for _, cmd := range rootCmd.Commands() {
|
||||
actualCommands = append(actualCommands, cmd.Name())
|
||||
}
|
||||
|
||||
for _, expected := range expectedCommands {
|
||||
found := false
|
||||
for _, actual := range actualCommands {
|
||||
if actual == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected command %q not found in actual commands: %v",
|
||||
expected, actualCommands)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew_FlagCompletions(t *testing.T) {
|
||||
rootCmd := New()
|
||||
|
||||
// Test that flag completion functions are registered
|
||||
// This is a basic smoke test - we can't easily test the actual completion logic
|
||||
// without more complex setup
|
||||
flags := []string{
|
||||
"output-format",
|
||||
"input-format",
|
||||
"xml-attribute-prefix",
|
||||
"xml-content-name",
|
||||
"xml-proc-inst-prefix",
|
||||
"xml-directive-name",
|
||||
"lua-prefix",
|
||||
"lua-suffix",
|
||||
"properties-separator",
|
||||
"indent",
|
||||
"front-matter",
|
||||
"expression",
|
||||
"split-exp",
|
||||
}
|
||||
|
||||
for _, flagName := range flags {
|
||||
flag := rootCmd.PersistentFlags().Lookup(flagName)
|
||||
if flag == nil {
|
||||
t.Errorf("Expected flag %q to exist", flagName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type boolFlag interface {
|
||||
pflag.Value
|
||||
IsExplicitlySet() bool
|
||||
IsSet() bool
|
||||
}
|
||||
|
||||
type unwrapScalarFlagStrc struct {
|
||||
explicitlySet bool
|
||||
value bool
|
||||
}
|
||||
|
||||
func newUnwrapFlag() boolFlag {
|
||||
return &unwrapScalarFlagStrc{value: true}
|
||||
}
|
||||
|
||||
func (f *unwrapScalarFlagStrc) IsExplicitlySet() bool {
|
||||
return f.explicitlySet
|
||||
}
|
||||
|
||||
func (f *unwrapScalarFlagStrc) IsSet() bool {
|
||||
return f.value
|
||||
}
|
||||
|
||||
func (f *unwrapScalarFlagStrc) String() string {
|
||||
return strconv.FormatBool(f.value)
|
||||
}
|
||||
|
||||
func (f *unwrapScalarFlagStrc) Set(value string) error {
|
||||
|
||||
v, err := strconv.ParseBool(value)
|
||||
f.value = v
|
||||
f.explicitlySet = true
|
||||
return err
|
||||
}
|
||||
|
||||
func (*unwrapScalarFlagStrc) Type() string {
|
||||
return "bool"
|
||||
}
|
||||
+241
-22
@@ -4,72 +4,291 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mikefarah/yq/v4/pkg/yqlib"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
)
|
||||
|
||||
func initCommand(cmd *cobra.Command, args []string) (firstFileIndex int, err error) {
|
||||
func isAutomaticOutputFormat() bool {
|
||||
return outputFormat == "" || outputFormat == "auto" || outputFormat == "a"
|
||||
}
|
||||
|
||||
func initCommand(cmd *cobra.Command, args []string) (string, []string, error) {
|
||||
cmd.SilenceUsage = true
|
||||
|
||||
setupColors()
|
||||
|
||||
expression, args, err := processArgs(args)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err := loadSplitFileExpression(); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
handleBackwardsCompatibility()
|
||||
|
||||
if err := validateCommandFlags(args); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err := configureFormats(args); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
configureUnwrapScalar()
|
||||
|
||||
return expression, args, nil
|
||||
}
|
||||
|
||||
func setupColors() {
|
||||
fileInfo, _ := os.Stdout.Stat()
|
||||
|
||||
if forceColor || (!forceNoColor && (fileInfo.Mode()&os.ModeCharDevice) != 0) {
|
||||
colorsEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
firstFileIndex = -1
|
||||
if !nullInput && len(args) == 1 {
|
||||
firstFileIndex = 0
|
||||
} else if len(args) > 1 {
|
||||
firstFileIndex = 1
|
||||
func loadSplitFileExpression() error {
|
||||
if splitFileExpFile != "" {
|
||||
splitExpressionBytes, err := os.ReadFile(splitFileExpFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
splitFileExp = string(splitExpressionBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleBackwardsCompatibility() {
|
||||
// backwards compatibility
|
||||
if outputToJSON {
|
||||
outputFormat = "json"
|
||||
}
|
||||
}
|
||||
|
||||
if writeInplace && (firstFileIndex == -1) {
|
||||
return 0, fmt.Errorf("write inplace flag only applicable when giving an expression and at least one file")
|
||||
func validateCommandFlags(args []string) error {
|
||||
if writeInplace && (len(args) == 0 || args[0] == "-") {
|
||||
return fmt.Errorf("write in place flag only applicable when giving an expression and at least one file")
|
||||
}
|
||||
|
||||
if frontMatter != "" && len(args) == 0 {
|
||||
return fmt.Errorf("front matter flag only applicable when giving an expression and at least one file")
|
||||
}
|
||||
|
||||
if writeInplace && splitFileExp != "" {
|
||||
return 0, fmt.Errorf("write inplace cannot be used with split file")
|
||||
return fmt.Errorf("write in place cannot be used with split file")
|
||||
}
|
||||
|
||||
if nullInput && len(args) > 1 {
|
||||
return 0, fmt.Errorf("cannot pass files in when using null-input flag")
|
||||
if nullInput && len(args) > 0 {
|
||||
return fmt.Errorf("cannot pass files in when using null-input flag")
|
||||
}
|
||||
|
||||
return firstFileIndex, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureDecoder() (yqlib.Decoder, error) {
|
||||
yqlibInputFormat, err := yqlib.InputFormatFromString(inputFormat)
|
||||
func configureFormats(args []string) error {
|
||||
inputFilename := ""
|
||||
if len(args) > 0 {
|
||||
inputFilename = args[0]
|
||||
}
|
||||
|
||||
if err := configureInputFormat(inputFilename); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := configureOutputFormat(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
yqlib.GetLogger().Debug("Using input format %v", inputFormat)
|
||||
yqlib.GetLogger().Debug("Using output format %v", outputFormat)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureInputFormat(inputFilename string) error {
|
||||
if inputFormat == "" || inputFormat == "auto" || inputFormat == "a" {
|
||||
inputFormat = yqlib.FormatStringFromFilename(inputFilename)
|
||||
|
||||
_, err := yqlib.FormatFromString(inputFormat)
|
||||
if err != nil {
|
||||
// unknown file type, default to yaml
|
||||
yqlib.GetLogger().Debug("Unknown file format extension '%v', defaulting to yaml", inputFormat)
|
||||
inputFormat = "yaml"
|
||||
if isAutomaticOutputFormat() {
|
||||
outputFormat = "yaml"
|
||||
}
|
||||
} else if isAutomaticOutputFormat() {
|
||||
outputFormat = inputFormat
|
||||
}
|
||||
} else if isAutomaticOutputFormat() {
|
||||
// backwards compatibility -
|
||||
// before this was introduced, `yq -pcsv things.csv`
|
||||
// would produce *yaml* output.
|
||||
//
|
||||
outputFormat = yqlib.FormatStringFromFilename(inputFilename)
|
||||
if inputFilename != "-" {
|
||||
yqlib.GetLogger().Warning("yq default output is now 'auto' (based on the filename extension). Normally yq would output '%v', but for backwards compatibility 'yaml' has been set. Please use -oy to specify yaml, or drop the -p flag.", outputFormat)
|
||||
}
|
||||
outputFormat = "yaml"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureOutputFormat() error {
|
||||
outputFormatType, err := yqlib.FormatFromString(outputFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if outputFormatType == yqlib.YamlFormat ||
|
||||
outputFormatType == yqlib.PropertiesFormat {
|
||||
unwrapScalar = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureUnwrapScalar() {
|
||||
if unwrapScalarFlag.IsExplicitlySet() {
|
||||
unwrapScalar = unwrapScalarFlag.IsSet()
|
||||
}
|
||||
}
|
||||
|
||||
func configureDecoder(evaluateTogether bool) (yqlib.Decoder, error) {
|
||||
format, err := yqlib.FormatFromString(inputFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch yqlibInputFormat {
|
||||
case yqlib.XmlInputFormat:
|
||||
return yqlib.NewXmlDecoder(xmlAttributePrefix, xmlContentName), nil
|
||||
yqlib.ConfiguredYamlPreferences.EvaluateTogether = evaluateTogether
|
||||
|
||||
yqlibDecoder := format.DecoderFactory()
|
||||
if yqlibDecoder == nil {
|
||||
return nil, fmt.Errorf("no support for %s input format", inputFormat)
|
||||
}
|
||||
return yqlib.NewYamlDecoder(), nil
|
||||
return yqlibDecoder, nil
|
||||
}
|
||||
|
||||
func configurePrinterWriter(format yqlib.PrinterOutputFormat, out io.Writer) yqlib.PrinterWriter {
|
||||
func configurePrinterWriter(format *yqlib.Format, out io.Writer) (yqlib.PrinterWriter, error) {
|
||||
|
||||
var printerWriter yqlib.PrinterWriter
|
||||
|
||||
if splitFileExp != "" {
|
||||
colorsEnabled = forceColor
|
||||
splitExp, err := yqlib.NewExpressionParser().ParseExpression(splitFileExp)
|
||||
splitExp, err := yqlib.ExpressionParser.ParseExpression(splitFileExp)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, fmt.Errorf("bad split document expression: %w", err)
|
||||
}
|
||||
printerWriter = yqlib.NewMultiPrinterWriter(splitExp, format)
|
||||
} else {
|
||||
printerWriter = yqlib.NewSinglePrinterWriter(out)
|
||||
}
|
||||
return printerWriter
|
||||
return printerWriter, nil
|
||||
}
|
||||
|
||||
func configureEncoder() (yqlib.Encoder, error) {
|
||||
yqlibOutputFormat, err := yqlib.FormatFromString(outputFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
yqlib.ConfiguredXMLPreferences.Indent = indent
|
||||
yqlib.ConfiguredYamlPreferences.Indent = indent
|
||||
yqlib.ConfiguredJSONPreferences.Indent = indent
|
||||
|
||||
yqlib.ConfiguredYamlPreferences.UnwrapScalar = unwrapScalar
|
||||
yqlib.ConfiguredPropertiesPreferences.UnwrapScalar = unwrapScalar
|
||||
yqlib.ConfiguredJSONPreferences.UnwrapScalar = unwrapScalar
|
||||
|
||||
yqlib.ConfiguredYamlPreferences.ColorsEnabled = colorsEnabled
|
||||
yqlib.ConfiguredJSONPreferences.ColorsEnabled = colorsEnabled
|
||||
|
||||
yqlib.ConfiguredYamlPreferences.PrintDocSeparators = !noDocSeparators
|
||||
|
||||
encoder := yqlibOutputFormat.EncoderFactory()
|
||||
|
||||
if encoder == nil {
|
||||
return nil, fmt.Errorf("no support for %s output format", outputFormat)
|
||||
}
|
||||
return encoder, err
|
||||
}
|
||||
|
||||
// this is a hack to enable backwards compatibility with githubactions (which pipe /dev/null into everything)
|
||||
// and being able to call yq with the filename as a single parameter
|
||||
//
|
||||
// without this - yq detects there is stdin (thanks githubactions),
|
||||
// then tries to parse the filename as an expression
|
||||
func maybeFile(str string) bool {
|
||||
yqlib.GetLogger().Debugf("checking '%v' is a file", str)
|
||||
stat, err := os.Stat(str) // #nosec
|
||||
result := err == nil && !stat.IsDir()
|
||||
if yqlib.GetLogger().IsEnabledFor(logging.DEBUG) {
|
||||
if err != nil {
|
||||
yqlib.GetLogger().Debugf("error: %v", err)
|
||||
} else {
|
||||
yqlib.GetLogger().Debugf("error: %v, dir: %v", err, stat.IsDir())
|
||||
}
|
||||
yqlib.GetLogger().Debugf("result: %v", result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func processStdInArgs(args []string) []string {
|
||||
stat, err := os.Stdin.Stat()
|
||||
if err != nil {
|
||||
yqlib.GetLogger().Debugf("error getting stdin: %v", err)
|
||||
}
|
||||
pipingStdin := stat != nil && (stat.Mode()&os.ModeCharDevice) == 0
|
||||
|
||||
// if we've been given a file, don't automatically
|
||||
// read from stdin.
|
||||
// this happens if there is more than one argument
|
||||
// or only one argument and its a file
|
||||
if nullInput || !pipingStdin || len(args) > 1 || (len(args) > 0 && maybeFile(args[0])) {
|
||||
return args
|
||||
}
|
||||
|
||||
for _, arg := range args {
|
||||
if arg == "-" {
|
||||
return args
|
||||
}
|
||||
}
|
||||
yqlib.GetLogger().Debugf("missing '-', adding it to the end")
|
||||
|
||||
// we're piping from stdin, but there's no '-' arg
|
||||
// lets add one to the end
|
||||
return append(args, "-")
|
||||
}
|
||||
|
||||
func processArgs(originalArgs []string) (string, []string, error) {
|
||||
expression := forceExpression
|
||||
args := processStdInArgs(originalArgs)
|
||||
maybeFirstArgIsAFile := len(args) > 0 && maybeFile(args[0])
|
||||
|
||||
if expressionFile == "" && maybeFirstArgIsAFile && strings.HasSuffix(args[0], ".yq") {
|
||||
// lets check if an expression file was given
|
||||
yqlib.GetLogger().Debug("Assuming arg %v is an expression file", args[0])
|
||||
expressionFile = args[0]
|
||||
args = args[1:]
|
||||
}
|
||||
|
||||
if expressionFile != "" {
|
||||
expressionBytes, err := os.ReadFile(expressionFile)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
//replace \r\n (windows) with good ol' unix file endings.
|
||||
expression = strings.ReplaceAll(string(expressionBytes), "\r\n", "\n")
|
||||
}
|
||||
|
||||
yqlib.GetLogger().Debugf("processed args: %v", args)
|
||||
if expression == "" && len(args) > 0 && args[0] != "-" && !maybeFile(args[0]) {
|
||||
yqlib.GetLogger().Debug("assuming expression is '%v'", args[0])
|
||||
expression = args[0]
|
||||
args = args[1:]
|
||||
}
|
||||
return expression, args, nil
|
||||
}
|
||||
|
||||
+1435
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -11,7 +11,7 @@ var (
|
||||
GitDescribe string
|
||||
|
||||
// Version is main version number that is being run at the moment.
|
||||
Version = "4.16.2"
|
||||
Version = "v4.49.2"
|
||||
|
||||
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
||||
// then it means that it is a final release. Otherwise, this is a pre-release
|
||||
@@ -45,5 +45,5 @@ func getHumanVersion() string {
|
||||
}
|
||||
|
||||
// Strip off any single quotes added by the git information.
|
||||
return strings.Replace(version, "'", "", -1)
|
||||
return strings.ReplaceAll(version, "'", "")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
$schema: https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json
|
||||
version: '0.2'
|
||||
language: en-GB
|
||||
dictionaryDefinitions:
|
||||
- name: project-words
|
||||
path: './project-words.txt'
|
||||
addWords: true
|
||||
dictionaries:
|
||||
- project-words
|
||||
ignorePaths:
|
||||
- 'vendor'
|
||||
- 'bin'
|
||||
- '/project-words.txt'
|
||||
Vendored
-210
@@ -1,210 +0,0 @@
|
||||
yq (4.16.2) focal; urgency=medium
|
||||
|
||||
* Fixed with semicolon space issue
|
||||
* Updating with documentation
|
||||
* Added STDIN example to the top
|
||||
* minor readme cleanup
|
||||
* Help text tweak
|
||||
* Fixed docker timeout - simplify docker builds
|
||||
* New release with docker build fixes
|
||||
* Updating to go 1.17 to fix CVE #944
|
||||
* Fix a typo in root.go
|
||||
* Skip the tests if the nocheck Debian build option is specified
|
||||
* Fixed select bug (#958)
|
||||
* Sped up explode operator
|
||||
* Slight performance improvement to context.ChildContext
|
||||
* Speed up multiply
|
||||
* Update README with recently added / changed options
|
||||
* Make deepMatch report in linear time
|
||||
* Removed leadingContentPreProcessing flag - header preprocessing is stable
|
||||
* Revert "Removed leadingContentPreProcessing flag - header preprocessing is stable"
|
||||
* Keep flag, it is needed in corner cases
|
||||
* Updated Readme
|
||||
* Man page
|
||||
* Fixed expression parsing bug #970
|
||||
* Bumping go-lang, docker versions
|
||||
* Added test release flow
|
||||
* Updated github action release to generate man page
|
||||
* Bumping version
|
||||
* Removing no longer needed github action
|
||||
* Added decoder op
|
||||
* Fixed newline handling when decoding/encoding
|
||||
* Fixed newline handling in encoder/decoder
|
||||
* Can specify indent in encode ops
|
||||
* Added group_by operator
|
||||
* Added flatten operator
|
||||
* Fixed flatten error message
|
||||
* Improving docs
|
||||
* Split printer
|
||||
* Refactored command logic
|
||||
* Fix JSON encoding removing null #985
|
||||
* Fixed acceptance tests
|
||||
* gitbook
|
||||
* Update document generation script
|
||||
* Updating README
|
||||
* Updating release instructions
|
||||
* github action no longer uses data1.yml
|
||||
* Create dependabot.yml
|
||||
* Bump actions/create-release from 1.0.0 to 1.1.4
|
||||
* Bump actions/setup-go from 1 to 2.1.4
|
||||
* Bump github.com/goccy/go-yaml from 1.8.9 to 1.9.4
|
||||
* Bump github.com/jinzhu/copier from 0.2.8 to 0.3.2
|
||||
* Bump github.com/fatih/color from 1.10.0 to 1.13.0
|
||||
* Bump github.com/spf13/cobra from 1.1.3 to 1.2.1
|
||||
* Update dependabot.yml
|
||||
* Update go.yml
|
||||
* add build check to PRs
|
||||
* Include secure as part of build process
|
||||
* Fixing bad label in github action
|
||||
* fixed printer test
|
||||
* remove leading content indicator
|
||||
* Fixed header preprocessing!
|
||||
* lint : define golangci configuration file
|
||||
* Update check.sh
|
||||
* Load file acceptance test
|
||||
* Minor improvement on handling front matter
|
||||
* Improved load doc
|
||||
* feature: detect MANPATh and install there
|
||||
* Update install-man-page.sh
|
||||
* simplify prod stage, move version label to action
|
||||
* add labels, quote some values
|
||||
* enable errorlint linter
|
||||
* Added errorlint to devtools
|
||||
* Added key operator
|
||||
* Added more tests
|
||||
* Fixing comments
|
||||
* Attempt to fix golint problem
|
||||
* Include version query for tools
|
||||
* Clean up errored file?
|
||||
* enable misspell linter
|
||||
* updated readme
|
||||
* update Golangci version to v1.43.0
|
||||
* gci linter
|
||||
* Better merge array by key example
|
||||
* Added credit for merge by array example
|
||||
* Better formatting of merge arrays example
|
||||
* Better merge example
|
||||
* Add accessor for the yq logger instance (#1013)
|
||||
* Fixed collect op when working with multiple nodes
|
||||
* Added map, map_values
|
||||
* Add support for Podman as well as Docker (#1026)
|
||||
* Bump github.com/jinzhu/copier from 0.3.2 to 0.3.4 (#1027)
|
||||
* Added csv, tsv output formats
|
||||
* Added encoder tests
|
||||
* Cleanup test
|
||||
* Fixed docker permission issue #1014
|
||||
* Recording release notes for next release
|
||||
* Assignment op no longer clobbers anchor (#1029)
|
||||
* Added sort_by operator
|
||||
* Improved error message
|
||||
* Improved tips and tricks
|
||||
* Report while filename failed to parse #1030
|
||||
* Added script for extracting checksums
|
||||
* Improved extract-checksum.sh
|
||||
* Bump github.com/spf13/cobra from 1.2.1 to 1.3.0 (#1039)
|
||||
* enable more linters (#1043)
|
||||
* Bump golang compiler #1037
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Tue, 21 Dec 2021 09:41:44 +0000
|
||||
|
||||
yq (4.13.0) focal; urgency=medium
|
||||
|
||||
* New `with` operator for making multiple changes to a given path
|
||||
* New `contains` operator, works like the `jq` equivalent
|
||||
* Subtract operator now supports subtracting elements from arrays!
|
||||
* Fixed Swapping values using variables #934
|
||||
* Github Action now properly supports multiline output #936, thanks @pjxiao
|
||||
* Fixed missing closing bracket validation #932
|
||||
* Fix processing of hex numbers #929
|
||||
* Fixed alternative and union operator issues #930
|
||||
* Can now convert yaml to properties properties format (`-o=props`), See [docs](https://mikefarah.gitbook.io/yq/v/v4.x/usage/properties) for more info.
|
||||
* Fixed document header/footer comment handling when merging (https://github.com/mikefarah/yq/issues/919)
|
||||
* pretty print yaml 1.1 compatibility (https://github.com/mikefarah/yq/issues/914)
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Thu, 16 Sep 2021 20:58:30 +0200
|
||||
|
||||
yq (4.9.6) focal; urgency=medium
|
||||
|
||||
* Added darwin/arm64 build, thanks @alecthomas
|
||||
* Incremented docker alpine base version, thanks @da6d6i7-bronga
|
||||
* Bug fix: multine expression
|
||||
* Bug fix: special character
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Tue, 29 Jun 2021 21:32:14 +0200
|
||||
|
||||
yq (3.3.2) focal; urgency=medium
|
||||
|
||||
* Bug fix: existStatus bug (#459)
|
||||
* Automatically makes a os temp directory if it does not exist (#461)
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Fri, 07 Aug 2020 18:53:01 +0200
|
||||
|
||||
yq (3.3-0) focal; urgency=medium
|
||||
|
||||
* You can control string styles (quotes) using the new --style flag
|
||||
* String values now always have quotes when outputting to json
|
||||
* Negative array indices now traverse the array backwards
|
||||
* Added a --stripComments flag to print yaml without any comments
|
||||
* Bumped go to version 1.14
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Thu, 30 Apr 2020 20:45:44 +0200
|
||||
|
||||
yq (3.1-2) eoan; urgency=medium
|
||||
|
||||
* Bug fix: yq 3 was removing empty inline-style objects and arrays (#355)
|
||||
* Bug fix: Merge option returned different output when switching order of
|
||||
merging files(#347)
|
||||
* Bug fix: Add new object to existing array object was failing in 3.1.1 (#361)
|
||||
* Bug fix: yq 3 empty keys did not allow merging of values (#356)
|
||||
* Bug fix: keys quoted during merge (#363)
|
||||
* Bug fix: Correct length with wc -l (#362)
|
||||
* Bug fix: Write to empty document removed path (#359)
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Mon, 24 Feb 2020 20:31:58 +0100
|
||||
|
||||
yq (3.1-1) eoan; urgency=medium
|
||||
|
||||
* Keeps yaml comments and formatting, can specify yaml tags when updating.
|
||||
* Handles anchors
|
||||
* Can print out matching paths and values when splatting
|
||||
* JSON output works for all commands
|
||||
* Yaml files with multiple documents are printed out as one JSON
|
||||
document per line.
|
||||
* Deep splat (**) to match arbitrary paths
|
||||
* Update scripts file format has changed to be more powerful
|
||||
* Reading and splatting, matching results are printed once per line
|
||||
* Bugfixing
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Tue, 11 Feb 2020 22:18:24 +0100
|
||||
|
||||
yq (2.2-1) bionic; urgency=medium
|
||||
|
||||
* Added Windows support for the "--inplace" command flag
|
||||
* Prefix now supports arrays
|
||||
* Add prefix command
|
||||
* Bump Alpine version to 3.8
|
||||
* Improved docker build process
|
||||
* Lint fixes
|
||||
* Build support for all linux architectures supported by gox
|
||||
|
||||
-- Roberto Mier Escandon <rmescandon@gmail.com> Sat, 19 Jan 2019 15:50:47 +0100
|
||||
|
||||
yq (2.1-0) bionic; urgency=medium
|
||||
|
||||
* Ability to read multiple documents in a single file
|
||||
* Ability to append list items instead of overwriting
|
||||
|
||||
-- Roberto Mier Escandón <rmescandon@gmail.com> Tue, 10 Jul 2018 14:02:42 +0200
|
||||
|
||||
yq (2.0-0) bionic; urgency=medium
|
||||
|
||||
* Release 2.0.0
|
||||
|
||||
-- Roberto Mier Escandón <rmescandon@gmail.com> Wed, 20 Jun 2018 10:29:53 +0200
|
||||
|
||||
yq (1.15-0) bionic; urgency=medium
|
||||
|
||||
* Release 1.15
|
||||
|
||||
-- Roberto Mier Escandón <rmescandon@gmail.com> Wed, 06 Jun 2018 11:32:03 +0200
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
10
|
||||
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
Source: yq
|
||||
Section: devel
|
||||
Priority: optional
|
||||
Maintainer: Roberto Mier Escandón <rmescandon@gmail.com>
|
||||
Build-Depends: debhelper (>=10),
|
||||
golang-1.17-go,
|
||||
pandoc,
|
||||
rsync
|
||||
Standards-Version: 4.1.4
|
||||
Homepage: https://github.com/mikefarah/yq.git
|
||||
Vcs-Browser: https://github.com/mikefarah/yq.git
|
||||
Vcs-Git: https://github.com/mikefarah/yq.git
|
||||
XS-Go-Import-Path: github.com/mikefarah/yq
|
||||
XSBC-Original-Maintainer: Roberto Mier Escandón <rmescandon@gmail.com>
|
||||
|
||||
Package: yq
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: lightweight and portable command-line YAML processor
|
||||
.
|
||||
The aim of the project is to be the
|
||||
[jq](https://github.com/stedolan/jq) or sed of yaml files.
|
||||
Vendored
-24
@@ -1,24 +0,0 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: yq
|
||||
Source: https://github.com/mikefarah/yq.git
|
||||
|
||||
Files: *
|
||||
Copyright: 2017 Mike Farah
|
||||
License: Expat
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
[DEFAULT]
|
||||
pristine-tar = True
|
||||
Vendored
-74
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
#
|
||||
# Copyright (C) 2018-2021 Roberto Mier Escandón <rmescandon@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 3 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
PROJECT := yq
|
||||
OWNER := mikefarah
|
||||
REPO := github.com
|
||||
|
||||
export DH_OPTIONS
|
||||
export DH_GOPKG := ${REPO}/${OWNER}/${PROJECT}
|
||||
export GOROOT := /usr/local/go
|
||||
export GOPATH := ${CURDIR}/_build
|
||||
export GOBIN := ${GOPATH}/bin
|
||||
export PATH := ${GOROOT}/bin:${GOBIN}:${PATH}
|
||||
export GOCACHE := /tmp/gocache
|
||||
export GOFLAGS := -mod=vendor
|
||||
export GO111MODULE := on
|
||||
|
||||
SRCDIR := ${GOPATH}/src/${DH_GOPKG}
|
||||
DESTDIR := ${CURDIR}/debian/${PROJECT}
|
||||
BINDIR := /usr/bin
|
||||
MANDIR := /usr/share/man/man1/
|
||||
ASSETSDIR := /usr/share/${PROJECT}
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
mkdir -p ${SRCDIR}
|
||||
mkdir -p ${GOBIN}
|
||||
# copy project to local srcdir to build from there
|
||||
rsync -avz --progress --exclude=_build --exclude=debian --exclude=tmp. --exclude=go.mod --exclude=docs . $(SRCDIR)
|
||||
# build go code
|
||||
( \
|
||||
cd ${SRCDIR} && \
|
||||
go install -buildmode=pie ./... \
|
||||
)
|
||||
|
||||
# build man page
|
||||
( \
|
||||
cd ${SRCDIR} && \
|
||||
./scripts/generate-man-page-md.sh && \
|
||||
./scripts/generate-man-page.sh \
|
||||
)
|
||||
|
||||
override_dh_auto_test:
|
||||
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
|
||||
(cd ${SRCDIR} && go test -v ./...)
|
||||
endif
|
||||
|
||||
override_dh_auto_install:
|
||||
cp ${GOBIN}/yq ${DESTDIR}/${BINDIR}
|
||||
cp -f ${SRCDIR}/LICENSE ${DESTDIR}/${ASSETSDIR}
|
||||
chmod a+x ${DESTDIR}/${BINDIR}/yq
|
||||
|
||||
# man
|
||||
mkdir -p "${DESTDIR}"/"${MANDIR}"
|
||||
cp "${SRCDIR}"/yq.1 "${DESTDIR}"/"${MANDIR}" \
|
||||
|
||||
override_dh_auto_clean:
|
||||
dh_clean
|
||||
rm -rf ${CURDIR}/_build
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
3.0 (native)
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
usr/bin
|
||||
usr/share/yq
|
||||
usr/share/man/man1
|
||||
+2
-11
@@ -1,11 +1,2 @@
|
||||
---
|
||||
- become: true
|
||||
gather_facts: false
|
||||
hosts: lalaland
|
||||
name: "Apply smth"
|
||||
roles:
|
||||
- lala
|
||||
- land
|
||||
serial: 1
|
||||
- become: false
|
||||
gather_facts: true
|
||||
- [cat, dog, frog, cow]
|
||||
- [apple, banana, grape, mango]
|
||||
@@ -0,0 +1 @@
|
||||
bXkgc2VjcmV0IGNoaWxsaSByZWNpcGUgaXMuLi4u
|
||||
@@ -0,0 +1,9 @@
|
||||
return {
|
||||
["country"] = "Australia"; -- this place
|
||||
["cities"] = {
|
||||
"Sydney",
|
||||
"Melbourne",
|
||||
"Brisbane",
|
||||
"Perth",
|
||||
};
|
||||
};
|
||||
+1
-15
@@ -1,15 +1 @@
|
||||
strings:
|
||||
- a: banana
|
||||
- a: cat
|
||||
- a: apple
|
||||
|
||||
numbers:
|
||||
- a: 12
|
||||
- a: 13
|
||||
- a: 120
|
||||
|
||||
|
||||
obj:
|
||||
- a: {cat: "adog"}
|
||||
- a: {cat: "doga"}
|
||||
- a: apple
|
||||
a: apple
|
||||
+5
-2
@@ -1,2 +1,5 @@
|
||||
c:
|
||||
d: hamster
|
||||
# --------------------------------------------------
|
||||
# It's a test with comment
|
||||
# --------------------------------------------------
|
||||
groups:
|
||||
- name: d
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#! yq
|
||||
.[] |(
|
||||
( select(kind == "scalar") | key + "='" + . + "'"),
|
||||
( select(kind == "seq") | key + "=(" + (map("'" + . + "'") | join(",")) + ")")
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
# comments on values appear
|
||||
person.name = Mike
|
||||
|
||||
# comments on array values appear
|
||||
person.pets.0 = cat
|
||||
person.food.0 = pizza
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
a: apple
|
||||
b: bannana
|
||||
b: banana
|
||||
---
|
||||
hello there
|
||||
apples: great
|
||||
+1
-2
@@ -1,2 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cat legs="4">BiBi</cat>
|
||||
<zoo><thing><frog>boing</frog></thing></zoo>
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
; This is a INI document
|
||||
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
dob = 1979-05-27T07:32:00-08:00
|
||||
|
||||
[database]
|
||||
db_host = "localhost"
|
||||
db_port = 5432
|
||||
db_user = "postgres"
|
||||
db_password = ""
|
||||
db_name = "postgres"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[[fruits]]
|
||||
|
||||
[animals]
|
||||
|
||||
[[fruits.varieties]] # nested array of tables
|
||||
name = "red delicious"
|
||||
+2
-11
@@ -1,11 +1,2 @@
|
||||
# Some doc
|
||||
|
||||
a: true
|
||||
b:
|
||||
c: 2
|
||||
d: [3, 4, 5]
|
||||
e:
|
||||
- name: fred
|
||||
value: 3
|
||||
- name: sam
|
||||
value: 4
|
||||
# things
|
||||
a: apple
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
; This is a INI document
|
||||
name = "Tom Preston-Werner"
|
||||
dob = 1979-05-27T07:32:00-08:00
|
||||
enabled = true
|
||||
ip = "10.0.0.1"
|
||||
role = "frontend"
|
||||
treads = 4
|
||||
@@ -0,0 +1,3 @@
|
||||
name,numberOfCats,likesApples,height
|
||||
,1,true,168.8
|
||||
Samantha's Rabbit,2,false,-188.8
|
||||
|
@@ -0,0 +1 @@
|
||||
this.is = a properties file
|
||||
@@ -0,0 +1 @@
|
||||
<this>is some xml</this>
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
# comment
|
||||
# about things
|
||||
a: cat
|
||||
@@ -2,7 +2,8 @@ FROM mikefarah/yq:4
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# this seems to be the default user in github actions
|
||||
USER 1001
|
||||
# github action recommendation is to run as root.
|
||||
# https://docs.github.com/en/actions/creating-actions/dockerfile-support-for-github-actions#user
|
||||
USER root
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
set -e
|
||||
echo "::debug::\$cmd: $1"
|
||||
RESULT=$(eval "$1")
|
||||
RESULT="${RESULT//'%'/'%25'}"
|
||||
RESULT="${RESULT//$'\n'/'%0A'}"
|
||||
RESULT="${RESULT//$'\r'/'%0D'}"
|
||||
echo "::debug::\$RESULT: $RESULT"
|
||||
echo ::set-output name=result::"$RESULT"
|
||||
# updating from
|
||||
# https://github.com/orgs/community/discussions/26288#discussioncomment-3876281
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter
|
||||
delimiter=$(cat /proc/sys/kernel/random/uuid)
|
||||
echo "result<<${delimiter}" >> "${GITHUB_OUTPUT}"
|
||||
echo "${RESULT}" >> "${GITHUB_OUTPUT}"
|
||||
echo "${delimiter}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
module github.com/mikefarah/yq/v4
|
||||
|
||||
require (
|
||||
github.com/elliotchance/orderedmap v1.4.0
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/goccy/go-yaml v1.9.5
|
||||
github.com/jinzhu/copier v0.3.4
|
||||
github.com/magiconair/properties v1.8.5
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/timtadh/lexmachine v0.2.2
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d
|
||||
github.com/a8m/envsubst v1.4.3
|
||||
github.com/alecthomas/participle/v2 v2.1.4
|
||||
github.com/alecthomas/repr v0.5.2
|
||||
github.com/dimchansky/utfbom v1.1.1
|
||||
github.com/elliotchance/orderedmap v1.8.0
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/go-ini/ini v1.67.0
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/goccy/go-yaml v1.18.0
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/magiconair/properties v1.8.10
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/yuin/gopher-lua v1.1.1
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.3
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/text v0.31.0
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/timtadh/data-structures v0.5.3 // indirect
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
)
|
||||
|
||||
go 1.17
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
@@ -1,792 +1,67 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
|
||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY=
|
||||
github.com/a8m/envsubst v1.4.3/go.mod h1:4jjHWQlZoaXPoLQUb7H2qT4iLkZDdmEQiOUogdUmqVU=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/participle/v2 v2.1.4 h1:W/H79S8Sat/krZ3el6sQMvMaahJ+XcM9WSI2naI7w2U=
|
||||
github.com/alecthomas/participle/v2 v2.1.4/go.mod h1:8tqVbpTX20Ru4NfYQgZf4mP18eXPTBViyMWiArNEgGI=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A=
|
||||
github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0=
|
||||
github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jinzhu/copier v0.3.4 h1:mfU6jI9PtCeUjkjQ322dlff9ELjGDu975C2p/nrubVI=
|
||||
github.com/jinzhu/copier v0.3.4/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
|
||||
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||
github.com/elliotchance/orderedmap v1.8.0 h1:TrOREecvh3JbS+NCgwposXG5ZTFHtEsQiCGOhPElnMw=
|
||||
github.com/elliotchance/orderedmap v1.8.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
|
||||
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/timtadh/data-structures v0.5.3 h1:F2tEjoG9qWIyUjbvXVgJqEOGJPMIiYn7U5W5mE+i/vQ=
|
||||
github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU=
|
||||
github.com/timtadh/lexmachine v0.2.2 h1:g55RnjdYazm5wnKv59pwFcBJHOyvTPfDEoz21s4PHmY=
|
||||
github.com/timtadh/lexmachine v0.2.2/go.mod h1:GBJvD5OAfRn/gnp92zb9KTgHLB7akKyxmVivoYCcjQI=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
|
||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE=
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
+65
-11
@@ -1,8 +1,63 @@
|
||||
# How it works
|
||||
# Expression Syntax: A Visual Guide
|
||||
In `yq`, expressions are made up of operators and pipes. A context of nodes is passed through the expression, and each operation takes the context as input and returns a new context as output. That output is piped in as input for the next operation in the expression.
|
||||
|
||||
In `yq` expressions are made up of operators and pipes. A context of nodes is passed through the expression and each operation takes the context as input and returns a new context as output. That output is piped in as input for the next operation in the expression. To begin with, the context is set to the first yaml document of the first yaml file (if processing in sequence using eval).
|
||||
Let's break down the process step by step using a diagram. We'll start with a single YAML document, apply an expression, and observe how the context changes at each step.
|
||||
|
||||
Lets look at a couple of examples.
|
||||
Given a document like:
|
||||
|
||||
```yaml
|
||||
root:
|
||||
items:
|
||||
- name: apple
|
||||
type: fruit
|
||||
- name: carrot
|
||||
type: vegetable
|
||||
- name: banana
|
||||
type: fruit
|
||||
```
|
||||
|
||||
You can use dot notation to access nested structures. For example, to access the `name` of the first item, you would use the expression `.root.items[0].name`, which would return `apple`.
|
||||
|
||||
But lets see how we could find all the fruit under `items`
|
||||
|
||||
## Step 1: Initial Context
|
||||
The context starts at the root of the YAML document. In this case, the entire document is the initial context.
|
||||
|
||||
```
|
||||
root
|
||||
└── items
|
||||
├── name: apple
|
||||
│ type: fruit
|
||||
├── name: carrot
|
||||
│ type: vegetable
|
||||
└── name: banana
|
||||
type: fruit
|
||||
```
|
||||
|
||||
## Step 2: Splatting the Array
|
||||
Using the expression `.root.items[]`, we "splat" the items array. This means each element of the array becomes its own node in the context:
|
||||
|
||||
```
|
||||
Node 1: { name: apple, type: fruit }
|
||||
Node 2: { name: carrot, type: vegetable }
|
||||
Node 3: { name: banana, type: fruit }
|
||||
```
|
||||
|
||||
## Step 3: Filtering the Nodes
|
||||
Next, we apply a filter to select only the nodes where type is fruit. The expression `.root.items[] | select(.type == "fruit")` filters the nodes:
|
||||
|
||||
```
|
||||
Filtered Node 1: { name: apple, type: fruit }
|
||||
Filtered Node 2: { name: banana, type: fruit }
|
||||
```
|
||||
|
||||
## Step 4: Extracting a Field
|
||||
Finally, we extract the name field from the filtered nodes using `.root.items[] | select(.type == "fruit") | .name` This results in:
|
||||
|
||||
```
|
||||
apple
|
||||
banana
|
||||
```
|
||||
|
||||
## Simple assignment example
|
||||
|
||||
@@ -19,7 +74,7 @@ with an expression:
|
||||
.a = .b
|
||||
```
|
||||
|
||||
Like math expression - operator precedence is important.
|
||||
Like math expressions - operator precedence is important.
|
||||
|
||||
The `=` operator takes two arguments, a `lhs` expression, which in this case is `.a` and `rhs` expression which is `.b`.
|
||||
|
||||
@@ -29,7 +84,7 @@ It pipes the current, lets call it 'root' context through the `lhs` expression o
|
||||
cat
|
||||
```
|
||||
|
||||
Sidenote: this node holds not only its value 'cat', but comments and metadata too, including path and parent information.
|
||||
Side note: this node holds not only its value 'cat', but comments and metadata too, including path and parent information.
|
||||
|
||||
The `=` operator then pipes the 'root' context through the `rhs` expression of `.b` to return the node
|
||||
|
||||
@@ -37,17 +92,16 @@ The `=` operator then pipes the 'root' context through the `rhs` expression of `
|
||||
dog
|
||||
```
|
||||
|
||||
Both sides have now been evaluated, so now the operator copies across the value from the RHS (`.b`) to the the LHS (`.a`), and it returns the now updated context:
|
||||
Both sides have now been evaluated, so now the operator copies across the value from the RHS (`.b`) to the LHS (`.a`), and it returns the now updated context:
|
||||
|
||||
```yaml
|
||||
a: dog
|
||||
b: dog
|
||||
```
|
||||
|
||||
|
||||
## Complex assignment, operator precedence rules
|
||||
|
||||
Just like math expression - `yq` expression have an order of precedence. The pipe `|` operator has a low order of precedence, so operators with higher precedence will get evalated first.
|
||||
Just like math expressions - `yq` expressions have an order of precedence. The pipe `|` operator has a low order of precedence, so operators with higher precedence will get evaluated first.
|
||||
|
||||
Most of the time, this is intuitively what you'd want, for instance `.a = "cat" | .b = "dog"` is effectively: `(.a = "cat") | (.b = "dog")`.
|
||||
|
||||
@@ -66,14 +120,14 @@ Lets say you had:
|
||||
Lets say you wanted to update the `sally` entry to have fruit: 'mango'. The _incorrect_ way to do that is:
|
||||
`.[] | select(.name == "sally") | .fruit = "mango"`.
|
||||
|
||||
Becasue `|` has a low operator precedence, this will be evaluated (_incorrectly_) as : `(.[]) | (select(.name == "sally")) | (.fruit = "mango")`. What you'll see is only the updated segment returned:
|
||||
Because `|` has a low operator precedence, this will be evaluated (_incorrectly_) as : `(.[]) | (select(.name == "sally")) | (.fruit = "mango")`. What you'll see is only the updated segment returned:
|
||||
|
||||
```yaml
|
||||
name: sally
|
||||
fruit: mango
|
||||
```
|
||||
|
||||
To properly update this yaml, you will need to use brackets (think BODMAS from maths) and wrap the entire LHS:
|
||||
**Important**: To properly update this YAML, you must wrap the entire LHS in parentheses. Think of it like using brackets in math to ensure the correct order of operations.
|
||||
`(.[] | select(.name == "sally") | .fruit) = "mango"`
|
||||
|
||||
|
||||
@@ -126,4 +180,4 @@ The assignment operator then copies across the value from the RHS to the value o
|
||||
```yaml
|
||||
a: 2
|
||||
b: thing
|
||||
```
|
||||
```
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ./yq ea '.[]' examples/data*.yaml
|
||||
|
||||
|
||||
./yq ea '
|
||||
((.[] | {.name: .}) as $item ireduce ({}; . * $item )) as $uniqueMap
|
||||
| ( $uniqueMap | to_entries | .[]) as $item ireduce([]; . + $item.value)
|
||||
' examples/data*.yaml
|
||||
@@ -2,16 +2,14 @@ package yqlib
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// A yaml expression evaluator that runs the expression once against all files/nodes in memory.
|
||||
type Evaluator interface {
|
||||
EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool, decoder Decoder) error
|
||||
EvaluateFiles(expression string, filenames []string, printer Printer, decoder Decoder) error
|
||||
|
||||
// EvaluateNodes takes an expression and one or more yaml nodes, returning a list of matching candidate nodes
|
||||
EvaluateNodes(expression string, nodes ...*yaml.Node) (*list.List, error)
|
||||
EvaluateNodes(expression string, nodes ...*CandidateNode) (*list.List, error)
|
||||
|
||||
// EvaluateCandidateNodes takes an expression and list of candidate nodes, returning a list of matching candidate nodes
|
||||
EvaluateCandidateNodes(expression string, inputCandidateNodes *list.List) (*list.List, error)
|
||||
@@ -19,23 +17,23 @@ type Evaluator interface {
|
||||
|
||||
type allAtOnceEvaluator struct {
|
||||
treeNavigator DataTreeNavigator
|
||||
treeCreator ExpressionParser
|
||||
}
|
||||
|
||||
func NewAllAtOnceEvaluator() Evaluator {
|
||||
return &allAtOnceEvaluator{treeNavigator: NewDataTreeNavigator(), treeCreator: NewExpressionParser()}
|
||||
InitExpressionParser()
|
||||
return &allAtOnceEvaluator{treeNavigator: NewDataTreeNavigator()}
|
||||
}
|
||||
|
||||
func (e *allAtOnceEvaluator) EvaluateNodes(expression string, nodes ...*yaml.Node) (*list.List, error) {
|
||||
func (e *allAtOnceEvaluator) EvaluateNodes(expression string, nodes ...*CandidateNode) (*list.List, error) {
|
||||
inputCandidates := list.New()
|
||||
for _, node := range nodes {
|
||||
inputCandidates.PushBack(&CandidateNode{Node: node})
|
||||
inputCandidates.PushBack(node)
|
||||
}
|
||||
return e.EvaluateCandidateNodes(expression, inputCandidates)
|
||||
}
|
||||
|
||||
func (e *allAtOnceEvaluator) EvaluateCandidateNodes(expression string, inputCandidates *list.List) (*list.List, error) {
|
||||
node, err := e.treeCreator.ParseExpression(expression)
|
||||
node, err := ExpressionParser.ParseExpression(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -46,21 +44,16 @@ func (e *allAtOnceEvaluator) EvaluateCandidateNodes(expression string, inputCand
|
||||
return context.MatchingNodes, nil
|
||||
}
|
||||
|
||||
func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool, decoder Decoder) error {
|
||||
func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer, decoder Decoder) error {
|
||||
fileIndex := 0
|
||||
firstFileLeadingContent := ""
|
||||
|
||||
var allDocuments = list.New()
|
||||
for _, filename := range filenames {
|
||||
reader, leadingContent, err := readStream(filename, fileIndex == 0 && leadingContentPreProcessing)
|
||||
reader, err := readStream(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fileIndex == 0 {
|
||||
firstFileLeadingContent = leadingContent
|
||||
}
|
||||
|
||||
fileDocuments, err := readDocuments(reader, filename, fileIndex, decoder)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -70,16 +63,8 @@ func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string
|
||||
}
|
||||
|
||||
if allDocuments.Len() == 0 {
|
||||
candidateNode := &CandidateNode{
|
||||
Document: 0,
|
||||
Filename: "",
|
||||
Node: &yaml.Node{Kind: yaml.DocumentNode, Content: []*yaml.Node{{Tag: "!!null", Kind: yaml.ScalarNode}}},
|
||||
FileIndex: 0,
|
||||
LeadingContent: firstFileLeadingContent,
|
||||
}
|
||||
candidateNode := createScalarNode(nil, "")
|
||||
allDocuments.PushBack(candidateNode)
|
||||
} else {
|
||||
allDocuments.Front().Value.(*CandidateNode).LeadingContent = firstFileLeadingContent
|
||||
}
|
||||
|
||||
matches, err := e.EvaluateCandidateNodes(expression, allDocuments)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
@@ -18,7 +20,7 @@ var evaluateNodesScenario = []expressionScenario{
|
||||
document: `a: hello`,
|
||||
expression: `.`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::a: hello\n",
|
||||
"D0, P[], (!!map)::a: hello\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -32,9 +34,23 @@ var evaluateNodesScenario = []expressionScenario{
|
||||
|
||||
func TestAllAtOnceEvaluateNodes(t *testing.T) {
|
||||
var evaluator = NewAllAtOnceEvaluator()
|
||||
// logging.SetLevel(logging.DEBUG, "")
|
||||
for _, tt := range evaluateNodesScenario {
|
||||
node := test.ParseData(tt.document)
|
||||
list, _ := evaluator.EvaluateNodes(tt.expression, &node)
|
||||
decoder := NewYamlDecoder(ConfiguredYamlPreferences)
|
||||
reader := bufio.NewReader(strings.NewReader(tt.document))
|
||||
err := decoder.Init(reader)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
candidateNode, errorReading := decoder.Decode()
|
||||
|
||||
if errorReading != nil {
|
||||
t.Error(errorReading)
|
||||
return
|
||||
}
|
||||
|
||||
list, _ := evaluator.EvaluateNodes(tt.expression, candidateNode)
|
||||
test.AssertResultComplex(t, tt.expected, resultsToString(t, list))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
//go:build !yq_nobase64
|
||||
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
)
|
||||
|
||||
const base64EncodedSimple = "YSBzcGVjaWFsIHN0cmluZw=="
|
||||
const base64DecodedSimpleExtraSpaces = "\n " + base64EncodedSimple + " \n"
|
||||
const base64DecodedSimple = "a special string"
|
||||
|
||||
const base64EncodedUTF8 = "V29ya3Mgd2l0aCBVVEYtMTYg8J+Yig=="
|
||||
const base64DecodedUTF8 = "Works with UTF-16 😊"
|
||||
|
||||
const base64EncodedYaml = "YTogYXBwbGUK"
|
||||
const base64DecodedYaml = "a: apple\n"
|
||||
|
||||
const base64EncodedEmpty = ""
|
||||
const base64DecodedEmpty = ""
|
||||
|
||||
const base64MissingPadding = "Y2F0cw"
|
||||
const base64DecodedMissingPadding = "cats"
|
||||
|
||||
const base64EncodedCats = "Y2F0cw=="
|
||||
const base64DecodedCats = "cats"
|
||||
|
||||
var base64Scenarios = []formatScenario{
|
||||
{
|
||||
skipDoc: true,
|
||||
description: "empty decode",
|
||||
input: base64EncodedEmpty,
|
||||
expected: base64DecodedEmpty + "\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
description: "simple decode",
|
||||
input: base64EncodedSimple,
|
||||
expected: base64DecodedSimple + "\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
description: "Decode base64: simple",
|
||||
subdescription: "Decoded data is assumed to be a string.",
|
||||
input: base64EncodedSimple,
|
||||
expected: base64DecodedSimple + "\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
description: "Decode base64: UTF-8",
|
||||
subdescription: "Base64 decoding supports UTF-8 encoded strings.",
|
||||
input: base64EncodedUTF8,
|
||||
expected: base64DecodedUTF8 + "\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
description: "decode missing padding",
|
||||
input: base64MissingPadding,
|
||||
expected: base64DecodedMissingPadding + "\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
|
||||
description: "Decode with extra spaces",
|
||||
subdescription: "Extra leading/trailing whitespace is stripped",
|
||||
input: base64DecodedSimpleExtraSpaces,
|
||||
expected: base64DecodedSimple + "\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
description: "decode with padding",
|
||||
input: base64EncodedCats,
|
||||
expected: base64DecodedCats + "\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
description: "decode yaml document",
|
||||
input: base64EncodedYaml,
|
||||
expected: base64DecodedYaml + "\n",
|
||||
scenarioType: "decode",
|
||||
},
|
||||
{
|
||||
description: "Encode base64: string",
|
||||
input: "\"" + base64DecodedSimple + "\"",
|
||||
expected: base64EncodedSimple,
|
||||
scenarioType: "encode",
|
||||
},
|
||||
{
|
||||
description: "Encode base64: string from document",
|
||||
subdescription: "Extract a string field and encode it to base64.",
|
||||
input: "coolData: \"" + base64DecodedSimple + "\"",
|
||||
expression: ".coolData",
|
||||
expected: base64EncodedSimple,
|
||||
scenarioType: "encode",
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
description: "encode empty string",
|
||||
input: "\"\"",
|
||||
expected: "",
|
||||
scenarioType: "encode",
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
description: "encode UTF-8 string",
|
||||
input: "\"" + base64DecodedUTF8 + "\"",
|
||||
expected: base64EncodedUTF8,
|
||||
scenarioType: "encode",
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
description: "encode cats",
|
||||
input: "\"" + base64DecodedCats + "\"",
|
||||
expected: base64EncodedCats,
|
||||
scenarioType: "encode",
|
||||
},
|
||||
{
|
||||
description: "Roundtrip: simple",
|
||||
skipDoc: true,
|
||||
input: base64EncodedSimple,
|
||||
expected: base64EncodedSimple,
|
||||
scenarioType: "roundtrip",
|
||||
},
|
||||
{
|
||||
description: "Roundtrip: UTF-8",
|
||||
skipDoc: true,
|
||||
input: base64EncodedUTF8,
|
||||
expected: base64EncodedUTF8,
|
||||
scenarioType: "roundtrip",
|
||||
},
|
||||
{
|
||||
description: "Roundtrip: missing padding",
|
||||
skipDoc: true,
|
||||
input: base64MissingPadding,
|
||||
expected: base64EncodedCats,
|
||||
scenarioType: "roundtrip",
|
||||
},
|
||||
{
|
||||
description: "Roundtrip: empty",
|
||||
skipDoc: true,
|
||||
input: base64EncodedEmpty,
|
||||
expected: base64EncodedEmpty,
|
||||
scenarioType: "roundtrip",
|
||||
},
|
||||
{
|
||||
description: "Encode error: non-string",
|
||||
skipDoc: true,
|
||||
input: "123",
|
||||
expectedError: "cannot encode !!int as base64, can only operate on strings",
|
||||
scenarioType: "encode-error",
|
||||
},
|
||||
{
|
||||
description: "Encode error: array",
|
||||
skipDoc: true,
|
||||
input: "[1, 2, 3]",
|
||||
expectedError: "cannot encode !!seq as base64, can only operate on strings",
|
||||
scenarioType: "encode-error",
|
||||
},
|
||||
{
|
||||
description: "Encode error: map",
|
||||
skipDoc: true,
|
||||
input: "{b: c}",
|
||||
expectedError: "cannot encode !!map as base64, can only operate on strings",
|
||||
scenarioType: "encode-error",
|
||||
},
|
||||
}
|
||||
|
||||
func testBase64Scenario(t *testing.T, s formatScenario) {
|
||||
switch s.scenarioType {
|
||||
case "", "decode":
|
||||
yamlPrefs := ConfiguredYamlPreferences.Copy()
|
||||
yamlPrefs.Indent = 4
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewBase64Decoder(), NewYamlEncoder(yamlPrefs)), s.description)
|
||||
case "encode":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewBase64Encoder()), s.description)
|
||||
case "roundtrip":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewBase64Decoder(), NewBase64Encoder()), s.description)
|
||||
case "encode-error":
|
||||
result, err := processFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewBase64Encoder())
|
||||
if err == nil {
|
||||
t.Errorf("Expected error '%v' but it worked: %v", s.expectedError, result)
|
||||
} else {
|
||||
test.AssertResultComplexWithContext(t, s.expectedError, err.Error(), s.description)
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
|
||||
}
|
||||
}
|
||||
|
||||
func documentBase64Scenario(_ *testing.T, w *bufio.Writer, i interface{}) {
|
||||
s := i.(formatScenario)
|
||||
|
||||
if s.skipDoc {
|
||||
return
|
||||
}
|
||||
switch s.scenarioType {
|
||||
case "", "decode":
|
||||
documentBase64DecodeScenario(w, s)
|
||||
case "encode":
|
||||
documentBase64EncodeScenario(w, s)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
|
||||
}
|
||||
}
|
||||
|
||||
func documentBase64DecodeScenario(w *bufio.Writer, s formatScenario) {
|
||||
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
|
||||
|
||||
if s.subdescription != "" {
|
||||
writeOrPanic(w, s.subdescription)
|
||||
writeOrPanic(w, "\n\n")
|
||||
}
|
||||
|
||||
writeOrPanic(w, "Given a sample.txt file of:\n")
|
||||
writeOrPanic(w, fmt.Sprintf("```\n%v\n```\n", s.input))
|
||||
|
||||
writeOrPanic(w, "then\n")
|
||||
expression := s.expression
|
||||
if expression == "" {
|
||||
expression = "."
|
||||
}
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -p=base64 -oy '%v' sample.txt\n```\n", expression))
|
||||
writeOrPanic(w, "will output\n")
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewBase64Decoder(), NewYamlEncoder(ConfiguredYamlPreferences))))
|
||||
}
|
||||
|
||||
func documentBase64EncodeScenario(w *bufio.Writer, s formatScenario) {
|
||||
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
|
||||
|
||||
if s.subdescription != "" {
|
||||
writeOrPanic(w, s.subdescription)
|
||||
writeOrPanic(w, "\n\n")
|
||||
}
|
||||
|
||||
writeOrPanic(w, "Given a sample.yml file of:\n")
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v\n```\n", s.input))
|
||||
|
||||
writeOrPanic(w, "then\n")
|
||||
expression := s.expression
|
||||
if expression == "" {
|
||||
expression = "."
|
||||
}
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -o=base64 '%v' sample.yml\n```\n", expression))
|
||||
writeOrPanic(w, "will output\n")
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("```\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewBase64Encoder())))
|
||||
}
|
||||
|
||||
func TestBase64Scenarios(t *testing.T) {
|
||||
for _, tt := range base64Scenarios {
|
||||
testBase64Scenario(t, tt)
|
||||
}
|
||||
genericScenarios := make([]interface{}, len(base64Scenarios))
|
||||
for i, s := range base64Scenarios {
|
||||
genericScenarios[i] = s
|
||||
}
|
||||
documentScenarios(t, "usage", "base64", genericScenarios, documentBase64Scenario)
|
||||
}
|
||||
+465
-86
@@ -3,50 +3,215 @@ package yqlib
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Kind uint32
|
||||
|
||||
const (
|
||||
SequenceNode Kind = 1 << iota
|
||||
MappingNode
|
||||
ScalarNode
|
||||
AliasNode
|
||||
)
|
||||
|
||||
type Style uint32
|
||||
|
||||
const (
|
||||
TaggedStyle Style = 1 << iota
|
||||
DoubleQuotedStyle
|
||||
SingleQuotedStyle
|
||||
LiteralStyle
|
||||
FoldedStyle
|
||||
FlowStyle
|
||||
)
|
||||
|
||||
func createStringScalarNode(stringValue string) *CandidateNode {
|
||||
var node = &CandidateNode{Kind: ScalarNode}
|
||||
node.Value = stringValue
|
||||
node.Tag = "!!str"
|
||||
return node
|
||||
}
|
||||
|
||||
func createScalarNode(value interface{}, stringValue string) *CandidateNode {
|
||||
var node = &CandidateNode{Kind: ScalarNode}
|
||||
node.Value = stringValue
|
||||
|
||||
switch value.(type) {
|
||||
case float32, float64:
|
||||
node.Tag = "!!float"
|
||||
case int, int64, int32:
|
||||
node.Tag = "!!int"
|
||||
case bool:
|
||||
node.Tag = "!!bool"
|
||||
case string:
|
||||
node.Tag = "!!str"
|
||||
case nil:
|
||||
node.Tag = "!!null"
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
type NodeInfo struct {
|
||||
Kind string `yaml:"kind"`
|
||||
Style string `yaml:"style,omitempty"`
|
||||
Anchor string `yaml:"anchor,omitempty"`
|
||||
Tag string `yaml:"tag,omitempty"`
|
||||
HeadComment string `yaml:"headComment,omitempty"`
|
||||
LineComment string `yaml:"lineComment,omitempty"`
|
||||
FootComment string `yaml:"footComment,omitempty"`
|
||||
Value string `yaml:"value,omitempty"`
|
||||
Line int `yaml:"line,omitempty"`
|
||||
Column int `yaml:"column,omitempty"`
|
||||
Content []*NodeInfo `yaml:"content,omitempty"`
|
||||
}
|
||||
|
||||
type CandidateNode struct {
|
||||
Node *yaml.Node // the actual node
|
||||
Kind Kind
|
||||
Style Style
|
||||
|
||||
Tag string
|
||||
Value string
|
||||
Anchor string
|
||||
Alias *CandidateNode
|
||||
Content []*CandidateNode
|
||||
|
||||
HeadComment string
|
||||
LineComment string
|
||||
FootComment string
|
||||
|
||||
Parent *CandidateNode // parent node
|
||||
Key *yaml.Node // node key, if this is a value from a map (or index in an array)
|
||||
Key *CandidateNode // node key, if this is a value from a map (or index in an array)
|
||||
|
||||
LeadingContent string
|
||||
|
||||
Path []interface{} /// the path we took to get to this node
|
||||
Document uint // the document index of this node
|
||||
Filename string
|
||||
FileIndex int
|
||||
document uint // the document index of this node
|
||||
filename string
|
||||
|
||||
Line int
|
||||
Column int
|
||||
|
||||
fileIndex int
|
||||
// when performing op against all nodes given, this will treat all the nodes as one
|
||||
// (e.g. top level cross document merge). This property does not propegate to child nodes.
|
||||
// (e.g. top level cross document merge). This property does not propagate to child nodes.
|
||||
EvaluateTogether bool
|
||||
IsMapKey bool
|
||||
}
|
||||
|
||||
func (n *CandidateNode) CreateChild() *CandidateNode {
|
||||
return &CandidateNode{
|
||||
Parent: n,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *CandidateNode) SetDocument(idx uint) {
|
||||
n.document = idx
|
||||
}
|
||||
|
||||
func (n *CandidateNode) GetDocument() uint {
|
||||
// defer to parent
|
||||
if n.Parent != nil {
|
||||
return n.Parent.GetDocument()
|
||||
}
|
||||
return n.document
|
||||
}
|
||||
|
||||
func (n *CandidateNode) SetFilename(name string) {
|
||||
n.filename = name
|
||||
}
|
||||
|
||||
func (n *CandidateNode) GetFilename() string {
|
||||
if n.Parent != nil {
|
||||
return n.Parent.GetFilename()
|
||||
}
|
||||
return n.filename
|
||||
}
|
||||
|
||||
func (n *CandidateNode) SetFileIndex(idx int) {
|
||||
n.fileIndex = idx
|
||||
}
|
||||
|
||||
func (n *CandidateNode) GetFileIndex() int {
|
||||
if n.Parent != nil {
|
||||
return n.Parent.GetFileIndex()
|
||||
}
|
||||
return n.fileIndex
|
||||
}
|
||||
|
||||
func (n *CandidateNode) GetKey() string {
|
||||
keyPrefix := ""
|
||||
if n.IsMapKey {
|
||||
keyPrefix = "key-"
|
||||
keyPrefix = fmt.Sprintf("key-%v-", n.Value)
|
||||
}
|
||||
return fmt.Sprintf("%v%v - %v", keyPrefix, n.Document, n.Path)
|
||||
key := ""
|
||||
if n.Key != nil {
|
||||
key = n.Key.Value
|
||||
}
|
||||
return fmt.Sprintf("%v%v - %v", keyPrefix, n.GetDocument(), key)
|
||||
}
|
||||
|
||||
func (n *CandidateNode) GetNiceTag() string {
|
||||
return unwrapDoc(n.Node).Tag
|
||||
func (n *CandidateNode) getParsedKey() interface{} {
|
||||
if n.IsMapKey {
|
||||
return n.Value
|
||||
}
|
||||
if n.Key == nil {
|
||||
return nil
|
||||
}
|
||||
if n.Key.Tag == "!!str" {
|
||||
return n.Key.Value
|
||||
}
|
||||
index, err := parseInt(n.Key.Value)
|
||||
if err != nil {
|
||||
return n.Key.Value
|
||||
}
|
||||
return index
|
||||
|
||||
}
|
||||
|
||||
func (n *CandidateNode) FilterMapContentByKey(keyPredicate func(*CandidateNode) bool) []*CandidateNode {
|
||||
var result []*CandidateNode
|
||||
for index := 0; index < len(n.Content); index = index + 2 {
|
||||
keyNode := n.Content[index]
|
||||
valueNode := n.Content[index+1]
|
||||
if keyPredicate(keyNode) {
|
||||
result = append(result, keyNode, valueNode)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (n *CandidateNode) GetPath() []interface{} {
|
||||
key := n.getParsedKey()
|
||||
if n.Parent != nil && key != nil {
|
||||
return append(n.Parent.GetPath(), key)
|
||||
}
|
||||
|
||||
if key != nil {
|
||||
return []interface{}{key}
|
||||
}
|
||||
return make([]interface{}, 0)
|
||||
}
|
||||
|
||||
func (n *CandidateNode) GetNicePath() string {
|
||||
if n.Path != nil && len(n.Path) >= 0 {
|
||||
pathStr := make([]string, len(n.Path))
|
||||
for i, v := range n.Path {
|
||||
pathStr[i] = fmt.Sprintf("%v", v)
|
||||
var sb strings.Builder
|
||||
path := n.GetPath()
|
||||
for i, element := range path {
|
||||
elementStr := fmt.Sprintf("%v", element)
|
||||
switch element.(type) {
|
||||
case int:
|
||||
sb.WriteString("[" + elementStr + "]")
|
||||
default:
|
||||
if i == 0 {
|
||||
sb.WriteString(elementStr)
|
||||
} else if strings.ContainsRune(elementStr, '.') {
|
||||
sb.WriteString("[" + elementStr + "]")
|
||||
} else {
|
||||
sb.WriteString("." + elementStr)
|
||||
}
|
||||
}
|
||||
return strings.Join(pathStr, ".")
|
||||
}
|
||||
return ""
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (n *CandidateNode) AsList() *list.List {
|
||||
@@ -55,107 +220,321 @@ func (n *CandidateNode) AsList() *list.List {
|
||||
return elMap
|
||||
}
|
||||
|
||||
func (n *CandidateNode) CreateChildInMap(key *yaml.Node, node *yaml.Node) *CandidateNode {
|
||||
var value interface{}
|
||||
if key != nil {
|
||||
value = key.Value
|
||||
func (n *CandidateNode) SetParent(parent *CandidateNode) {
|
||||
n.Parent = parent
|
||||
}
|
||||
|
||||
type ValueVisitor func(*CandidateNode) error
|
||||
|
||||
func (n *CandidateNode) VisitValues(visitor ValueVisitor) error {
|
||||
switch n.Kind {
|
||||
case MappingNode:
|
||||
for i := 1; i < len(n.Content); i = i + 2 {
|
||||
if err := visitor(n.Content[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case SequenceNode:
|
||||
for i := 0; i < len(n.Content); i = i + 1 {
|
||||
if err := visitor(n.Content[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return &CandidateNode{
|
||||
Node: node,
|
||||
Path: n.createChildPath(value),
|
||||
Parent: n,
|
||||
Key: key,
|
||||
Document: n.Document,
|
||||
Filename: n.Filename,
|
||||
FileIndex: n.FileIndex,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *CandidateNode) CanVisitValues() bool {
|
||||
return n.Kind == MappingNode || n.Kind == SequenceNode
|
||||
}
|
||||
|
||||
func (n *CandidateNode) AddKeyValueChild(rawKey *CandidateNode, rawValue *CandidateNode) (*CandidateNode, *CandidateNode) {
|
||||
key := rawKey.Copy()
|
||||
key.SetParent(n)
|
||||
key.IsMapKey = true
|
||||
|
||||
value := rawValue.Copy()
|
||||
value.SetParent(n)
|
||||
value.IsMapKey = false // force this, incase we are creating a value from a key
|
||||
value.Key = key
|
||||
|
||||
n.Content = append(n.Content, key, value)
|
||||
return key, value
|
||||
}
|
||||
|
||||
func (n *CandidateNode) AddChild(rawChild *CandidateNode) {
|
||||
value := rawChild.Copy()
|
||||
value.SetParent(n)
|
||||
value.IsMapKey = false
|
||||
|
||||
index := len(n.Content)
|
||||
keyNode := createScalarNode(index, fmt.Sprintf("%v", index))
|
||||
keyNode.SetParent(n)
|
||||
value.Key = keyNode
|
||||
|
||||
n.Content = append(n.Content, value)
|
||||
}
|
||||
|
||||
func (n *CandidateNode) AddChildren(children []*CandidateNode) {
|
||||
if n.Kind == MappingNode {
|
||||
for i := 0; i < len(children); i += 2 {
|
||||
key := children[i]
|
||||
value := children[i+1]
|
||||
n.AddKeyValueChild(key, value)
|
||||
}
|
||||
|
||||
} else {
|
||||
for _, rawChild := range children {
|
||||
n.AddChild(rawChild)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *CandidateNode) CreateChildInArray(index int, node *yaml.Node) *CandidateNode {
|
||||
return &CandidateNode{
|
||||
Node: node,
|
||||
Path: n.createChildPath(index),
|
||||
Parent: n,
|
||||
Key: &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", index), Tag: "!!int"},
|
||||
Document: n.Document,
|
||||
Filename: n.Filename,
|
||||
FileIndex: n.FileIndex,
|
||||
func (n *CandidateNode) GetValueRep() (interface{}, error) {
|
||||
log.Debugf("GetValueRep for %v value: %v", n.GetNicePath(), n.Value)
|
||||
realTag := n.guessTagFromCustomType()
|
||||
|
||||
switch realTag {
|
||||
case "!!int":
|
||||
_, val, err := parseInt64(n.Value)
|
||||
return val, err
|
||||
case "!!float":
|
||||
// need to test this
|
||||
return strconv.ParseFloat(n.Value, 64)
|
||||
case "!!bool":
|
||||
return isTruthyNode(n), nil
|
||||
case "!!null":
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return n.Value, nil
|
||||
}
|
||||
|
||||
func (n *CandidateNode) CreateReplacement(node *yaml.Node) *CandidateNode {
|
||||
return &CandidateNode{
|
||||
Node: node,
|
||||
Path: n.createChildPath(nil),
|
||||
Parent: n.Parent,
|
||||
Key: n.Key,
|
||||
IsMapKey: n.IsMapKey,
|
||||
Document: n.Document,
|
||||
Filename: n.Filename,
|
||||
FileIndex: n.FileIndex,
|
||||
func (n *CandidateNode) guessTagFromCustomType() string {
|
||||
if strings.HasPrefix(n.Tag, "!!") {
|
||||
return n.Tag
|
||||
} else if n.Value == "" {
|
||||
log.Debug("guessTagFromCustomType: node has no value to guess the type with")
|
||||
return n.Tag
|
||||
}
|
||||
dataBucket, errorReading := parseSnippet(n.Value)
|
||||
|
||||
if errorReading != nil {
|
||||
log.Debug("guessTagFromCustomType: could not guess underlying tag type %v", errorReading)
|
||||
return n.Tag
|
||||
}
|
||||
guessedTag := dataBucket.Tag
|
||||
log.Info("im guessing the tag %v is a %v", n.Tag, guessedTag)
|
||||
return guessedTag
|
||||
}
|
||||
|
||||
func (n *CandidateNode) createChildPath(path interface{}) []interface{} {
|
||||
if path == nil {
|
||||
newPath := make([]interface{}, len(n.Path))
|
||||
copy(newPath, n.Path)
|
||||
return newPath
|
||||
func (n *CandidateNode) CreateReplacement(kind Kind, tag string, value string) *CandidateNode {
|
||||
node := &CandidateNode{
|
||||
Kind: kind,
|
||||
Tag: tag,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
//don't use append as they may actually modify the path of the orignal node!
|
||||
newPath := make([]interface{}, len(n.Path)+1)
|
||||
copy(newPath, n.Path)
|
||||
newPath[len(n.Path)] = path
|
||||
return newPath
|
||||
return n.CopyAsReplacement(node)
|
||||
}
|
||||
|
||||
func (n *CandidateNode) Copy() (*CandidateNode, error) {
|
||||
clone := &CandidateNode{}
|
||||
err := copier.Copy(clone, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (n *CandidateNode) CopyAsReplacement(replacement *CandidateNode) *CandidateNode {
|
||||
newCopy := replacement.Copy()
|
||||
newCopy.Parent = n.Parent
|
||||
|
||||
if n.IsMapKey {
|
||||
newCopy.Key = n
|
||||
} else {
|
||||
newCopy.Key = n.Key
|
||||
}
|
||||
return clone, nil
|
||||
|
||||
return newCopy
|
||||
}
|
||||
|
||||
func (n *CandidateNode) CreateReplacementWithComments(kind Kind, tag string, style Style) *CandidateNode {
|
||||
replacement := n.CreateReplacement(kind, tag, "")
|
||||
replacement.LeadingContent = n.LeadingContent
|
||||
replacement.HeadComment = n.HeadComment
|
||||
replacement.LineComment = n.LineComment
|
||||
replacement.FootComment = n.FootComment
|
||||
replacement.Style = style
|
||||
return replacement
|
||||
}
|
||||
|
||||
func (n *CandidateNode) Copy() *CandidateNode {
|
||||
return n.doCopy(true)
|
||||
}
|
||||
|
||||
func (n *CandidateNode) CopyWithoutContent() *CandidateNode {
|
||||
return n.doCopy(false)
|
||||
}
|
||||
|
||||
func (n *CandidateNode) doCopy(cloneContent bool) *CandidateNode {
|
||||
var content []*CandidateNode
|
||||
|
||||
var copyKey *CandidateNode
|
||||
if n.Key != nil {
|
||||
copyKey = n.Key.Copy()
|
||||
}
|
||||
|
||||
clone := &CandidateNode{
|
||||
Kind: n.Kind,
|
||||
Style: n.Style,
|
||||
|
||||
Tag: n.Tag,
|
||||
Value: n.Value,
|
||||
Anchor: n.Anchor,
|
||||
|
||||
// ok not to clone this,
|
||||
// as its a reference to somewhere else.
|
||||
Alias: n.Alias,
|
||||
Content: content,
|
||||
|
||||
HeadComment: n.HeadComment,
|
||||
LineComment: n.LineComment,
|
||||
FootComment: n.FootComment,
|
||||
|
||||
Parent: n.Parent,
|
||||
Key: copyKey,
|
||||
|
||||
LeadingContent: n.LeadingContent,
|
||||
|
||||
document: n.document,
|
||||
filename: n.filename,
|
||||
fileIndex: n.fileIndex,
|
||||
|
||||
Line: n.Line,
|
||||
Column: n.Column,
|
||||
|
||||
EvaluateTogether: n.EvaluateTogether,
|
||||
IsMapKey: n.IsMapKey,
|
||||
}
|
||||
|
||||
if cloneContent {
|
||||
clone.AddChildren(n.Content)
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// updates this candidate from the given candidate node
|
||||
func (n *CandidateNode) UpdateFrom(other *CandidateNode, prefs assignPreferences) {
|
||||
if n == other {
|
||||
log.Debugf("UpdateFrom, no need to update from myself.")
|
||||
return
|
||||
}
|
||||
// if this is an empty map or empty array, use the style of other node.
|
||||
if (n.Kind != ScalarNode && len(n.Content) == 0) ||
|
||||
// if the tag has changed (e.g. from str to bool)
|
||||
(n.guessTagFromCustomType() != other.guessTagFromCustomType()) {
|
||||
n.Style = other.Style
|
||||
}
|
||||
|
||||
n.Content = make([]*CandidateNode, 0)
|
||||
n.Kind = other.Kind
|
||||
n.AddChildren(other.Content)
|
||||
|
||||
n.Value = other.Value
|
||||
|
||||
n.UpdateAttributesFrom(other, prefs)
|
||||
n.Node.Content = other.Node.Content
|
||||
n.Node.Value = other.Node.Value
|
||||
|
||||
}
|
||||
|
||||
func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignPreferences) {
|
||||
log.Debug("UpdateAttributesFrom: n: %v other: %v", n.GetKey(), other.GetKey())
|
||||
if n.Node.Kind != other.Node.Kind {
|
||||
log.Debug("UpdateAttributesFrom: n: %v other: %v", NodeToString(n), NodeToString(other))
|
||||
if n.Kind != other.Kind {
|
||||
// clear out the contents when switching to a different type
|
||||
// e.g. map to array
|
||||
n.Node.Content = make([]*yaml.Node, 0)
|
||||
n.Node.Value = ""
|
||||
n.Content = make([]*CandidateNode, 0)
|
||||
n.Value = ""
|
||||
}
|
||||
n.Node.Kind = other.Node.Kind
|
||||
n.Node.Tag = other.Node.Tag
|
||||
n.Node.Alias = other.Node.Alias
|
||||
n.Kind = other.Kind
|
||||
|
||||
// don't clobber custom tags...
|
||||
if prefs.ClobberCustomTags || strings.HasPrefix(n.Tag, "!!") || n.Tag == "" {
|
||||
n.Tag = other.Tag
|
||||
}
|
||||
|
||||
n.Alias = other.Alias
|
||||
|
||||
if !prefs.DontOverWriteAnchor {
|
||||
n.Node.Anchor = other.Node.Anchor
|
||||
n.Anchor = other.Anchor
|
||||
}
|
||||
|
||||
// merge will pickup the style of the new thing
|
||||
// when autocreating nodes
|
||||
if n.Node.Style == 0 {
|
||||
n.Node.Style = other.Node.Style
|
||||
|
||||
if n.Style == 0 {
|
||||
n.Style = other.Style
|
||||
}
|
||||
|
||||
if other.Node.FootComment != "" {
|
||||
n.Node.FootComment = other.Node.FootComment
|
||||
if other.FootComment != "" {
|
||||
n.FootComment = other.FootComment
|
||||
}
|
||||
if other.Node.HeadComment != "" {
|
||||
n.Node.HeadComment = other.Node.HeadComment
|
||||
if other.HeadComment != "" {
|
||||
n.HeadComment = other.HeadComment
|
||||
}
|
||||
if other.Node.LineComment != "" {
|
||||
n.Node.LineComment = other.Node.LineComment
|
||||
if other.LineComment != "" {
|
||||
n.LineComment = other.LineComment
|
||||
}
|
||||
}
|
||||
|
||||
func (n *CandidateNode) ConvertToNodeInfo() *NodeInfo {
|
||||
info := &NodeInfo{
|
||||
Kind: kindToString(n.Kind),
|
||||
Style: styleToString(n.Style),
|
||||
Anchor: n.Anchor,
|
||||
Tag: n.Tag,
|
||||
HeadComment: n.HeadComment,
|
||||
LineComment: n.LineComment,
|
||||
FootComment: n.FootComment,
|
||||
Value: n.Value,
|
||||
Line: n.Line,
|
||||
Column: n.Column,
|
||||
}
|
||||
if len(n.Content) > 0 {
|
||||
info.Content = make([]*NodeInfo, len(n.Content))
|
||||
for i, child := range n.Content {
|
||||
info.Content[i] = child.ConvertToNodeInfo()
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// Helper functions to convert Kind and Style to string for NodeInfo
|
||||
func kindToString(k Kind) string {
|
||||
switch k {
|
||||
case SequenceNode:
|
||||
return "SequenceNode"
|
||||
case MappingNode:
|
||||
return "MappingNode"
|
||||
case ScalarNode:
|
||||
return "ScalarNode"
|
||||
case AliasNode:
|
||||
return "AliasNode"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func styleToString(s Style) string {
|
||||
var styles []string
|
||||
if s&TaggedStyle != 0 {
|
||||
styles = append(styles, "TaggedStyle")
|
||||
}
|
||||
if s&DoubleQuotedStyle != 0 {
|
||||
styles = append(styles, "DoubleQuotedStyle")
|
||||
}
|
||||
if s&SingleQuotedStyle != 0 {
|
||||
styles = append(styles, "SingleQuotedStyle")
|
||||
}
|
||||
if s&LiteralStyle != 0 {
|
||||
styles = append(styles, "LiteralStyle")
|
||||
}
|
||||
if s&FoldedStyle != 0 {
|
||||
styles = append(styles, "FoldedStyle")
|
||||
}
|
||||
if s&FlowStyle != 0 {
|
||||
styles = append(styles, "FlowStyle")
|
||||
}
|
||||
return strings.Join(styles, ",")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
"github.com/goccy/go-yaml/ast"
|
||||
goccyToken "github.com/goccy/go-yaml/token"
|
||||
)
|
||||
|
||||
func (o *CandidateNode) goccyDecodeIntoChild(childNode ast.Node, cm yaml.CommentMap, anchorMap map[string]*CandidateNode) (*CandidateNode, error) {
|
||||
newChild := o.CreateChild()
|
||||
|
||||
err := newChild.UnmarshalGoccyYAML(childNode, cm, anchorMap)
|
||||
return newChild, err
|
||||
}
|
||||
|
||||
func (o *CandidateNode) UnmarshalGoccyYAML(node ast.Node, cm yaml.CommentMap, anchorMap map[string]*CandidateNode) error {
|
||||
log.Debugf("UnmarshalYAML %v", node)
|
||||
log.Debugf("UnmarshalYAML %v", node.Type().String())
|
||||
log.Debugf("UnmarshalYAML Node Value: %v", node.String())
|
||||
log.Debugf("UnmarshalYAML Node GetComment: %v", node.GetComment())
|
||||
|
||||
if node.GetComment() != nil {
|
||||
commentMapComments := cm[node.GetPath()]
|
||||
for _, comment := range node.GetComment().Comments {
|
||||
// need to use the comment map to find the position :/
|
||||
log.Debugf("%v has a comment of [%v]", node.GetPath(), comment.Token.Value)
|
||||
for _, commentMapComment := range commentMapComments {
|
||||
commentMapValue := strings.Join(commentMapComment.Texts, "\n")
|
||||
if commentMapValue == comment.Token.Value {
|
||||
log.Debug("found a matching entry in comment map")
|
||||
// we found the comment in the comment map,
|
||||
// now we can process the position
|
||||
switch commentMapComment.Position {
|
||||
case yaml.CommentHeadPosition:
|
||||
o.HeadComment = comment.String()
|
||||
log.Debug("its a head comment %v", comment.String())
|
||||
case yaml.CommentLinePosition:
|
||||
o.LineComment = comment.String()
|
||||
log.Debug("its a line comment %v", comment.String())
|
||||
case yaml.CommentFootPosition:
|
||||
o.FootComment = comment.String()
|
||||
log.Debug("its a foot comment %v", comment.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
o.Value = node.String()
|
||||
o.Line = node.GetToken().Position.Line
|
||||
o.Column = node.GetToken().Position.Column
|
||||
|
||||
switch node.Type() {
|
||||
case ast.IntegerType:
|
||||
o.Kind = ScalarNode
|
||||
o.Tag = "!!int"
|
||||
case ast.FloatType:
|
||||
o.Kind = ScalarNode
|
||||
o.Tag = "!!float"
|
||||
case ast.BoolType:
|
||||
o.Kind = ScalarNode
|
||||
o.Tag = "!!bool"
|
||||
case ast.NullType:
|
||||
log.Debugf("its a null type with value %v", node.GetToken().Value)
|
||||
o.Kind = ScalarNode
|
||||
o.Tag = "!!null"
|
||||
o.Value = node.GetToken().Value
|
||||
if node.GetToken().Type == goccyToken.ImplicitNullType {
|
||||
o.Value = ""
|
||||
}
|
||||
case ast.StringType:
|
||||
o.Kind = ScalarNode
|
||||
o.Tag = "!!str"
|
||||
switch node.GetToken().Type {
|
||||
case goccyToken.SingleQuoteType:
|
||||
o.Style = SingleQuotedStyle
|
||||
case goccyToken.DoubleQuoteType:
|
||||
o.Style = DoubleQuotedStyle
|
||||
}
|
||||
o.Value = node.(*ast.StringNode).Value
|
||||
log.Debugf("string value %v", node.(*ast.StringNode).Value)
|
||||
case ast.LiteralType:
|
||||
o.Kind = ScalarNode
|
||||
o.Tag = "!!str"
|
||||
o.Style = LiteralStyle
|
||||
astLiteral := node.(*ast.LiteralNode)
|
||||
log.Debugf("astLiteral.Start.Type %v", astLiteral.Start.Type)
|
||||
if astLiteral.Start.Type == goccyToken.FoldedType {
|
||||
log.Debugf("folded Type %v", astLiteral.Start.Type)
|
||||
o.Style = FoldedStyle
|
||||
}
|
||||
log.Debug("start value: %v ", node.(*ast.LiteralNode).Start.Value)
|
||||
log.Debug("start value: %v ", node.(*ast.LiteralNode).Start.Type)
|
||||
// TODO: here I could put the original value with line breaks
|
||||
// to solve the multiline > problem
|
||||
o.Value = astLiteral.Value.Value
|
||||
case ast.TagType:
|
||||
if err := o.UnmarshalGoccyYAML(node.(*ast.TagNode).Value, cm, anchorMap); err != nil {
|
||||
return err
|
||||
}
|
||||
o.Tag = node.(*ast.TagNode).Start.Value
|
||||
case ast.MappingType:
|
||||
log.Debugf("UnmarshalYAML - a mapping node")
|
||||
o.Kind = MappingNode
|
||||
o.Tag = "!!map"
|
||||
|
||||
mappingNode := node.(*ast.MappingNode)
|
||||
if mappingNode.IsFlowStyle {
|
||||
o.Style = FlowStyle
|
||||
}
|
||||
for _, mappingValueNode := range mappingNode.Values {
|
||||
err := o.goccyProcessMappingValueNode(mappingValueNode, cm, anchorMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mappingNode.FootComment != nil {
|
||||
log.Debugf("mapping node has a foot comment of: %v", mappingNode.FootComment)
|
||||
o.FootComment = mappingNode.FootComment.String()
|
||||
}
|
||||
case ast.MappingValueType:
|
||||
log.Debugf("UnmarshalYAML - a mapping node")
|
||||
o.Kind = MappingNode
|
||||
o.Tag = "!!map"
|
||||
mappingValueNode := node.(*ast.MappingValueNode)
|
||||
err := o.goccyProcessMappingValueNode(mappingValueNode, cm, anchorMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case ast.SequenceType:
|
||||
log.Debugf("UnmarshalYAML - a sequence node")
|
||||
o.Kind = SequenceNode
|
||||
o.Tag = "!!seq"
|
||||
sequenceNode := node.(*ast.SequenceNode)
|
||||
if sequenceNode.IsFlowStyle {
|
||||
o.Style = FlowStyle
|
||||
}
|
||||
astSeq := sequenceNode.Values
|
||||
o.Content = make([]*CandidateNode, len(astSeq))
|
||||
for i := 0; i < len(astSeq); i++ {
|
||||
keyNode := o.CreateChild()
|
||||
keyNode.IsMapKey = true
|
||||
keyNode.Tag = "!!int"
|
||||
keyNode.Kind = ScalarNode
|
||||
keyNode.Value = fmt.Sprintf("%v", i)
|
||||
|
||||
valueNode, err := o.goccyDecodeIntoChild(astSeq[i], cm, anchorMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
valueNode.Key = keyNode
|
||||
o.Content[i] = valueNode
|
||||
}
|
||||
case ast.AnchorType:
|
||||
log.Debugf("UnmarshalYAML - an anchor node")
|
||||
anchorNode := node.(*ast.AnchorNode)
|
||||
err := o.UnmarshalGoccyYAML(anchorNode.Value, cm, anchorMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Anchor = anchorNode.Name.String()
|
||||
anchorMap[o.Anchor] = o
|
||||
|
||||
case ast.AliasType:
|
||||
log.Debugf("UnmarshalYAML - an alias node")
|
||||
aliasNode := node.(*ast.AliasNode)
|
||||
o.Kind = AliasNode
|
||||
o.Value = aliasNode.Value.String()
|
||||
o.Alias = anchorMap[o.Value]
|
||||
|
||||
case ast.MergeKeyType:
|
||||
log.Debugf("UnmarshalYAML - a merge key")
|
||||
o.Kind = ScalarNode
|
||||
o.Tag = "!!merge" // note - I should be able to get rid of this.
|
||||
o.Value = "<<"
|
||||
|
||||
default:
|
||||
log.Debugf("UnmarshalYAML - no idea of the type!!\n%v: %v", node.Type(), node.String())
|
||||
}
|
||||
log.Debugf("KIND: %v", o.Kind)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *CandidateNode) goccyProcessMappingValueNode(mappingEntry *ast.MappingValueNode, cm yaml.CommentMap, anchorMap map[string]*CandidateNode) error {
|
||||
log.Debug("UnmarshalYAML MAP KEY entry %v", mappingEntry.Key)
|
||||
|
||||
// AddKeyValueFirst because it clones the nodes, and we want to have the real refs when Unmarshalling
|
||||
// particularly for the anchorMap
|
||||
keyNode, valueNode := o.AddKeyValueChild(&CandidateNode{}, &CandidateNode{})
|
||||
|
||||
if err := keyNode.UnmarshalGoccyYAML(mappingEntry.Key, cm, anchorMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("UnmarshalYAML MAP VALUE entry %v", mappingEntry.Value)
|
||||
if err := valueNode.UnmarshalGoccyYAML(mappingEntry.Value, cm, anchorMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mappingEntry.FootComment != nil {
|
||||
valueNode.FootComment = mappingEntry.FootComment.String()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
)
|
||||
|
||||
type valueRepScenario struct {
|
||||
input string
|
||||
tag string
|
||||
expected interface{}
|
||||
}
|
||||
|
||||
var valueRepScenarios = []valueRepScenario{
|
||||
{
|
||||
input: `"cat"`,
|
||||
expected: `"cat"`,
|
||||
},
|
||||
{
|
||||
input: `3`,
|
||||
expected: int64(3),
|
||||
},
|
||||
{
|
||||
input: `3.1`,
|
||||
expected: float64(3.1),
|
||||
},
|
||||
{
|
||||
input: `true`,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
input: `y`,
|
||||
tag: "!!bool",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
tag: "!!null",
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
func TestCandidateNodeGetValueRepScenarios(t *testing.T) {
|
||||
for _, tt := range valueRepScenarios {
|
||||
node := CandidateNode{Value: tt.input, Tag: tt.tag}
|
||||
actual, err := node.GetValueRep()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
test.AssertResult(t, tt.expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateNodeChildWhenParentUpdated(t *testing.T) {
|
||||
parent := CandidateNode{}
|
||||
child := parent.CreateChild()
|
||||
parent.SetDocument(1)
|
||||
parent.SetFileIndex(2)
|
||||
parent.SetFilename("meow")
|
||||
test.AssertResultWithContext(t, "meow", child.GetFilename(), "filename")
|
||||
test.AssertResultWithContext(t, 2, child.GetFileIndex(), "file index")
|
||||
test.AssertResultWithContext(t, uint(1), child.GetDocument(), "document index")
|
||||
}
|
||||
|
||||
type createScalarNodeScenario struct {
|
||||
value interface{}
|
||||
stringValue string
|
||||
expectedTag string
|
||||
}
|
||||
|
||||
var createScalarScenarios = []createScalarNodeScenario{
|
||||
{
|
||||
value: "mike",
|
||||
stringValue: "mike",
|
||||
expectedTag: "!!str",
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
stringValue: "3",
|
||||
expectedTag: "!!int",
|
||||
},
|
||||
{
|
||||
value: 3.1,
|
||||
stringValue: "3.1",
|
||||
expectedTag: "!!float",
|
||||
},
|
||||
{
|
||||
value: true,
|
||||
stringValue: "true",
|
||||
expectedTag: "!!bool",
|
||||
},
|
||||
{
|
||||
value: nil,
|
||||
stringValue: "~",
|
||||
expectedTag: "!!null",
|
||||
},
|
||||
}
|
||||
|
||||
func TestCreateScalarNodeScenarios(t *testing.T) {
|
||||
for _, tt := range createScalarScenarios {
|
||||
actual := createScalarNode(tt.value, tt.stringValue)
|
||||
test.AssertResultWithContext(t, tt.stringValue, actual.Value, fmt.Sprintf("Value for: Value: [%v], String: %v", tt.value, tt.stringValue))
|
||||
test.AssertResultWithContext(t, tt.expectedTag, actual.Tag, fmt.Sprintf("Value for: Value: [%v], String: %v", tt.value, tt.stringValue))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetKeyForMapValue(t *testing.T) {
|
||||
key := createStringScalarNode("yourKey")
|
||||
n := CandidateNode{Key: key, Value: "meow", document: 3}
|
||||
test.AssertResult(t, "3 - yourKey", n.GetKey())
|
||||
}
|
||||
|
||||
func TestGetKeyForMapKey(t *testing.T) {
|
||||
key := createStringScalarNode("yourKey")
|
||||
key.IsMapKey = true
|
||||
key.document = 3
|
||||
test.AssertResult(t, "key-yourKey-3 - ", key.GetKey())
|
||||
}
|
||||
|
||||
func TestGetKeyForValue(t *testing.T) {
|
||||
n := CandidateNode{Value: "meow", document: 3}
|
||||
test.AssertResult(t, "3 - ", n.GetKey())
|
||||
}
|
||||
|
||||
func TestGetParsedKeyForMapKey(t *testing.T) {
|
||||
key := createStringScalarNode("yourKey")
|
||||
key.IsMapKey = true
|
||||
key.document = 3
|
||||
test.AssertResult(t, "yourKey", key.getParsedKey())
|
||||
}
|
||||
|
||||
func TestGetParsedKeyForLooseValue(t *testing.T) {
|
||||
n := CandidateNode{Value: "meow", document: 3}
|
||||
test.AssertResult(t, nil, n.getParsedKey())
|
||||
}
|
||||
|
||||
func TestGetParsedKeyForMapValue(t *testing.T) {
|
||||
key := createStringScalarNode("yourKey")
|
||||
n := CandidateNode{Key: key, Value: "meow", document: 3}
|
||||
test.AssertResult(t, "yourKey", n.getParsedKey())
|
||||
}
|
||||
|
||||
func TestGetParsedKeyForArrayValue(t *testing.T) {
|
||||
key := createScalarNode(4, "4")
|
||||
n := CandidateNode{Key: key, Value: "meow", document: 3}
|
||||
test.AssertResult(t, 4, n.getParsedKey())
|
||||
}
|
||||
|
||||
func TestCandidateNodeAddKeyValueChild(t *testing.T) {
|
||||
key := CandidateNode{Value: "cool", IsMapKey: true}
|
||||
node := CandidateNode{}
|
||||
|
||||
_, keyIsValueNow := node.AddKeyValueChild(&CandidateNode{Value: "newKey"}, &key)
|
||||
|
||||
test.AssertResult(t, keyIsValueNow.IsMapKey, false)
|
||||
test.AssertResult(t, key.IsMapKey, true)
|
||||
|
||||
}
|
||||
|
||||
func TestConvertToNodeInfo(t *testing.T) {
|
||||
child := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Style: DoubleQuotedStyle,
|
||||
Tag: "!!str",
|
||||
Value: "childValue",
|
||||
Line: 2,
|
||||
Column: 3,
|
||||
}
|
||||
parent := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Style: TaggedStyle,
|
||||
Tag: "!!map",
|
||||
Value: "",
|
||||
Line: 1,
|
||||
Column: 1,
|
||||
Content: []*CandidateNode{child},
|
||||
HeadComment: "head",
|
||||
LineComment: "line",
|
||||
FootComment: "foot",
|
||||
Anchor: "anchor",
|
||||
}
|
||||
info := parent.ConvertToNodeInfo()
|
||||
test.AssertResult(t, "MappingNode", info.Kind)
|
||||
test.AssertResult(t, "TaggedStyle", info.Style)
|
||||
test.AssertResult(t, "!!map", info.Tag)
|
||||
test.AssertResult(t, "head", info.HeadComment)
|
||||
test.AssertResult(t, "line", info.LineComment)
|
||||
test.AssertResult(t, "foot", info.FootComment)
|
||||
test.AssertResult(t, "anchor", info.Anchor)
|
||||
test.AssertResult(t, 1, info.Line)
|
||||
test.AssertResult(t, 1, info.Column)
|
||||
if len(info.Content) != 1 {
|
||||
t.Fatalf("Expected 1 child, got %d", len(info.Content))
|
||||
}
|
||||
childInfo := info.Content[0]
|
||||
test.AssertResult(t, "ScalarNode", childInfo.Kind)
|
||||
test.AssertResult(t, "DoubleQuotedStyle", childInfo.Style)
|
||||
test.AssertResult(t, "!!str", childInfo.Tag)
|
||||
test.AssertResult(t, "childValue", childInfo.Value)
|
||||
test.AssertResult(t, 2, childInfo.Line)
|
||||
test.AssertResult(t, 3, childInfo.Column)
|
||||
}
|
||||
|
||||
func TestCandidateNodeGetPath(t *testing.T) {
|
||||
// Test root node with no parent
|
||||
root := CandidateNode{Value: "root"}
|
||||
path := root.GetPath()
|
||||
test.AssertResult(t, 0, len(path))
|
||||
|
||||
// Test node with key
|
||||
key := createStringScalarNode("myKey")
|
||||
node := CandidateNode{Key: key, Value: "myValue"}
|
||||
path = node.GetPath()
|
||||
test.AssertResult(t, 1, len(path))
|
||||
test.AssertResult(t, "myKey", path[0])
|
||||
|
||||
// Test nested path
|
||||
parent := CandidateNode{}
|
||||
parentKey := createStringScalarNode("parent")
|
||||
parent.Key = parentKey
|
||||
node.Parent = &parent
|
||||
path = node.GetPath()
|
||||
test.AssertResult(t, 2, len(path))
|
||||
test.AssertResult(t, "parent", path[0])
|
||||
test.AssertResult(t, "myKey", path[1])
|
||||
}
|
||||
|
||||
func TestCandidateNodeGetNicePath(t *testing.T) {
|
||||
// Test simple key
|
||||
key := createStringScalarNode("simple")
|
||||
node := CandidateNode{Key: key}
|
||||
nicePath := node.GetNicePath()
|
||||
test.AssertResult(t, "simple", nicePath)
|
||||
|
||||
// Test array index
|
||||
arrayKey := createScalarNode(0, "0")
|
||||
arrayNode := CandidateNode{Key: arrayKey}
|
||||
nicePath = arrayNode.GetNicePath()
|
||||
test.AssertResult(t, "[0]", nicePath)
|
||||
|
||||
dotKey := createStringScalarNode("key.with.dots")
|
||||
dotNode := CandidateNode{Key: dotKey}
|
||||
nicePath = dotNode.GetNicePath()
|
||||
test.AssertResult(t, "key.with.dots", nicePath)
|
||||
|
||||
// Test nested path
|
||||
parentKey := createStringScalarNode("parent")
|
||||
parent := CandidateNode{Key: parentKey}
|
||||
childKey := createStringScalarNode("child")
|
||||
child := CandidateNode{Key: childKey, Parent: &parent}
|
||||
nicePath = child.GetNicePath()
|
||||
test.AssertResult(t, "parent.child", nicePath)
|
||||
}
|
||||
|
||||
func TestCandidateNodeFilterMapContentByKey(t *testing.T) {
|
||||
// Create a map with multiple key-value pairs
|
||||
key1 := createStringScalarNode("key1")
|
||||
value1 := createStringScalarNode("value1")
|
||||
key2 := createStringScalarNode("key2")
|
||||
value2 := createStringScalarNode("value2")
|
||||
key3 := createStringScalarNode("key3")
|
||||
value3 := createStringScalarNode("value3")
|
||||
|
||||
mapNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Content: []*CandidateNode{key1, value1, key2, value2, key3, value3},
|
||||
}
|
||||
|
||||
// Filter by key predicate that matches key1 and key3
|
||||
filtered := mapNode.FilterMapContentByKey(func(key *CandidateNode) bool {
|
||||
return key.Value == "key1" || key.Value == "key3"
|
||||
})
|
||||
|
||||
// Should return key1, value1, key3, value3
|
||||
test.AssertResult(t, 4, len(filtered))
|
||||
test.AssertResult(t, "key1", filtered[0].Value)
|
||||
test.AssertResult(t, "value1", filtered[1].Value)
|
||||
test.AssertResult(t, "key3", filtered[2].Value)
|
||||
test.AssertResult(t, "value3", filtered[3].Value)
|
||||
}
|
||||
|
||||
func TestCandidateNodeVisitValues(t *testing.T) {
|
||||
// Test mapping node
|
||||
key1 := createStringScalarNode("key1")
|
||||
value1 := createStringScalarNode("value1")
|
||||
key2 := createStringScalarNode("key2")
|
||||
value2 := createStringScalarNode("value2")
|
||||
|
||||
mapNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Content: []*CandidateNode{key1, value1, key2, value2},
|
||||
}
|
||||
|
||||
var visited []string
|
||||
err := mapNode.VisitValues(func(node *CandidateNode) error {
|
||||
visited = append(visited, node.Value)
|
||||
return nil
|
||||
})
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
test.AssertResult(t, 2, len(visited))
|
||||
test.AssertResult(t, "value1", visited[0])
|
||||
test.AssertResult(t, "value2", visited[1])
|
||||
|
||||
// Test sequence node
|
||||
item1 := createStringScalarNode("item1")
|
||||
item2 := createStringScalarNode("item2")
|
||||
|
||||
seqNode := &CandidateNode{
|
||||
Kind: SequenceNode,
|
||||
Content: []*CandidateNode{item1, item2},
|
||||
}
|
||||
|
||||
visited = []string{}
|
||||
err = seqNode.VisitValues(func(node *CandidateNode) error {
|
||||
visited = append(visited, node.Value)
|
||||
return nil
|
||||
})
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
test.AssertResult(t, 2, len(visited))
|
||||
test.AssertResult(t, "item1", visited[0])
|
||||
test.AssertResult(t, "item2", visited[1])
|
||||
|
||||
// Test scalar node (should not visit anything)
|
||||
scalarNode := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Value: "scalar",
|
||||
}
|
||||
|
||||
visited = []string{}
|
||||
err = scalarNode.VisitValues(func(node *CandidateNode) error {
|
||||
visited = append(visited, node.Value)
|
||||
return nil
|
||||
})
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
test.AssertResult(t, 0, len(visited))
|
||||
}
|
||||
|
||||
func TestCandidateNodeCanVisitValues(t *testing.T) {
|
||||
mapNode := &CandidateNode{Kind: MappingNode}
|
||||
seqNode := &CandidateNode{Kind: SequenceNode}
|
||||
scalarNode := &CandidateNode{Kind: ScalarNode}
|
||||
|
||||
test.AssertResult(t, true, mapNode.CanVisitValues())
|
||||
test.AssertResult(t, true, seqNode.CanVisitValues())
|
||||
test.AssertResult(t, false, scalarNode.CanVisitValues())
|
||||
}
|
||||
|
||||
func TestCandidateNodeAddChild(t *testing.T) {
|
||||
parent := &CandidateNode{Kind: SequenceNode}
|
||||
child := createStringScalarNode("child")
|
||||
|
||||
parent.AddChild(child)
|
||||
|
||||
test.AssertResult(t, 1, len(parent.Content))
|
||||
test.AssertResult(t, false, parent.Content[0].IsMapKey)
|
||||
test.AssertResult(t, "0", parent.Content[0].Key.Value)
|
||||
// Check that parent is set correctly
|
||||
if parent.Content[0].Parent != parent {
|
||||
t.Errorf("Expected parent to be set correctly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateNodeAddChildren(t *testing.T) {
|
||||
// Test sequence node
|
||||
parent := &CandidateNode{Kind: SequenceNode}
|
||||
child1 := createStringScalarNode("child1")
|
||||
child2 := createStringScalarNode("child2")
|
||||
|
||||
parent.AddChildren([]*CandidateNode{child1, child2})
|
||||
|
||||
test.AssertResult(t, 2, len(parent.Content))
|
||||
test.AssertResult(t, "child1", parent.Content[0].Value)
|
||||
test.AssertResult(t, "child2", parent.Content[1].Value)
|
||||
|
||||
// Test mapping node
|
||||
mapParent := &CandidateNode{Kind: MappingNode}
|
||||
key1 := createStringScalarNode("key1")
|
||||
value1 := createStringScalarNode("value1")
|
||||
key2 := createStringScalarNode("key2")
|
||||
value2 := createStringScalarNode("value2")
|
||||
|
||||
mapParent.AddChildren([]*CandidateNode{key1, value1, key2, value2})
|
||||
|
||||
test.AssertResult(t, 4, len(mapParent.Content))
|
||||
test.AssertResult(t, true, mapParent.Content[0].IsMapKey) // key1
|
||||
test.AssertResult(t, false, mapParent.Content[1].IsMapKey) // value1
|
||||
test.AssertResult(t, true, mapParent.Content[2].IsMapKey) // key2
|
||||
test.AssertResult(t, false, mapParent.Content[3].IsMapKey) // value2
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
yaml "go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
func MapYamlStyle(original yaml.Style) Style {
|
||||
switch original {
|
||||
case yaml.TaggedStyle:
|
||||
return TaggedStyle
|
||||
case yaml.DoubleQuotedStyle:
|
||||
return DoubleQuotedStyle
|
||||
case yaml.SingleQuotedStyle:
|
||||
return SingleQuotedStyle
|
||||
case yaml.LiteralStyle:
|
||||
return LiteralStyle
|
||||
case yaml.FoldedStyle:
|
||||
return FoldedStyle
|
||||
case yaml.FlowStyle:
|
||||
return FlowStyle
|
||||
case 0:
|
||||
return 0
|
||||
}
|
||||
return Style(original)
|
||||
}
|
||||
|
||||
func MapToYamlStyle(original Style) yaml.Style {
|
||||
switch original {
|
||||
case TaggedStyle:
|
||||
return yaml.TaggedStyle
|
||||
case DoubleQuotedStyle:
|
||||
return yaml.DoubleQuotedStyle
|
||||
case SingleQuotedStyle:
|
||||
return yaml.SingleQuotedStyle
|
||||
case LiteralStyle:
|
||||
return yaml.LiteralStyle
|
||||
case FoldedStyle:
|
||||
return yaml.FoldedStyle
|
||||
case FlowStyle:
|
||||
return yaml.FlowStyle
|
||||
case 0:
|
||||
return 0
|
||||
}
|
||||
return yaml.Style(original)
|
||||
}
|
||||
|
||||
func (o *CandidateNode) copyFromYamlNode(node *yaml.Node, anchorMap map[string]*CandidateNode) {
|
||||
o.Style = MapYamlStyle(node.Style)
|
||||
|
||||
o.Tag = node.Tag
|
||||
o.Value = node.Value
|
||||
o.Anchor = node.Anchor
|
||||
|
||||
if o.Anchor != "" {
|
||||
anchorMap[o.Anchor] = o
|
||||
log.Debug("set anchor %v to %v", o.Anchor, NodeToString(o))
|
||||
}
|
||||
|
||||
// its a single alias
|
||||
if node.Alias != nil && node.Alias.Anchor != "" {
|
||||
o.Alias = anchorMap[node.Alias.Anchor]
|
||||
log.Debug("set alias to %v", NodeToString(anchorMap[node.Alias.Anchor]))
|
||||
}
|
||||
o.HeadComment = node.HeadComment
|
||||
o.LineComment = node.LineComment
|
||||
o.FootComment = node.FootComment
|
||||
|
||||
o.Line = node.Line
|
||||
o.Column = node.Column
|
||||
}
|
||||
|
||||
func (o *CandidateNode) copyToYamlNode(node *yaml.Node) {
|
||||
node.Style = MapToYamlStyle(o.Style)
|
||||
|
||||
node.Tag = o.Tag
|
||||
node.Value = o.Value
|
||||
node.Anchor = o.Anchor
|
||||
|
||||
node.HeadComment = o.HeadComment
|
||||
|
||||
node.LineComment = o.LineComment
|
||||
node.FootComment = o.FootComment
|
||||
|
||||
node.Line = o.Line
|
||||
node.Column = o.Column
|
||||
}
|
||||
|
||||
func (o *CandidateNode) decodeIntoChild(childNode *yaml.Node, anchorMap map[string]*CandidateNode) (*CandidateNode, error) {
|
||||
newChild := o.CreateChild()
|
||||
|
||||
// null yaml.Nodes to not end up calling UnmarshalYAML
|
||||
// so we call it explicitly
|
||||
if childNode.Tag == "!!null" {
|
||||
newChild.Kind = ScalarNode
|
||||
newChild.copyFromYamlNode(childNode, anchorMap)
|
||||
return newChild, nil
|
||||
}
|
||||
|
||||
err := newChild.UnmarshalYAML(childNode, anchorMap)
|
||||
return newChild, err
|
||||
}
|
||||
|
||||
func (o *CandidateNode) UnmarshalYAML(node *yaml.Node, anchorMap map[string]*CandidateNode) error {
|
||||
log.Debugf("UnmarshalYAML %v", node.Tag)
|
||||
switch node.Kind {
|
||||
case yaml.AliasNode:
|
||||
log.Debug("UnmarshalYAML - alias from yaml: %v", o.Tag)
|
||||
o.Kind = AliasNode
|
||||
o.copyFromYamlNode(node, anchorMap)
|
||||
return nil
|
||||
case yaml.ScalarNode:
|
||||
log.Debugf("UnmarshalYAML - a scalar")
|
||||
o.Kind = ScalarNode
|
||||
o.copyFromYamlNode(node, anchorMap)
|
||||
return nil
|
||||
case yaml.MappingNode:
|
||||
log.Debugf("UnmarshalYAML - a mapping node")
|
||||
o.Kind = MappingNode
|
||||
o.copyFromYamlNode(node, anchorMap)
|
||||
o.Content = make([]*CandidateNode, len(node.Content))
|
||||
for i := 0; i < len(node.Content); i += 2 {
|
||||
|
||||
keyNode, err := o.decodeIntoChild(node.Content[i], anchorMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyNode.IsMapKey = true
|
||||
|
||||
valueNode, err := o.decodeIntoChild(node.Content[i+1], anchorMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
valueNode.Key = keyNode
|
||||
|
||||
o.Content[i] = keyNode
|
||||
o.Content[i+1] = valueNode
|
||||
}
|
||||
log.Debugf("UnmarshalYAML - finished mapping node")
|
||||
return nil
|
||||
case yaml.SequenceNode:
|
||||
log.Debugf("UnmarshalYAML - a sequence: %v", len(node.Content))
|
||||
o.Kind = SequenceNode
|
||||
|
||||
o.copyFromYamlNode(node, anchorMap)
|
||||
log.Debugf("node Style: %v", node.Style)
|
||||
log.Debugf("o Style: %v", o.Style)
|
||||
o.Content = make([]*CandidateNode, len(node.Content))
|
||||
for i := 0; i < len(node.Content); i++ {
|
||||
keyNode := o.CreateChild()
|
||||
keyNode.IsMapKey = true
|
||||
keyNode.Tag = "!!int"
|
||||
keyNode.Kind = ScalarNode
|
||||
keyNode.Value = fmt.Sprintf("%v", i)
|
||||
|
||||
valueNode, err := o.decodeIntoChild(node.Content[i], anchorMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
valueNode.Key = keyNode
|
||||
o.Content[i] = valueNode
|
||||
}
|
||||
return nil
|
||||
case 0:
|
||||
// not sure when this happens
|
||||
o.copyFromYamlNode(node, anchorMap)
|
||||
log.Debugf("UnmarshalYAML - err.. %v", NodeToString(o))
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("orderedMap: invalid yaml node")
|
||||
}
|
||||
}
|
||||
|
||||
func (o *CandidateNode) MarshalYAML() (*yaml.Node, error) {
|
||||
log.Debug("MarshalYAML to yaml: %v", o.Tag)
|
||||
switch o.Kind {
|
||||
case AliasNode:
|
||||
log.Debug("MarshalYAML - alias to yaml: %v", o.Tag)
|
||||
target := &yaml.Node{Kind: yaml.AliasNode}
|
||||
o.copyToYamlNode(target)
|
||||
return target, nil
|
||||
case ScalarNode:
|
||||
log.Debug("MarshalYAML - scalar: %v", o.Value)
|
||||
target := &yaml.Node{Kind: yaml.ScalarNode}
|
||||
o.copyToYamlNode(target)
|
||||
return target, nil
|
||||
case MappingNode, SequenceNode:
|
||||
targetKind := yaml.MappingNode
|
||||
if o.Kind == SequenceNode {
|
||||
targetKind = yaml.SequenceNode
|
||||
}
|
||||
target := &yaml.Node{Kind: targetKind}
|
||||
o.copyToYamlNode(target)
|
||||
log.Debugf("original style: %v", o.Style)
|
||||
log.Debugf("original: %v, tag: %v, style: %v, kind: %v", NodeToString(o), target.Tag, target.Style, target.Kind == yaml.SequenceNode)
|
||||
target.Content = make([]*yaml.Node, len(o.Content))
|
||||
for i := 0; i < len(o.Content); i++ {
|
||||
|
||||
child, err := o.Content[i].MarshalYAML()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target.Content[i] = child
|
||||
}
|
||||
return target, nil
|
||||
}
|
||||
target := &yaml.Node{}
|
||||
o.copyToYamlNode(target)
|
||||
return target, nil
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
//go:build !yq_nojson
|
||||
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
func (o *CandidateNode) setScalarFromJson(value interface{}) error {
|
||||
o.Kind = ScalarNode
|
||||
switch rawData := value.(type) {
|
||||
case nil:
|
||||
o.Tag = "!!null"
|
||||
o.Value = "null"
|
||||
case float64, float32:
|
||||
o.Value = fmt.Sprintf("%v", value)
|
||||
o.Tag = "!!float"
|
||||
// json decoder returns ints as float.
|
||||
if value == float64(int64(rawData.(float64))) {
|
||||
// aha it's an int disguised as a float
|
||||
o.Tag = "!!int"
|
||||
o.Value = fmt.Sprintf("%v", int64(value.(float64)))
|
||||
}
|
||||
case int, int64, int32:
|
||||
o.Value = fmt.Sprintf("%v", value)
|
||||
o.Tag = "!!int"
|
||||
case bool:
|
||||
o.Value = fmt.Sprintf("%v", value)
|
||||
o.Tag = "!!bool"
|
||||
case string:
|
||||
o.Value = rawData
|
||||
o.Tag = "!!str"
|
||||
default:
|
||||
return fmt.Errorf("unrecognised type :( %v", rawData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *CandidateNode) UnmarshalJSON(data []byte) error {
|
||||
log.Debug("UnmarshalJSON")
|
||||
switch data[0] {
|
||||
case '{':
|
||||
log.Debug("UnmarshalJSON - its a map!")
|
||||
// its a map
|
||||
o.Kind = MappingNode
|
||||
o.Tag = "!!map"
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
_, err := dec.Token() // open object
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// cycle through k/v
|
||||
var tok json.Token
|
||||
for tok, err = dec.Token(); err == nil; tok, err = dec.Token() {
|
||||
// we can expect two types: string or Delim. Delim automatically means
|
||||
// that it is the closing bracket of the object, whereas string means
|
||||
// that there is another key.
|
||||
if _, ok := tok.(json.Delim); ok {
|
||||
break
|
||||
}
|
||||
|
||||
childKey := o.CreateChild()
|
||||
childKey.IsMapKey = true
|
||||
childKey.Value = tok.(string)
|
||||
childKey.Kind = ScalarNode
|
||||
childKey.Tag = "!!str"
|
||||
|
||||
childValue := o.CreateChild()
|
||||
childValue.Key = childKey
|
||||
|
||||
if err := dec.Decode(childValue); err != nil {
|
||||
return err
|
||||
}
|
||||
o.Content = append(o.Content, childKey, childValue)
|
||||
}
|
||||
// unexpected error
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case '[':
|
||||
o.Kind = SequenceNode
|
||||
o.Tag = "!!seq"
|
||||
log.Debug("UnmarshalJSON - its an array!")
|
||||
var children []*CandidateNode
|
||||
if err := json.Unmarshal(data, &children); err != nil {
|
||||
return err
|
||||
}
|
||||
// now we put the children into the content, and set a key value for them
|
||||
for i, child := range children {
|
||||
|
||||
if child == nil {
|
||||
// need to represent it as a null scalar
|
||||
child = createScalarNode(nil, "null")
|
||||
}
|
||||
childKey := o.CreateChild()
|
||||
childKey.Kind = ScalarNode
|
||||
childKey.Tag = "!!int"
|
||||
childKey.Value = fmt.Sprintf("%v", i)
|
||||
childKey.IsMapKey = true
|
||||
|
||||
child.Parent = o
|
||||
child.Key = childKey
|
||||
o.Content = append(o.Content, child)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
log.Debug("UnmarshalJSON - its a scalar!")
|
||||
// otherwise, must be a scalar
|
||||
var scalar interface{}
|
||||
err := json.Unmarshal(data, &scalar)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("UnmarshalJSON - scalar is %v", scalar)
|
||||
|
||||
return o.setScalarFromJson(scalar)
|
||||
|
||||
}
|
||||
|
||||
func (o *CandidateNode) MarshalJSON() ([]byte, error) {
|
||||
log.Debugf("MarshalJSON %v", NodeToString(o))
|
||||
buf := new(bytes.Buffer)
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetIndent("", " ")
|
||||
enc.SetEscapeHTML(false) // do not escape html chars e.g. &, <, >
|
||||
|
||||
switch o.Kind {
|
||||
case AliasNode:
|
||||
log.Debugf("MarshalJSON AliasNode")
|
||||
err := enc.Encode(o.Alias)
|
||||
return buf.Bytes(), err
|
||||
case ScalarNode:
|
||||
log.Debugf("MarshalJSON ScalarNode")
|
||||
value, err := o.GetValueRep()
|
||||
if err != nil {
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
err = enc.Encode(value)
|
||||
return buf.Bytes(), err
|
||||
case MappingNode:
|
||||
log.Debugf("MarshalJSON MappingNode")
|
||||
buf.WriteByte('{')
|
||||
for i := 0; i < len(o.Content); i += 2 {
|
||||
if err := enc.Encode(o.Content[i].Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteByte(':')
|
||||
if err := enc.Encode(o.Content[i+1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i != len(o.Content)-2 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.Bytes(), nil
|
||||
case SequenceNode:
|
||||
log.Debugf("MarshalJSON SequenceNode, %v, len: %v", o.Content, len(o.Content))
|
||||
var err error
|
||||
if len(o.Content) == 0 {
|
||||
buf.WriteString("[]")
|
||||
} else {
|
||||
err = enc.Encode(o.Content)
|
||||
}
|
||||
return buf.Bytes(), err
|
||||
default:
|
||||
err := enc.Encode(nil)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//go:build linux
|
||||
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func changeOwner(info fs.FileInfo, file *os.File) error {
|
||||
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
||||
uid := int(stat.Uid)
|
||||
gid := int(stat.Gid)
|
||||
|
||||
err := os.Chown(file.Name(), uid, gid)
|
||||
if err != nil {
|
||||
// this happens with snap confinement
|
||||
// not really a big issue as users can chown
|
||||
// the file themselves if required.
|
||||
log.Info("Skipping chown: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//go:build linux
|
||||
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestChangeOwner(t *testing.T) {
|
||||
// Create a temporary file for testing
|
||||
tempDir := t.TempDir()
|
||||
testFile := filepath.Join(tempDir, "testfile.txt")
|
||||
|
||||
// Create a test file
|
||||
err := os.WriteFile(testFile, []byte("test content"), 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test file: %v", err)
|
||||
}
|
||||
|
||||
// Get file info
|
||||
info, err := os.Stat(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to stat test file: %v", err)
|
||||
}
|
||||
|
||||
// Create another temporary file to change ownership of
|
||||
tempFile, err := os.CreateTemp(tempDir, "chown_test_*.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
// Test changeOwner function
|
||||
err = changeOwner(info, tempFile)
|
||||
if err != nil {
|
||||
t.Errorf("changeOwner failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify that the function doesn't panic with valid input
|
||||
tempFile2, err := os.CreateTemp(tempDir, "chown_test2_*.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create second temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(tempFile2.Name())
|
||||
tempFile2.Close()
|
||||
|
||||
// Test with the second file
|
||||
err = changeOwner(info, tempFile2)
|
||||
if err != nil {
|
||||
t.Errorf("changeOwner failed on second file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangeOwnerWithInvalidFileInfo(t *testing.T) {
|
||||
// Create a mock file info that doesn't have syscall.Stat_t
|
||||
mockInfo := &mockFileInfo{
|
||||
name: "mock",
|
||||
size: 0,
|
||||
mode: 0600,
|
||||
}
|
||||
|
||||
// Create a temporary file
|
||||
tempFile, err := os.CreateTemp(t.TempDir(), "chown_test_*.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
// Test changeOwner with mock file info (should not panic)
|
||||
err = changeOwner(mockInfo, tempFile)
|
||||
if err != nil {
|
||||
t.Errorf("changeOwner failed with mock file info: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangeOwnerWithNonExistentFile(t *testing.T) {
|
||||
// Create a temporary file
|
||||
tempFile, err := os.CreateTemp(t.TempDir(), "chown_test_*.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
// Get file info
|
||||
info, err := os.Stat(tempFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to stat temp file: %v", err)
|
||||
}
|
||||
|
||||
// Remove the file
|
||||
os.Remove(tempFile.Name())
|
||||
|
||||
err = changeOwner(info, tempFile)
|
||||
// The function should not panic even if the file doesn't exist
|
||||
if err != nil {
|
||||
t.Logf("Expected error when changing owner of non-existent file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// mockFileInfo implements fs.FileInfo but doesn't have syscall.Stat_t
|
||||
type mockFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
}
|
||||
|
||||
func (m *mockFileInfo) Name() string { return m.name }
|
||||
func (m *mockFileInfo) Size() int64 { return m.size }
|
||||
func (m *mockFileInfo) Mode() os.FileMode { return m.mode }
|
||||
func (m *mockFileInfo) ModTime() time.Time { return time.Time{} }
|
||||
func (m *mockFileInfo) IsDir() bool { return false }
|
||||
func (m *mockFileInfo) Sys() interface{} { return nil } // This will cause the type assertion to fail
|
||||
|
||||
func TestChangeOwnerWithSyscallStatT(t *testing.T) {
|
||||
// Create a temporary file
|
||||
tempFile, err := os.CreateTemp(t.TempDir(), "chown_test_*.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
// Get file info
|
||||
info, err := os.Stat(tempFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to stat temp file: %v", err)
|
||||
}
|
||||
|
||||
err = changeOwner(info, tempFile)
|
||||
if err != nil {
|
||||
t.Logf("changeOwner returned error (this might be expected in some environments): %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build !linux
|
||||
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
func changeOwner(_ fs.FileInfo, _ *os.File) error {
|
||||
return nil
|
||||
}
|
||||
@@ -56,6 +56,12 @@ func colorizeAndPrint(yamlBytes []byte, writer io.Writer) error {
|
||||
Suffix: format(color.Reset),
|
||||
}
|
||||
}
|
||||
p.Comment = func() *printer.Property {
|
||||
return &printer.Property{
|
||||
Prefix: format(color.FgHiBlack),
|
||||
Suffix: format(color.Reset),
|
||||
}
|
||||
}
|
||||
_, err := writer.Write([]byte(p.PrintTokens(tokens) + "\n"))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
attr color.Attribute
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "reset color",
|
||||
attr: color.Reset,
|
||||
expected: "\x1b[0m",
|
||||
},
|
||||
{
|
||||
name: "red color",
|
||||
attr: color.FgRed,
|
||||
expected: "\x1b[31m",
|
||||
},
|
||||
{
|
||||
name: "green color",
|
||||
attr: color.FgGreen,
|
||||
expected: "\x1b[32m",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := format(tt.attr)
|
||||
if result != tt.expected {
|
||||
t.Errorf("format(%d) = %q, want %q", tt.attr, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorizeAndPrint(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
yamlBytes []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "simple yaml",
|
||||
yamlBytes: []byte("name: test\nage: 25\n"),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "yaml with strings",
|
||||
yamlBytes: []byte("name: \"hello world\"\nactive: true\ncount: 42\n"),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "yaml with anchors and aliases",
|
||||
yamlBytes: []byte("default: &default\n name: test\nuser: *default\n"),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "yaml with comments",
|
||||
yamlBytes: []byte("# This is a comment\nname: test\n"),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty yaml",
|
||||
yamlBytes: []byte(""),
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
err := colorizeAndPrint(tt.yamlBytes, &buf)
|
||||
|
||||
if tt.expectErr && err == nil {
|
||||
t.Error("Expected error but got none")
|
||||
}
|
||||
if !tt.expectErr && err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Check that output contains escape sequences (color codes)
|
||||
if !tt.expectErr && len(tt.yamlBytes) > 0 {
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "\x1b[") {
|
||||
t.Error("Expected output to contain color escape sequences")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorizeAndPrintWithDifferentYamlTypes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
yaml string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "boolean values",
|
||||
yaml: "active: true\ninactive: false\n",
|
||||
},
|
||||
{
|
||||
name: "numeric values",
|
||||
yaml: "integer: 42\nfloat: 3.14\nnegative: -10\n",
|
||||
},
|
||||
{
|
||||
name: "map keys",
|
||||
yaml: "user:\n name: john\n age: 30\n",
|
||||
},
|
||||
{
|
||||
name: "string values",
|
||||
yaml: "message: \"hello world\"\ndescription: 'single quotes'\n",
|
||||
},
|
||||
{
|
||||
name: "mixed types",
|
||||
yaml: "config:\n debug: true\n port: 8080\n host: \"localhost\"\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
err := colorizeAndPrint([]byte(tc.yaml), &buf)
|
||||
|
||||
if tc.expectErr && err == nil {
|
||||
t.Error("Expected error but got none")
|
||||
}
|
||||
if !tc.expectErr && err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Verify output contains color codes
|
||||
if !tc.expectErr {
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "\x1b[") {
|
||||
t.Error("Expected output to contain color escape sequences")
|
||||
}
|
||||
// Should end with newline
|
||||
if !strings.HasSuffix(output, "\n") {
|
||||
t.Error("Expected output to end with newline")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+31
-30
@@ -3,8 +3,8 @@ package yqlib
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
logging "gopkg.in/op/go-logging.v1"
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ type Context struct {
|
||||
MatchingNodes *list.List
|
||||
Variables map[string]*list.List
|
||||
DontAutoCreate bool
|
||||
datetimeLayout string
|
||||
}
|
||||
|
||||
func (n *Context) SingleReadonlyChildContext(candidate *CandidateNode) Context {
|
||||
@@ -28,6 +29,17 @@ func (n *Context) SingleChildContext(candidate *CandidateNode) Context {
|
||||
return n.ChildContext(list)
|
||||
}
|
||||
|
||||
func (n *Context) SetDateTimeLayout(newDateTimeLayout string) {
|
||||
n.datetimeLayout = newDateTimeLayout
|
||||
}
|
||||
|
||||
func (n *Context) GetDateTimeLayout() string {
|
||||
if n.datetimeLayout != "" {
|
||||
return n.datetimeLayout
|
||||
}
|
||||
return time.RFC3339
|
||||
}
|
||||
|
||||
func (n *Context) GetVariable(name string) *list.List {
|
||||
if n.Variables == nil {
|
||||
return nil
|
||||
@@ -43,15 +55,21 @@ func (n *Context) SetVariable(name string, value *list.List) {
|
||||
}
|
||||
|
||||
func (n *Context) ChildContext(results *list.List) Context {
|
||||
clone := Context{DontAutoCreate: n.DontAutoCreate}
|
||||
clone := Context{DontAutoCreate: n.DontAutoCreate, datetimeLayout: n.datetimeLayout}
|
||||
clone.Variables = make(map[string]*list.List)
|
||||
if len(n.Variables) > 0 {
|
||||
err := copier.Copy(&clone.Variables, n.Variables)
|
||||
if err != nil {
|
||||
log.Error("Error cloning context :(")
|
||||
panic(err)
|
||||
for variableKey, originalValueList := range n.Variables {
|
||||
|
||||
variableCopyList := list.New()
|
||||
for el := originalValueList.Front(); el != nil; el = el.Next() {
|
||||
// note that we dont make a copy of the candidate node
|
||||
// this is so the 'ref' operator can work correctly.
|
||||
clonedNode := el.Value.(*CandidateNode)
|
||||
variableCopyList.PushBack(clonedNode)
|
||||
}
|
||||
|
||||
clone.Variables[variableKey] = variableCopyList
|
||||
}
|
||||
|
||||
clone.MatchingNodes = results
|
||||
return clone
|
||||
}
|
||||
@@ -65,35 +83,18 @@ func (n *Context) ToString() string {
|
||||
}
|
||||
|
||||
func (n *Context) DeepClone() Context {
|
||||
clone := Context{}
|
||||
err := copier.Copy(&clone, n)
|
||||
// copier doesn't do lists properly for some reason
|
||||
clone.MatchingNodes = list.New()
|
||||
|
||||
clonedContent := list.New()
|
||||
for el := n.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||
clonedNode, err := el.Value.(*CandidateNode).Copy()
|
||||
if err != nil {
|
||||
log.Error("Error cloning context :(")
|
||||
panic(err)
|
||||
}
|
||||
clone.MatchingNodes.PushBack(clonedNode)
|
||||
clonedNode := el.Value.(*CandidateNode).Copy()
|
||||
clonedContent.PushBack(clonedNode)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error("Error cloning context :(")
|
||||
panic(err)
|
||||
}
|
||||
return clone
|
||||
return n.ChildContext(clonedContent)
|
||||
}
|
||||
|
||||
func (n *Context) Clone() Context {
|
||||
clone := Context{}
|
||||
err := copier.Copy(&clone, n)
|
||||
|
||||
if err != nil {
|
||||
log.Error("Error cloning context :(")
|
||||
panic(err)
|
||||
}
|
||||
return clone
|
||||
return n.ChildContext(n.MatchingNodes)
|
||||
}
|
||||
|
||||
func (n *Context) ReadOnlyClone() Context {
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
logging "gopkg.in/op/go-logging.v1"
|
||||
)
|
||||
|
||||
func TestChildContext(t *testing.T) {
|
||||
|
||||
expectedOriginal := make(map[string]*list.List)
|
||||
expectedOriginal["dog"] = list.New()
|
||||
expectedOriginal["dog"].PushBack(&CandidateNode{Value: "woof"})
|
||||
|
||||
originalVariables := make(map[string]*list.List)
|
||||
originalVariables["dog"] = list.New()
|
||||
originalVariables["dog"].PushBack(&CandidateNode{Value: "woof"})
|
||||
|
||||
original := Context{
|
||||
DontAutoCreate: true,
|
||||
datetimeLayout: "cat",
|
||||
Variables: originalVariables,
|
||||
}
|
||||
|
||||
newResults := list.New()
|
||||
newResults.PushBack(&CandidateNode{Value: "bar"})
|
||||
|
||||
clone := original.ChildContext(newResults)
|
||||
test.AssertResultComplex(t, originalVariables, clone.Variables)
|
||||
|
||||
clone.Variables["dog"].PushBack("bark")
|
||||
// ensure this is a separate copy
|
||||
test.AssertResultComplex(t, 1, originalVariables["dog"].Len())
|
||||
|
||||
}
|
||||
|
||||
func TestChildContextNoVariables(t *testing.T) {
|
||||
|
||||
original := Context{
|
||||
DontAutoCreate: true,
|
||||
datetimeLayout: "cat",
|
||||
}
|
||||
|
||||
newResults := list.New()
|
||||
newResults.PushBack(&CandidateNode{Value: "bar"})
|
||||
|
||||
clone := original.ChildContext(newResults)
|
||||
test.AssertResultComplex(t, make(map[string]*list.List), clone.Variables)
|
||||
|
||||
}
|
||||
|
||||
func TestSingleReadonlyChildContext(t *testing.T) {
|
||||
original := Context{
|
||||
DontAutoCreate: false,
|
||||
datetimeLayout: "2006-01-02",
|
||||
}
|
||||
|
||||
candidate := &CandidateNode{Value: "test"}
|
||||
clone := original.SingleReadonlyChildContext(candidate)
|
||||
|
||||
// Should have DontAutoCreate set to true
|
||||
test.AssertResultComplex(t, true, clone.DontAutoCreate)
|
||||
|
||||
// Should have the candidate node in MatchingNodes
|
||||
test.AssertResultComplex(t, 1, clone.MatchingNodes.Len())
|
||||
test.AssertResultComplex(t, candidate, clone.MatchingNodes.Front().Value)
|
||||
}
|
||||
|
||||
func TestSingleChildContext(t *testing.T) {
|
||||
original := Context{
|
||||
DontAutoCreate: true,
|
||||
datetimeLayout: "2006-01-02",
|
||||
}
|
||||
|
||||
candidate := &CandidateNode{Value: "test"}
|
||||
clone := original.SingleChildContext(candidate)
|
||||
|
||||
// Should preserve DontAutoCreate
|
||||
test.AssertResultComplex(t, true, clone.DontAutoCreate)
|
||||
|
||||
// Should have the candidate node in MatchingNodes
|
||||
test.AssertResultComplex(t, 1, clone.MatchingNodes.Len())
|
||||
test.AssertResultComplex(t, candidate, clone.MatchingNodes.Front().Value)
|
||||
}
|
||||
|
||||
func TestSetDateTimeLayout(t *testing.T) {
|
||||
context := Context{}
|
||||
|
||||
// Test setting datetime layout
|
||||
context.SetDateTimeLayout("2006-01-02T15:04:05Z07:00")
|
||||
test.AssertResultComplex(t, "2006-01-02T15:04:05Z07:00", context.datetimeLayout)
|
||||
}
|
||||
|
||||
func TestGetDateTimeLayout(t *testing.T) {
|
||||
// Test with custom layout
|
||||
context := Context{datetimeLayout: "2006-01-02"}
|
||||
result := context.GetDateTimeLayout()
|
||||
test.AssertResultComplex(t, "2006-01-02", result)
|
||||
|
||||
// Test with empty layout (should return default)
|
||||
context = Context{}
|
||||
result = context.GetDateTimeLayout()
|
||||
test.AssertResultComplex(t, "2006-01-02T15:04:05Z07:00", result)
|
||||
}
|
||||
|
||||
func TestGetVariable(t *testing.T) {
|
||||
// Test with nil Variables
|
||||
context := Context{}
|
||||
result := context.GetVariable("nonexistent")
|
||||
test.AssertResultComplex(t, (*list.List)(nil), result)
|
||||
|
||||
// Test with existing variable
|
||||
variables := make(map[string]*list.List)
|
||||
variables["test"] = list.New()
|
||||
variables["test"].PushBack(&CandidateNode{Value: "value"})
|
||||
|
||||
context = Context{Variables: variables}
|
||||
result = context.GetVariable("test")
|
||||
test.AssertResultComplex(t, variables["test"], result)
|
||||
|
||||
// Test with non-existent variable
|
||||
result = context.GetVariable("nonexistent")
|
||||
test.AssertResultComplex(t, (*list.List)(nil), result)
|
||||
}
|
||||
|
||||
func TestSetVariable(t *testing.T) {
|
||||
// Test setting variable when Variables is nil
|
||||
context := Context{}
|
||||
value := list.New()
|
||||
value.PushBack(&CandidateNode{Value: "test"})
|
||||
|
||||
context.SetVariable("key", value)
|
||||
test.AssertResultComplex(t, value, context.Variables["key"])
|
||||
|
||||
// Test setting variable when Variables already exists
|
||||
context.SetVariable("key2", value)
|
||||
test.AssertResultComplex(t, value, context.Variables["key2"])
|
||||
}
|
||||
|
||||
func TestToString(t *testing.T) {
|
||||
context := Context{
|
||||
DontAutoCreate: true,
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
|
||||
// Add a node to test the full string representation
|
||||
node := &CandidateNode{Value: "test"}
|
||||
context.MatchingNodes.PushBack(node)
|
||||
|
||||
// Test with debug logging disabled (default)
|
||||
result := context.ToString()
|
||||
test.AssertResultComplex(t, "", result)
|
||||
|
||||
// Test with debug logging enabled
|
||||
logging.SetLevel(logging.DEBUG, "")
|
||||
defer logging.SetLevel(logging.INFO, "") // Reset to default
|
||||
|
||||
result2 := context.ToString()
|
||||
test.AssertResultComplex(t, true, len(result2) > 0)
|
||||
test.AssertResultComplex(t, true, strings.Contains(result2, "Context"))
|
||||
test.AssertResultComplex(t, true, strings.Contains(result2, "DontAutoCreate: true"))
|
||||
}
|
||||
|
||||
func TestDeepClone(t *testing.T) {
|
||||
// Create original context with variables and matching nodes
|
||||
originalVariables := make(map[string]*list.List)
|
||||
originalVariables["test"] = list.New()
|
||||
originalVariables["test"].PushBack(&CandidateNode{Value: "original"})
|
||||
|
||||
original := Context{
|
||||
DontAutoCreate: true,
|
||||
datetimeLayout: "2006-01-02",
|
||||
Variables: originalVariables,
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
|
||||
// Add a node to MatchingNodes
|
||||
node := &CandidateNode{Value: "test"}
|
||||
original.MatchingNodes.PushBack(node)
|
||||
|
||||
clone := original.DeepClone()
|
||||
|
||||
// Should preserve DontAutoCreate and datetimeLayout
|
||||
test.AssertResultComplex(t, original.DontAutoCreate, clone.DontAutoCreate)
|
||||
test.AssertResultComplex(t, original.datetimeLayout, clone.datetimeLayout)
|
||||
|
||||
// Should have copied variables
|
||||
test.AssertResultComplex(t, 1, len(clone.Variables))
|
||||
test.AssertResultComplex(t, "original", clone.Variables["test"].Front().Value.(*CandidateNode).Value)
|
||||
|
||||
// Should have deep copied MatchingNodes
|
||||
test.AssertResultComplex(t, 1, clone.MatchingNodes.Len())
|
||||
|
||||
// Verify it's a deep copy by modifying the original
|
||||
original.MatchingNodes.Front().Value.(*CandidateNode).Value = "modified"
|
||||
test.AssertResultComplex(t, "test", clone.MatchingNodes.Front().Value.(*CandidateNode).Value)
|
||||
}
|
||||
|
||||
func TestClone(t *testing.T) {
|
||||
// Create original context
|
||||
original := Context{
|
||||
DontAutoCreate: true,
|
||||
datetimeLayout: "2006-01-02",
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
|
||||
node := &CandidateNode{Value: "test"}
|
||||
original.MatchingNodes.PushBack(node)
|
||||
|
||||
clone := original.Clone()
|
||||
|
||||
// Should preserve DontAutoCreate and datetimeLayout
|
||||
test.AssertResultComplex(t, original.DontAutoCreate, clone.DontAutoCreate)
|
||||
test.AssertResultComplex(t, original.datetimeLayout, clone.datetimeLayout)
|
||||
|
||||
// Should have the same MatchingNodes reference
|
||||
test.AssertResultComplex(t, original.MatchingNodes, clone.MatchingNodes)
|
||||
}
|
||||
|
||||
func TestReadOnlyClone(t *testing.T) {
|
||||
original := Context{
|
||||
DontAutoCreate: false,
|
||||
datetimeLayout: "2006-01-02",
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
|
||||
node := &CandidateNode{Value: "test"}
|
||||
original.MatchingNodes.PushBack(node)
|
||||
|
||||
clone := original.ReadOnlyClone()
|
||||
|
||||
// Should set DontAutoCreate to true
|
||||
test.AssertResultComplex(t, true, clone.DontAutoCreate)
|
||||
|
||||
// Should preserve other fields
|
||||
test.AssertResultComplex(t, original.datetimeLayout, clone.datetimeLayout)
|
||||
test.AssertResultComplex(t, original.MatchingNodes, clone.MatchingNodes)
|
||||
}
|
||||
|
||||
func TestWritableClone(t *testing.T) {
|
||||
original := Context{
|
||||
DontAutoCreate: true,
|
||||
datetimeLayout: "2006-01-02",
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
|
||||
node := &CandidateNode{Value: "test"}
|
||||
original.MatchingNodes.PushBack(node)
|
||||
|
||||
clone := original.WritableClone()
|
||||
|
||||
// Should set DontAutoCreate to false
|
||||
test.AssertResultComplex(t, false, clone.DontAutoCreate)
|
||||
|
||||
// Should preserve other fields
|
||||
test.AssertResultComplex(t, original.datetimeLayout, clone.datetimeLayout)
|
||||
test.AssertResultComplex(t, original.MatchingNodes, clone.MatchingNodes)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package yqlib
|
||||
|
||||
type CsvPreferences struct {
|
||||
Separator rune
|
||||
AutoParse bool
|
||||
}
|
||||
|
||||
func NewDefaultCsvPreferences() CsvPreferences {
|
||||
return CsvPreferences{
|
||||
Separator: ',',
|
||||
AutoParse: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultTsvPreferences() CsvPreferences {
|
||||
return CsvPreferences{
|
||||
Separator: '\t',
|
||||
AutoParse: true,
|
||||
}
|
||||
}
|
||||
|
||||
var ConfiguredCsvPreferences = NewDefaultCsvPreferences()
|
||||
var ConfiguredTsvPreferences = NewDefaultTsvPreferences()
|
||||
@@ -0,0 +1,397 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
)
|
||||
|
||||
const csvSimple = `name,numberOfCats,likesApples,height
|
||||
Gary,1,true,168.8
|
||||
Samantha's Rabbit,2,false,-188.8
|
||||
`
|
||||
|
||||
const csvSimpleWithObject = `name,numberOfCats,likesApples,height,facts
|
||||
Gary,1,true,168.8,cool: true
|
||||
Samantha's Rabbit,2,false,-188.8,tall: indeed
|
||||
`
|
||||
const csvMissing = `name,numberOfCats,likesApples,height
|
||||
,null,,168.8
|
||||
`
|
||||
const expectedUpdatedSimpleCsv = `name,numberOfCats,likesApples,height
|
||||
Gary,3,true,168.8
|
||||
Samantha's Rabbit,2,false,-188.8
|
||||
`
|
||||
|
||||
const csvSimpleShort = `Name,Number of Cats
|
||||
Gary,1
|
||||
Samantha's Rabbit,2
|
||||
`
|
||||
|
||||
const tsvSimple = `name numberOfCats likesApples height
|
||||
Gary 1 true 168.8
|
||||
Samantha's Rabbit 2 false -188.8
|
||||
`
|
||||
|
||||
const expectedYamlFromCSV = `- name: Gary
|
||||
numberOfCats: 1
|
||||
likesApples: true
|
||||
height: 168.8
|
||||
- name: Samantha's Rabbit
|
||||
numberOfCats: 2
|
||||
likesApples: false
|
||||
height: -188.8
|
||||
`
|
||||
const expectedYamlFromCSVWithObject = `- name: Gary
|
||||
numberOfCats: 1
|
||||
likesApples: true
|
||||
height: 168.8
|
||||
facts:
|
||||
cool: true
|
||||
- name: Samantha's Rabbit
|
||||
numberOfCats: 2
|
||||
likesApples: false
|
||||
height: -188.8
|
||||
facts:
|
||||
tall: indeed
|
||||
`
|
||||
|
||||
const expectedYamlFromCSVNoParsing = `- name: Gary
|
||||
numberOfCats: 1
|
||||
likesApples: true
|
||||
height: 168.8
|
||||
facts: 'cool: true'
|
||||
- name: Samantha's Rabbit
|
||||
numberOfCats: 2
|
||||
likesApples: false
|
||||
height: -188.8
|
||||
facts: 'tall: indeed'
|
||||
`
|
||||
|
||||
const expectedYamlFromCSVMissingData = `- name: Gary
|
||||
numberOfCats: 1
|
||||
height: 168.8
|
||||
- name: Samantha's Rabbit
|
||||
height: -188.8
|
||||
likesApples: false
|
||||
`
|
||||
|
||||
const csvSimpleMissingData = `name,numberOfCats,height
|
||||
Gary,1,168.8
|
||||
Samantha's Rabbit,,-188.8
|
||||
`
|
||||
|
||||
const csvTestSimpleYaml = `- [i, like, csv]
|
||||
- [because, excel, is, cool]`
|
||||
|
||||
const expectedSimpleCsv = `i,like,csv
|
||||
because,excel,is,cool
|
||||
`
|
||||
|
||||
const tsvTestExpectedSimpleCsv = `i like csv
|
||||
because excel is cool
|
||||
`
|
||||
|
||||
var csvScenarios = []formatScenario{
|
||||
{
|
||||
description: "Encode CSV simple",
|
||||
input: csvTestSimpleYaml,
|
||||
expected: expectedSimpleCsv,
|
||||
scenarioType: "encode-csv",
|
||||
},
|
||||
{
|
||||
description: "Encode TSV simple",
|
||||
input: csvTestSimpleYaml,
|
||||
expected: tsvTestExpectedSimpleCsv,
|
||||
scenarioType: "encode-tsv",
|
||||
},
|
||||
{
|
||||
description: "Encode Empty",
|
||||
skipDoc: true,
|
||||
input: `[]`,
|
||||
expected: "",
|
||||
scenarioType: "encode-csv",
|
||||
},
|
||||
{
|
||||
description: "Comma in value",
|
||||
skipDoc: true,
|
||||
input: `["comma, in, value", things]`,
|
||||
expected: "\"comma, in, value\",things\n",
|
||||
scenarioType: "encode-csv",
|
||||
},
|
||||
{
|
||||
description: "Encode array of objects to csv",
|
||||
input: expectedYamlFromCSV,
|
||||
expected: csvSimple,
|
||||
scenarioType: "encode-csv",
|
||||
},
|
||||
{
|
||||
description: "Encode array of objects to custom csv format",
|
||||
subdescription: "Add the header row manually, then the we convert each object into an array of values - resulting in an array of arrays. Pick the columns and call the header whatever you like.",
|
||||
input: expectedYamlFromCSV,
|
||||
expected: csvSimpleShort,
|
||||
expression: `[["Name", "Number of Cats"]] + [.[] | [.name, .numberOfCats ]]`,
|
||||
scenarioType: "encode-csv",
|
||||
},
|
||||
{
|
||||
description: "Encode array of objects to csv - missing fields behaviour",
|
||||
subdescription: "First entry is used to determine the headers, and it is missing 'likesApples', so it is not included in the csv. Second entry does not have 'numberOfCats' so that is blank",
|
||||
input: expectedYamlFromCSVMissingData,
|
||||
expected: csvSimpleMissingData,
|
||||
scenarioType: "encode-csv",
|
||||
},
|
||||
{
|
||||
description: "decode csv missing",
|
||||
skipDoc: true,
|
||||
input: csvMissing,
|
||||
expected: csvMissing,
|
||||
scenarioType: "roundtrip-csv",
|
||||
},
|
||||
{
|
||||
description: "decode csv key",
|
||||
skipDoc: true,
|
||||
input: csvSimple,
|
||||
expression: ".[0].name | key",
|
||||
expected: "name\n",
|
||||
scenarioType: "decode-csv",
|
||||
},
|
||||
{
|
||||
description: "decode csv parent",
|
||||
skipDoc: true,
|
||||
input: csvSimple,
|
||||
expression: ".[0].name | parent | .height",
|
||||
expected: "168.8\n",
|
||||
scenarioType: "decode-csv",
|
||||
},
|
||||
{
|
||||
description: "Parse CSV into an array of objects",
|
||||
subdescription: "First row is assumed to be the header row. By default, entries with YAML/JSON formatting will be parsed!",
|
||||
input: csvSimpleWithObject,
|
||||
expected: expectedYamlFromCSVWithObject,
|
||||
scenarioType: "decode-csv",
|
||||
},
|
||||
{
|
||||
description: "Decode CSV line breaks",
|
||||
skipDoc: true,
|
||||
input: "heading1\n\"some data\nwith a line break\"\n",
|
||||
expected: "- heading1: |-\n some data\n with a line break\n",
|
||||
scenarioType: "decode-csv",
|
||||
},
|
||||
{
|
||||
description: "Parse CSV into an array of objects, no auto-parsing",
|
||||
subdescription: "First row is assumed to be the header row. Entries with YAML/JSON will be left as strings.",
|
||||
input: csvSimpleWithObject,
|
||||
expected: expectedYamlFromCSVNoParsing,
|
||||
scenarioType: "decode-csv-no-auto",
|
||||
},
|
||||
{
|
||||
description: "values starting with #, no auto parse",
|
||||
skipDoc: true,
|
||||
input: "value\n#ffff",
|
||||
expected: "- value: '#ffff'\n",
|
||||
scenarioType: "decode-csv-no-auto",
|
||||
},
|
||||
{
|
||||
description: "values starting with #",
|
||||
skipDoc: true,
|
||||
input: "value\n#ffff",
|
||||
expected: "- value: #ffff\n",
|
||||
scenarioType: "decode-csv",
|
||||
},
|
||||
{
|
||||
description: "Scalar roundtrip",
|
||||
skipDoc: true,
|
||||
input: "mike\ncat",
|
||||
expression: ".[0].mike",
|
||||
expected: "cat\n",
|
||||
scenarioType: "roundtrip-csv",
|
||||
},
|
||||
{
|
||||
description: "Parse TSV into an array of objects",
|
||||
subdescription: "First row is assumed to be the header row.",
|
||||
input: tsvSimple,
|
||||
expected: expectedYamlFromCSV,
|
||||
scenarioType: "decode-tsv-object",
|
||||
},
|
||||
{
|
||||
description: "Round trip",
|
||||
input: csvSimple,
|
||||
expected: expectedUpdatedSimpleCsv,
|
||||
expression: `(.[] | select(.name == "Gary") | .numberOfCats) = 3`,
|
||||
scenarioType: "roundtrip-csv",
|
||||
},
|
||||
}
|
||||
|
||||
func testCSVScenario(t *testing.T, s formatScenario) {
|
||||
switch s.scenarioType {
|
||||
case "encode-csv":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(ConfiguredCsvPreferences)), s.description)
|
||||
case "encode-tsv":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(ConfiguredTsvPreferences)), s.description)
|
||||
case "decode-csv":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredCsvPreferences), NewYamlEncoder(ConfiguredYamlPreferences)), s.description)
|
||||
case "decode-csv-no-auto":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(CsvPreferences{Separator: ',', AutoParse: false}), NewYamlEncoder(ConfiguredYamlPreferences)), s.description)
|
||||
case "decode-tsv-object":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredTsvPreferences), NewYamlEncoder(ConfiguredYamlPreferences)), s.description)
|
||||
case "roundtrip-csv":
|
||||
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredCsvPreferences), NewCsvEncoder(ConfiguredCsvPreferences)), s.description)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
|
||||
}
|
||||
}
|
||||
|
||||
func documentCSVDecodeObjectScenario(w *bufio.Writer, s formatScenario, formatType string) {
|
||||
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
|
||||
|
||||
if s.subdescription != "" {
|
||||
writeOrPanic(w, s.subdescription)
|
||||
writeOrPanic(w, "\n\n")
|
||||
}
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("Given a sample.%v file of:\n", formatType))
|
||||
writeOrPanic(w, fmt.Sprintf("```%v\n%v\n```\n", formatType, s.input))
|
||||
|
||||
writeOrPanic(w, "then\n")
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -p=%v sample.%v\n```\n", formatType, formatType))
|
||||
writeOrPanic(w, "will output\n")
|
||||
|
||||
separator := ','
|
||||
if formatType == "tsv" {
|
||||
separator = '\t'
|
||||
}
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n",
|
||||
mustProcessFormatScenario(s, NewCSVObjectDecoder(CsvPreferences{Separator: separator, AutoParse: true}), NewYamlEncoder(ConfiguredYamlPreferences))),
|
||||
)
|
||||
}
|
||||
|
||||
func documentCSVDecodeObjectNoAutoScenario(w *bufio.Writer, s formatScenario, formatType string) {
|
||||
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
|
||||
|
||||
if s.subdescription != "" {
|
||||
writeOrPanic(w, s.subdescription)
|
||||
writeOrPanic(w, "\n\n")
|
||||
}
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("Given a sample.%v file of:\n", formatType))
|
||||
writeOrPanic(w, fmt.Sprintf("```%v\n%v\n```\n", formatType, s.input))
|
||||
|
||||
writeOrPanic(w, "then\n")
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -p=%v --csv-auto-parse=f sample.%v\n```\n", formatType, formatType))
|
||||
writeOrPanic(w, "will output\n")
|
||||
|
||||
separator := ','
|
||||
if formatType == "tsv" {
|
||||
separator = '\t'
|
||||
}
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n",
|
||||
mustProcessFormatScenario(s, NewCSVObjectDecoder(CsvPreferences{Separator: separator, AutoParse: false}), NewYamlEncoder(ConfiguredYamlPreferences))),
|
||||
)
|
||||
}
|
||||
|
||||
func documentCSVEncodeScenario(w *bufio.Writer, s formatScenario, formatType string) {
|
||||
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
|
||||
|
||||
if s.subdescription != "" {
|
||||
writeOrPanic(w, s.subdescription)
|
||||
writeOrPanic(w, "\n\n")
|
||||
}
|
||||
|
||||
writeOrPanic(w, "Given a sample.yml file of:\n")
|
||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v\n```\n", s.input))
|
||||
|
||||
writeOrPanic(w, "then\n")
|
||||
|
||||
expression := s.expression
|
||||
|
||||
if expression != "" {
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -o=%v '%v' sample.yml\n```\n", formatType, expression))
|
||||
} else {
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -o=%v sample.yml\n```\n", formatType))
|
||||
}
|
||||
writeOrPanic(w, "will output\n")
|
||||
|
||||
separator := ','
|
||||
if formatType == "tsv" {
|
||||
separator = '\t'
|
||||
}
|
||||
csvPrefs := NewDefaultCsvPreferences()
|
||||
csvPrefs.Separator = separator
|
||||
writeOrPanic(w, fmt.Sprintf("```%v\n%v```\n\n", formatType,
|
||||
mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(csvPrefs))),
|
||||
)
|
||||
}
|
||||
|
||||
func documentCSVRoundTripScenario(w *bufio.Writer, s formatScenario, formatType string) {
|
||||
writeOrPanic(w, fmt.Sprintf("## %v\n", s.description))
|
||||
|
||||
if s.subdescription != "" {
|
||||
writeOrPanic(w, s.subdescription)
|
||||
writeOrPanic(w, "\n\n")
|
||||
}
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("Given a sample.%v file of:\n", formatType))
|
||||
writeOrPanic(w, fmt.Sprintf("```%v\n%v\n```\n", formatType, s.input))
|
||||
|
||||
writeOrPanic(w, "then\n")
|
||||
|
||||
expression := s.expression
|
||||
|
||||
if expression != "" {
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -p=%v -o=%v '%v' sample.%v\n```\n", formatType, formatType, expression, formatType))
|
||||
} else {
|
||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -p=%v -o=%v sample.%v\n```\n", formatType, formatType, formatType))
|
||||
}
|
||||
writeOrPanic(w, "will output\n")
|
||||
|
||||
separator := ','
|
||||
if formatType == "tsv" {
|
||||
separator = '\t'
|
||||
}
|
||||
|
||||
csvPrefs := NewDefaultCsvPreferences()
|
||||
csvPrefs.Separator = separator
|
||||
|
||||
writeOrPanic(w, fmt.Sprintf("```%v\n%v```\n\n", formatType,
|
||||
mustProcessFormatScenario(s, NewCSVObjectDecoder(CsvPreferences{Separator: separator, AutoParse: true}), NewCsvEncoder(csvPrefs))),
|
||||
)
|
||||
}
|
||||
|
||||
func documentCSVScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
|
||||
s := i.(formatScenario)
|
||||
if s.skipDoc {
|
||||
return
|
||||
}
|
||||
switch s.scenarioType {
|
||||
case "encode-csv":
|
||||
documentCSVEncodeScenario(w, s, "csv")
|
||||
case "encode-tsv":
|
||||
documentCSVEncodeScenario(w, s, "tsv")
|
||||
case "decode-csv":
|
||||
documentCSVDecodeObjectScenario(w, s, "csv")
|
||||
case "decode-csv-no-auto":
|
||||
documentCSVDecodeObjectNoAutoScenario(w, s, "csv")
|
||||
case "decode-tsv-object":
|
||||
documentCSVDecodeObjectScenario(w, s, "tsv")
|
||||
case "roundtrip-csv":
|
||||
documentCSVRoundTripScenario(w, s, "csv")
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSVScenarios(t *testing.T) {
|
||||
for _, tt := range csvScenarios {
|
||||
testCSVScenario(t, tt)
|
||||
}
|
||||
genericScenarios := make([]interface{}, len(csvScenarios))
|
||||
for i, s := range csvScenarios {
|
||||
genericScenarios[i] = s
|
||||
}
|
||||
documentScenarios(t, "usage", "csv-tsv", genericScenarios, documentCSVScenario)
|
||||
}
|
||||
@@ -7,10 +7,12 @@ import (
|
||||
)
|
||||
|
||||
type DataTreeNavigator interface {
|
||||
// given the context and a expressionNode,
|
||||
// given the context and an expressionNode,
|
||||
// this will process the against the given expressionNode and return
|
||||
// a new context of matching candidates
|
||||
GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error)
|
||||
|
||||
DeeplyAssign(context Context, path []interface{}, rhsNode *CandidateNode) error
|
||||
}
|
||||
|
||||
type dataTreeNavigator struct {
|
||||
@@ -20,6 +22,33 @@ func NewDataTreeNavigator() DataTreeNavigator {
|
||||
return &dataTreeNavigator{}
|
||||
}
|
||||
|
||||
func (d *dataTreeNavigator) DeeplyAssign(context Context, path []interface{}, rhsCandidateNode *CandidateNode) error {
|
||||
|
||||
assignmentOp := &Operation{OperationType: assignOpType, Preferences: assignPreferences{}}
|
||||
|
||||
if rhsCandidateNode.Kind == MappingNode {
|
||||
log.Debug("DeeplyAssign: deeply merging object")
|
||||
// if the rhs is a map, we need to deeply merge it in.
|
||||
// otherwise we'll clobber any existing fields
|
||||
assignmentOp = &Operation{OperationType: multiplyAssignOpType, Preferences: multiplyPreferences{
|
||||
AppendArrays: true,
|
||||
TraversePrefs: traversePreferences{DontFollowAlias: true},
|
||||
AssignPrefs: assignPreferences{},
|
||||
}}
|
||||
}
|
||||
|
||||
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhsCandidateNode}
|
||||
|
||||
assignmentOpNode := &ExpressionNode{
|
||||
Operation: assignmentOp,
|
||||
LHS: createTraversalTree(path, traversePreferences{}, false),
|
||||
RHS: &ExpressionNode{Operation: rhsOp},
|
||||
}
|
||||
|
||||
_, err := d.GetMatchingNodes(context, assignmentOpNode)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *dataTreeNavigator) GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||
if expressionNode == nil {
|
||||
log.Debugf("getMatchingNodes - nothing to do")
|
||||
@@ -31,11 +60,10 @@ func (d *dataTreeNavigator) GetMatchingNodes(context Context, expressionNode *Ex
|
||||
log.Debug(NodeToString(el.Value.(*CandidateNode)))
|
||||
}
|
||||
}
|
||||
log.Debug(">>")
|
||||
handler := expressionNode.Operation.OperationType.Handler
|
||||
if handler != nil {
|
||||
return handler(d, context, expressionNode)
|
||||
}
|
||||
return Context{}, fmt.Errorf("Unknown operator %v", expressionNode.Operation.OperationType)
|
||||
return Context{}, fmt.Errorf("unknown operator %v", expressionNode.Operation.OperationType.Type)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"testing"
|
||||
|
||||
"github.com/mikefarah/yq/v4/test"
|
||||
)
|
||||
|
||||
func TestGetMatchingNodes_NilExpressionNode(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
|
||||
result, err := navigator.GetMatchingNodes(context, nil)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
test.AssertResultComplex(t, context, result)
|
||||
}
|
||||
|
||||
func TestGetMatchingNodes_UnknownOperator(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
|
||||
// Create an expression node with an unknown operation type
|
||||
unknownOpType := &operationType{Type: "UNKNOWN", Handler: nil}
|
||||
expressionNode := &ExpressionNode{
|
||||
Operation: &Operation{OperationType: unknownOpType},
|
||||
}
|
||||
|
||||
result, err := navigator.GetMatchingNodes(context, expressionNode)
|
||||
|
||||
test.AssertResult(t, "unknown operator UNKNOWN", err.Error())
|
||||
test.AssertResultComplex(t, Context{}, result)
|
||||
}
|
||||
|
||||
func TestGetMatchingNodes_ValidOperator(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a simple context with a scalar node
|
||||
scalarNode := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: "test",
|
||||
}
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
context.MatchingNodes.PushBack(scalarNode)
|
||||
|
||||
// Create an expression node with a valid operation (self reference)
|
||||
expressionNode := &ExpressionNode{
|
||||
Operation: &Operation{OperationType: selfReferenceOpType},
|
||||
}
|
||||
|
||||
result, err := navigator.GetMatchingNodes(context, expressionNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
test.AssertResult(t, 1, result.MatchingNodes.Len())
|
||||
|
||||
// Verify the result contains the same node
|
||||
resultNode := result.MatchingNodes.Front().Value.(*CandidateNode)
|
||||
test.AssertResult(t, scalarNode, resultNode)
|
||||
}
|
||||
|
||||
func TestDeeplyAssign_ScalarNode(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a context with a root mapping node
|
||||
rootNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Tag: "!!map",
|
||||
Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "existing", IsMapKey: true},
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "old_value"},
|
||||
},
|
||||
}
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
context.MatchingNodes.PushBack(rootNode)
|
||||
|
||||
// Create a scalar node to assign
|
||||
scalarNode := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: "new_value",
|
||||
}
|
||||
|
||||
// Assign to path ["new_key"]
|
||||
path := []interface{}{"new_key"}
|
||||
err := navigator.DeeplyAssign(context, path, scalarNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
|
||||
// Verify the assignment was made
|
||||
// The root node should now have the new key-value pair
|
||||
test.AssertResult(t, 4, len(rootNode.Content)) // 2 original + 2 new
|
||||
|
||||
// Find the new key-value pair
|
||||
found := false
|
||||
for i := 0; i < len(rootNode.Content)-1; i += 2 {
|
||||
key := rootNode.Content[i]
|
||||
value := rootNode.Content[i+1]
|
||||
if key.Value == "new_key" && value.Value == "new_value" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
test.AssertResult(t, true, found)
|
||||
}
|
||||
|
||||
func TestDeeplyAssign_MappingNode(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a context with a root mapping node
|
||||
rootNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Tag: "!!map",
|
||||
Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "existing", IsMapKey: true},
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "old_value"},
|
||||
},
|
||||
}
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
context.MatchingNodes.PushBack(rootNode)
|
||||
|
||||
// Create a mapping node to assign (this should trigger deep merge)
|
||||
mappingNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Tag: "!!map",
|
||||
Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "nested_key", IsMapKey: true},
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "nested_value"},
|
||||
},
|
||||
}
|
||||
|
||||
// Assign to path ["new_map"]
|
||||
path := []interface{}{"new_map"}
|
||||
err := navigator.DeeplyAssign(context, path, mappingNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
|
||||
// Verify the assignment was made
|
||||
// The root node should now have the new mapping
|
||||
test.AssertResult(t, 4, len(rootNode.Content)) // 2 original + 2 new
|
||||
|
||||
// Find the new mapping
|
||||
found := false
|
||||
for i := 0; i < len(rootNode.Content); i += 2 {
|
||||
if i+1 < len(rootNode.Content) {
|
||||
key := rootNode.Content[i]
|
||||
value := rootNode.Content[i+1]
|
||||
if key.Value == "new_map" && value.Kind == MappingNode {
|
||||
found = true
|
||||
// Verify the nested content
|
||||
test.AssertResult(t, 2, len(value.Content))
|
||||
test.AssertResult(t, "nested_key", value.Content[0].Value)
|
||||
test.AssertResult(t, "nested_value", value.Content[1].Value)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
test.AssertResult(t, true, found)
|
||||
}
|
||||
|
||||
func TestDeeplyAssign_DeepPath(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a context with a root mapping node
|
||||
rootNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Tag: "!!map",
|
||||
Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "level1", IsMapKey: true},
|
||||
{Kind: MappingNode, Tag: "!!map", Content: []*CandidateNode{}},
|
||||
},
|
||||
}
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
context.MatchingNodes.PushBack(rootNode)
|
||||
|
||||
// Create a scalar node to assign
|
||||
scalarNode := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: "deep_value",
|
||||
}
|
||||
|
||||
// Assign to deep path ["level1", "level2", "level3"]
|
||||
path := []interface{}{"level1", "level2", "level3"}
|
||||
err := navigator.DeeplyAssign(context, path, scalarNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
|
||||
// Verify the deep assignment was made
|
||||
level1Node := rootNode.Content[1] // The mapping node
|
||||
test.AssertResult(t, 2, len(level1Node.Content)) // Should have level2 key-value
|
||||
|
||||
level2Key := level1Node.Content[0]
|
||||
level2Value := level1Node.Content[1]
|
||||
test.AssertResult(t, "level2", level2Key.Value)
|
||||
test.AssertResult(t, MappingNode, level2Value.Kind)
|
||||
|
||||
level3Key := level2Value.Content[0]
|
||||
level3Value := level2Value.Content[1]
|
||||
test.AssertResult(t, "level3", level3Key.Value)
|
||||
test.AssertResult(t, "deep_value", level3Value.Value)
|
||||
}
|
||||
|
||||
func TestDeeplyAssign_ArrayPath(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a context with a root mapping node containing an array
|
||||
rootNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Tag: "!!map",
|
||||
Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "array", IsMapKey: true},
|
||||
{Kind: SequenceNode, Tag: "!!seq", Content: []*CandidateNode{}},
|
||||
},
|
||||
}
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
context.MatchingNodes.PushBack(rootNode)
|
||||
|
||||
// Create a scalar node to assign
|
||||
scalarNode := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: "array_value",
|
||||
}
|
||||
|
||||
// Assign to array path ["array", 0]
|
||||
path := []interface{}{"array", 0}
|
||||
err := navigator.DeeplyAssign(context, path, scalarNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
|
||||
// Verify the array assignment was made
|
||||
arrayNode := rootNode.Content[1] // The sequence node
|
||||
test.AssertResult(t, 1, len(arrayNode.Content)) // Should have one element
|
||||
|
||||
arrayElement := arrayNode.Content[0]
|
||||
test.AssertResult(t, "array_value", arrayElement.Value)
|
||||
}
|
||||
|
||||
func TestDeeplyAssign_OverwriteExisting(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a context with a root mapping node
|
||||
rootNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Tag: "!!map",
|
||||
Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "key", IsMapKey: true},
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "old_value"},
|
||||
},
|
||||
}
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
context.MatchingNodes.PushBack(rootNode)
|
||||
|
||||
// Create a scalar node to assign
|
||||
scalarNode := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: "new_value",
|
||||
}
|
||||
|
||||
// Assign to existing path ["key"]
|
||||
path := []interface{}{"key"}
|
||||
err := navigator.DeeplyAssign(context, path, scalarNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
|
||||
// Verify the value was overwritten
|
||||
test.AssertResult(t, 2, len(rootNode.Content)) // Should still have 2 elements
|
||||
|
||||
key := rootNode.Content[0]
|
||||
value := rootNode.Content[1]
|
||||
test.AssertResult(t, "key", key.Value)
|
||||
test.AssertResult(t, "new_value", value.Value) // Should be overwritten
|
||||
}
|
||||
|
||||
func TestDeeplyAssign_ErrorHandling(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a context with a scalar node (not a mapping)
|
||||
scalarNode := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: "not_a_map",
|
||||
}
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
context.MatchingNodes.PushBack(scalarNode)
|
||||
|
||||
// Create a scalar node to assign
|
||||
assignNode := &CandidateNode{
|
||||
Kind: ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: "value",
|
||||
}
|
||||
|
||||
path := []interface{}{"key"}
|
||||
err := navigator.DeeplyAssign(context, path, assignNode)
|
||||
|
||||
// Print the actual error for debugging
|
||||
if err != nil {
|
||||
t.Logf("Actual error: %v", err)
|
||||
}
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
}
|
||||
|
||||
func TestGetMatchingNodes_WithVariables(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a context with variables
|
||||
variables := make(map[string]*list.List)
|
||||
varList := list.New()
|
||||
varList.PushBack(&CandidateNode{Kind: ScalarNode, Tag: "!!str", Value: "var_value"})
|
||||
variables["test_var"] = varList
|
||||
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
Variables: variables,
|
||||
}
|
||||
|
||||
// Create an expression node that gets a variable
|
||||
expressionNode := &ExpressionNode{
|
||||
Operation: &Operation{OperationType: getVariableOpType, StringValue: "test_var"},
|
||||
}
|
||||
|
||||
result, err := navigator.GetMatchingNodes(context, expressionNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
test.AssertResult(t, 1, result.MatchingNodes.Len())
|
||||
|
||||
// Verify the variable was retrieved
|
||||
resultNode := result.MatchingNodes.Front().Value.(*CandidateNode)
|
||||
test.AssertResult(t, "var_value", resultNode.Value)
|
||||
}
|
||||
|
||||
func TestGetMatchingNodes_EmptyContext(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create an empty context
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
|
||||
// Create an expression node with self reference
|
||||
expressionNode := &ExpressionNode{
|
||||
Operation: &Operation{OperationType: selfReferenceOpType},
|
||||
}
|
||||
|
||||
result, err := navigator.GetMatchingNodes(context, expressionNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
test.AssertResult(t, 0, result.MatchingNodes.Len())
|
||||
}
|
||||
|
||||
func TestDeeplyAssign_ComplexMappingMerge(t *testing.T) {
|
||||
navigator := NewDataTreeNavigator()
|
||||
|
||||
// Create a context with a root mapping node containing nested data
|
||||
rootNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Tag: "!!map",
|
||||
Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "config", IsMapKey: true},
|
||||
{Kind: MappingNode, Tag: "!!map", Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "existing_key", IsMapKey: true},
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "existing_value"},
|
||||
}},
|
||||
},
|
||||
}
|
||||
context := Context{
|
||||
MatchingNodes: list.New(),
|
||||
}
|
||||
context.MatchingNodes.PushBack(rootNode)
|
||||
|
||||
// Create a mapping node to merge
|
||||
mappingNode := &CandidateNode{
|
||||
Kind: MappingNode,
|
||||
Tag: "!!map",
|
||||
Content: []*CandidateNode{
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "new_key", IsMapKey: true},
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "new_value"},
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "existing_key", IsMapKey: true},
|
||||
{Kind: ScalarNode, Tag: "!!str", Value: "updated_value"},
|
||||
},
|
||||
}
|
||||
|
||||
// Assign to path ["config"] (should merge with existing mapping)
|
||||
path := []interface{}{"config"}
|
||||
err := navigator.DeeplyAssign(context, path, mappingNode)
|
||||
|
||||
test.AssertResult(t, nil, err)
|
||||
|
||||
// Verify the merge was successful
|
||||
configNode := rootNode.Content[1] // The config mapping node
|
||||
test.AssertResult(t, 4, len(configNode.Content)) // Should have 2 key-value pairs
|
||||
|
||||
// Check that both existing and new keys are present
|
||||
foundExisting := false
|
||||
foundNew := false
|
||||
for i := 0; i < len(configNode.Content); i += 2 {
|
||||
if i+1 < len(configNode.Content) {
|
||||
key := configNode.Content[i]
|
||||
value := configNode.Content[i+1]
|
||||
switch key.Value {
|
||||
case "existing_key":
|
||||
foundExisting = true
|
||||
test.AssertResult(t, "updated_value", value.Value) // Should be updated
|
||||
case "new_key":
|
||||
foundNew = true
|
||||
test.AssertResult(t, "new_value", value.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
test.AssertResult(t, true, foundExisting)
|
||||
test.AssertResult(t, true, foundNew)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user