Compare commits
2729 Commits
dependenci
...
updater-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c423fe90e2 | ||
|
|
623461b649 | ||
|
|
1f9ba4de5c | ||
|
|
10f792b8bc | ||
|
|
357b64e9a0 | ||
|
|
f3ad6cee41 | ||
|
|
972310ea4e | ||
|
|
f5315fd737 | ||
|
|
c40c1960ed | ||
|
|
c8ae37811c | ||
|
|
15f05748a5 | ||
|
|
0a0a25913b | ||
|
|
945999889c | ||
|
|
ce7e1d6456 | ||
|
|
72c4b9de8a | ||
|
|
a8257e8cb2 | ||
|
|
15d3c765ac | ||
|
|
e7d3d7e0ae | ||
|
|
c0f3d35e13 | ||
|
|
fc30fab9cd | ||
|
|
a67e8388a9 | ||
|
|
ac7307b1f7 | ||
|
|
4068e5ec9c | ||
|
|
ea71181692 | ||
|
|
9b46c1a991 | ||
|
|
26acce94a4 | ||
|
|
2a0e1e206e | ||
|
|
f83d1d1582 | ||
|
|
77a090d5eb | ||
|
|
a9561e0ded | ||
|
|
dd505e4d58 | ||
|
|
dd0e9d4e1b | ||
|
|
8bc451ff08 | ||
|
|
032e5bf32e | ||
|
|
41ba8f6c7a | ||
|
|
799783d3ef | ||
|
|
77fb40506f | ||
|
|
5858f05c13 | ||
|
|
0432cad112 | ||
|
|
47ec8a348c | ||
|
|
eef348b8dc | ||
|
|
e78a02d0ba | ||
|
|
4e54b61380 | ||
|
|
cc39b2734e | ||
|
|
16eaccb10e | ||
|
|
dbc6f54a77 | ||
|
|
9bd5950f60 | ||
|
|
2c7c416f60 | ||
|
|
3eb0ab4197 | ||
|
|
b7cb511032 | ||
|
|
18850bb51a | ||
|
|
4d718e0cea | ||
|
|
3a2f567055 | ||
|
|
4f474e1098 | ||
|
|
22eac1a832 | ||
|
|
07738dd82d | ||
|
|
f07d508018 | ||
|
|
b591a90100 | ||
|
|
db3cfdf66f | ||
|
|
5cf3e1a817 | ||
|
|
25cfd162f6 | ||
|
|
7001ef2030 | ||
|
|
cbcab72a7c | ||
|
|
d05b1c3130 | ||
|
|
fbdcda9a7f | ||
|
|
0ebc482698 | ||
|
|
09969d95de | ||
|
|
689042df60 | ||
|
|
564fe15df2 | ||
|
|
30015dfd67 | ||
|
|
698c47da69 | ||
|
|
46b7a520cd | ||
|
|
cadd6c3497 | ||
|
|
c4682ab6e9 | ||
|
|
59594855b8 | ||
|
|
44b7af0c6e | ||
|
|
80f550d67e | ||
|
|
44f085604a | ||
|
|
1144f8017a | ||
|
|
d8fa17a3e7 | ||
|
|
bf868e0ae2 | ||
|
|
e1dac63f69 | ||
|
|
2657234761 | ||
|
|
4a345fdd41 | ||
|
|
fee6a586d7 | ||
|
|
7c738483b7 | ||
|
|
c3d8ed28a2 | ||
|
|
f47b4b961b | ||
|
|
7763abf6c2 | ||
|
|
9d9d078346 | ||
|
|
76cec7aa54 | ||
|
|
2223a99ed1 | ||
|
|
4926d33849 | ||
|
|
9978182cae | ||
|
|
1d77bc57df | ||
|
|
1bcb6646c4 | ||
|
|
06130b95d2 | ||
|
|
0536a45959 | ||
|
|
b150eb7e64 | ||
|
|
d5c0b09a2f | ||
|
|
a36d2633c9 | ||
|
|
72783e3ff5 | ||
|
|
929abb3c04 | ||
|
|
6810b1f221 | ||
|
|
a96678caea | ||
|
|
2d59177256 | ||
|
|
62b743fa20 | ||
|
|
b85aaa816e | ||
|
|
74fc3dea33 | ||
|
|
55167e75af | ||
|
|
63d39c24dc | ||
|
|
1d4d8aeb2b | ||
|
|
c9589fc75d | ||
|
|
d0c3a306bc | ||
|
|
1e3566ed7d | ||
|
|
756d303f6a | ||
|
|
a5b948a41c | ||
|
|
6440f856a2 | ||
|
|
25acb8121f | ||
|
|
c05bd31f6b | ||
|
|
9416de6442 | ||
|
|
c507c483fb | ||
|
|
32ee1b36d2 | ||
|
|
2b89f07fe5 | ||
|
|
0621a17d17 | ||
|
|
4840e07da8 | ||
|
|
af1689ee07 | ||
|
|
d3dbc11b1b | ||
|
|
10479b0936 | ||
|
|
fa1f9875d9 | ||
|
|
7466139320 | ||
|
|
7a5bcf67c5 | ||
|
|
10e3010324 | ||
|
|
464f8095ab | ||
|
|
8b800f679b | ||
|
|
5b2f946828 | ||
|
|
b1c31f7a6f | ||
|
|
d49618786e | ||
|
|
866ec2720d | ||
|
|
b4595d7886 | ||
|
|
9af0803e9b | ||
|
|
ebecabc0cc | ||
|
|
c9a9e934f4 | ||
|
|
544f63a2ff | ||
|
|
1dbfc96ebf | ||
|
|
250dcf569c | ||
|
|
0ef552d384 | ||
|
|
998ded5e0b | ||
|
|
d60c3b4d64 | ||
|
|
b6db209670 | ||
|
|
29fd97e402 | ||
|
|
32ebc8d174 | ||
|
|
420b0a254a | ||
|
|
400efa00ec | ||
|
|
4024b72954 | ||
|
|
7cd1816866 | ||
|
|
5983ac5449 | ||
|
|
b70e202201 | ||
|
|
cc5ebec0cb | ||
|
|
1d2fd06507 | ||
|
|
8d99af4c15 | ||
|
|
861428d3bd | ||
|
|
00e3f13bc9 | ||
|
|
2a897ecf8a | ||
|
|
8448217bd4 | ||
|
|
305a64c3e3 | ||
|
|
e1d6c74e4f | ||
|
|
7556758284 | ||
|
|
3926288b7f | ||
|
|
4d56a5cd8c | ||
|
|
d554679ee2 | ||
|
|
6fe7aabe14 | ||
|
|
f4e57a2831 | ||
|
|
32008b9364 | ||
|
|
9d89eeb974 | ||
|
|
95c23730ef | ||
|
|
7acbb5da4f | ||
|
|
ddd85d4d87 | ||
|
|
53a46d0dc6 | ||
|
|
92ae277e3a | ||
|
|
dd190659ef | ||
|
|
6c5488be70 | ||
|
|
316e1d4268 | ||
|
|
c3c13ba6e6 | ||
|
|
b0a82bc7ad | ||
|
|
7f0ebbd83d | ||
|
|
4f86f3c0ec | ||
|
|
c100b9c54d | ||
|
|
ebc9fc5eba | ||
|
|
29ae70bbf6 | ||
|
|
becc51bcd2 | ||
|
|
1993e5dd51 | ||
|
|
d1a2bd78d7 | ||
|
|
a5521404b6 | ||
|
|
8aa7b34197 | ||
|
|
1ddbe7c2cc | ||
|
|
6578cd8c51 | ||
|
|
c2c46d1cae | ||
|
|
95231e7cd3 | ||
|
|
5b6c9be99f | ||
|
|
d587ed09a5 | ||
|
|
10576780ed | ||
|
|
b37b121afb | ||
|
|
2b69ed4915 | ||
|
|
9a666d807c | ||
|
|
050b363066 | ||
|
|
8cae9d4e0a | ||
|
|
b5b65ac8c5 | ||
|
|
7370f00857 | ||
|
|
dc798fe2dd | ||
|
|
eda8fc125f | ||
|
|
8296675574 | ||
|
|
e2ad2d23f8 | ||
|
|
d8df283e93 | ||
|
|
ce42ca77a9 | ||
|
|
cfe8328f9e | ||
|
|
ff5a2c6ca4 | ||
|
|
23b0493d0b | ||
|
|
779291151e | ||
|
|
ba0f4cdde0 | ||
|
|
3983762202 | ||
|
|
c72413cbe6 | ||
|
|
73b9a71c84 | ||
|
|
e7bf997f8c | ||
|
|
dbfcc80afe | ||
|
|
32b6821b8a | ||
|
|
3ce5d3bf2d | ||
|
|
f6023dc618 | ||
|
|
9d9dd73790 | ||
|
|
7383459b9a | ||
|
|
55cde38562 | ||
|
|
d6a79316a6 | ||
|
|
bd3231bfa8 | ||
|
|
8d62c0d521 | ||
|
|
4d37e6870c | ||
|
|
0bb042e085 | ||
|
|
65ed9cb5b2 | ||
|
|
ffc0693afc | ||
|
|
2622cc06eb | ||
|
|
15b117dc15 | ||
|
|
16846aefd3 | ||
|
|
babc2c8f9b | ||
|
|
a0b70f9c34 | ||
|
|
dc46eb9d21 | ||
|
|
824ad9fa29 | ||
|
|
0646fa96a6 | ||
|
|
d05952e945 | ||
|
|
abe5cf1b84 | ||
|
|
07b424cb09 | ||
|
|
c7494de0a7 | ||
|
|
1a9a2ff9e0 | ||
|
|
4f15bdf74d | ||
|
|
541b78ba6f | ||
|
|
05b910dc17 | ||
|
|
41629df189 | ||
|
|
c718ef3058 | ||
|
|
2b1d02f0cc | ||
|
|
272a0c916d | ||
|
|
16187858a3 | ||
|
|
f6ea01238a | ||
|
|
ae4067aee6 | ||
|
|
d401a83c75 | ||
|
|
41a662f784 | ||
|
|
1df9fff0a7 | ||
|
|
40de99e65d | ||
|
|
76227770a4 | ||
|
|
b70cad537c | ||
|
|
1282cc56bf | ||
|
|
85d08fadd9 | ||
|
|
fad73a281a | ||
|
|
18d24d5952 | ||
|
|
6e6462742c | ||
|
|
16381d1895 | ||
|
|
1970155c51 | ||
|
|
51ef1329be | ||
|
|
b6a6f5f434 | ||
|
|
644fdc071f | ||
|
|
25d66a4eee | ||
|
|
9070ef1dbe | ||
|
|
f700dc124e | ||
|
|
5fcea4c684 | ||
|
|
843f40d7d5 | ||
|
|
84fbccbfd9 | ||
|
|
49c81f6201 | ||
|
|
c894a15d13 | ||
|
|
196b887381 | ||
|
|
2ad20ed239 | ||
|
|
98de91771e | ||
|
|
9dfd9bad20 | ||
|
|
0b8d08d13b | ||
|
|
0de304d4e3 | ||
|
|
55f1766ebc | ||
|
|
6d1a8fb264 | ||
|
|
d3958594d9 | ||
|
|
b5952f320b | ||
|
|
98be9621a6 | ||
|
|
e4eb13ce22 | ||
|
|
ecf2da7c0a | ||
|
|
7d7c8988d7 | ||
|
|
5e11d36972 | ||
|
|
3039f39d40 | ||
|
|
7b5fd104de | ||
|
|
30ea408019 | ||
|
|
3fa130695c | ||
|
|
fece31438a | ||
|
|
ad1f2bea3b | ||
|
|
cd4bfdd743 | ||
|
|
cde8c4004f | ||
|
|
c53514e060 | ||
|
|
8e99672265 | ||
|
|
5b9b5cb6a8 | ||
|
|
62141380d8 | ||
|
|
52a15bb281 | ||
|
|
b092f74c88 | ||
|
|
937f43c270 | ||
|
|
9b02088918 | ||
|
|
1bd503a654 | ||
|
|
c6477dfda4 | ||
|
|
4831d88467 | ||
|
|
9ebde802d4 | ||
|
|
a9cccc7b97 | ||
|
|
d54a765bd6 | ||
|
|
5a2751162f | ||
|
|
492a5a6de7 | ||
|
|
f6c0f144a6 | ||
|
|
1c046f3ca3 | ||
|
|
4a47c5bb6f | ||
|
|
b7e01aefb4 | ||
|
|
e8e16f7d57 | ||
|
|
59caa22431 | ||
|
|
e2046f3e48 | ||
|
|
8fdcffc731 | ||
|
|
3ec77c6256 | ||
|
|
b09313756e | ||
|
|
f800e2e3b6 | ||
|
|
daad623855 | ||
|
|
7716e2bc87 | ||
|
|
70bd5ec03c | ||
|
|
ce5c86c3b0 | ||
|
|
a6a6d9d036 | ||
|
|
971dd6a2cf | ||
|
|
42d0ea7e36 | ||
|
|
006bcffe8c | ||
|
|
ff4101fa47 | ||
|
|
7280635741 | ||
|
|
7ede91599c | ||
|
|
6e40dd9862 | ||
|
|
42db9ea0bb | ||
|
|
ca0cf4552c | ||
|
|
d91653b218 | ||
|
|
1ace560531 | ||
|
|
81968a579d | ||
|
|
5a0eb56f70 | ||
|
|
804fad6083 | ||
|
|
98d3a48710 | ||
|
|
0ec4f46052 | ||
|
|
a891341e35 | ||
|
|
10426af3ad | ||
|
|
5be1d604ee | ||
|
|
1baa840160 | ||
|
|
14347f60d5 | ||
|
|
df5424d55e | ||
|
|
12065330e1 | ||
|
|
e054ac67fb | ||
|
|
31a7750482 | ||
|
|
2e38307f65 | ||
|
|
47accdd2b1 | ||
|
|
cb0146573f | ||
|
|
cf78bb3686 | ||
|
|
b5b5ae4e7b | ||
|
|
b99bc7fcd1 | ||
|
|
f50fe9159d | ||
|
|
09f6917638 | ||
|
|
e330d75a89 | ||
|
|
4f0ce7458e | ||
|
|
a2811c4803 | ||
|
|
1c233783a7 | ||
|
|
3e45cc4650 | ||
|
|
1a7c076e07 | ||
|
|
da4fddf150 | ||
|
|
970eb62aa6 | ||
|
|
d669650758 | ||
|
|
69347160e9 | ||
|
|
a345b54a77 | ||
|
|
8aabcd77a5 | ||
|
|
c30f54609d | ||
|
|
0830236a73 | ||
|
|
44f21444bb | ||
|
|
1d88d98ea1 | ||
|
|
86f69fd574 | ||
|
|
e21846a2ce | ||
|
|
d5981ca94f | ||
|
|
8c5eb3b550 | ||
|
|
55dc416109 | ||
|
|
ec30b888d1 | ||
|
|
2a92755e65 | ||
|
|
6976ea3c09 | ||
|
|
b07ed2dbf5 | ||
|
|
2ab923da87 | ||
|
|
9799d4f747 | ||
|
|
f739836891 | ||
|
|
a28887be8e | ||
|
|
0f13691ae0 | ||
|
|
ae72b83dbe | ||
|
|
2e38404434 | ||
|
|
11b8c8be45 | ||
|
|
a06597a3a6 | ||
|
|
108840c4be | ||
|
|
16c8672aeb | ||
|
|
167edcf8ef | ||
|
|
d6dd89b674 | ||
|
|
fac2ee6374 | ||
|
|
dd7876845a | ||
|
|
56e6139c2b | ||
|
|
04bdd48a2a | ||
|
|
5b47fe5b88 | ||
|
|
84a5cf6b89 | ||
|
|
618ba52bca | ||
|
|
5c0cde517f | ||
|
|
1b249564a3 | ||
|
|
81b5501b0e | ||
|
|
91ccb3045c | ||
|
|
e31f176c25 | ||
|
|
ad45485009 | ||
|
|
25e5cf2ac2 | ||
|
|
bd58d935c6 | ||
|
|
da2705ff7d | ||
|
|
61f019f194 | ||
|
|
74e441df5b | ||
|
|
772ecdd3b0 | ||
|
|
baa535b609 | ||
|
|
a2ff0a7e20 | ||
|
|
84732f9835 | ||
|
|
dd17bcb0d6 | ||
|
|
cab8e613a6 | ||
|
|
fe1227618a | ||
|
|
596c52de87 | ||
|
|
ba5d5e9f86 | ||
|
|
530669d288 | ||
|
|
70b0f9a03a | ||
|
|
105de99d06 | ||
|
|
6239f81f36 | ||
|
|
697d200ffe | ||
|
|
16d5077f55 | ||
|
|
e0e1a05448 | ||
|
|
bcaafa67a3 | ||
|
|
36142656a4 | ||
|
|
d6a48deb5a | ||
|
|
e98ce0c2ae | ||
|
|
8118fc754c | ||
|
|
1ec7a0f23c | ||
|
|
488e8ef1d5 | ||
|
|
1f99cee78b | ||
|
|
c25015ed54 | ||
|
|
aaefc5b479 | ||
|
|
1c58816c73 | ||
|
|
0fd99358aa | ||
|
|
d4012bace9 | ||
|
|
af7660686d | ||
|
|
b57c6e408a | ||
|
|
124934b012 | ||
|
|
1bef6d085d | ||
|
|
c73927c5ba | ||
|
|
692deb6012 | ||
|
|
8ec499f631 | ||
|
|
2bcd653a56 | ||
|
|
0f10952979 | ||
|
|
58fa67100f | ||
|
|
8e294916c4 | ||
|
|
6877e0c95d | ||
|
|
37a333a023 | ||
|
|
48f1da963a | ||
|
|
e1905aced4 | ||
|
|
f18202a3a4 | ||
|
|
c1a9de4d66 | ||
|
|
18f86874ee | ||
|
|
e6686e0b82 | ||
|
|
4bf166986d | ||
|
|
0f60d84f6c | ||
|
|
15e54df67c | ||
|
|
4cb6ad7736 | ||
|
|
e27a32395a | ||
|
|
eddcf209c1 | ||
|
|
10a151d411 | ||
|
|
54d5586a60 | ||
|
|
30ca547e50 | ||
|
|
a1944d1a90 | ||
|
|
c2b35fdaa5 | ||
|
|
805b54d81e | ||
|
|
e3579dac65 | ||
|
|
f80591242e | ||
|
|
69cb9769c1 | ||
|
|
efd42d9da0 | ||
|
|
21a6340095 | ||
|
|
6c96724dce | ||
|
|
ebb194d2a2 | ||
|
|
1a51a92b70 | ||
|
|
5760f16272 | ||
|
|
4ed36f6223 | ||
|
|
7ea7ca1415 | ||
|
|
1ee8786ab7 | ||
|
|
44ca513241 | ||
|
|
73310b466b | ||
|
|
1ba688727e | ||
|
|
3b69465016 | ||
|
|
3e53ea7209 | ||
|
|
07bdc108ed | ||
|
|
a18efb0e71 | ||
|
|
de1c825ad3 | ||
|
|
de2cff824e | ||
|
|
aff504bddc | ||
|
|
277390e597 | ||
|
|
fdcefe458e | ||
|
|
9bb2160abe | ||
|
|
97d683541d | ||
|
|
9f7ffb80e1 | ||
|
|
c957ea7b24 | ||
|
|
181fce16b1 | ||
|
|
825f023505 | ||
|
|
347ea53b32 | ||
|
|
d525e0dd70 | ||
|
|
365e844b83 | ||
|
|
44bdeb555a | ||
|
|
520c33557e | ||
|
|
bfad5ac091 | ||
|
|
3ecf4bc238 | ||
|
|
028e4012aa | ||
|
|
dc6d429b9c | ||
|
|
625cf1a803 | ||
|
|
9ee011514a | ||
|
|
184fd4a1ba | ||
|
|
23dcfd9401 | ||
|
|
1cdba297fb | ||
|
|
1ed6743bbb | ||
|
|
41c42bba32 | ||
|
|
8fb66ea32c | ||
|
|
51d4c1c4a5 | ||
|
|
86e069994e | ||
|
|
1cb923b6d8 | ||
|
|
b1d003b073 | ||
|
|
0fb4254481 | ||
|
|
ae7f456011 | ||
|
|
cd4bec6bfd | ||
|
|
5906e0126d | ||
|
|
dce1395af1 | ||
|
|
e7db13af37 | ||
|
|
2eee8cd7d3 | ||
|
|
836a2abae1 | ||
|
|
a68a86d6db | ||
|
|
ee9f0990fd | ||
|
|
59d0629e3f | ||
|
|
17af292761 | ||
|
|
a4dd4bcc8a | ||
|
|
1a9b0a476b | ||
|
|
bb015506e7 | ||
|
|
76be5d8469 | ||
|
|
1258e187f5 | ||
|
|
4056a4c35f | ||
|
|
b6677f0f72 | ||
|
|
e8c1e6f241 | ||
|
|
618595ac4c | ||
|
|
d54ba48c11 | ||
|
|
a489012a0c | ||
|
|
a5acdc04e3 | ||
|
|
709a20ed7b | ||
|
|
c88f2099c1 | ||
|
|
f72a2a943b | ||
|
|
c51199719d | ||
|
|
bf374f2e85 | ||
|
|
34f450fcdb | ||
|
|
23f75598e5 | ||
|
|
afc238d60e | ||
|
|
1291c38d58 | ||
|
|
16caccde51 | ||
|
|
33f199fcd2 | ||
|
|
2b534e0d51 | ||
|
|
23d1d210c7 | ||
|
|
39a1d6202a | ||
|
|
f00a5af6c9 | ||
|
|
48d68f5766 | ||
|
|
f948da748e | ||
|
|
8b25d45109 | ||
|
|
c4ddc35746 | ||
|
|
0122e2bdcf | ||
|
|
94b75f463b | ||
|
|
3c2e04290c | ||
|
|
f0331ec2d9 | ||
|
|
3b4013a1b0 | ||
|
|
31ddccd3e1 | ||
|
|
d29fe4cb6c | ||
|
|
6763537f22 | ||
|
|
8ab4bd6293 | ||
|
|
31bc644763 | ||
|
|
fcd672abeb | ||
|
|
5f550da0bb | ||
|
|
e865a86eef | ||
|
|
80ee2e4289 | ||
|
|
4d327594d3 | ||
|
|
3363c37457 | ||
|
|
cee9be81bf | ||
|
|
932d36462f | ||
|
|
75c930f7ef | ||
|
|
bdb178d893 | ||
|
|
3b0635e8a1 | ||
|
|
f5760784bf | ||
|
|
67f3554095 | ||
|
|
3bb3872e38 | ||
|
|
c98330ea1f | ||
|
|
d895b68f04 | ||
|
|
e230981ac4 | ||
|
|
20763a741a | ||
|
|
4babcd9442 | ||
|
|
d75f36066a | ||
|
|
26ca4670ad | ||
|
|
c4d6c167a2 | ||
|
|
e5af9541da | ||
|
|
89d9f47191 | ||
|
|
ff2cf30238 | ||
|
|
ebe0899eb1 | ||
|
|
a3d0a38b1e | ||
|
|
63bd0c87b2 | ||
|
|
db593fb188 | ||
|
|
67ae10b593 | ||
|
|
c8d91c9e14 | ||
|
|
6c54f5e9b4 | ||
|
|
8749648d97 | ||
|
|
0b75b5ef26 | ||
|
|
fbcadd0493 | ||
|
|
75bb7a4dd7 | ||
|
|
4604fe4841 | ||
|
|
e8badb0c0f | ||
|
|
8906a8f3c6 | ||
|
|
4e32990a5d | ||
|
|
9f2583d1f2 | ||
|
|
a9b3d8885d | ||
|
|
29ec4dc546 | ||
|
|
d0d5204cbc | ||
|
|
3d84acd7ac | ||
|
|
5057221f59 | ||
|
|
3ddfbc5d2f | ||
|
|
362270e3ea | ||
|
|
db91177e90 | ||
|
|
d2f51ce509 | ||
|
|
146a66fb09 | ||
|
|
03305f03c1 | ||
|
|
82e76bc58e | ||
|
|
e8ff6c785a | ||
|
|
a8fafb469a | ||
|
|
b9a220cb63 | ||
|
|
0b44d40b39 | ||
|
|
6b349eda45 | ||
|
|
ad335ba005 | ||
|
|
04d766884a | ||
|
|
44d1ec433d | ||
|
|
97864e8df3 | ||
|
|
3916293e8f | ||
|
|
a527177b67 | ||
|
|
9c027b10b2 | ||
|
|
5da7086475 | ||
|
|
0006012ae7 | ||
|
|
a3f46ec037 | ||
|
|
bfea52f9dd | ||
|
|
53334f05b8 | ||
|
|
ba195c41b6 | ||
|
|
cca2f1ce61 | ||
|
|
a51191c661 | ||
|
|
4cdb5f93b9 | ||
|
|
fae658c9c2 | ||
|
|
886a469634 | ||
|
|
c3c1394e86 | ||
|
|
6a00255fff | ||
|
|
4f0aae0879 | ||
|
|
4f4fe4c41c | ||
|
|
2a4a3c8250 | ||
|
|
7864acbadb | ||
|
|
ba18e64be0 | ||
|
|
ba8c1e5eb2 | ||
|
|
2737fb2d87 | ||
|
|
899285735f | ||
|
|
f561d12d35 | ||
|
|
be258b13e0 | ||
|
|
dbce6b5f1a | ||
|
|
30f0c99a58 | ||
|
|
49880c05d9 | ||
|
|
dc2fc84f58 | ||
|
|
78c2a1694f | ||
|
|
baf34dd0d3 | ||
|
|
48f9dede7b | ||
|
|
a1f2a621ef | ||
|
|
a1e8ddb461 | ||
|
|
d33d90a36e | ||
|
|
c3114b876f | ||
|
|
50285aebde | ||
|
|
0eb5ee6ea8 | ||
|
|
16c9c95e19 | ||
|
|
3bc4da3e85 | ||
|
|
a028a2e1cc | ||
|
|
9675a35dff | ||
|
|
c1546fdd64 | ||
|
|
a109efc1d6 | ||
|
|
0782b25830 | ||
|
|
0041ff13b8 | ||
|
|
4693a25aa0 | ||
|
|
609df5b4a6 | ||
|
|
6df8140cb1 | ||
|
|
65b4cb3191 | ||
|
|
e1de481349 | ||
|
|
6e1cc80b91 | ||
|
|
b658ce7e75 | ||
|
|
f91f374dfa | ||
|
|
56f6de5410 | ||
|
|
94d22ecfc3 | ||
|
|
e25d71c6c8 | ||
|
|
bb1b156d2f | ||
|
|
1b80ddf1e9 | ||
|
|
66d2fe9074 | ||
|
|
6e36910734 | ||
|
|
a553a33c46 | ||
|
|
2a6f8b401b | ||
|
|
fa30567140 | ||
|
|
243f685b83 | ||
|
|
6cf2373b34 | ||
|
|
e842ea745a | ||
|
|
9696c7cec0 | ||
|
|
c4986eec50 | ||
|
|
61079e769e | ||
|
|
00d2c915d1 | ||
|
|
6ad975c420 | ||
|
|
1cd1a2d907 | ||
|
|
39a3c3d3a7 | ||
|
|
dfefcf03ad | ||
|
|
825b00e618 | ||
|
|
ae562e1e92 | ||
|
|
21c7888595 | ||
|
|
3b87a4f9d0 | ||
|
|
8564a58eab | ||
|
|
c5d009c2cd | ||
|
|
c2e165d825 | ||
|
|
922020c57a | ||
|
|
8cdc33beab | ||
|
|
23eafdfe00 | ||
|
|
e72e8ea631 | ||
|
|
a610a43db0 | ||
|
|
4a90ffe619 | ||
|
|
18e8357b6a | ||
|
|
df39347b19 | ||
|
|
a36261d705 | ||
|
|
f133d22124 | ||
|
|
6ba276b43f | ||
|
|
44db98f260 | ||
|
|
8873526619 | ||
|
|
37c2599754 | ||
|
|
a079b470b8 | ||
|
|
0f9ed02bf0 | ||
|
|
9aeb68205c | ||
|
|
82b4cf259c | ||
|
|
566fd3e88b | ||
|
|
fbecf4f47b | ||
|
|
52899d4def | ||
|
|
a89a828b35 | ||
|
|
4d0dbdaced | ||
|
|
8003f9902e | ||
|
|
15bd7324fe | ||
|
|
bb44fc51bd | ||
|
|
67a32e60c7 | ||
|
|
960725777c | ||
|
|
98c6e0311b | ||
|
|
95b7641f9c | ||
|
|
18b0c3f7aa | ||
|
|
49d3644d6a | ||
|
|
ee9d12d933 | ||
|
|
5d37015f4d | ||
|
|
dca25637c9 | ||
|
|
a7020fd46c | ||
|
|
1ef2b1aaf1 | ||
|
|
a7a661e60f | ||
|
|
2a9e2d47f5 | ||
|
|
e33b3043df | ||
|
|
3f41618aa1 | ||
|
|
a507d7567f | ||
|
|
62a6f58705 | ||
|
|
77dd074fc3 | ||
|
|
e8c0051be3 | ||
|
|
b3923eafc7 | ||
|
|
9ebd96611a | ||
|
|
824325a2eb | ||
|
|
e8b3bd5bdc | ||
|
|
a59fda512c | ||
|
|
ae181f6835 | ||
|
|
67bb242778 | ||
|
|
2028c189aa | ||
|
|
ba0dc4fb81 | ||
|
|
c40db417d2 | ||
|
|
0eb776cdd3 | ||
|
|
c79a7a7f6f | ||
|
|
1e3c995e6a | ||
|
|
3f79e42628 | ||
|
|
c16ae89a3d | ||
|
|
7132eaeb11 | ||
|
|
aef96f0d27 | ||
|
|
b20a56f1de | ||
|
|
3073b4e48e | ||
|
|
7b53752ccd | ||
|
|
03eedf6175 | ||
|
|
2330a4bc93 | ||
|
|
36afae50b1 | ||
|
|
272ee7577c | ||
|
|
7f34073da6 | ||
|
|
f46ee2a0a3 | ||
|
|
4a79f0c75d | ||
|
|
27a78af269 | ||
|
|
586af67829 | ||
|
|
575d8c4240 | ||
|
|
d32734214b | ||
|
|
22ce5aab25 | ||
|
|
9f90a1c58e | ||
|
|
b5e0374946 | ||
|
|
44cb1c7f3e | ||
|
|
80aba859e7 | ||
|
|
08360edd26 | ||
|
|
6e69b3f032 | ||
|
|
19bb9c7f50 | ||
|
|
f5dee51e9c | ||
|
|
bd37fef720 | ||
|
|
c22e4e5e2c | ||
|
|
2887a2b6d3 | ||
|
|
01bde19701 | ||
|
|
792f1826ee | ||
|
|
c16795dce9 | ||
|
|
c1597a0968 | ||
|
|
590aa950df | ||
|
|
402018b95c | ||
|
|
d5101ac2f3 | ||
|
|
251942c91d | ||
|
|
fe86b812cd | ||
|
|
cb3bff589f | ||
|
|
ec7d7ec559 | ||
|
|
24c7a5b805 | ||
|
|
0a4ecb1507 | ||
|
|
d736dace50 | ||
|
|
70bbab909f | ||
|
|
6625f78e4f | ||
|
|
da2b4c8858 | ||
|
|
12df415dfd | ||
|
|
2493f463f3 | ||
|
|
f4238b1fb9 | ||
|
|
794783ab4e | ||
|
|
02634622a5 | ||
|
|
ac24501e76 | ||
|
|
e40ea38112 | ||
|
|
b809b9bb80 | ||
|
|
73bad8f355 | ||
|
|
ac884da56b | ||
|
|
c35ab2e1cd | ||
|
|
ed3907c273 | ||
|
|
5e00287045 | ||
|
|
95c6578911 | ||
|
|
d22097ee33 | ||
|
|
74251af163 | ||
|
|
7c1b11851f | ||
|
|
f8724c4cb9 | ||
|
|
3baac034e5 | ||
|
|
114f1426f3 | ||
|
|
7de63cea5c | ||
|
|
34e3af2b38 | ||
|
|
da907d0eea | ||
|
|
9cbc2d9206 | ||
|
|
db2e466d60 | ||
|
|
e5cdbf7361 | ||
|
|
a919f493d6 | ||
|
|
3660298683 | ||
|
|
c845efe475 | ||
|
|
38eb47132d | ||
|
|
b1f097f32b | ||
|
|
d3123253b3 | ||
|
|
7b1ec1ec22 | ||
|
|
4cefacfe73 | ||
|
|
978acfa471 | ||
|
|
fb2d138cbf | ||
|
|
0edd63edb5 | ||
|
|
97b730668c | ||
|
|
26b8cf6d52 | ||
|
|
a979638368 | ||
|
|
97f434ad4a | ||
|
|
34af040c48 | ||
|
|
cc81b443be | ||
|
|
d44f3c22c7 | ||
|
|
3795b537f6 | ||
|
|
ecb5f0885c | ||
|
|
86d2234713 | ||
|
|
62ddf26150 | ||
|
|
ec14b7c52f | ||
|
|
5f9cc38e82 | ||
|
|
f48c58f299 | ||
|
|
c2843f3c4b | ||
|
|
5d33df4e12 | ||
|
|
c030fb47ca | ||
|
|
964daadb18 | ||
|
|
71a5698ac7 | ||
|
|
d41d74d0f8 | ||
|
|
f6c7a611a3 | ||
|
|
06f4e79e5c | ||
|
|
154cf44f0a | ||
|
|
f6e2ff0e44 | ||
|
|
2aba616f7f | ||
|
|
95e21386b8 | ||
|
|
250e908d9a | ||
|
|
9742fb296c | ||
|
|
9ff3c2c0d4 | ||
|
|
9dd7bd9530 | ||
|
|
6f477b7147 | ||
|
|
8389826e30 | ||
|
|
aa31fb7470 | ||
|
|
a013fe663c | ||
|
|
89ce497431 | ||
|
|
60c0b649e8 | ||
|
|
2bbb5ea23b | ||
|
|
4f9c1533c1 | ||
|
|
a6a3847e30 | ||
|
|
118f38dba3 | ||
|
|
879f946b28 | ||
|
|
6acb8a5a91 | ||
|
|
a38f1e92e3 | ||
|
|
4ca977466e | ||
|
|
ec45dc56fb | ||
|
|
5686302653 | ||
|
|
6322773513 | ||
|
|
8e845fc919 | ||
|
|
f90c8f2ae5 | ||
|
|
b5af06529f | ||
|
|
fac3669f8e | ||
|
|
28ff8d6dcc | ||
|
|
d0e7f6673c | ||
|
|
800dc21202 | ||
|
|
f52089a674 | ||
|
|
82543de95e | ||
|
|
12db69407e | ||
|
|
9b2b447b8b | ||
|
|
35f5e4ca41 | ||
|
|
8a69713f6c | ||
|
|
efd8ef0380 | ||
|
|
c5eacd1627 | ||
|
|
5fdb52d8d0 | ||
|
|
7869ce060f | ||
|
|
e0d96c0ce1 | ||
|
|
30678904ee | ||
|
|
953be61d89 | ||
|
|
d8c85007d4 | ||
|
|
591c1cb454 | ||
|
|
0ca90ed082 | ||
|
|
4c963b3978 | ||
|
|
071665f0c3 | ||
|
|
9a7826752f | ||
|
|
44b4187365 | ||
|
|
148807543f | ||
|
|
2b9fa09293 | ||
|
|
10211d1d03 | ||
|
|
46811f33ad | ||
|
|
32b16790d3 | ||
|
|
10592ca5a8 | ||
|
|
dc5cb2e1b8 | ||
|
|
c10d782524 | ||
|
|
d73366984f | ||
|
|
60b1e47ae6 | ||
|
|
9591fb2c21 | ||
|
|
c3cba03ac6 | ||
|
|
ce7818c436 | ||
|
|
f367a81e44 | ||
|
|
de507f7ec9 | ||
|
|
0a8be603c8 | ||
|
|
d7f033bd46 | ||
|
|
1fb3b87697 | ||
|
|
b9c8fa61b2 | ||
|
|
99ea6d5080 | ||
|
|
0bacfa9286 | ||
|
|
b350b605a8 | ||
|
|
d1eeeab7b1 | ||
|
|
54296ba84a | ||
|
|
f82b0f259c | ||
|
|
57f1c005e6 | ||
|
|
d9e5387bff | ||
|
|
3154b8ce55 | ||
|
|
45b48ede44 | ||
|
|
961b86dcd2 | ||
|
|
4d57c64b0d | ||
|
|
1c894f3cfa | ||
|
|
3d6faecaed | ||
|
|
2263ade187 | ||
|
|
2cdf33d8a1 | ||
|
|
f6fce6bd31 | ||
|
|
f9f1721d66 | ||
|
|
0792ac7de8 | ||
|
|
98edb048b7 | ||
|
|
52fcdf28fa | ||
|
|
d9671faca7 | ||
|
|
f456004543 | ||
|
|
a38040d0ea | ||
|
|
f18cd92318 | ||
|
|
06e1d0f8da | ||
|
|
dffd663d7a | ||
|
|
414f9e9e96 | ||
|
|
c17ea74856 | ||
|
|
9a08740e5b | ||
|
|
8bea0db843 | ||
|
|
2c612e371f | ||
|
|
2f61dc9bc6 | ||
|
|
d18b78c11c | ||
|
|
95fb4f2e50 | ||
|
|
f31fe1440d | ||
|
|
10cd0a1f30 | ||
|
|
659f854f62 | ||
|
|
4a7f8dbe09 | ||
|
|
3e19e574e6 | ||
|
|
2af2d3664f | ||
|
|
7dad46adb4 | ||
|
|
b6fc6a751a | ||
|
|
934674a8d7 | ||
|
|
c66986f065 | ||
|
|
fc49e4a0da | ||
|
|
b6e1d71b81 | ||
|
|
b3626a786d | ||
|
|
a398e28ac0 | ||
|
|
892d4e597e | ||
|
|
e8311dd306 | ||
|
|
a0b266fef8 | ||
|
|
983d1ea361 | ||
|
|
db615b932c | ||
|
|
07415e512f | ||
|
|
0c6d417d8c | ||
|
|
772e01ad40 | ||
|
|
1b2509d5bc | ||
|
|
fd963a8e66 | ||
|
|
3c17fca369 | ||
|
|
4a76997044 | ||
|
|
894960ef5a | ||
|
|
3c24d4bc4e | ||
|
|
ed7e6a3495 | ||
|
|
07de032e62 | ||
|
|
c07165531a | ||
|
|
b1a22d4412 | ||
|
|
6734e5ef57 | ||
|
|
6cc81fe6b8 | ||
|
|
ad80d21e89 | ||
|
|
97689c6cbb | ||
|
|
41c83dabde | ||
|
|
f909f0dcf9 | ||
|
|
5c9bf30c79 | ||
|
|
6efc518eed | ||
|
|
198bd3a3dc | ||
|
|
7b1055702b | ||
|
|
d21bcce3c4 | ||
|
|
b5a26941ef | ||
|
|
6ab7131378 | ||
|
|
22bcc2e438 | ||
|
|
32edc0f1fe | ||
|
|
8faa0ce2c2 | ||
|
|
bf05e5999b | ||
|
|
3d7bdded31 | ||
|
|
7507182097 | ||
|
|
6853b3c531 | ||
|
|
f2372a13e8 | ||
|
|
3f321c8801 | ||
|
|
8b47107df8 | ||
|
|
97e7136293 | ||
|
|
c8db58150e | ||
|
|
5e1067df59 | ||
|
|
175444c59f | ||
|
|
b09d5ff3c9 | ||
|
|
43fc97137e | ||
|
|
c67eee57d6 | ||
|
|
607aa78059 | ||
|
|
7edbae7b4c | ||
|
|
232ff38084 | ||
|
|
d9d9ca67cd | ||
|
|
57fa48aef4 | ||
|
|
5a8e0749c2 | ||
|
|
f834f069cd | ||
|
|
9b2dc10da2 | ||
|
|
c3730b7efd | ||
|
|
23499497a3 | ||
|
|
3b78d609b7 | ||
|
|
65529c3356 | ||
|
|
899849d4dc | ||
|
|
52393206e6 | ||
|
|
2cd1fa6601 | ||
|
|
e371bbedc0 | ||
|
|
9dde385073 | ||
|
|
afa3d39cb3 | ||
|
|
fe41817f25 | ||
|
|
a865465514 | ||
|
|
9278e74e9e | ||
|
|
689a1f739f | ||
|
|
19dee57b7e | ||
|
|
8690b91632 | ||
|
|
fa31cab11b | ||
|
|
46ee783f99 | ||
|
|
199bba5da4 | ||
|
|
16e8791472 | ||
|
|
1728442d62 | ||
|
|
22f7f059ce | ||
|
|
97629c1fc3 | ||
|
|
4727d613c0 | ||
|
|
9ed138ea2b | ||
|
|
2cbd998941 | ||
|
|
806d70c243 | ||
|
|
74a1c7d489 | ||
|
|
f6ed5dc126 | ||
|
|
e25185b9b8 | ||
|
|
5e6d8873b9 | ||
|
|
951b48c337 | ||
|
|
7f209b76bf | ||
|
|
890bfbe02d | ||
|
|
70f8c28ca6 | ||
|
|
47bacdaed0 | ||
|
|
bcd8eb2a09 | ||
|
|
e4855d0143 | ||
|
|
94f0ff1ed1 | ||
|
|
b5f0243a89 | ||
|
|
17e59b8783 | ||
|
|
ffdf308b40 | ||
|
|
cdadc80945 | ||
|
|
294e1f5b10 | ||
|
|
1c5eab6055 | ||
|
|
f74f06e403 | ||
|
|
f04ee0baf2 | ||
|
|
689273fc24 | ||
|
|
6f4c59a15c | ||
|
|
dc87097dfe | ||
|
|
24f4ab7597 | ||
|
|
ad94f0a292 | ||
|
|
695613a063 | ||
|
|
6f1828eabc | ||
|
|
bf158b3bf0 | ||
|
|
13618e6a0a | ||
|
|
c424e9dec8 | ||
|
|
a2e9523707 | ||
|
|
606817ae06 | ||
|
|
7124d326fc | ||
|
|
f9f4653e33 | ||
|
|
bf8eebe537 | ||
|
|
bd9eef6502 | ||
|
|
e343b1790e | ||
|
|
d81ef1d67c | ||
|
|
6e374bcd4e | ||
|
|
fb4648d2af | ||
|
|
a63fc25f14 | ||
|
|
5e20e9ae1c | ||
|
|
7d5d604ea6 | ||
|
|
a722581868 | ||
|
|
28f3044bdd | ||
|
|
497804434d | ||
|
|
d2d6ee806d | ||
|
|
2e106265f9 | ||
|
|
28fb0b433b | ||
|
|
171bd6b327 | ||
|
|
198e215d54 | ||
|
|
4d424e70bc | ||
|
|
3efef52398 | ||
|
|
b85929772e | ||
|
|
041522f94e | ||
|
|
80d3c9e96f | ||
|
|
6ee5e560cc | ||
|
|
2be9eb4bae | ||
|
|
0a8935686a | ||
|
|
0109d9148b | ||
|
|
212518c682 | ||
|
|
59b4f1ebab | ||
|
|
ee9462c221 | ||
|
|
c648dc6c99 | ||
|
|
4f1b8094a3 | ||
|
|
c89ccf7185 | ||
|
|
753395965a | ||
|
|
04be747d52 | ||
|
|
f828ed3edf | ||
|
|
b98d9c2932 | ||
|
|
aba2ce8390 | ||
|
|
8bd8e149cf | ||
|
|
e7c359a2e7 | ||
|
|
d64d25380a | ||
|
|
6cba6166fb | ||
|
|
e66f5fe253 | ||
|
|
1d4388d444 | ||
|
|
28ab08a7ca | ||
|
|
6fa0f92ceb | ||
|
|
3083ab74a6 | ||
|
|
b6ea73af83 | ||
|
|
1fa3ffb1ff | ||
|
|
af89630095 | ||
|
|
18f0177fce | ||
|
|
d89eecacba | ||
|
|
a9149fb92e | ||
|
|
9a04208a11 | ||
|
|
6cdf199531 | ||
|
|
44dc7fe24a | ||
|
|
455892b414 | ||
|
|
4f5227782a | ||
|
|
481e473b60 | ||
|
|
2c2a1f638b | ||
|
|
973e269f46 | ||
|
|
9f76e0e056 | ||
|
|
3a5f1b41a4 | ||
|
|
e2d8369daf | ||
|
|
a20d4959bf | ||
|
|
7b887e4cdd | ||
|
|
bc9cbd2993 | ||
|
|
9baa0e247f | ||
|
|
2df8d2bc69 | ||
|
|
b8165fb06e | ||
|
|
c698b24e01 | ||
|
|
e70249cb2e | ||
|
|
8a9bfe8281 | ||
|
|
8c2a4e627e | ||
|
|
bf35c92c14 | ||
|
|
019293a034 | ||
|
|
46dc40149e | ||
|
|
444643eb6f | ||
|
|
2913b911e3 | ||
|
|
ca323371a7 | ||
|
|
a06cb39777 | ||
|
|
b0ec8767a2 | ||
|
|
353fb49a87 | ||
|
|
e453b40e0b | ||
|
|
0c6f8ce77d | ||
|
|
c0219662bb | ||
|
|
aae71d375c | ||
|
|
b6228e4c59 | ||
|
|
3a9a1439d9 | ||
|
|
7cf256dc7c | ||
|
|
c3c26998bf | ||
|
|
02e860480b | ||
|
|
7737b8b596 | ||
|
|
2725322fd5 | ||
|
|
6c6ccda6b3 | ||
|
|
d71269e223 | ||
|
|
36266d2b10 | ||
|
|
acae62de87 | ||
|
|
9fe4197cae | ||
|
|
7fa1a8d54a | ||
|
|
2333271c20 | ||
|
|
5b83149567 | ||
|
|
250a35baab | ||
|
|
d60ba95532 | ||
|
|
c901472198 | ||
|
|
2a5b70fb13 | ||
|
|
dc6db6e4b3 | ||
|
|
8df6f32314 | ||
|
|
a2d8c894fe | ||
|
|
a1996768f1 | ||
|
|
205587cb9e | ||
|
|
224b2ef952 | ||
|
|
90a83dc753 | ||
|
|
a7cf968d04 | ||
|
|
80ff72bae1 | ||
|
|
5320fc8111 | ||
|
|
8753531e82 | ||
|
|
03a845f2b3 | ||
|
|
25b05f127d | ||
|
|
073beb0135 | ||
|
|
ef7659691b | ||
|
|
e69c0c079e | ||
|
|
dc31269a06 | ||
|
|
df9eccabea | ||
|
|
7788f5ae4c | ||
|
|
ff5456c178 | ||
|
|
40ed702437 | ||
|
|
65924e9a5d | ||
|
|
a88d149dad | ||
|
|
b9ec94d835 | ||
|
|
fc1675575a | ||
|
|
2f7229720f | ||
|
|
0187fc7b22 | ||
|
|
540e1a9650 | ||
|
|
a371cd1d79 | ||
|
|
16b11fee31 | ||
|
|
1bc46f22b4 | ||
|
|
554c8fe163 | ||
|
|
8f6bf6e002 | ||
|
|
d5dd8e9346 | ||
|
|
4a67e1021a | ||
|
|
c97061770a | ||
|
|
55b331511e | ||
|
|
4b9b5e861f | ||
|
|
b8599a0642 | ||
|
|
ae6530585a | ||
|
|
39aa1fa2a4 | ||
|
|
4f740acabd | ||
|
|
2cc9b91895 | ||
|
|
4eedc39e97 | ||
|
|
b99e8d7f46 | ||
|
|
a8a27aeadd | ||
|
|
21176d2fd3 | ||
|
|
224c65438f | ||
|
|
f1c21b642f | ||
|
|
030d1f374a | ||
|
|
0b29fa2288 | ||
|
|
b721f148f0 | ||
|
|
63434a2f87 | ||
|
|
0d6f0e66be | ||
|
|
fb3f1365c5 | ||
|
|
8de7d5d377 | ||
|
|
952d7494ac | ||
|
|
9aeba20086 | ||
|
|
9150c9c40e | ||
|
|
3e75897154 | ||
|
|
b0aa4402c2 | ||
|
|
41f80bcafd | ||
|
|
c67359c49d | ||
|
|
2f7c3cf21e | ||
|
|
9731c8a750 | ||
|
|
8092e5c3a8 | ||
|
|
f7ab8cc471 | ||
|
|
b593f62c4f | ||
|
|
6905b7a410 | ||
|
|
402f27b2a3 | ||
|
|
c9c46d05d0 | ||
|
|
6591575d22 | ||
|
|
6064119779 | ||
|
|
00cd9b581d | ||
|
|
a3d7b72485 | ||
|
|
88c73be2f4 | ||
|
|
39a9181cdd | ||
|
|
0e5c6f56a0 | ||
|
|
5147a070a1 | ||
|
|
b11be1838a | ||
|
|
be99768a32 | ||
|
|
fe439a0cb6 | ||
|
|
87f49ec879 | ||
|
|
20f2730125 | ||
|
|
bc5b34db6b | ||
|
|
bcf9df3744 | ||
|
|
4d3674ee0a | ||
|
|
28567e4629 | ||
|
|
1180a4fb0b | ||
|
|
71928d2c9f | ||
|
|
3853072a2e | ||
|
|
0cf630ef23 | ||
|
|
202015fe34 | ||
|
|
ae43e5cae4 | ||
|
|
67b67bae6a | ||
|
|
dbb8fe15cf | ||
|
|
56efa10f64 | ||
|
|
5ff776f90d | ||
|
|
a25b072bf6 | ||
|
|
c95951c0e4 | ||
|
|
4c8193b801 | ||
|
|
598a544ff8 | ||
|
|
465ef3fa9a | ||
|
|
2f876d93e3 | ||
|
|
84e8c44e4f | ||
|
|
855d794bdb | ||
|
|
a3333f8fe1 | ||
|
|
2e64d62ca4 | ||
|
|
26a3dbcbe1 | ||
|
|
fa2e86df29 | ||
|
|
b4f0ece78f | ||
|
|
630b319a37 | ||
|
|
4a37e49798 | ||
|
|
cfbe98a39a | ||
|
|
91f097d514 | ||
|
|
c545521cd9 | ||
|
|
11e0f49ada | ||
|
|
19ce53128b | ||
|
|
deccff623a | ||
|
|
b2a210ec0d | ||
|
|
bdb5169a6f | ||
|
|
0865b702a3 | ||
|
|
d13b8fd486 | ||
|
|
494911805e | ||
|
|
cba3a2be24 | ||
|
|
0686781359 | ||
|
|
e5b82dca4d | ||
|
|
2144a42a22 | ||
|
|
07a989e004 | ||
|
|
4f7e8116cb | ||
|
|
c0f650d7dc | ||
|
|
d4a0136504 | ||
|
|
5f25e027c4 | ||
|
|
9610dcce20 | ||
|
|
3ee3e7c17b | ||
|
|
98536250bd | ||
|
|
e95808e6be | ||
|
|
9fc819a410 | ||
|
|
b0f1ce1fa0 | ||
|
|
503579a638 | ||
|
|
3ad216751a | ||
|
|
ca8e3179bb | ||
|
|
fd84e56c00 | ||
|
|
8b67fb7290 | ||
|
|
2d1fdb319d | ||
|
|
c200e18434 | ||
|
|
b3ffcd020f | ||
|
|
9258a3dcd4 | ||
|
|
57f5478731 | ||
|
|
973d75ebdd | ||
|
|
33519b27c8 | ||
|
|
7da7ff4a69 | ||
|
|
dbd2f697f9 | ||
|
|
f435762b88 | ||
|
|
ae46332e42 | ||
|
|
d003883de9 | ||
|
|
4e438a44f1 | ||
|
|
02e19b3d44 | ||
|
|
ccc19512e7 | ||
|
|
c34539e389 | ||
|
|
f8aeacb949 | ||
|
|
9940190679 | ||
|
|
e2498b3e91 | ||
|
|
82246fd9c7 | ||
|
|
11538552eb | ||
|
|
4ce28f54de | ||
|
|
e887ed74a3 | ||
|
|
28c086e97c | ||
|
|
11465e89a3 | ||
|
|
b2197187c1 | ||
|
|
2b26a10745 | ||
|
|
daf726ebbf | ||
|
|
88dd886687 | ||
|
|
609da457f7 | ||
|
|
363e28ff69 | ||
|
|
bdd6bf9020 | ||
|
|
5c3dab3466 | ||
|
|
9b2c8fa25d | ||
|
|
df2f102d9e | ||
|
|
95ebb0e6d2 | ||
|
|
e5d03652a9 | ||
|
|
56b53e2dd8 | ||
|
|
cf0b7b213f | ||
|
|
d214c8e01b | ||
|
|
fba0f362a5 | ||
|
|
ec05d0857c | ||
|
|
54b744b7de | ||
|
|
1a76780fff | ||
|
|
58f5c44533 | ||
|
|
d085da4dbf | ||
|
|
c4a5c356f7 | ||
|
|
18fdc5c6a2 | ||
|
|
35dabaab9c | ||
|
|
9bf31b10bb | ||
|
|
7186575cb1 | ||
|
|
2ecae40130 | ||
|
|
778ed62a90 | ||
|
|
5e863a87dc | ||
|
|
7ce8597c25 | ||
|
|
1992237ce5 | ||
|
|
8f247b0f73 | ||
|
|
e2159d80af | ||
|
|
057023531e | ||
|
|
dfed65bf9f | ||
|
|
739161849a | ||
|
|
c65b280020 | ||
|
|
d3bcf25ef0 | ||
|
|
f6bd3340e7 | ||
|
|
6a7c09bfe3 | ||
|
|
8b9f294a5d | ||
|
|
4a282d9629 | ||
|
|
909b88864f | ||
|
|
1346a7992c | ||
|
|
bba607e987 | ||
|
|
f9cc490c35 | ||
|
|
a5aec2d9fa | ||
|
|
c0df368dc6 | ||
|
|
aa77433523 | ||
|
|
44ad99f693 | ||
|
|
b7f7a82ea9 | ||
|
|
c69978c9fd | ||
|
|
6174aa6ee1 | ||
|
|
74b8d2e908 | ||
|
|
63a515944f | ||
|
|
5b5db7b860 | ||
|
|
b3bbacf2ef | ||
|
|
3a0429d049 | ||
|
|
ab539081fa | ||
|
|
f0d88d4e73 | ||
|
|
772cbd6ffd | ||
|
|
17b9dbe9d7 | ||
|
|
75e5d42d8b | ||
|
|
031c15fd7d | ||
|
|
de1924cefc | ||
|
|
66db0a4751 | ||
|
|
c309410965 | ||
|
|
7f461b99e2 | ||
|
|
7bfe0eeae9 | ||
|
|
8619bd5be3 | ||
|
|
56011d37d4 | ||
|
|
c2852c8a82 | ||
|
|
6f546a424e | ||
|
|
447f7530af | ||
|
|
6136f1206b | ||
|
|
36a3c5b501 | ||
|
|
dcd6c1f522 | ||
|
|
2b074bcdcb | ||
|
|
b20ec7f0eb | ||
|
|
58cf69a2fe | ||
|
|
096c148228 | ||
|
|
a68005d4ab | ||
|
|
bf3a281987 | ||
|
|
2965a6827d | ||
|
|
9f43a73c36 | ||
|
|
7551b45da2 | ||
|
|
bca3685eda | ||
|
|
6d20175800 | ||
|
|
a62dd4c020 | ||
|
|
d1d9620a61 | ||
|
|
5106d77c77 | ||
|
|
69cf237d7a | ||
|
|
7d9d1c82b6 | ||
|
|
228ff5edf4 | ||
|
|
2baac618a8 | ||
|
|
e8f499a938 | ||
|
|
96ca20d0b4 | ||
|
|
54acdc86e7 | ||
|
|
0815e02895 | ||
|
|
30cbb72b57 | ||
|
|
0b91397709 | ||
|
|
89934c17ca | ||
|
|
e4a38e62eb | ||
|
|
5e1e09d7bf | ||
|
|
51ce3a1e42 | ||
|
|
54b46dfad9 | ||
|
|
787917ac66 | ||
|
|
619b49bdc4 | ||
|
|
4b3a73d440 | ||
|
|
54f9c59d6e | ||
|
|
1d123996f6 | ||
|
|
c4768f6138 | ||
|
|
5f3551ff34 | ||
|
|
d3985c2e3b | ||
|
|
3b1843f3a3 | ||
|
|
2c36796362 | ||
|
|
655ccba89b | ||
|
|
150d72f0f8 | ||
|
|
6a316b34a2 | ||
|
|
8e6b600609 | ||
|
|
5630a4dd67 | ||
|
|
38ee8aedc1 | ||
|
|
d594615532 | ||
|
|
2739fa60be | ||
|
|
327301782d | ||
|
|
8781c5db8d | ||
|
|
31c34ea158 | ||
|
|
ef9bbaca19 | ||
|
|
02d89072bc | ||
|
|
4e8bd640a7 | ||
|
|
8c71a00600 | ||
|
|
3d36c70d53 | ||
|
|
c72479c4d6 | ||
|
|
4bb88d8e44 | ||
|
|
0ee0958539 | ||
|
|
b6dd6f3a94 | ||
|
|
d11c322e1f | ||
|
|
cd92b34ef1 | ||
|
|
b6481cfcda | ||
|
|
db7eb92638 | ||
|
|
f2d0477550 | ||
|
|
776e207f09 | ||
|
|
733e8a0043 | ||
|
|
4fa19006ad | ||
|
|
73a597e3e5 | ||
|
|
d776f1765d | ||
|
|
741b6f6f9a | ||
|
|
b6f4695bcd | ||
|
|
b7d3b807d2 | ||
|
|
0cc386bc28 | ||
|
|
cb155707cd | ||
|
|
9b6b250cbd | ||
|
|
5b7e29b8ad | ||
|
|
b6d748b414 | ||
|
|
8fc4b338c2 | ||
|
|
6d3ea19ac5 | ||
|
|
d01ef48bf0 | ||
|
|
71103bb7b9 | ||
|
|
1c9bc00acc | ||
|
|
bed128e8cf | ||
|
|
b5c3f18f24 | ||
|
|
cbccdf5d93 | ||
|
|
a46f3a31e1 | ||
|
|
a808f7b04e | ||
|
|
3a883b9e41 | ||
|
|
17d8691300 | ||
|
|
523ce1dbdd | ||
|
|
4a3a9bf62c | ||
|
|
3b30177959 | ||
|
|
7e66f89260 | ||
|
|
1a93ba634f | ||
|
|
d93f823fc6 | ||
|
|
f2198bf938 | ||
|
|
965f10698b | ||
|
|
b71367cd2a | ||
|
|
abe18ac825 | ||
|
|
bb193d3768 | ||
|
|
7a7f5cd4a8 | ||
|
|
ac9f49f8c9 | ||
|
|
8f53859e00 | ||
|
|
bfb7ff88d9 | ||
|
|
c36425fd3a | ||
|
|
981f9d0b01 | ||
|
|
fa89fe3e87 | ||
|
|
d132357c20 | ||
|
|
a719237556 | ||
|
|
8955ca5216 | ||
|
|
f665762cc8 | ||
|
|
d16fc2b68b | ||
|
|
e1df32c32d | ||
|
|
943c6f77dc | ||
|
|
4964382966 | ||
|
|
021c6fdbe2 | ||
|
|
711f9805c9 | ||
|
|
0aaae3afd6 | ||
|
|
eadd1042fb | ||
|
|
16fa2c9f5e | ||
|
|
d4ab1df870 | ||
|
|
4a5aa1bcc1 | ||
|
|
01a9cda99a | ||
|
|
dcdf606ff6 | ||
|
|
ea021de5eb | ||
|
|
5cc3526f8f | ||
|
|
3060fc2af4 | ||
|
|
e4395dfeb4 | ||
|
|
f65dadf1d7 | ||
|
|
f048762fd9 | ||
|
|
bb985f826e | ||
|
|
bb239cba2a | ||
|
|
e7e66e580a | ||
|
|
689d689d3b | ||
|
|
125c3d3e0d | ||
|
|
ffaa06560e | ||
|
|
f9b716201f | ||
|
|
8b14a5f0d8 | ||
|
|
c5855119d8 | ||
|
|
a036597f5f | ||
|
|
b8688e2e66 | ||
|
|
d8b2e08717 | ||
|
|
7da78d3312 | ||
|
|
2292b107dc | ||
|
|
1fcc74c658 | ||
|
|
73be027951 | ||
|
|
132d91c2ab | ||
|
|
f08ce82c3f | ||
|
|
8de2712652 | ||
|
|
55835785c0 | ||
|
|
cb711b7758 | ||
|
|
19c75293bf | ||
|
|
33219f00d5 | ||
|
|
4aaedbb0e6 | ||
|
|
ee1f14d4eb | ||
|
|
feac8085c9 | ||
|
|
0a5ec11b1b | ||
|
|
03937174e5 | ||
|
|
e00529c05f | ||
|
|
5dbf8e1d2b | ||
|
|
bebf672186 | ||
|
|
6d4a8f2a5a | ||
|
|
14db0ae663 | ||
|
|
88d1c1d140 | ||
|
|
9106055be1 | ||
|
|
2cf52f15ab | ||
|
|
b7e9d61c72 | ||
|
|
0bc22db296 | ||
|
|
6a943acc70 | ||
|
|
7c97416e7d | ||
|
|
dd75504a66 | ||
|
|
04c4ab2289 | ||
|
|
310a2e2511 | ||
|
|
f6314431f0 | ||
|
|
89100d0ca0 | ||
|
|
2b646636c1 | ||
|
|
b10c1d5006 | ||
|
|
225b829c1a | ||
|
|
5f4c7076ab | ||
|
|
61c9b304d7 | ||
|
|
789d7000cf | ||
|
|
d23ef2bd59 | ||
|
|
8a77f832a3 | ||
|
|
5a6d318cfb | ||
|
|
e9f14de05d | ||
|
|
2d8da45bda | ||
|
|
83de33f5b8 | ||
|
|
e63844f786 | ||
|
|
0a805c16fd | ||
|
|
653c7d4430 | ||
|
|
306c3bea21 | ||
|
|
389ce60bc9 | ||
|
|
2d453a1a6c | ||
|
|
0775560ad2 | ||
|
|
2680c1e8b3 | ||
|
|
50ba2e3ad4 | ||
|
|
c16875d0de | ||
|
|
a4205cd0c2 | ||
|
|
2fb3e373c6 | ||
|
|
ac1fa7209c | ||
|
|
fd820d6af8 | ||
|
|
b88486601b | ||
|
|
92e712a508 | ||
|
|
64a9079ce4 | ||
|
|
f18d0ab923 | ||
|
|
def49e6d20 | ||
|
|
f142db3d49 | ||
|
|
e7b04a89e2 | ||
|
|
1cb59f46e3 | ||
|
|
a604746e0b | ||
|
|
aba706a293 | ||
|
|
c1b9113347 | ||
|
|
5e7db2807d | ||
|
|
203f830a30 | ||
|
|
3dbe9193e2 | ||
|
|
717cf29595 | ||
|
|
72300fec5e | ||
|
|
ec50b1d67a | ||
|
|
408a4420c9 | ||
|
|
568218204a | ||
|
|
bd39e98c66 | ||
|
|
01be65a624 | ||
|
|
a59d8a6a17 | ||
|
|
70b71aa4f9 | ||
|
|
aaf7991139 | ||
|
|
265d579fd9 | ||
|
|
f6dd91e47c | ||
|
|
cdae552ab0 | ||
|
|
730e887454 | ||
|
|
5a3852d82c | ||
|
|
5b35580d2d | ||
|
|
4b0705fe36 | ||
|
|
d219b4d12a | ||
|
|
6e38331328 | ||
|
|
c4bc9aea22 | ||
|
|
6c692d9308 | ||
|
|
7ce8bd8988 | ||
|
|
b4ead4076a | ||
|
|
052893bbf8 | ||
|
|
a319fe8632 | ||
|
|
8a84e68c13 | ||
|
|
0ca7defe83 | ||
|
|
b704706ee9 | ||
|
|
23fb634847 | ||
|
|
0bd37eb8f9 | ||
|
|
b86294e9cc | ||
|
|
f11f968f99 | ||
|
|
ad81642954 | ||
|
|
ea42103ae7 | ||
|
|
82435e37be | ||
|
|
b61d29b22a | ||
|
|
04392a6a4c | ||
|
|
6e9798d596 | ||
|
|
bef553c9ae | ||
|
|
7a76efa7a0 | ||
|
|
c5f64374ed | ||
|
|
b767caa704 | ||
|
|
63974f97ab | ||
|
|
37764a7caa | ||
|
|
fb586f0043 | ||
|
|
9990f4d8cf | ||
|
|
80f73cc5d0 | ||
|
|
8775f67416 | ||
|
|
2ce302f6f8 | ||
|
|
69b9944b8e | ||
|
|
37f35e8b2d | ||
|
|
8ad9531fa6 | ||
|
|
5ec37c08bf | ||
|
|
84cb008421 | ||
|
|
a744cca35a | ||
|
|
1950cf5f99 | ||
|
|
fbf230cd01 | ||
|
|
05abf1e419 | ||
|
|
f0c13980ed | ||
|
|
d136207d2f | ||
|
|
b331ee5d93 | ||
|
|
73cd6e981a | ||
|
|
6826be73c7 | ||
|
|
0a61a607c9 | ||
|
|
7444e1566b | ||
|
|
e401661cfc | ||
|
|
89d32f7109 | ||
|
|
5b4c88f933 | ||
|
|
966bce973a | ||
|
|
69cbcae7ed | ||
|
|
e26cf282d6 | ||
|
|
6ec7d55cbc | ||
|
|
9888f2a322 | ||
|
|
15d29b2c79 | ||
|
|
7e768e0a17 | ||
|
|
affa41b5a9 | ||
|
|
c8cf179ed9 | ||
|
|
9ef7310fc2 | ||
|
|
cdd7d0f4c5 | ||
|
|
813d706dac | ||
|
|
223d4133c5 | ||
|
|
bb62ebc23e | ||
|
|
a4c2adf69a | ||
|
|
0baff0a1e1 | ||
|
|
51766ad72c | ||
|
|
bf00b42941 | ||
|
|
56482acfc2 | ||
|
|
8898b1a608 | ||
|
|
525bc37649 | ||
|
|
9d29450f47 | ||
|
|
26c98bdace | ||
|
|
19ccd35f3f | ||
|
|
efa7529c5c | ||
|
|
334c11ccd1 | ||
|
|
c318a5dd79 | ||
|
|
25d1a8957d | ||
|
|
9f4853b9d6 | ||
|
|
ac58f50b0a | ||
|
|
36f22b660e | ||
|
|
398046eca6 | ||
|
|
a6f2ce4bdd | ||
|
|
950a8ea8df | ||
|
|
2b6b979f88 | ||
|
|
4b80e5ff47 | ||
|
|
a14d18c6b6 | ||
|
|
fe88c7ce97 | ||
|
|
c54b00a701 | ||
|
|
4a4b8c2e12 | ||
|
|
f1770711cb | ||
|
|
94757f0f0c | ||
|
|
15cf9be90d | ||
|
|
cb1955c217 | ||
|
|
2ce944034d | ||
|
|
53a207e859 | ||
|
|
dbdd2411c3 | ||
|
|
4ac3fbd726 | ||
|
|
8f8f62555c | ||
|
|
73b980c6a9 | ||
|
|
d2cce3cc40 | ||
|
|
4139360788 | ||
|
|
ca7bb50212 | ||
|
|
48ebf626b8 | ||
|
|
564eb802b1 | ||
|
|
c968949f49 | ||
|
|
74268ecce6 | ||
|
|
93f9db3af4 | ||
|
|
4cc0a9d4a9 | ||
|
|
a04dfba16b | ||
|
|
7f83b58b92 | ||
|
|
ca2e6353ae | ||
|
|
6bf7795529 | ||
|
|
401f4828b6 | ||
|
|
7a0ce1efe7 | ||
|
|
cdc2550e34 | ||
|
|
2d760241f3 | ||
|
|
3748e420a0 | ||
|
|
031a253101 | ||
|
|
cfce6d548b | ||
|
|
038e93ea6a | ||
|
|
8028e145f1 | ||
|
|
ddfb9fd0cc | ||
|
|
a36ed6ab1e | ||
|
|
0c7fe0664e | ||
|
|
7ae2f1980b | ||
|
|
0c89583b1b | ||
|
|
f5ec43276a | ||
|
|
e55e46011c | ||
|
|
77c0304faf | ||
|
|
ea476e26ae | ||
|
|
2eb47aef62 | ||
|
|
3e4084d99c | ||
|
|
955ac92043 | ||
|
|
5a65a07a39 | ||
|
|
554d73c6ee | ||
|
|
7bc7bc7c49 | ||
|
|
7b8d47cdeb | ||
|
|
63a8509f1f | ||
|
|
4e69454f72 | ||
|
|
ec3e237093 | ||
|
|
edd224a185 | ||
|
|
b5142b8ef5 | ||
|
|
ddfdf1d49d | ||
|
|
e7675c29da | ||
|
|
0bf3fef431 | ||
|
|
8cb0e81e89 | ||
|
|
d468cb051d | ||
|
|
0b24be7532 | ||
|
|
6316a0ede9 | ||
|
|
3c9d0e02f6 | ||
|
|
02d4360526 | ||
|
|
aedf80e382 | ||
|
|
51b492e1e2 | ||
|
|
d283f236db | ||
|
|
371e937a0e | ||
|
|
5778c25477 | ||
|
|
d2cd3ec879 | ||
|
|
ba0a59629b | ||
|
|
03fea7558d | ||
|
|
4901673c4e | ||
|
|
a4d84bd2e8 | ||
|
|
0289218c92 | ||
|
|
c3d32e59e7 | ||
|
|
8cab08351b | ||
|
|
5de0e58d5d | ||
|
|
438106f42e | ||
|
|
6b21f38047 | ||
|
|
1b07a5f3c0 | ||
|
|
a39ec5a061 | ||
|
|
015df9e6dd | ||
|
|
bf95284b72 | ||
|
|
07c04f0c2f | ||
|
|
b9976ee68e | ||
|
|
c8d9951ae4 | ||
|
|
97339512e1 | ||
|
|
95682a7a0b | ||
|
|
87038c4c75 | ||
|
|
1e5b5193fe | ||
|
|
9cc47678a2 | ||
|
|
198109cd43 | ||
|
|
16490541e4 | ||
|
|
6d5ca25e03 | ||
|
|
0ca44bd859 | ||
|
|
124b5b0549 | ||
|
|
fed62fae3c | ||
|
|
9e2812d55c | ||
|
|
fdcbc3904a | ||
|
|
cc0114cd90 | ||
|
|
f97753c1d0 | ||
|
|
fcdf545a3c | ||
|
|
bf28f887eb | ||
|
|
5f1f04e486 | ||
|
|
2242174749 | ||
|
|
e8af077fe2 | ||
|
|
055252f746 | ||
|
|
8dc84d7c17 | ||
|
|
f140c75fb9 | ||
|
|
c63dd46bac | ||
|
|
a23b0ba945 | ||
|
|
7e768aa6e9 | ||
|
|
18765508c0 | ||
|
|
efb3464ee2 | ||
|
|
f3319c5f75 | ||
|
|
384623fbb7 | ||
|
|
3bcccdf3dd | ||
|
|
d9b913fa69 | ||
|
|
7b9a430e8a | ||
|
|
4f3ab0dc69 | ||
|
|
427caaf9aa | ||
|
|
85eefdebbb | ||
|
|
b31b70302b | ||
|
|
c830ea676f | ||
|
|
9249059cb7 | ||
|
|
9fee228d1a | ||
|
|
20718c6ec1 | ||
|
|
b8754f3f44 | ||
|
|
6f598ae59e | ||
|
|
ca81fcaf37 | ||
|
|
75867da94f | ||
|
|
53c11701de | ||
|
|
4741793bb6 | ||
|
|
ab161a42ee | ||
|
|
4642b79b5b | ||
|
|
1cb43fe110 | ||
|
|
0123135237 | ||
|
|
1d77f91acc | ||
|
|
9b89333b1b | ||
|
|
829361d767 | ||
|
|
d3fca26499 | ||
|
|
ff3460e100 | ||
|
|
1a209a54c6 | ||
|
|
3bf0ac087f | ||
|
|
96cf6215ce | ||
|
|
71ce6120c3 | ||
|
|
21e82d0daa | ||
|
|
8c20bb003b | ||
|
|
90913d2486 | ||
|
|
ef0de04a0f | ||
|
|
8879a0ce8a | ||
|
|
18db46800e | ||
|
|
955c5b5605 | ||
|
|
c3b2c88213 | ||
|
|
7412bb35ad | ||
|
|
2a6fbc5c5d | ||
|
|
90c9b87f8d | ||
|
|
ffe2557e84 | ||
|
|
a77798e5b4 | ||
|
|
c1fa7bfce6 | ||
|
|
bf50da1e6b | ||
|
|
2cd4aac5ce | ||
|
|
208b96c092 | ||
|
|
36a53f8134 | ||
|
|
e8d8a8737e | ||
|
|
41b9f90a0b | ||
|
|
5e39493e89 | ||
|
|
31f3c60401 | ||
|
|
519c74e4dd | ||
|
|
4419770b15 | ||
|
|
b7fa86c848 | ||
|
|
4d620c3db9 | ||
|
|
319c8cb54c | ||
|
|
868b6f141c | ||
|
|
589fdd4cfe | ||
|
|
3f36dd9a14 | ||
|
|
0a6568bbab | ||
|
|
4e26d56746 | ||
|
|
d4e363fbd7 | ||
|
|
b721b822eb | ||
|
|
58d6985080 | ||
|
|
ee2abd415e | ||
|
|
09c9321159 | ||
|
|
0900a7cf80 | ||
|
|
d3ec7f65fb | ||
|
|
b24a94d6ce | ||
|
|
65769c7766 | ||
|
|
cbeef9fe06 | ||
|
|
b199209d0d | ||
|
|
cb48545600 | ||
|
|
5e626e2cc5 | ||
|
|
2709d1ff6e | ||
|
|
de3ca6e237 | ||
|
|
c2253e868c | ||
|
|
3a77c6f7eb | ||
|
|
8e3ff7670b | ||
|
|
7870147c16 | ||
|
|
64a371e5d8 | ||
|
|
f837736a20 | ||
|
|
dec503da81 | ||
|
|
1328299fda | ||
|
|
05190a52f1 | ||
|
|
46cc3105c4 | ||
|
|
8ba5853cdd | ||
|
|
e5fbcc4a8c | ||
|
|
e559194e8e | ||
|
|
c0d2994b8e | ||
|
|
89009aa1f8 | ||
|
|
ee8b580a98 | ||
|
|
f4656fa493 | ||
|
|
adf9405fd7 | ||
|
|
f46db7ce1a | ||
|
|
f00d726347 | ||
|
|
e8d8063ebc | ||
|
|
b4bc4f5ddc | ||
|
|
0d3ffe210f | ||
|
|
8eb88a161a | ||
|
|
346c964419 | ||
|
|
bc8be2460f | ||
|
|
a33f24d19c | ||
|
|
8f839fbf8e | ||
|
|
10dd9101cd | ||
|
|
aa5d3af8a1 | ||
|
|
dace993c21 | ||
|
|
32b72f0ef6 | ||
|
|
3dbc54c8ae | ||
|
|
9dd3b8fd68 | ||
|
|
5fb1afc681 | ||
|
|
a4c985a219 | ||
|
|
a1f819a458 | ||
|
|
166d7ba1cf | ||
|
|
a089accd23 | ||
|
|
38546e557f | ||
|
|
080dca7ee0 | ||
|
|
a89e433f3d | ||
|
|
f30ef61b06 | ||
|
|
03a36c1100 | ||
|
|
546eb6e73e | ||
|
|
bedd3abf8a | ||
|
|
ce2d4498e1 | ||
|
|
a786023160 | ||
|
|
98cf8aa445 | ||
|
|
d733d9fd4c | ||
|
|
58e9cb8b93 | ||
|
|
bb669acf95 | ||
|
|
4f3751b7ce | ||
|
|
84c12dee80 | ||
|
|
f5f865a139 | ||
|
|
902aed671a | ||
|
|
1423fe7e16 | ||
|
|
58ed2e6f33 | ||
|
|
a90939f46a | ||
|
|
db7456b9c5 | ||
|
|
a964b30c34 | ||
|
|
d566629d51 | ||
|
|
837422fbb8 | ||
|
|
afc37c71a6 | ||
|
|
e8b014ea6d | ||
|
|
b124512020 | ||
|
|
158f352328 | ||
|
|
f1895e32fb | ||
|
|
6c3be01093 | ||
|
|
ebe548438c | ||
|
|
a45c61f19e | ||
|
|
b07a4b95aa | ||
|
|
777b4e13e6 | ||
|
|
92cfc9324d | ||
|
|
97dfa18b9b | ||
|
|
515c07ea2b | ||
|
|
4d17e45f86 | ||
|
|
32095daf90 | ||
|
|
aa23ed892b | ||
|
|
3f079c8501 | ||
|
|
0aaf4bfde8 | ||
|
|
b967cfc775 | ||
|
|
2ca0483bf4 | ||
|
|
03cedc7b35 | ||
|
|
35049b5830 | ||
|
|
b707dde4da | ||
|
|
1ec8339b13 | ||
|
|
8bbe04a174 | ||
|
|
0b6a173ba0 | ||
|
|
8b41dfe94f | ||
|
|
a18b1ac99b | ||
|
|
e007bcb640 | ||
|
|
1090e7ceae | ||
|
|
933035dd7e | ||
|
|
aaaac78170 | ||
|
|
b5d2392def | ||
|
|
b88a736735 | ||
|
|
f238da416b | ||
|
|
10c7e75491 | ||
|
|
60af0735f4 | ||
|
|
273cbf333e | ||
|
|
189b17ad8f | ||
|
|
1eecf26429 | ||
|
|
2f9a3fa942 | ||
|
|
f166fb132e | ||
|
|
0adb69139e | ||
|
|
ab309e4b90 | ||
|
|
532c6d4fa5 | ||
|
|
14f627d0d3 | ||
|
|
947c38c124 | ||
|
|
f991e49203 | ||
|
|
4fbb6ed4ff | ||
|
|
2fa49303de | ||
|
|
f9527e9d2d | ||
|
|
80c63cb9d6 | ||
|
|
a2455eeade | ||
|
|
5d7ad4b3bd | ||
|
|
f46fa61cf3 | ||
|
|
da24a9637b | ||
|
|
2cdc11c2ad | ||
|
|
191cd3f25c | ||
|
|
f158bce8bd | ||
|
|
94eebb2dd6 | ||
|
|
375b146690 | ||
|
|
8c13214ff2 | ||
|
|
681dfadb57 | ||
|
|
b8b5d1cb09 | ||
|
|
e187dd86ed | ||
|
|
c380d6988c | ||
|
|
444acc8ef5 | ||
|
|
068f08aa45 | ||
|
|
bdc101f69c | ||
|
|
0ece530b9e | ||
|
|
5853a86091 | ||
|
|
20ba87bc88 | ||
|
|
be166ec158 | ||
|
|
02147891c2 | ||
|
|
c08cd588eb | ||
|
|
882bb564b1 | ||
|
|
18edf06a20 | ||
|
|
68b52b6130 | ||
|
|
3503f3eb4b | ||
|
|
a75706f329 | ||
|
|
29fe9d973c | ||
|
|
87d3320fa1 | ||
|
|
0b96dbc04c | ||
|
|
55ed58c4a0 | ||
|
|
8503e8c9ad | ||
|
|
fcdf783c40 | ||
|
|
008b92acb2 | ||
|
|
baf8ef8516 | ||
|
|
0cf89467e3 | ||
|
|
37d8a563c0 | ||
|
|
f3de93d73a | ||
|
|
4de23bd160 | ||
|
|
29397ca04f | ||
|
|
6038a36532 | ||
|
|
186a922d06 | ||
|
|
be1f9e66e3 | ||
|
|
9ac015e550 | ||
|
|
f7a9ac0ba2 | ||
|
|
39a8ddcfec | ||
|
|
3a9e28ba48 | ||
|
|
b6a479355e | ||
|
|
a791c964e8 | ||
|
|
9686a7f9bf | ||
|
|
6b65b89350 | ||
|
|
09a1915ec6 | ||
|
|
3424c421a4 | ||
|
|
77b89b6841 | ||
|
|
09807cfcad | ||
|
|
6ca81df40c | ||
|
|
2d00ddad2b | ||
|
|
04f4934adb | ||
|
|
0593007fd0 | ||
|
|
c07cbf9dbb | ||
|
|
ddd60aa1ff | ||
|
|
b5ecdbbc48 | ||
|
|
0cca613b04 | ||
|
|
b254ad177a | ||
|
|
4fd9213d2c | ||
|
|
982d1e219c | ||
|
|
3923b8631d | ||
|
|
72dcdf4483 | ||
|
|
5f3a71ed5f | ||
|
|
000d6ebcd0 | ||
|
|
7f0a04e3e3 | ||
|
|
005c6bb9f1 | ||
|
|
59c856fc0e | ||
|
|
5bf26619ee | ||
|
|
676c1d560b | ||
|
|
5afaa7c079 | ||
|
|
b0b49b72b8 | ||
|
|
e7600458a9 | ||
|
|
e0fa308984 | ||
|
|
6ce6fed10d | ||
|
|
b23ca2e138 | ||
|
|
2798dc3d42 | ||
|
|
0586d4d7a9 | ||
|
|
d2b3a52424 | ||
|
|
ffb97242d9 | ||
|
|
13ab400d6d | ||
|
|
db6d47b3f3 | ||
|
|
393943bb9f | ||
|
|
4c72ac14e6 | ||
|
|
7156871412 | ||
|
|
20665a49ed | ||
|
|
ad1dfc3137 | ||
|
|
8de67411ac | ||
|
|
2e75c9951b | ||
|
|
77f66f44a4 | ||
|
|
3278a397f4 | ||
|
|
d1c2abbaf3 | ||
|
|
8a443013c0 | ||
|
|
d3735c4763 | ||
|
|
f9887434d1 | ||
|
|
12afa005d9 | ||
|
|
7c0129b911 | ||
|
|
332f92d08e | ||
|
|
281ac16dca | ||
|
|
e8a2b9446d | ||
|
|
4e33f3e722 | ||
|
|
e46e60153d | ||
|
|
d3559bb1b7 | ||
|
|
c8a046996b | ||
|
|
6c85b8717f | ||
|
|
142a62e371 | ||
|
|
ff6abf08b7 | ||
|
|
2fd921cd60 | ||
|
|
1d7bd57357 | ||
|
|
1a4d76b4c3 | ||
|
|
853d869086 | ||
|
|
c99d669b06 | ||
|
|
19d3921637 | ||
|
|
4ee716457f | ||
|
|
5fb19bb187 | ||
|
|
8b83c5349d | ||
|
|
ac91942452 | ||
|
|
5ef23ddc1d | ||
|
|
2ad6c66a8d | ||
|
|
630f877e95 | ||
|
|
7dd5ce1356 | ||
|
|
54b18d15b6 | ||
|
|
2b0880af80 | ||
|
|
786ea17f95 | ||
|
|
c699bae99c | ||
|
|
588e8e019c | ||
|
|
1e7a86b7c0 | ||
|
|
2bb18b18b7 | ||
|
|
da77c549a7 | ||
|
|
fc707c157a | ||
|
|
5be6f550be | ||
|
|
66abf27edd | ||
|
|
3d1b6d7de7 | ||
|
|
115e604627 | ||
|
|
f9aeec8eb5 | ||
|
|
acf4d15a8d | ||
|
|
e5e08d1762 | ||
|
|
178c7817fa | ||
|
|
a93a428a89 | ||
|
|
d126a361e1 | ||
|
|
9e42ec0c06 | ||
|
|
1f05654faa | ||
|
|
3ddef6440a | ||
|
|
036b47acbc | ||
|
|
42e585e7a2 | ||
|
|
93d066bef7 | ||
|
|
580c6fca00 | ||
|
|
03300da93a | ||
|
|
2a1fc38799 | ||
|
|
646afbfa36 | ||
|
|
45a1435ce7 | ||
|
|
f4d7d32fd7 | ||
|
|
1b00f93091 | ||
|
|
9c9e5a54a7 | ||
|
|
4cf8c9c477 | ||
|
|
a78e1d7f2b | ||
|
|
00c1d6c42c | ||
|
|
5d92f09449 | ||
|
|
ac7d820e6f | ||
|
|
7d752443ca | ||
|
|
3311b5a6e3 | ||
|
|
3801443c9c | ||
|
|
9b2a2eb229 | ||
|
|
d165f66815 | ||
|
|
6715d52b81 | ||
|
|
ae160556d0 | ||
|
|
c040e1f9b7 | ||
|
|
db14a8ac1a | ||
|
|
9beafd03ee | ||
|
|
4f081c5e14 | ||
|
|
ff4e9e42e5 | ||
|
|
d5e3e6f256 | ||
|
|
cc9660c0e7 | ||
|
|
90d92707c8 | ||
|
|
fe77bc7c37 | ||
|
|
54a7d8291b | ||
|
|
7bb924ce5b | ||
|
|
9f2e25309e | ||
|
|
b32b39579e | ||
|
|
00513e66bc | ||
|
|
8850867b82 | ||
|
|
d6dfbbf8dd | ||
|
|
6c4797bce4 | ||
|
|
2c97eb4115 | ||
|
|
54b0828c20 | ||
|
|
0d241a4993 | ||
|
|
802b0d1a75 | ||
|
|
e76a7716bc | ||
|
|
a9cc0a61d9 | ||
|
|
fe50e47fc6 | ||
|
|
0006b286ca | ||
|
|
17f99da45e | ||
|
|
f7ce03a819 | ||
|
|
c073501ac0 | ||
|
|
d4a4747c08 | ||
|
|
e7b19a5f66 | ||
|
|
0070ec09a2 | ||
|
|
b3ec62a3db | ||
|
|
34ca4ad3b0 | ||
|
|
e0909731a4 | ||
|
|
d8d1cb2eda | ||
|
|
2a24fbadae | ||
|
|
85e1c4bd47 | ||
|
|
09fcb0739d | ||
|
|
0882c2b5c7 | ||
|
|
bb7763d72b | ||
|
|
b919d7364a | ||
|
|
942a37290c | ||
|
|
ef24c6cbbd | ||
|
|
cf05571f1c | ||
|
|
0fa038b4f2 | ||
|
|
10a33a2978 | ||
|
|
60a88381cf | ||
|
|
48d83c7d44 | ||
|
|
b967b7ed6b | ||
|
|
b60ecb6171 | ||
|
|
e8b80216e8 | ||
|
|
69a5472a16 | ||
|
|
bd6eb606ca | ||
|
|
e7f1e83a43 | ||
|
|
357fe3b793 | ||
|
|
ad6c06409e | ||
|
|
2a7feba808 | ||
|
|
45b7e28db2 | ||
|
|
daa9479352 | ||
|
|
0f688bd71a | ||
|
|
ffe0843bd6 | ||
|
|
0ee4039853 | ||
|
|
a3a6e8cdf6 | ||
|
|
30f60f87f4 | ||
|
|
0d91657557 | ||
|
|
ec9b80a64e | ||
|
|
91909f8886 | ||
|
|
cc335dae38 | ||
|
|
0d6ea03ac8 | ||
|
|
e1370b75e8 | ||
|
|
c2d0ccea3c | ||
|
|
9319851118 | ||
|
|
d45ad76fbc | ||
|
|
d8b9c3b832 | ||
|
|
9f2534af3d | ||
|
|
2c2978b93a | ||
|
|
c9b0f22a2e | ||
|
|
2959cd140b | ||
|
|
7834cfe5f0 | ||
|
|
2b9c5d0cff | ||
|
|
cb3b51dcde | ||
|
|
0c23fe96be | ||
|
|
9d0bd73ee6 | ||
|
|
15f096f764 | ||
|
|
9dd41be608 | ||
|
|
17f8703c71 | ||
|
|
5edfd7b6f7 | ||
|
|
4371772ec7 | ||
|
|
8e4e08a828 | ||
|
|
401e6b5a57 | ||
|
|
01dfe83fcd | ||
|
|
177c5700ff | ||
|
|
5d401a4fbb | ||
|
|
c60ab97103 | ||
|
|
f3d7a90ac7 | ||
|
|
7b114f961a | ||
|
|
8caaf13c84 | ||
|
|
0218da1ee6 | ||
|
|
f4786755f1 | ||
|
|
b9b1fcb4e5 | ||
|
|
5bfda557bc | ||
|
|
6e53df403e | ||
|
|
4a7246ec6e | ||
|
|
f53a021c08 | ||
|
|
8fa95d06e1 | ||
|
|
b34bd9ffd2 | ||
|
|
fd1a7e34a8 | ||
|
|
973653a690 | ||
|
|
11e4dfe940 | ||
|
|
e7804d39b3 | ||
|
|
6cfbde43ab | ||
|
|
30421bf247 | ||
|
|
ab1b6b45a2 | ||
|
|
59366c04dd | ||
|
|
fac437c1ef | ||
|
|
797f0d92b3 | ||
|
|
75f3fa5144 | ||
|
|
6ea1b5c7c3 | ||
|
|
6a81cc0e83 | ||
|
|
d3e2c7c51a | ||
|
|
1c930a2eb9 | ||
|
|
fb1ea5b37c | ||
|
|
e82f344a2c | ||
|
|
10ba205f97 | ||
|
|
fbc8b97c25 | ||
|
|
c6a31b21e3 | ||
|
|
4aa8c4b79d | ||
|
|
04179ae833 | ||
|
|
205ecde505 | ||
|
|
7cb467c960 | ||
|
|
cbe7c69154 | ||
|
|
c93cbb614d | ||
|
|
42c570a6e2 | ||
|
|
aed47c1178 | ||
|
|
a1ac4784c8 | ||
|
|
d8b751d56b | ||
|
|
213eb2ae88 | ||
|
|
17b7265a9a | ||
|
|
3afdf9571e | ||
|
|
bb8a59d1db | ||
|
|
d26830d81e | ||
|
|
1f2c703ddc | ||
|
|
dd51a6e4d1 | ||
|
|
caa7597097 | ||
|
|
409996cb29 | ||
|
|
a15b37d177 | ||
|
|
a436ef2126 | ||
|
|
4c53e6e89d | ||
|
|
083eb98949 | ||
|
|
418951415c | ||
|
|
bac0d0a175 | ||
|
|
7bde7ebc30 | ||
|
|
7b7e6555c1 | ||
|
|
ea8565d0ca | ||
|
|
1ba2902618 | ||
|
|
e91e8d565a | ||
|
|
206e613ace | ||
|
|
9a88dcf33e | ||
|
|
9fc9e8972f | ||
|
|
4fe6703dd4 | ||
|
|
d613cbb68f | ||
|
|
be2247b36f | ||
|
|
66d1f49116 | ||
|
|
37d35bbbdb | ||
|
|
321d4ccc8e | ||
|
|
6e4338d13c | ||
|
|
677d15d8a7 | ||
|
|
a7e978dc06 | ||
|
|
67f5218a73 | ||
|
|
c2d1329b8b | ||
|
|
0530ec0651 | ||
|
|
8a6e18badc | ||
|
|
1a144a7070 | ||
|
|
e115432e11 | ||
|
|
145f6e2d53 | ||
|
|
9c62311b56 | ||
|
|
a150d91c48 | ||
|
|
4730a4e352 | ||
|
|
67c57be830 | ||
|
|
d98e72ca25 | ||
|
|
d81fa1e610 | ||
|
|
8c12de8b73 | ||
|
|
9b5218f85b | ||
|
|
a6cf5c7667 | ||
|
|
4fca73ffbd | ||
|
|
46a9023782 | ||
|
|
5fed443476 | ||
|
|
0fab0f207c | ||
|
|
ee67358ee6 | ||
|
|
cefd44304b | ||
|
|
2a88cdc76d | ||
|
|
4c39d37ad5 | ||
|
|
9b9865f717 | ||
|
|
3f0456322d | ||
|
|
16ac005fd6 | ||
|
|
003fcf446a | ||
|
|
39703120a4 | ||
|
|
656c0efc0d | ||
|
|
63f7c0ed69 | ||
|
|
5103463524 | ||
|
|
97254a1e3a | ||
|
|
9f171a01e8 | ||
|
|
6c9b6007a3 | ||
|
|
f4a7c4bd69 | ||
|
|
5abad18e51 | ||
|
|
57f70a6d35 | ||
|
|
cb78f3a707 | ||
|
|
b331db74ee | ||
|
|
8fb9ad3fe7 | ||
|
|
d4b5c55169 | ||
|
|
a41438d779 | ||
|
|
0895d8a813 | ||
|
|
89310d7b7c | ||
|
|
ca0e936f7c | ||
|
|
afe767e28a | ||
|
|
1481b011d9 | ||
|
|
5f1b968b60 | ||
|
|
5a729043ce | ||
|
|
2de0dfcef7 | ||
|
|
7cc83ed080 | ||
|
|
31ec03f8e5 | ||
|
|
0d095c4cf1 | ||
|
|
dbde09d008 | ||
|
|
43d33e21e6 | ||
|
|
c8cf43a255 | ||
|
|
b2d05672b1 | ||
|
|
4123c789e6 | ||
|
|
c65f3c7b68 | ||
|
|
41e3642e02 | ||
|
|
eae7602f80 | ||
|
|
fd12c73cf9 | ||
|
|
2ef9d70224 | ||
|
|
3861531fb7 | ||
|
|
aeff41ff41 | ||
|
|
b5c8283575 | ||
|
|
b31cdce073 | ||
|
|
6040aae12a | ||
|
|
6ba4afc5bb | ||
|
|
4707e2b02a | ||
|
|
08dd73fd72 | ||
|
|
83bf67b8ca | ||
|
|
b758ead371 | ||
|
|
357a0db03e | ||
|
|
6a3219a5e8 | ||
|
|
b7e7e82f85 | ||
|
|
e4c66c8b2b | ||
|
|
4d4f8b5ee8 | ||
|
|
5ab75f7eb9 | ||
|
|
8ff22bc737 | ||
|
|
1d65287fbb | ||
|
|
dc24993bf6 | ||
|
|
ef606d1365 | ||
|
|
e26ecec545 | ||
|
|
28e8aa9111 | ||
|
|
a8ff67e3b8 | ||
|
|
c311b92ae2 | ||
|
|
b04e22cd2c | ||
|
|
4903c70686 | ||
|
|
04aa963b9a | ||
|
|
7fdd2e81cf | ||
|
|
38778c8cac | ||
|
|
565851b113 | ||
|
|
cd216b0591 | ||
|
|
031e2acc63 | ||
|
|
b3afa6b0a4 | ||
|
|
40fc2b78d3 | ||
|
|
24e6c1ea36 | ||
|
|
275b4e2944 | ||
|
|
061c93cb7d | ||
|
|
3b91117724 | ||
|
|
eb4fac28c4 | ||
|
|
acb5fc6393 | ||
|
|
961ed728a1 | ||
|
|
678fcfc3a3 | ||
|
|
79d60e6b4a | ||
|
|
858b2ff6da | ||
|
|
ba3355c74c | ||
|
|
6e3028cf86 | ||
|
|
a28598630d | ||
|
|
36810b2d42 | ||
|
|
de9923f2e1 | ||
|
|
716b1ac528 | ||
|
|
77663d64a0 | ||
|
|
49540c7151 | ||
|
|
03710bc6ba | ||
|
|
489ae6c9b6 | ||
|
|
3b54eeb1c5 | ||
|
|
a9bd753e38 | ||
|
|
bf15580081 | ||
|
|
5b5a299b55 | ||
|
|
051b529c11 | ||
|
|
fa5fb815fb | ||
|
|
a4c0a61698 | ||
|
|
80d8a8190d | ||
|
|
e69a62c41a | ||
|
|
46183d4d43 | ||
|
|
4f2d3cc250 | ||
|
|
79daf543b0 | ||
|
|
5b83fb496c | ||
|
|
3dfe7c27cd | ||
|
|
e50538c634 | ||
|
|
92b6ecc4dd | ||
|
|
588ade0ab2 | ||
|
|
4375aefb49 | ||
|
|
c5cba7775a | ||
|
|
7cf8ec6d61 | ||
|
|
46cbd3ae7b | ||
|
|
678cf8a341 | ||
|
|
3a0f1f6197 | ||
|
|
902ff23a95 | ||
|
|
7e2e9aa836 | ||
|
|
491ef4559f | ||
|
|
b7bad0f585 | ||
|
|
ce248bc169 | ||
|
|
d599be7e7c | ||
|
|
5b1fc0e6c0 | ||
|
|
aeece6c201 | ||
|
|
d7a1b974cd | ||
|
|
0db1872bd2 | ||
|
|
4333bc7004 | ||
|
|
f6caac749e | ||
|
|
723303e1b6 | ||
|
|
0daf5bae96 | ||
|
|
6d59fe2a34 | ||
|
|
3ed0f005ee | ||
|
|
66b89ee817 | ||
|
|
c596c875a6 | ||
|
|
08c1a29383 | ||
|
|
ab078d1ea0 | ||
|
|
b028b09041 | ||
|
|
44c3eb7b35 | ||
|
|
f54052c320 | ||
|
|
d23a055e3b | ||
|
|
8f76f3184f | ||
|
|
619c4853fd | ||
|
|
58fb6ade7c | ||
|
|
f2c0626c68 | ||
|
|
3956d2bd63 | ||
|
|
1d851631d6 | ||
|
|
517e38e33f | ||
|
|
a2644a4ebf | ||
|
|
f7bf6a8080 | ||
|
|
681c1d9e9a | ||
|
|
9ab2e69f4f | ||
|
|
9af2ab57d1 | ||
|
|
f14e4dc0be | ||
|
|
39bd9641f4 | ||
|
|
177c283011 | ||
|
|
62ff1421e7 | ||
|
|
2577cbe1ac | ||
|
|
7542f065a1 | ||
|
|
aa2abb92ef | ||
|
|
efc00c1610 | ||
|
|
7585893cf0 | ||
|
|
4fd9019c25 | ||
|
|
f4990aaa95 | ||
|
|
834fa77385 | ||
|
|
3acd20c04d | ||
|
|
878859d7cc | ||
|
|
7038776ec5 | ||
|
|
a74001ef6a | ||
|
|
0bad1794c4 | ||
|
|
ceec72b869 | ||
|
|
5c1ed0ec06 | ||
|
|
ce00fc502b | ||
|
|
ab82d2af4f | ||
|
|
2eb3e1aef6 | ||
|
|
32cb0388cf | ||
|
|
30f9039a2c | ||
|
|
8bad63f72d | ||
|
|
aec4d89550 | ||
|
|
e8639a37af | ||
|
|
a92ab2cbe3 | ||
|
|
fb0bb6e7f3 | ||
|
|
8dbf870122 | ||
|
|
50dab48f25 | ||
|
|
e0c6354ff3 | ||
|
|
5b03d251fd | ||
|
|
9c4b69d1ae | ||
|
|
feba220fff | ||
|
|
f9efa3bf8f | ||
|
|
6ebf333982 | ||
|
|
2c7d07d606 | ||
|
|
b05185e7cc | ||
|
|
148547de95 | ||
|
|
fb6a97789e | ||
|
|
271f34c7e3 | ||
|
|
9309f877d1 | ||
|
|
88db5fb38e | ||
|
|
4ee0a1b8c5 | ||
|
|
7617d734e2 | ||
|
|
a15ae9badb | ||
|
|
97b6fed909 | ||
|
|
ab653afae9 | ||
|
|
df158a741a | ||
|
|
fb1371ebfa | ||
|
|
82d5494235 | ||
|
|
6b754723d9 | ||
|
|
e003113132 | ||
|
|
dda492ff87 | ||
|
|
2459699d27 | ||
|
|
989acec027 | ||
|
|
701ab8b24f | ||
|
|
ed6bfa5fa2 | ||
|
|
2d2899b68a | ||
|
|
2d8b80bfa9 | ||
|
|
c1cc560458 | ||
|
|
95fb562533 | ||
|
|
17853ac237 | ||
|
|
e064a8bbdb | ||
|
|
8b9e3f1b59 | ||
|
|
1d38739016 | ||
|
|
3bb46eaa6f | ||
|
|
3b1561a99b | ||
|
|
a61e5c7310 | ||
|
|
ef8d1ebc0e | ||
|
|
348b3d3738 | ||
|
|
3550aa10ba | ||
|
|
c29eadd9ee | ||
|
|
aecefaee8d | ||
|
|
3bab5ec3a7 | ||
|
|
3c908963ae | ||
|
|
cba176cdc9 | ||
|
|
cfdc854409 | ||
|
|
1d87f78088 | ||
|
|
63b01376d6 | ||
|
|
010e518adb | ||
|
|
6570784b46 | ||
|
|
63b31f41a7 | ||
|
|
96eca59afc | ||
|
|
25978bde8e | ||
|
|
7691d2b5db | ||
|
|
edb108322e | ||
|
|
af3936b68e | ||
|
|
e76c25f9d3 | ||
|
|
124f455be2 | ||
|
|
f679eefe0a |
5
.cargo/config.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
|
||||||
|
[target.armv7-unknown-linux-gnueabihf]
|
||||||
|
linker = "arm-linux-gnueabihf-gcc"
|
||||||
27
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -2,29 +2,38 @@ name: 问题反馈 / Bug report
|
|||||||
title: "[BUG] "
|
title: "[BUG] "
|
||||||
description: 反馈你遇到的问题 / Report the issue you are experiencing
|
description: 反馈你遇到的问题 / Report the issue you are experiencing
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
|
type: "Bug"
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
## 在提交问题之前,请确认以下事项:
|
## 在提交问题之前,请确认以下事项:
|
||||||
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq/install/)
|
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
|
||||||
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
|
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
|
||||||
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
|
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
|
||||||
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保问题依然存在
|
4. 请 **务必** 查看 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本更新日志
|
||||||
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
|
5. 请 **务必** 尝试 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本,确定问题是否仍然存在
|
||||||
|
6. 请 **务必** 按照模板规范详细描述问题以及尝试更新 Alpha 版本,否则issue将会被直接关闭
|
||||||
## Before submitting the issue, please make sure of the following checklist:
|
## Before submitting the issue, please make sure of the following checklist:
|
||||||
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide.html) and [FAQ](https://clash-verge-rev.github.io/faq/install/)
|
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) and [FAQ](https://clash-verge-rev.github.io/faq/windows.html)
|
||||||
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
|
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
|
||||||
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
|
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
|
||||||
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the problem still exists
|
4. Please be sure to check out [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version update log
|
||||||
5. Please describe the problem in detail according to the template specification, otherwise the issue will be closed
|
5. Please be sure to try the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version to ensure that the problem still exists
|
||||||
|
6. Please describe the problem in detail according to the template specification and try to update the Alpha version, otherwise the issue will be closed
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: description
|
id: description
|
||||||
attributes:
|
attributes:
|
||||||
label: 问题描述 / Describe the bug
|
label: 问题描述 / Describe the bug
|
||||||
description: 详细清晰地描述你遇到的问题 / A clear and concise description of what the bug is
|
description: 详细清晰地描述你遇到的问题,并配合截图 / Describe the problem you encountered in detail and clearly, and provide screenshots
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 软件版本 / Verge Version
|
||||||
|
description: 请提供Verge的具体版本,如果是alpha版本,请注明下载时间(精确到小时分钟) / Please provide the specific version of Verge. If it is an alpha version, please indicate the download time (accurate to hours and minutes)
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@@ -50,7 +59,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: 日志 / Log
|
label: 日志(勿上传日志文件,请粘贴日志内容) / Log (Do not upload the log file, paste the log content directly)
|
||||||
description: 请提供完整或相关部分的Debug日志 / Please provide the complete or relevant part of the Debug log
|
description: 请提供完整或相关部分的Debug日志(请在“软件左侧菜单”->“设置”->“日志等级”调整到debug,Verge错误请把“杂项设置”->“app日志等级”调整到debug,并重启Verge生效。日志文件在“软件左侧菜单”->“设置”->“日志目录”下) / Please provide a complete or relevant part of the Debug log (please adjust the "Log level" to debug in "Software left menu" -> "Settings" -> "Log level". If there is a Verge error, please adjust "Miscellaneous settings" -> "app log level" to debug, and restart Verge to take effect. The log file is under "Software left menu" -> "Settings" -> "Log directory")
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
16
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -2,19 +2,20 @@ name: 功能请求 / Feature request
|
|||||||
title: "[Feature] "
|
title: "[Feature] "
|
||||||
description: 提出你的功能请求 / Propose your feature request
|
description: 提出你的功能请求 / Propose your feature request
|
||||||
labels: ["enhancement"]
|
labels: ["enhancement"]
|
||||||
|
type: "Feature"
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
## 在提交问题之前,请确认以下事项:
|
## 在提交问题之前,请确认以下事项:
|
||||||
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide.html) 确认软件不存在类似的功能
|
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 确认软件不存在类似的功能
|
||||||
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
|
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
|
||||||
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
|
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
|
||||||
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保该功能还未实现
|
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保该功能还未实现
|
||||||
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
|
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
|
||||||
## Before submitting the issue, please make sure of the following checklist:
|
## Before submitting the issue, please make sure of the following checklist:
|
||||||
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide.html) to confirm that the software does not have similar functions
|
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) to confirm that the software does not have similar functions
|
||||||
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
|
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
|
||||||
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
|
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
|
||||||
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the function has not been implemented
|
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the function has not been implemented
|
||||||
@@ -33,3 +34,14 @@ body:
|
|||||||
description: 请描述你的功能请求的使用场景 / Please describe the use case of your feature request
|
description: 请描述你的功能请求的使用场景 / Please describe the use case of your feature request
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: os-labels
|
||||||
|
attributes:
|
||||||
|
label: 适用系统 / Target OS
|
||||||
|
description: 请选择该功能适用的操作系统(至少选择一个) / Please select the operating system(s) for this feature request (select at least one)
|
||||||
|
options:
|
||||||
|
- label: windows
|
||||||
|
- label: macos
|
||||||
|
- label: linux
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|||||||
58
.github/ISSUE_TEMPLATE/i18n_request.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
name: I18N / 多语言相关
|
||||||
|
title: "[I18N] "
|
||||||
|
description: 用于多语言翻译、国际化相关问题或建议 / For issues or suggestions related to translations and internationalization
|
||||||
|
labels: ["I18n"]
|
||||||
|
type: "Task"
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## I18N 相关问题/建议
|
||||||
|
请用此模板提交翻译错误、缺失、建议或新增语言请求。
|
||||||
|
Please use this template for translation errors, missing translations, suggestions, or new language requests.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: 问题描述 / Description
|
||||||
|
description: 详细描述你的 I18N 问题或建议 / Please describe your I18N issue or suggestion in detail
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: language
|
||||||
|
attributes:
|
||||||
|
label: 相关语言 / Language
|
||||||
|
description: 例如 zh, en, jp, ru, ... / e.g. zh, en, jp, ru, ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: suggestion
|
||||||
|
attributes:
|
||||||
|
label: 建议或修正内容 / Suggestion or Correction
|
||||||
|
description: 如果是翻译修正或建议,请填写建议的内容 / If this is a translation correction or suggestion, please provide the suggested content
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: i18n-type
|
||||||
|
attributes:
|
||||||
|
label: 问题类型 / Issue Type
|
||||||
|
description: 请选择适用类型(可多选) / Please select the applicable type(s)
|
||||||
|
options:
|
||||||
|
- label: 翻译错误 / Translation error
|
||||||
|
- label: 翻译缺失 / Missing translation
|
||||||
|
- label: 建议优化 / Suggestion
|
||||||
|
- label: 新增语言 / New language
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: verge-version
|
||||||
|
attributes:
|
||||||
|
label: 软件版本 / Verge Version
|
||||||
|
description: 请提供你使用的 Verge 具体版本 / Please provide the specific version of Verge you are using
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
4
.github/build-for-linux/Dockerfile
vendored
@@ -1,4 +0,0 @@
|
|||||||
FROM rust:buster
|
|
||||||
COPY entrypoint.sh /entrypoint.sh
|
|
||||||
RUN chmod a+x /entrypoint.sh
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
|
||||||
14
.github/build-for-linux/action.yml
vendored
@@ -1,14 +0,0 @@
|
|||||||
name: "Build for Linux"
|
|
||||||
branding:
|
|
||||||
icon: user-check
|
|
||||||
color: gray-dark
|
|
||||||
inputs:
|
|
||||||
target:
|
|
||||||
required: true
|
|
||||||
description: "Rust Target"
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "docker"
|
|
||||||
image: "Dockerfile"
|
|
||||||
args:
|
|
||||||
- ${{ inputs.target }}
|
|
||||||
8
.github/build-for-linux/build.sh
vendored
@@ -1,8 +0,0 @@
|
|||||||
pnpm install
|
|
||||||
pnpm check $INPUT_TARGET
|
|
||||||
sed -i "s/#openssl/openssl={version=\"0.10\",features=[\"vendored\"]}/g" src-tauri/Cargo.toml
|
|
||||||
if [ "$INPUT_TARGET" = "x86_64-unknown-linux-gnu" ]; then
|
|
||||||
pnpm build --target $INPUT_TARGET
|
|
||||||
else
|
|
||||||
pnpm build --target $INPUT_TARGET -b deb
|
|
||||||
fi
|
|
||||||
47
.github/build-for-linux/entrypoint.sh
vendored
@@ -1,47 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
wget https://nodejs.org/dist/v20.10.0/node-v20.10.0-linux-x64.tar.xz
|
|
||||||
tar -Jxvf ./node-v20.10.0-linux-x64.tar.xz
|
|
||||||
export PATH=$(pwd)/node-v20.10.0-linux-x64/bin:$PATH
|
|
||||||
npm install pnpm -g
|
|
||||||
|
|
||||||
rustup target add "$INPUT_TARGET"
|
|
||||||
|
|
||||||
if [ "$INPUT_TARGET" = "x86_64-unknown-linux-gnu" ]; then
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
|
||||||
elif [ "$INPUT_TARGET" = "aarch64-unknown-linux-gnu" ]; then
|
|
||||||
dpkg --add-architecture arm64
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y libncurses6:arm64 libtinfo6:arm64 linux-libc-dev:arm64 libncursesw6:arm64 libssl3:arm64 libcups2:arm64
|
|
||||||
apt-get install -y --no-install-recommends g++-aarch64-linux-gnu libc6-dev-arm64-cross libwebkit2gtk-4.0-dev:arm64 libgtk-3-dev:arm64 patchelf:arm64 librsvg2-dev:arm64 libayatana-appindicator3-dev:arm64
|
|
||||||
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
|
|
||||||
export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
|
|
||||||
export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
|
|
||||||
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
|
|
||||||
export PKG_CONFIG_ALLOW_CROSS=1
|
|
||||||
elif [ "$INPUT_TARGET" = "armv7-unknown-linux-gnueabihf" ]; then
|
|
||||||
dpkg --add-architecture armhf
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y libncurses6:armhf libtinfo6:armhf linux-libc-dev:armhf libncursesw6:armhf libssl3:armhf libcups2:armhf
|
|
||||||
apt-get install -y --no-install-recommends g++-arm-linux-gnueabihf libc6-dev-armhf-cross libwebkit2gtk-4.0-dev:armhf libgtk-3-dev:armhf patchelf:armhf librsvg2-dev:armhf libayatana-appindicator3-dev:armhf
|
|
||||||
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
|
|
||||||
export CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc
|
|
||||||
export CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++
|
|
||||||
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
|
|
||||||
export PKG_CONFIG_ALLOW_CROSS=1
|
|
||||||
elif [ "$INPUT_TARGET" = "riscv64gc-unknown-linux-gnu" ]; then
|
|
||||||
dpkg --add-architecture riscv64
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y libncurses6:riscv64 libtinfo6:riscv64 linux-libc-dev:riscv64 libncursesw6:riscv64 libssl3:riscv64 libcups2:riscv64
|
|
||||||
apt-get install -y --no-install-recommends g++-riscv64-linux-gnu libc6-dev-riscv64-cross libwebkit2gtk-4.0-dev:riscv64 libgtk-3-dev:riscv64 patchelf:riscv64 librsvg2-dev:riscv64 libayatana-appindicator3-dev:riscv64
|
|
||||||
export CARGO_TARGET_RISCV64_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc
|
|
||||||
export CC_riscv64_unknown_linux_gnu=riscv64-linux-gnu-gcc
|
|
||||||
export CXX_riscv64_unknown_linux_gnu=riscv64-linux-gnu-g++
|
|
||||||
export PKG_CONFIG_PATH=/usr/lib/riscv64-linux-gnu/pkgconfig
|
|
||||||
export PKG_CONFIG_ALLOW_CROSS=1
|
|
||||||
else
|
|
||||||
echo "Unknown target: $INPUT_TARGET" && exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
bash .github/build-for-linux/build.sh
|
|
||||||
567
.github/workflows/alpha.yml
vendored
@@ -1,17 +1,262 @@
|
|||||||
name: Alpha Build
|
name: Alpha Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
# 因为 alpha 不再负责频繁构建,且需要相对于 autobuild 更稳定使用环境
|
||||||
|
# 所以不再使用 workflow_dispatch 触发
|
||||||
|
# 应当通过 git tag 来触发构建
|
||||||
|
# TODO 手动控制版本号
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
# inputs:
|
||||||
branches: [main]
|
# tag_name:
|
||||||
tags-ignore: [updater, alpha]
|
# description: "Alpha tag name (e.g. v1.2.3-alpha.1)"
|
||||||
|
# required: true
|
||||||
|
# type: string
|
||||||
|
|
||||||
|
# push:
|
||||||
|
# # 应当限制在 dev 分支上触发发布。
|
||||||
|
# branches:
|
||||||
|
# - dev
|
||||||
|
# # 应当限制 v*.*.*-alpha* 的 tag 来触发发布。
|
||||||
|
# tags:
|
||||||
|
# - "v*.*.*-alpha*"
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
env:
|
env:
|
||||||
|
TAG_NAME: alpha
|
||||||
|
TAG_CHANNEL: Alpha
|
||||||
CARGO_INCREMENTAL: 0
|
CARGO_INCREMENTAL: 0
|
||||||
RUST_BACKTRACE: short
|
RUST_BACKTRACE: short
|
||||||
|
concurrency:
|
||||||
|
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
alpha:
|
check_alpha_tag:
|
||||||
|
name: Check Alpha Tag package.json Version Consistency
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check tag and package.json version
|
||||||
|
id: check_tag
|
||||||
|
run: |
|
||||||
|
TAG_REF="${GITHUB_REF##*/}"
|
||||||
|
echo "Current tag: $TAG_REF"
|
||||||
|
if [[ ! "$TAG_REF" =~ -alpha ]]; then
|
||||||
|
echo "Current tag is not an alpha tag."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PKG_VERSION=$(jq -r .version package.json)
|
||||||
|
echo "package.json version: $PKG_VERSION"
|
||||||
|
if [[ "$PKG_VERSION" != *alpha* ]]; then
|
||||||
|
echo "package.json version is not an alpha version."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$TAG_REF" != "v$PKG_VERSION" ]]; then
|
||||||
|
echo "Tag ($TAG_REF) does not match package.json version (v$PKG_VERSION)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Alpha tag and package.json version are consistent."
|
||||||
|
|
||||||
|
delete_old_assets:
|
||||||
|
name: Delete Old Alpha Release Assets and Tags
|
||||||
|
needs: check_alpha_tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Delete Old Alpha Tags Except Latest
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const tagPattern = /-alpha.*/; // 匹配带有 -alpha 的 tag
|
||||||
|
const owner = context.repo.owner;
|
||||||
|
const repo = context.repo.repo;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取所有 tag
|
||||||
|
const { data: tags } = await github.rest.repos.listTags({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
per_page: 100 // 调整 per_page 以获取更多 tag
|
||||||
|
});
|
||||||
|
|
||||||
|
// 过滤出包含 -alpha 的 tag
|
||||||
|
const alphaTags = (await Promise.all(
|
||||||
|
tags
|
||||||
|
.filter(tag => tagPattern.test(tag.name))
|
||||||
|
.map(async tag => {
|
||||||
|
// 获取每个 tag 的 commit 信息以获得日期
|
||||||
|
const { data: commit } = await github.rest.repos.getCommit({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
ref: tag.commit.sha
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...tag,
|
||||||
|
commitDate: commit.committer && commit.committer.date ? commit.committer.date : commit.commit.author.date
|
||||||
|
};
|
||||||
|
})
|
||||||
|
)).sort((a, b) => {
|
||||||
|
// 按 commit 日期降序排序(最新的在前面)
|
||||||
|
return new Date(b.commitDate) - new Date(a.commitDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Found ${alphaTags.length} alpha tags`);
|
||||||
|
|
||||||
|
if (alphaTags.length === 0) {
|
||||||
|
console.log('No alpha tags found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保留最新的 tag
|
||||||
|
const latestTag = alphaTags[0];
|
||||||
|
console.log(`Keeping latest alpha tag: ${latestTag.name}`);
|
||||||
|
|
||||||
|
// 处理其他旧的 alpha tag
|
||||||
|
for (const tag of alphaTags.slice(1)) {
|
||||||
|
console.log(`Processing tag: ${tag.name}`);
|
||||||
|
|
||||||
|
// 获取与 tag 关联的 release
|
||||||
|
try {
|
||||||
|
const { data: release } = await github.rest.repos.getReleaseByTag({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
tag: tag.name
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除 release 下的所有资产
|
||||||
|
if (release.assets && release.assets.length > 0) {
|
||||||
|
console.log(`Deleting ${release.assets.length} assets for release ${tag.name}`);
|
||||||
|
for (const asset of release.assets) {
|
||||||
|
console.log(`Deleting asset: ${asset.name} (${asset.id})`);
|
||||||
|
await github.rest.repos.deleteReleaseAsset({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
asset_id: asset.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 release
|
||||||
|
console.log(`Deleting release for tag: ${tag.name}`);
|
||||||
|
await github.rest.repos.deleteRelease({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
release_id: release.id
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除 tag
|
||||||
|
console.log(`Deleting tag: ${tag.name}`);
|
||||||
|
await github.rest.git.deleteRef({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
ref: `tags/${tag.name}`
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
if (error.status === 404) {
|
||||||
|
console.log(`No release found for tag ${tag.name}, deleting tag directly`);
|
||||||
|
await github.rest.git.deleteRef({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
ref: `tags/${tag.name}`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error(`Error processing tag ${tag.name}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Old alpha tags and releases deleted successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_tag:
|
||||||
|
name: Update tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: delete_old_assets
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Fetch UPDATE logs
|
||||||
|
id: fetch_update_logs
|
||||||
|
run: |
|
||||||
|
if [ -f "UPDATELOG.md" ]; then
|
||||||
|
UPDATE_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md)
|
||||||
|
if [ -n "$UPDATE_LOGS" ]; then
|
||||||
|
echo "Found update logs"
|
||||||
|
echo "UPDATE_LOGS<<EOF" >> $GITHUB_ENV
|
||||||
|
echo "$UPDATE_LOGS" >> $GITHUB_ENV
|
||||||
|
echo "EOF" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "No update sections found in UPDATELOG.md"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "UPDATELOG.md file not found"
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Set Env
|
||||||
|
run: |
|
||||||
|
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
if [ -z "$UPDATE_LOGS" ]; then
|
||||||
|
echo "No update logs found, using default message"
|
||||||
|
UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon."
|
||||||
|
else
|
||||||
|
echo "Using found update logs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > release.txt << EOF
|
||||||
|
$UPDATE_LOGS
|
||||||
|
|
||||||
|
## 我应该下载哪个版本?
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
- MacOS intel芯片: x64.dmg
|
||||||
|
- MacOS apple M芯片: aarch64.dmg
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
- Linux 64位: amd64.deb/amd64.rpm
|
||||||
|
- Linux arm64 architecture: arm64.deb/aarch64.rpm
|
||||||
|
- Linux armv7架构: armhf.deb/armhfp.rpm
|
||||||
|
|
||||||
|
### Windows (不再支持Win7)
|
||||||
|
#### 正常版本(推荐)
|
||||||
|
- 64位: x64-setup.exe
|
||||||
|
- arm64架构: arm64-setup.exe
|
||||||
|
#### 便携版问题很多不再提供
|
||||||
|
#### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
|
||||||
|
- 64位: x64_fixed_webview2-setup.exe
|
||||||
|
- arm64架构: arm64_fixed_webview2-setup.exe
|
||||||
|
|
||||||
|
### FAQ
|
||||||
|
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
|
||||||
|
|
||||||
|
### 稳定机场VPN推荐
|
||||||
|
- [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)
|
||||||
|
|
||||||
|
Created at ${{ env.BUILDTIME }}.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Upload Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ env.TAG_NAME }}
|
||||||
|
name: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
|
||||||
|
body_path: release.txt
|
||||||
|
prerelease: true
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
generate_release_notes: true
|
||||||
|
|
||||||
|
alpha-x86-windows-macos-linux:
|
||||||
|
name: Alpha x86 Windows, MacOS and Linux
|
||||||
|
needs: update_tag
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -24,7 +269,8 @@ jobs:
|
|||||||
target: aarch64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
target: x86_64-apple-darwin
|
target: x86_64-apple-darwin
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
@@ -40,69 +286,166 @@ jobs:
|
|||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: src-tauri
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
|
- name: Install dependencies (ubuntu only)
|
||||||
|
if: matrix.os == 'ubuntu-22.04'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||||
|
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "22"
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
- uses: pnpm/action-setup@v4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9
|
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Pnpm install and check
|
- name: Pnpm install and check
|
||||||
run: |
|
run: |
|
||||||
pnpm i
|
pnpm i
|
||||||
pnpm check ${{ matrix.target }}
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
# - name: Release ${{ env.TAG_CHANNEL }} Version
|
||||||
|
# run: pnpm release-version ${{ env.TAG_NAME }}
|
||||||
|
|
||||||
- name: Tauri build
|
- name: Tauri build
|
||||||
uses: tauri-apps/tauri-action@v0
|
uses: tauri-apps/tauri-action@v0
|
||||||
env:
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
with:
|
with:
|
||||||
tagName: alpha
|
tagName: ${{ env.TAG_NAME }}
|
||||||
releaseName: "Clash Verge Rev Alpha"
|
releaseName: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
|
||||||
releaseBody: "More new features are now supported."
|
releaseBody: "More new features are now supported."
|
||||||
releaseDraft: false
|
releaseDraft: false
|
||||||
prerelease: true
|
prerelease: true
|
||||||
tauriScript: pnpm
|
tauriScript: pnpm
|
||||||
args: --target ${{ matrix.target }}
|
args: --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Portable Bundle
|
alpha-arm-linux:
|
||||||
if: matrix.os == 'windows-latest'
|
name: Alpha ARM Linux
|
||||||
run: pnpm portable ${{ matrix.target }} --alpha
|
needs: update_tag
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
|
||||||
|
|
||||||
alpha-for-linux:
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-22.04
|
||||||
target: x86_64-unknown-linux-gnu
|
|
||||||
- os: ubuntu-latest
|
|
||||||
target: aarch64-unknown-linux-gnu
|
target: aarch64-unknown-linux-gnu
|
||||||
- os: ubuntu-latest
|
arch: arm64
|
||||||
|
- os: ubuntu-22.04
|
||||||
target: armv7-unknown-linux-gnueabihf
|
target: armv7-unknown-linux-gnueabihf
|
||||||
|
arch: armhf
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build for Linux
|
- name: Install Rust Stable
|
||||||
uses: ./.github/build-for-linux
|
uses: dtolnay/rust-toolchain@stable
|
||||||
env:
|
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
- name: Add Rust Target
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
# - name: Release ${{ env.TAG_CHANNEL }} Version
|
||||||
|
# run: pnpm release-version ${{ env.TAG_NAME }}
|
||||||
|
|
||||||
|
- name: Setup for linux
|
||||||
|
run: |
|
||||||
|
sudo ls -lR /etc/apt/
|
||||||
|
|
||||||
|
cat > /tmp/sources.list << EOF
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-backports main multiverse universe restricted
|
||||||
|
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main multiverse universe restricted
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo mv /etc/apt/sources.list /etc/apt/sources.list.default
|
||||||
|
sudo mv /tmp/sources.list /etc/apt/sources.list
|
||||||
|
|
||||||
|
sudo dpkg --add-architecture ${{ matrix.arch }}
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get -f install -y
|
||||||
|
|
||||||
|
sudo apt-get install -y \
|
||||||
|
linux-libc-dev:${{ matrix.arch }} \
|
||||||
|
libc6-dev:${{ matrix.arch }}
|
||||||
|
|
||||||
|
sudo apt-get install -y \
|
||||||
|
libxslt1.1:${{ matrix.arch }} \
|
||||||
|
libwebkit2gtk-4.1-dev:${{ matrix.arch }} \
|
||||||
|
libayatana-appindicator3-dev:${{ matrix.arch }} \
|
||||||
|
libssl-dev:${{ matrix.arch }} \
|
||||||
|
patchelf:${{ matrix.arch }} \
|
||||||
|
librsvg2-dev:${{ matrix.arch }}
|
||||||
|
|
||||||
|
- name: Install aarch64 tools
|
||||||
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||||
|
run: |
|
||||||
|
sudo apt install -y \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
g++-aarch64-linux-gnu
|
||||||
|
|
||||||
|
- name: Install armv7 tools
|
||||||
|
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
|
||||||
|
run: |
|
||||||
|
sudo apt install -y \
|
||||||
|
gcc-arm-linux-gnueabihf \
|
||||||
|
g++-arm-linux-gnueabihf
|
||||||
|
|
||||||
|
- name: Build for Linux
|
||||||
|
run: |
|
||||||
|
export PKG_CONFIG_ALLOW_CROSS=1
|
||||||
|
if [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then
|
||||||
|
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
|
||||||
|
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/
|
||||||
|
elif [ "${{ matrix.target }}" == "armv7-unknown-linux-gnueabihf" ]; then
|
||||||
|
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/:$PKG_CONFIG_PATH
|
||||||
|
export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf/
|
||||||
|
fi
|
||||||
|
pnpm build --target ${{ matrix.target }}
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
|
||||||
- name: Get Version
|
- name: Get Version
|
||||||
run: |
|
run: |
|
||||||
@@ -111,95 +454,111 @@ jobs:
|
|||||||
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
|
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
|
||||||
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
|
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- run: |
|
|
||||||
cat > release.txt << 'EOF'
|
|
||||||
### 我应该下载哪个版本?
|
|
||||||
|
|
||||||
- Windows x86_64架构: x64-setup.exe (不支持win7)
|
|
||||||
- Windows arm64架构: arm64-setup.exe
|
|
||||||
- MacOS intel芯片: x64.dmg
|
|
||||||
- MacOS apple M芯片: aarch64.dmg (提示文件损坏看下面FAQ)
|
|
||||||
- Linux x64架构: amd64.AppImage/amd64.deb
|
|
||||||
- Linux arm64架构: arm64.deb
|
|
||||||
- Linux armv7架构: armhf.deb
|
|
||||||
- Windows 便携板 x86_64架构: x64_portable.zip (不推荐使用,无法自动更新)
|
|
||||||
- Windows 便携板 arm64架构: arm64_portable.zip (不推荐使用,无法自动更新)
|
|
||||||
|
|
||||||
### FAQ
|
|
||||||
|
|
||||||
- [FAQ](https://clash-verge-rev.github.io/faq/install/)
|
|
||||||
|
|
||||||
Created at ${{ env.BUILDTIME }}.
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Upload Release
|
- name: Upload Release
|
||||||
if: startsWith(matrix.target, 'x86_64')
|
uses: softprops/action-gh-release@v2
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
with:
|
||||||
tag_name: alpha
|
tag_name: ${{ env.TAG_NAME }}
|
||||||
name: "Clash Verge Rev Alpha"
|
name: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
|
||||||
body_path: release.txt
|
|
||||||
prerelease: true
|
prerelease: true
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
files: src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage*
|
files: |
|
||||||
|
src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
|
||||||
|
src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm
|
||||||
|
|
||||||
- name: Upload Release
|
alpha-x86-arm-windows_webview2:
|
||||||
uses: softprops/action-gh-release@v1
|
name: Alpha x86 and ARM Windows with WebView2
|
||||||
with:
|
needs: update_tag
|
||||||
tag_name: alpha
|
strategy:
|
||||||
name: "Clash Verge Rev Alpha"
|
fail-fast: false
|
||||||
body_path: release.txt
|
matrix:
|
||||||
prerelease: true
|
include:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
- os: windows-latest
|
||||||
files: src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
|
target: x86_64-pc-windows-msvc
|
||||||
|
arch: x64
|
||||||
update_tag:
|
- os: windows-latest
|
||||||
name: Update tag
|
target: aarch64-pc-windows-msvc
|
||||||
runs-on: ubuntu-latest
|
arch: arm64
|
||||||
needs: [alpha, alpha-for-linux]
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set Env
|
- name: Add Rust Target
|
||||||
run: |
|
run: rustup target add ${{ matrix.target }}
|
||||||
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Update Tag
|
- name: Rust Cache
|
||||||
uses: richardsimko/update-tag@v1
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
tag_name: alpha
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
# - name: Release ${{ env.TAG_CHANNEL }} Version
|
||||||
|
# run: pnpm release-version ${{ env.TAG_NAME }}
|
||||||
|
|
||||||
|
- name: Download WebView2 Runtime
|
||||||
|
run: |
|
||||||
|
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/109.0.1518.78/Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab
|
||||||
|
Expand .\Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -F:* ./src-tauri
|
||||||
|
Remove-Item .\src-tauri\tauri.windows.conf.json
|
||||||
|
Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json
|
||||||
|
|
||||||
|
- name: Tauri build
|
||||||
|
id: build
|
||||||
|
uses: tauri-apps/tauri-action@v0
|
||||||
env:
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
with:
|
||||||
|
tauriScript: pnpm
|
||||||
|
args: --target ${{ matrix.target }}
|
||||||
|
|
||||||
- run: |
|
- name: Rename
|
||||||
cat > release.txt << 'EOF'
|
run: |
|
||||||
### 我应该下载哪个版本?
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
|
||||||
|
Rename-Item $file.FullName $newName
|
||||||
|
}
|
||||||
|
|
||||||
- Windows x86_64架构: x64-setup.exe (不支持win7)
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
|
||||||
- Windows arm64架构: arm64-setup.exe
|
foreach ($file in $files) {
|
||||||
- MacOS intel芯片: x64.dmg
|
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
|
||||||
- MacOS apple M芯片: aarch64.dmg (提示文件损坏看下面FAQ)
|
Rename-Item $file.FullName $newName
|
||||||
- Linux x64架构: amd64.AppImage/amd64.deb
|
}
|
||||||
- Linux arm64架构: arm64.deb
|
|
||||||
- Linux armv7架构: armhf.deb
|
|
||||||
- Windows 便携板 x86_64架构: x64_portable.zip (不推荐使用,无法自动更新)
|
|
||||||
- Windows 便携板 arm64架构: arm64_portable.zip (不推荐使用,无法自动更新)
|
|
||||||
|
|
||||||
### FAQ
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
|
||||||
|
foreach ($file in $files) {
|
||||||
- [FAQ](https://clash-verge-rev.github.io/faq/install/)
|
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
|
||||||
|
Rename-Item $file.FullName $newName
|
||||||
Created at ${{ env.BUILDTIME }}.
|
}
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Upload Release
|
- name: Upload Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: alpha
|
tag_name: ${{ env.TAG_NAME }}
|
||||||
name: "Clash Verge Rev Alpha"
|
name: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
|
||||||
body_path: release.txt
|
|
||||||
prerelease: true
|
prerelease: true
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
generate_release_notes: true
|
files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
|
||||||
|
|
||||||
|
- name: Portable Bundle
|
||||||
|
run: pnpm portable-fixed-webview2 ${{ matrix.target }} --${{ env.TAG_NAME }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
466
.github/workflows/autobuild.yml
vendored
Normal file
@@ -0,0 +1,466 @@
|
|||||||
|
name: Auto Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
# UTC+8 0,6,12,18
|
||||||
|
- cron: "0 16,22,4,10 * * *"
|
||||||
|
permissions: write-all
|
||||||
|
env:
|
||||||
|
TAG_NAME: autobuild
|
||||||
|
TAG_CHANNEL: AutoBuild
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
RUST_BACKTRACE: short
|
||||||
|
concurrency:
|
||||||
|
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
|
||||||
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_commit:
|
||||||
|
name: Check Commit Needs Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Check if version changed or src changed
|
||||||
|
id: check
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||||
|
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
CURRENT_VERSION=$(cat package.json | jq -r '.version')
|
||||||
|
echo "Current version: $CURRENT_VERSION"
|
||||||
|
|
||||||
|
git checkout HEAD~1 package.json
|
||||||
|
PREVIOUS_VERSION=$(cat package.json | jq -r '.version')
|
||||||
|
echo "Previous version: $PREVIOUS_VERSION"
|
||||||
|
|
||||||
|
git checkout HEAD package.json
|
||||||
|
|
||||||
|
if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then
|
||||||
|
echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION"
|
||||||
|
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
CURRENT_SRC_HASH=$(git rev-parse HEAD:src)
|
||||||
|
PREVIOUS_SRC_HASH=$(git rev-parse HEAD~1:src 2>/dev/null || echo "")
|
||||||
|
CURRENT_TAURI_HASH=$(git rev-parse HEAD:src-tauri 2>/dev/null || echo "")
|
||||||
|
PREVIOUS_TAURI_HASH=$(git rev-parse HEAD~1:src-tauri 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
echo "Current src hash: $CURRENT_SRC_HASH"
|
||||||
|
echo "Previous src hash: $PREVIOUS_SRC_HASH"
|
||||||
|
echo "Current tauri hash: $CURRENT_TAURI_HASH"
|
||||||
|
echo "Previous tauri hash: $PREVIOUS_TAURI_HASH"
|
||||||
|
|
||||||
|
if [ "$CURRENT_SRC_HASH" != "$PREVIOUS_SRC_HASH" ] || [ "$CURRENT_TAURI_HASH" != "$PREVIOUS_TAURI_HASH" ]; then
|
||||||
|
echo "Source directories changed"
|
||||||
|
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "Version and source directories unchanged"
|
||||||
|
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
update_tag:
|
||||||
|
name: Update tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: check_commit
|
||||||
|
if: ${{ needs.check_commit.outputs.should_run == 'true' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Fetch UPDATE logs
|
||||||
|
id: fetch_update_logs
|
||||||
|
run: |
|
||||||
|
if [ -f "UPDATELOG.md" ]; then
|
||||||
|
UPDATE_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md)
|
||||||
|
if [ -n "$UPDATE_LOGS" ]; then
|
||||||
|
echo "Found update logs"
|
||||||
|
echo "UPDATE_LOGS<<EOF" >> $GITHUB_ENV
|
||||||
|
echo "$UPDATE_LOGS" >> $GITHUB_ENV
|
||||||
|
echo "EOF" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "No update sections found in UPDATELOG.md"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "UPDATELOG.md file not found"
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Set Env
|
||||||
|
run: |
|
||||||
|
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
if [ -z "$UPDATE_LOGS" ]; then
|
||||||
|
echo "No update logs found, using default message"
|
||||||
|
UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon."
|
||||||
|
else
|
||||||
|
echo "Using found update logs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > release.txt << EOF
|
||||||
|
$UPDATE_LOGS
|
||||||
|
|
||||||
|
## 我应该下载哪个版本?
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
- MacOS intel芯片: x64.dmg
|
||||||
|
- MacOS apple M芯片: aarch64.dmg
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
- Linux 64位: amd64.deb/amd64.rpm
|
||||||
|
- Linux arm64 architecture: arm64.deb/aarch64.rpm
|
||||||
|
- Linux armv7架构: armhf.deb/armhfp.rpm
|
||||||
|
|
||||||
|
### Windows (不再支持Win7)
|
||||||
|
#### 正常版本(推荐)
|
||||||
|
- 64位: x64-setup.exe
|
||||||
|
- arm64架构: arm64-setup.exe
|
||||||
|
#### 便携版问题很多不再提供
|
||||||
|
#### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
|
||||||
|
- 64位: x64_fixed_webview2-setup.exe
|
||||||
|
- arm64架构: arm64_fixed_webview2-setup.exe
|
||||||
|
|
||||||
|
### FAQ
|
||||||
|
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
|
||||||
|
|
||||||
|
### 稳定机场VPN推荐
|
||||||
|
- [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)
|
||||||
|
|
||||||
|
Created at ${{ env.BUILDTIME }}.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Upload Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ env.TAG_NAME }}
|
||||||
|
name: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
|
||||||
|
body_path: release.txt
|
||||||
|
prerelease: true
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
generate_release_notes: true
|
||||||
|
|
||||||
|
autobuild-x86-windows-macos-linux:
|
||||||
|
name: Autobuild x86 Windows, MacOS and Linux
|
||||||
|
needs: update_tag
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
- os: windows-latest
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust Stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Add Rust Target
|
||||||
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: src-tauri
|
||||||
|
cache-all-crates: true
|
||||||
|
save-if: ${{ github.ref == 'refs/heads/dev' }}
|
||||||
|
|
||||||
|
- name: Install dependencies (ubuntu only)
|
||||||
|
if: matrix.os == 'ubuntu-22.04'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Release ${{ env.TAG_CHANNEL }} Version
|
||||||
|
run: pnpm release-version ${{ env.TAG_NAME }}
|
||||||
|
|
||||||
|
- name: Tauri build
|
||||||
|
uses: tauri-apps/tauri-action@v0
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
with:
|
||||||
|
tagName: ${{ env.TAG_NAME }}
|
||||||
|
releaseName: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
|
||||||
|
releaseBody: "More new features are now supported."
|
||||||
|
releaseDraft: false
|
||||||
|
prerelease: true
|
||||||
|
tauriScript: pnpm
|
||||||
|
args: --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
autobuild-arm-linux:
|
||||||
|
name: Autobuild ARM Linux
|
||||||
|
needs: update_tag
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
target: aarch64-unknown-linux-gnu
|
||||||
|
arch: arm64
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
target: armv7-unknown-linux-gnueabihf
|
||||||
|
arch: armhf
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust Stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Add Rust Target
|
||||||
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: src-tauri
|
||||||
|
cache-all-crates: true
|
||||||
|
save-if: ${{ github.ref == 'refs/heads/dev' }}
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Release ${{ env.TAG_CHANNEL }} Version
|
||||||
|
run: pnpm release-version ${{ env.TAG_NAME }}
|
||||||
|
|
||||||
|
- name: Setup for linux
|
||||||
|
run: |
|
||||||
|
sudo ls -lR /etc/apt/
|
||||||
|
|
||||||
|
cat > /tmp/sources.list << EOF
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-backports main multiverse universe restricted
|
||||||
|
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main multiverse universe restricted
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo mv /etc/apt/sources.list /etc/apt/sources.list.default
|
||||||
|
sudo mv /tmp/sources.list /etc/apt/sources.list
|
||||||
|
|
||||||
|
sudo dpkg --add-architecture ${{ matrix.arch }}
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get -f install -y
|
||||||
|
|
||||||
|
sudo apt-get install -y \
|
||||||
|
linux-libc-dev:${{ matrix.arch }} \
|
||||||
|
libc6-dev:${{ matrix.arch }}
|
||||||
|
|
||||||
|
sudo apt-get install -y \
|
||||||
|
libxslt1.1:${{ matrix.arch }} \
|
||||||
|
libwebkit2gtk-4.1-dev:${{ matrix.arch }} \
|
||||||
|
libayatana-appindicator3-dev:${{ matrix.arch }} \
|
||||||
|
libssl-dev:${{ matrix.arch }} \
|
||||||
|
patchelf:${{ matrix.arch }} \
|
||||||
|
librsvg2-dev:${{ matrix.arch }}
|
||||||
|
|
||||||
|
- name: Install aarch64 tools
|
||||||
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||||
|
run: |
|
||||||
|
sudo apt install -y \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
g++-aarch64-linux-gnu
|
||||||
|
|
||||||
|
- name: Install armv7 tools
|
||||||
|
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
|
||||||
|
run: |
|
||||||
|
sudo apt install -y \
|
||||||
|
gcc-arm-linux-gnueabihf \
|
||||||
|
g++-arm-linux-gnueabihf
|
||||||
|
|
||||||
|
- name: Build for Linux
|
||||||
|
run: |
|
||||||
|
export PKG_CONFIG_ALLOW_CROSS=1
|
||||||
|
if [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then
|
||||||
|
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
|
||||||
|
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/
|
||||||
|
elif [ "${{ matrix.target }}" == "armv7-unknown-linux-gnueabihf" ]; then
|
||||||
|
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/:$PKG_CONFIG_PATH
|
||||||
|
export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf/
|
||||||
|
fi
|
||||||
|
pnpm build --target ${{ matrix.target }}
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Get Version
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install jq
|
||||||
|
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
|
||||||
|
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Upload Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ env.TAG_NAME }}
|
||||||
|
name: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
|
||||||
|
prerelease: true
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
files: |
|
||||||
|
src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
|
||||||
|
src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm
|
||||||
|
|
||||||
|
autobuild-x86-arm-windows_webview2:
|
||||||
|
name: Autobuild x86 and ARM Windows with WebView2
|
||||||
|
needs: update_tag
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
arch: x64
|
||||||
|
- os: windows-latest
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
|
arch: arm64
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Add Rust Target
|
||||||
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: src-tauri
|
||||||
|
cache-all-crates: true
|
||||||
|
save-if: ${{ github.ref == 'refs/heads/dev' }}
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Release ${{ env.TAG_CHANNEL }} Version
|
||||||
|
run: pnpm release-version ${{ env.TAG_NAME }}
|
||||||
|
|
||||||
|
- name: Download WebView2 Runtime
|
||||||
|
run: |
|
||||||
|
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/109.0.1518.78/Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab
|
||||||
|
Expand .\Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -F:* ./src-tauri
|
||||||
|
Remove-Item .\src-tauri\tauri.windows.conf.json
|
||||||
|
Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json
|
||||||
|
|
||||||
|
- name: Tauri build
|
||||||
|
id: build
|
||||||
|
uses: tauri-apps/tauri-action@v0
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
with:
|
||||||
|
tauriScript: pnpm
|
||||||
|
args: --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rename
|
||||||
|
run: |
|
||||||
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
|
||||||
|
Rename-Item $file.FullName $newName
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
|
||||||
|
Rename-Item $file.FullName $newName
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
|
||||||
|
Rename-Item $file.FullName $newName
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Upload Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ env.TAG_NAME }}
|
||||||
|
name: "Clash Verge Rev ${{ env.TAG_CHANNEL }}"
|
||||||
|
prerelease: true
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
|
||||||
|
|
||||||
|
- name: Portable Bundle
|
||||||
|
run: pnpm portable-fixed-webview2 ${{ matrix.target }} --${{ env.TAG_NAME }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
63
.github/workflows/clippy.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
name: Clippy Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
clippy:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust Stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Add Rust Target
|
||||||
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
|
- name: Install dependencies (ubuntu only)
|
||||||
|
if: matrix.os == 'ubuntu-22.04'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Build Web Assets
|
||||||
|
run: pnpm run web:build
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
|
||||||
|
- name: Run Clippy
|
||||||
|
run: cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets --all-features -- -D warnings
|
||||||
64
.github/workflows/cross_check.yaml
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
name: Cross Platform Cargo Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
# pull_request:
|
||||||
|
# push:
|
||||||
|
# branches: [main, dev]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cargo-check:
|
||||||
|
# Treat all Rust compiler warnings as errors
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: "-D warnings"
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust Stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Add Rust Target
|
||||||
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
|
- name: Cargo Check (deny warnings)
|
||||||
|
working-directory: src-tauri
|
||||||
|
run: |
|
||||||
|
cargo check --target ${{ matrix.target }} --workspace --all-features
|
||||||
93
.github/workflows/dev.yml
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
name: Development Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
permissions: write-all
|
||||||
|
env:
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
RUST_BACKTRACE: short
|
||||||
|
concurrency:
|
||||||
|
# only allow per workflow per commit (and not pr) to run at a time
|
||||||
|
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
|
||||||
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dev:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
bundle: nsis
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
bundle: dmg
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
bundle: dmg
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust Stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Add Rust Target
|
||||||
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Tauri build
|
||||||
|
uses: tauri-apps/tauri-action@v0
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
with:
|
||||||
|
tauriScript: pnpm
|
||||||
|
args: --target ${{ matrix.target }} -b ${{ matrix.bundle }}
|
||||||
|
|
||||||
|
- name: Upload Artifacts
|
||||||
|
if: matrix.os == 'macos-latest'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.target }}
|
||||||
|
path: src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Upload Artifacts
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.target }}
|
||||||
|
path: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*.exe
|
||||||
|
if-no-files-found: error
|
||||||
50
.github/workflows/fmt.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
name: Check Formatting
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rustfmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: install Rust stable and rustfmt
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- name: run cargo fmt
|
||||||
|
run: cargo fmt --manifest-path ./src-tauri/Cargo.toml --all -- --check
|
||||||
|
|
||||||
|
prettier:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: npm i -g --force corepack
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "lts/*"
|
||||||
|
- run: pnpm i --frozen-lockfile
|
||||||
|
- run: pnpm format:check
|
||||||
|
|
||||||
|
# taplo:
|
||||||
|
# name: taplo (.toml files)
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# - name: install Rust stable
|
||||||
|
# uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
# - name: install taplo-cli
|
||||||
|
# uses: taiki-e/install-action@v2
|
||||||
|
# with:
|
||||||
|
# tool: taplo-cli
|
||||||
|
|
||||||
|
# - run: taplo fmt --check --diff
|
||||||
352
.github/workflows/release.yml
vendored
@@ -1,14 +1,48 @@
|
|||||||
name: Release Build
|
name: Release Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
# ! 为了避免重复发布版本,应当通过独特 git tag 触发。
|
||||||
|
# ! 不再使用 workflow_dispatch 触发。
|
||||||
|
# workflow_dispatch:
|
||||||
|
push:
|
||||||
|
# 应当限制在 main 分支上触发发布。
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
# 应当限制 v*.*.* 的 tag 触发发布。
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
env:
|
env:
|
||||||
CARGO_INCREMENTAL: 0
|
CARGO_INCREMENTAL: 0
|
||||||
RUST_BACKTRACE: short
|
RUST_BACKTRACE: short
|
||||||
|
concurrency:
|
||||||
|
# only allow per workflow per commit (and not pr) to run at a time
|
||||||
|
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
|
||||||
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
check_tag_version:
|
||||||
|
name: Check Release Tag and package.json Version Consistency
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check tag and package.json version
|
||||||
|
run: |
|
||||||
|
TAG_REF="${GITHUB_REF##*/}"
|
||||||
|
echo "Current tag: $TAG_REF"
|
||||||
|
PKG_VERSION=$(jq -r .version package.json)
|
||||||
|
echo "package.json version: $PKG_VERSION"
|
||||||
|
if [[ "$TAG_REF" != "v$PKG_VERSION" ]]; then
|
||||||
|
echo "Tag ($TAG_REF) does not match package.json version (v$PKG_VERSION)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Tag and package.json version are consistent."
|
||||||
|
|
||||||
release:
|
release:
|
||||||
|
name: Release Build
|
||||||
|
needs: check_tag_version
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -21,6 +55,8 @@ jobs:
|
|||||||
target: aarch64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
target: x86_64-apple-darwin
|
target: x86_64-apple-darwin
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
@@ -37,111 +73,281 @@ jobs:
|
|||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: src-tauri
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
|
- name: Install dependencies (ubuntu only)
|
||||||
|
if: matrix.os == 'ubuntu-22.04'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||||
|
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "22"
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
- uses: pnpm/action-setup@v4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9
|
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Pnpm install and check
|
- name: Pnpm install and check
|
||||||
run: |
|
run: |
|
||||||
pnpm i
|
pnpm i
|
||||||
pnpm check ${{ matrix.target }}
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Tauri build
|
- name: Tauri build
|
||||||
uses: tauri-apps/tauri-action@v0
|
uses: tauri-apps/tauri-action@v0
|
||||||
env:
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
with:
|
with:
|
||||||
tagName: v__VERSION__
|
tagName: v__VERSION__
|
||||||
releaseName: "Clash Verge Rev v__VERSION__"
|
releaseName: "Clash Verge Rev v__VERSION__"
|
||||||
releaseBody: "More new features are now supported."
|
releaseBody: "More new features are now supported."
|
||||||
releaseDraft: false
|
|
||||||
prerelease: false
|
|
||||||
tauriScript: pnpm
|
tauriScript: pnpm
|
||||||
args: --target ${{ matrix.target }}
|
args: --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Portable Bundle
|
release-for-linux-arm:
|
||||||
if: matrix.os == 'windows-latest'
|
name: Release Build for Linux ARM
|
||||||
run: pnpm portable ${{ matrix.target }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
|
||||||
|
|
||||||
release-for-linux:
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-22.04
|
||||||
target: x86_64-unknown-linux-gnu
|
|
||||||
- os: ubuntu-latest
|
|
||||||
target: aarch64-unknown-linux-gnu
|
target: aarch64-unknown-linux-gnu
|
||||||
- os: ubuntu-latest
|
arch: arm64
|
||||||
|
- os: ubuntu-22.04
|
||||||
target: armv7-unknown-linux-gnueabihf
|
target: armv7-unknown-linux-gnueabihf
|
||||||
|
arch: armhf
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build for Linux
|
- name: Install Rust Stable
|
||||||
uses: ./.github/build-for-linux
|
uses: dtolnay/rust-toolchain@stable
|
||||||
env:
|
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
- name: Add Rust Target
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: "Setup for linux"
|
||||||
|
run: |-
|
||||||
|
sudo ls -lR /etc/apt/
|
||||||
|
|
||||||
|
cat > /tmp/sources.list << EOF
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main multiverse universe restricted
|
||||||
|
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-backports main multiverse universe restricted
|
||||||
|
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main multiverse universe restricted
|
||||||
|
deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main multiverse universe restricted
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo mv /etc/apt/sources.list /etc/apt/sources.list.default
|
||||||
|
sudo mv /tmp/sources.list /etc/apt/sources.list
|
||||||
|
|
||||||
|
sudo dpkg --add-architecture ${{ matrix.arch }}
|
||||||
|
sudo apt update
|
||||||
|
|
||||||
|
sudo apt install -y \
|
||||||
|
libxslt1.1:${{ matrix.arch }} \
|
||||||
|
libwebkit2gtk-4.1-dev:${{ matrix.arch }} \
|
||||||
|
libayatana-appindicator3-dev:${{ matrix.arch }} \
|
||||||
|
libssl-dev:${{ matrix.arch }} \
|
||||||
|
patchelf:${{ matrix.arch }} \
|
||||||
|
librsvg2-dev:${{ matrix.arch }}
|
||||||
|
|
||||||
|
- name: "Install aarch64 tools"
|
||||||
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||||
|
run: |
|
||||||
|
sudo apt install -y \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
g++-aarch64-linux-gnu
|
||||||
|
|
||||||
|
- name: "Install armv7 tools"
|
||||||
|
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
|
||||||
|
run: |
|
||||||
|
sudo apt install -y \
|
||||||
|
gcc-arm-linux-gnueabihf \
|
||||||
|
g++-arm-linux-gnueabihf
|
||||||
|
|
||||||
|
- name: Build for Linux
|
||||||
|
run: |
|
||||||
|
export PKG_CONFIG_ALLOW_CROSS=1
|
||||||
|
if [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then
|
||||||
|
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
|
||||||
|
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/
|
||||||
|
elif [ "${{ matrix.target }}" == "armv7-unknown-linux-gnueabihf" ]; then
|
||||||
|
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/:$PKG_CONFIG_PATH
|
||||||
|
export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf/
|
||||||
|
fi
|
||||||
|
pnpm build --target ${{ matrix.target }}
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
|
||||||
- name: Get Version
|
- name: Get Version
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install jq
|
sudo apt-get install jq
|
||||||
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
|
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
|
||||||
|
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Upload Release
|
- name: Upload Release
|
||||||
if: startsWith(matrix.target, 'x86_64')
|
uses: softprops/action-gh-release@v2
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
with:
|
||||||
tag_name: v${{env.VERSION}}
|
tag_name: v${{env.VERSION}}
|
||||||
name: "Clash Verge Rev v${{env.VERSION}}"
|
name: "Clash Verge Rev v${{env.VERSION}}"
|
||||||
body: "More new features are now supported."
|
body: "More new features are now supported."
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
files: src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage*
|
files: |
|
||||||
|
src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
|
||||||
|
src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm
|
||||||
|
|
||||||
- name: Upload Release
|
release-for-fixed-webview2:
|
||||||
uses: softprops/action-gh-release@v1
|
name: Release Build for Fixed WebView2
|
||||||
with:
|
strategy:
|
||||||
tag_name: v${{env.VERSION}}
|
fail-fast: false
|
||||||
name: "Clash Verge Rev v${{env.VERSION}}"
|
matrix:
|
||||||
body: "More new features are now supported."
|
include:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
- os: windows-latest
|
||||||
files: src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
|
target: x86_64-pc-windows-msvc
|
||||||
|
arch: x64
|
||||||
release-update:
|
- os: windows-latest
|
||||||
runs-on: ubuntu-latest
|
target: aarch64-pc-windows-msvc
|
||||||
needs: [release, release-for-linux]
|
arch: arm64
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Add Rust Target
|
||||||
|
run: rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: src-tauri
|
||||||
|
save-if: false
|
||||||
|
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "22"
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v4
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install and check
|
||||||
|
run: |
|
||||||
|
pnpm i
|
||||||
|
pnpm run prebuild ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Download WebView2 Runtime
|
||||||
|
run: |
|
||||||
|
invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/109.0.1518.78/Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab
|
||||||
|
Expand .\Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -F:* ./src-tauri
|
||||||
|
Remove-Item .\src-tauri\tauri.windows.conf.json
|
||||||
|
Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json
|
||||||
|
|
||||||
|
- name: Tauri build
|
||||||
|
id: build
|
||||||
|
uses: tauri-apps/tauri-action@v0
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
|
with:
|
||||||
|
tauriScript: pnpm
|
||||||
|
args: --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Rename
|
||||||
|
run: |
|
||||||
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
|
||||||
|
Rename-Item $file.FullName $newName
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
|
||||||
|
Rename-Item $file.FullName $newName
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
|
||||||
|
Rename-Item $file.FullName $newName
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Upload Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: v${{steps.build.outputs.appVersion}}
|
||||||
|
name: "Clash Verge Rev v${{steps.build.outputs.appVersion}}"
|
||||||
|
body: "More new features are now supported."
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup*
|
||||||
|
|
||||||
|
- name: Portable Bundle
|
||||||
|
run: pnpm portable-fixed-webview2 ${{ matrix.target }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-update:
|
||||||
|
name: Release Update
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [release, release-for-linux-arm]
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 8
|
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Pnpm install
|
- name: Pnpm install
|
||||||
@@ -151,3 +357,51 @@ jobs:
|
|||||||
run: pnpm updater
|
run: pnpm updater
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-update-for-fixed-webview2:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [release-for-fixed-webview2]
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install
|
||||||
|
run: pnpm i
|
||||||
|
|
||||||
|
- name: Release updater file
|
||||||
|
run: pnpm updater-fixed-webview2
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
submit-to-winget:
|
||||||
|
name: Submit to Winget
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [release-update]
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Get Version
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install jq
|
||||||
|
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
|
||||||
|
- name: Submit to Winget
|
||||||
|
uses: vedantmgoyal9/winget-releaser@main
|
||||||
|
with:
|
||||||
|
identifier: ClashVergeRev.ClashVergeRev
|
||||||
|
version: ${{env.VERSION}}
|
||||||
|
release-tag: v${{env.VERSION}}
|
||||||
|
installers-regex: '_(arm64|x64|x86)-setup\.exe$'
|
||||||
|
token: ${{ secrets.WINGET_TOKEN }}
|
||||||
|
|||||||
33
.github/workflows/updater.yml
vendored
@@ -7,17 +7,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "22"
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 8
|
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Pnpm install
|
- name: Pnpm install
|
||||||
@@ -27,3 +26,27 @@ jobs:
|
|||||||
run: pnpm updater
|
run: pnpm updater
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-update-for-fixed-webview2:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
name: Install pnpm
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Pnpm install
|
||||||
|
run: pnpm i
|
||||||
|
|
||||||
|
- name: Release updater file
|
||||||
|
run: pnpm updater-fixed-webview2
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
4
.gitignore
vendored
@@ -7,4 +7,6 @@ dist-ssr
|
|||||||
update.json
|
update.json
|
||||||
scripts/_env.sh
|
scripts/_env.sh
|
||||||
.vscode
|
.vscode
|
||||||
.tool-versions
|
.tool-versions
|
||||||
|
.idea
|
||||||
|
.old
|
||||||
|
|||||||
@@ -1,4 +1,26 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
pnpm pretty-quick --staged
|
#pnpm pretty-quick --staged
|
||||||
|
|
||||||
|
if git diff --cached --name-only | grep -q '^src/'; then
|
||||||
|
pnpm format:check
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Code format check failed in src/. Please fix formatting issues."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git diff --cached --name-only | grep -q '^src-tauri/'; then
|
||||||
|
cd src-tauri
|
||||||
|
cargo fmt
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "rustfmt failed to format the code. Please fix the issues and try again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
fi
|
||||||
|
|
||||||
|
#git add .
|
||||||
|
|
||||||
|
# 允许提交
|
||||||
|
exit 0
|
||||||
|
|||||||
27
.husky/pre-push
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if git diff --cached --name-only | grep -q '^src-tauri/'; then
|
||||||
|
cargo clippy --manifest-path ./src-tauri/Cargo.toml
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Clippy found issues in src-tauri. Please fix them before pushing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
remote_name="$1"
|
||||||
|
remote_url=$(git remote get-url "$remote_name")
|
||||||
|
|
||||||
|
if [[ "$remote_url" =~ github\.com[:/]+clash-verge-rev/clash-verge-rev(\.git)?$ ]]; then
|
||||||
|
echo "[pre-push] Detected push to clash-verge-rev/clash-verge-rev ($remote_url)"
|
||||||
|
echo "[pre-push] Running pnpm format:check..."
|
||||||
|
|
||||||
|
pnpm format:check
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "❌ Code format check failed. Please fix formatting before pushing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "[pre-push] Not pushing to target repo. Skipping format check."
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
8
.prettierignore
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# README.md
|
||||||
|
# UPDATELOG.md
|
||||||
|
# CONTRIBUTING.md
|
||||||
|
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
src-tauri/target/
|
||||||
|
src-tauri/gen/
|
||||||
6
.prettierrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": false,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"experimentalOperatorPosition": "start"
|
||||||
|
}
|
||||||
@@ -17,25 +17,46 @@ If you're a Windows user, you may need to perform some additional steps:
|
|||||||
- Make sure to add Rust and Node.js to your system's PATH. This is usually done during the installation process, but you can verify and manually add them if necessary.
|
- Make sure to add Rust and Node.js to your system's PATH. This is usually done during the installation process, but you can verify and manually add them if necessary.
|
||||||
- The gnu `patch` tool should be installed
|
- The gnu `patch` tool should be installed
|
||||||
|
|
||||||
### Install Node.js Packages
|
When you setup `Rust` environment, Only use toolchain with `Windows MSVC` , to change settings follow command:
|
||||||
|
|
||||||
After installing Rust and Node.js, install the necessary Node.js packages:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pnpm i
|
rustup target add x86_64-pc-windows-msvc
|
||||||
|
rustup set default-host x86_64-pc-windows-msvc
|
||||||
```
|
```
|
||||||
|
|
||||||
### Download the Clash Binary
|
### Install Node.js Package
|
||||||
|
|
||||||
|
After installing Rust and Node.js, install the necessary Node.js and Node Package Manager:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm install pnpm -g
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Dependencies
|
||||||
|
|
||||||
|
Install node packages
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Install apt packages ONLY for Ubuntu
|
||||||
|
|
||||||
|
```shell
|
||||||
|
apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download the Mihomo Core Binary
|
||||||
|
|
||||||
You have two options for downloading the clash binary:
|
You have two options for downloading the clash binary:
|
||||||
|
|
||||||
- Automatically download it via the provided script:
|
- Automatically download it via the provided script:
|
||||||
```shell
|
```shell
|
||||||
pnpm run check
|
pnpm run prebuild
|
||||||
# Use '--force' to force update to the latest version
|
# Use '--force' to force update to the latest version
|
||||||
# pnpm run check --force
|
# pnpm run prebuild --force
|
||||||
```
|
```
|
||||||
- Manually download it from the [Clash Meta release](https://github.com/MetaCubeX/Clash.Meta/releases). After downloading, rename the binary according to the [Tauri configuration](https://tauri.app/v1/api/config#bundleconfig.externalbin).
|
- Manually download it from the [Mihomo release](https://github.com/MetaCubeX/mihomo/releases). After downloading, rename the binary according to the [Tauri configuration](https://tauri.app/v1/api/config#bundleconfig.externalbin).
|
||||||
|
|
||||||
### Run the Development Server
|
### Run the Development Server
|
||||||
|
|
||||||
@@ -49,14 +70,63 @@ pnpm dev:diff
|
|||||||
|
|
||||||
### Build the Project
|
### Build the Project
|
||||||
|
|
||||||
If you want to build the project, use:
|
To build this project:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pnpm build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For a faster build, use the following command
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pnpm build:fast
|
||||||
|
```
|
||||||
|
|
||||||
|
This uses Rust's fast-release profile which significantly reduces compilation time by disabling optimization and LTO. The resulting binary will be larger and less performant than the standard build, but it's useful for testing changes quickly.
|
||||||
|
|
||||||
|
The `Artifacts` will display in the `log` in the Terminal.
|
||||||
|
|
||||||
|
### Build clean
|
||||||
|
|
||||||
|
To clean rust build:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pnpm clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### Portable Version (Windows Only)
|
||||||
|
|
||||||
|
To package portable version after the build:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pnpm portable
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing Your Changes
|
## Contributing Your Changes
|
||||||
|
|
||||||
|
#### Before commit your changes
|
||||||
|
|
||||||
|
If you changed the rust code, it's recommanded to execute code style formatting and quailty checks.
|
||||||
|
|
||||||
|
1. Code quailty checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For rust backend
|
||||||
|
$ clash-verge-rev: pnpm clippy
|
||||||
|
# For frontend (not yet).
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Code style formatting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For rust backend
|
||||||
|
$ clash-verge-rev: cd src-tauri
|
||||||
|
$ clash-verge-rev/src-tauri: cargo fmt
|
||||||
|
# For frontend
|
||||||
|
$ clash-verge-rev: pnpm format:check
|
||||||
|
$ clash-verge-rev: pnpm format
|
||||||
|
```
|
||||||
|
|
||||||
Once you have made your changes:
|
Once you have made your changes:
|
||||||
|
|
||||||
1. Fork the repository.
|
1. Fork the repository.
|
||||||
|
|||||||
57
README.md
@@ -1,5 +1,5 @@
|
|||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<img src="./src/assets/image/logo.png" alt="Clash" width="128" />
|
<img src="./src-tauri/icons/icon.png" alt="Clash" width="128" />
|
||||||
<br>
|
<br>
|
||||||
Continuation of <a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>
|
Continuation of <a href="https://github.com/zzzgydi/clash-verge">Clash Verge</a>
|
||||||
<br>
|
<br>
|
||||||
@@ -11,45 +11,74 @@ A Clash Meta GUI based on <a href="https://github.com/tauri-apps/tauri">Tauri</a
|
|||||||
|
|
||||||
## Preview
|
## Preview
|
||||||
|
|
||||||

|
| Dark | Light |
|
||||||
|
| -------------------------------- | --------------------------------- |
|
||||||
|
|  |  |
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
请到发布页面下载对应的安装包:[Release page](https://github.com/clash-verge-rev/clash-verge-rev/releases)<br>
|
请到发布页面下载对应的安装包:[Release page](https://github.com/clash-verge-rev/clash-verge-rev/releases)<br>
|
||||||
Go to the [release page](https://github.com/clash-verge-rev/clash-verge-rev/releases) to download the corresponding installation package<br>
|
Go to the [Release page](https://github.com/clash-verge-rev/clash-verge-rev/releases) to download the corresponding installation package<br>
|
||||||
Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
|
Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
|
||||||
|
|
||||||
### 安装说明和常见问题,请到[文档页](https://clash-verge-rev.github.io/)查看:[Doc](https://clash-verge-rev.github.io/)
|
#### 我应当怎样选择发行版
|
||||||
|
|
||||||
|
| 版本 | 特征 | 链接 |
|
||||||
|
| :-------- | :--------------------------------------- | :------------------------------------------------------------------------------------- |
|
||||||
|
| Stable | 正式版,高可靠性,适合日常使用。 | [Release](https://github.com/clash-verge-rev/clash-verge-rev/releases) |
|
||||||
|
| Alpha | 早期测试版,功能未完善,可能存在缺陷。 | [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) |
|
||||||
|
| AutoBuild | 滚动更新版,持续集成更新,适合开发测试。 | [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) |
|
||||||
|
|
||||||
|
#### 安装说明和常见问题,请到 [文档页](https://clash-verge-rev.github.io/) 查看
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### TG Group: [@clash_verge_rev](https://t.me/clash_verge_rev)
|
### TG 频道: [@clash_verge_rev](https://t.me/clash_verge_re)
|
||||||
|
|
||||||
## Promotion
|
## Promotion
|
||||||
|
|
||||||
[狗狗加速 —— 技术流机场 Doggygo VPN](https://狗狗加速.com)
|
#### [狗狗加速 —— 技术流机场 Doggygo VPN](https://verge.dginv.click/#/register?code=oaxsAGo6)
|
||||||
|
|
||||||
- 高性能海外机场,免费试用,优惠套餐,解锁流媒体,全球首家支持 Hysteria 协议。
|
- 高性能海外机场,免费试用,优惠套餐,解锁流媒体,全球首家支持 Hysteria 协议。
|
||||||
- 使用 Clash Verge 专属邀请链接注册送 3 天,每天 1G 流量免费试用:https://verge.狗狗加速.com/#/register?code=oaxsAGo6
|
- 使用 Clash Verge 专属邀请链接注册送 3 天,每天 1G 流量免费试用:[点此注册](https://verge.dginv.click/#/register?code=oaxsAGo6)
|
||||||
- Clash Verge 专属 8 折优惠码: verge20 (仅有 500 份)
|
- Clash Verge 专属 8 折优惠码: verge20 (仅有 500 份)
|
||||||
- 优惠套餐每月仅需 15.8 元,160G 流量,年付 8 折
|
- 优惠套餐每月仅需 15.8 元,160G 流量,年付 8 折
|
||||||
- 海外团队,无跑路风险,高达 50% 返佣
|
- 海外团队,无跑路风险,高达 50% 返佣
|
||||||
- 集群负载均衡设计,高速专线(兼容老客户端),极低延迟,无视晚高峰,4K 秒开
|
- 集群负载均衡设计,高速专线(兼容老客户端),极低延迟,无视晚高峰,4K 秒开
|
||||||
- 全球首家 Hysteria 协议机场,现已上线更快的 `Hysteria2` 协议(Clash Verge 客户端最佳搭配)
|
- 全球首家 Hysteria 协议机场,现已上线更快的 `Hysteria2` 协议(Clash Verge 客户端最佳搭配)
|
||||||
- 解锁流媒体及 ChatGPT
|
- 解锁流媒体及 ChatGPT
|
||||||
- 官网:https://狗狗加速.com
|
- 官网:[https://狗狗加速.com](https://verge.dginv.click/#/register?code=oaxsAGo6)
|
||||||
|
|
||||||
|
#### 本项目的构建与发布环境由 [YXVM](https://yxvm.com/aff.php?aff=827) 独立服务器全力支持,
|
||||||
|
|
||||||
|
感谢提供 独享资源、高性能、高速网络 的强大后端环境。如果你觉得下载够快、使用够爽,那是因为我们用了好服务器!
|
||||||
|
|
||||||
|
🧩 YXVM 独立服务器优势:
|
||||||
|
|
||||||
|
- 🌎 优质网络,回程优化,下载快到飞起
|
||||||
|
- 🔧 物理机独享资源,非VPS可比,性能拉满
|
||||||
|
- 🧠 适合跑代理、搭建 WEB 站 CDN 站 、搞 CI/CD 或任何高负载应用
|
||||||
|
- 💡 支持即开即用,多机房选择,CN2 / IEPL 可选
|
||||||
|
- 📦 本项目使用配置已在售,欢迎同款入手!
|
||||||
|
- 🎯 想要同款构建体验?[立即下单 YXVM 独立服务器!](https://yxvm.com/aff.php?aff=827)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Since the clash core has been removed. The project no longer maintains the clash core, but only the Clash Meta core.
|
- 基于性能强劲的 Rust 和 Tauri 2 框架
|
||||||
- Profiles management and enhancement (by yaml and Javascript). [Doc](https://clash-verge-rev.github.io)
|
- 内置[Clash.Meta(mihomo)](https://github.com/MetaCubeX/mihomo)内核,并支持切换 `Alpha` 版本内核。
|
||||||
- Improved UI and supports custom theme color.
|
- 简洁美观的用户界面,支持自定义主题颜色、代理组/托盘图标以及 `CSS Injection`。
|
||||||
- Built-in support [Clash.Meta(mihomo)](https://github.com/MetaCubeX/mihomo) core.
|
- 配置文件管理和增强(Merge 和 Script),配置文件语法提示。
|
||||||
- System proxy setting and guard.
|
- 系统代理和守卫、`TUN(虚拟网卡)` 模式。
|
||||||
|
- 可视化节点和规则编辑
|
||||||
|
- WebDav 配置备份和同步
|
||||||
|
|
||||||
### FAQ
|
### FAQ
|
||||||
|
|
||||||
Refer to [Doc FAQ Page](https://clash-verge-rev.github.io/faq/install/)
|
Refer to [Doc FAQ Page](https://clash-verge-rev.github.io/faq/windows.html)
|
||||||
|
|
||||||
|
### Donation
|
||||||
|
|
||||||
|
[捐助Clash Verge Rev的开发](https://github.com/sponsors/clash-verge-rev)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|||||||
848
UPDATELOG.md
@@ -1,3 +1,851 @@
|
|||||||
|
## v2.3.1
|
||||||
|
|
||||||
|
### 🐞 修复问题
|
||||||
|
|
||||||
|
- 增加配置文件校验,修复从古老版本升级上来的"No such file or directory (os error 2)"错误
|
||||||
|
- 修复扩展脚本转义错误
|
||||||
|
- 修复 macOS Intel X86 架构构建错误导致无法运行
|
||||||
|
- 修复 Linux 下界面边框白边问题
|
||||||
|
- 修复 托盘 无响应问题
|
||||||
|
- 修复 托盘 无法从轻量模式退出并恢复窗口
|
||||||
|
- 修复 快速切换订阅可能导致的卡死问题
|
||||||
|
|
||||||
|
### ✨ 新增功能
|
||||||
|
|
||||||
|
- 新增 window-state 窗口状态管理和恢复
|
||||||
|
|
||||||
|
### 🚀 优化改进
|
||||||
|
|
||||||
|
- 优化 托盘 统一响应
|
||||||
|
- 优化 静默启动+自启动轻量模式 运行方式
|
||||||
|
- 升级依赖
|
||||||
|
|
||||||
|
## v2.3.0
|
||||||
|
|
||||||
|
**发行代号:御**
|
||||||
|
代号释义: 「御」,象征掌控与守护,寓意本次版本对系统稳定性、安全性与用户体验的全面驾驭与提升。
|
||||||
|
|
||||||
|
尽管 `external-controller` 密钥现已自动补全默认值且不允许为空,**仍建议手动修改密钥以提高安全性**。
|
||||||
|
|
||||||
|
### ⚠️ 已知问题
|
||||||
|
|
||||||
|
- 仅在 Ubuntu 22.04/24.04、Fedora 41 的 **GNOME 桌面环境** 做过简单测试,不保证其他 Linux 发行版兼容,后续将逐步适配和优化。
|
||||||
|
- macOS:
|
||||||
|
|
||||||
|
- MacOS 下自动升级成功后请关闭程序等待 30 秒重启,因为 MacOS 的端口释放特性,卸载服务后需重启应用等 30 秒才能恢复内核通信。立即启动可能无法正常启动内核。
|
||||||
|
- 墙贴主要为浅色,深色 Tray 图标存在闪烁问题;
|
||||||
|
- 彩色 Tray 图标颜色偏淡;
|
||||||
|
|
||||||
|
- 已确认窗口状态管理器存在上游缺陷,已暂时移除窗口大小与位置记忆功能。
|
||||||
|
|
||||||
|
### 🐞 修复问题
|
||||||
|
|
||||||
|
- 修复首页“代理模式”快速切换导致的卡死问题
|
||||||
|
- 修复 MacOS 快捷键关闭窗口无法启用自动轻量模式
|
||||||
|
- 修复静默启动异常窗口的创建与关闭流程
|
||||||
|
- 修复 Windows 下错误注册的全局快捷键 `Ctrl+Q`
|
||||||
|
- 修复解锁测试报错信息与 VLESS URL 解码时的网络类型错误
|
||||||
|
- 修复切换自定义代理地址后系统代理状态异常
|
||||||
|
- 修复 macOS TUN 默认无效网卡名称
|
||||||
|
- 修复更改订阅后托盘 UI 不同步的问题
|
||||||
|
- 修复服务模式安装后无法立即开启 TUN 模式
|
||||||
|
- 修复无法删除 `.window-state.json`
|
||||||
|
- 修复无法修改配置更新 HTTP 请求超时问题
|
||||||
|
- 修复 `getDelayFix` 钩子异常
|
||||||
|
- 修复外部扩展脚本覆写代理组时首页无法显示代理组
|
||||||
|
- 修复 Verge 导出诊断版本与设置页面不同步
|
||||||
|
- 修复切换语言时设置页面可能加载失败
|
||||||
|
- 修复编辑器中连字符处理问题
|
||||||
|
- 修复提权漏洞,改用带认证的 IPC 通信机制
|
||||||
|
- 修复静默启动无法使用自动轻量模式
|
||||||
|
- 修复 JS 脚本转义特殊字符报错
|
||||||
|
- 修复 macOS 静默启动时异常启动 Dock 栏图标
|
||||||
|
|
||||||
|
### ✨ 新增功能
|
||||||
|
|
||||||
|
- **Mihomo(Meta) 内核升级至 v1.19.10**
|
||||||
|
- 支持设置代理地址为非 `127.0.0.1`,提升 WSL 兼容性
|
||||||
|
- 系统代理守卫:可检测意外变更并自动恢复
|
||||||
|
- 托盘新增当前轻量模式状态显示
|
||||||
|
- 关闭系统代理时同时断开已建立的连接
|
||||||
|
- 新增 WebDAV 功能:
|
||||||
|
|
||||||
|
- 加入 UA 请求头
|
||||||
|
- 支持目录重定向
|
||||||
|
- 备份目录检查与上传重试机制
|
||||||
|
|
||||||
|
- 自动订阅更新机制:
|
||||||
|
|
||||||
|
- 加入请求超时机制防止卡死
|
||||||
|
- 支持在代理状态下自动重试订阅更新
|
||||||
|
- 支持订阅卡片点击切换下次自动更新时间,并显示更新结果提示
|
||||||
|
|
||||||
|
- DNS 设置新增 Hosts 配置功能
|
||||||
|
- 首页代理节点支持排序
|
||||||
|
- 支持服务模式手动卸载,回退至 Sidecar 模式
|
||||||
|
- 核心状态管理支持切换、升级、重启
|
||||||
|
- 配置加载阶段自动补全 `external-controller secret`
|
||||||
|
- 新增日志自动清理周期选项(含1天)
|
||||||
|
- 新增 Zashboard 一键跳转入口
|
||||||
|
- 使用系统默认窗口管理器
|
||||||
|
|
||||||
|
### 🚀 优化改进
|
||||||
|
|
||||||
|
- **系统相关:**
|
||||||
|
|
||||||
|
- 系统代理 Bypass 设置优化
|
||||||
|
- 优化代理设置更新逻辑与守卫机制
|
||||||
|
- Windows 启动方式调整为 Startup 文件夹,解决管理员模式下自启问题
|
||||||
|
|
||||||
|
- **性能与稳定性:**
|
||||||
|
|
||||||
|
- 全面异步化处理配置加载、UI 启动、事件通知等关键流程,解决卡顿问题
|
||||||
|
- 优化 MihomoManager 实现与窗口创建流程
|
||||||
|
- 改进内核日志等级为 `warn`,减少噪音输出
|
||||||
|
- 重构主进程与通知系统,提升响应性与分离度
|
||||||
|
- 优化网络请求与错误处理机制
|
||||||
|
- 添加网络管理器防止资源竞争引发 UI 卡死
|
||||||
|
- 优化配置文件加载内存使用
|
||||||
|
- 优化缓存 Mihomo proxy 和 providers 信息内存使用
|
||||||
|
|
||||||
|
- **前端与界面体验:**
|
||||||
|
|
||||||
|
- 切换规则页自动刷新数据
|
||||||
|
- 非激活订阅编辑时不再触发配置重载
|
||||||
|
- 优化托盘速率显示,macOS 下默认关闭
|
||||||
|
- Windows 快捷键名称更名为 `Clash Verge`
|
||||||
|
- 更新失败可回退至使用代理重试
|
||||||
|
- 支持异步端口查找与保存,端口支持随机生成
|
||||||
|
- 修改端口检测范围至 `1111-65536`
|
||||||
|
- 优化保存机制,使用平滑函数防止卡顿
|
||||||
|
|
||||||
|
- **配置增强与安全性:**
|
||||||
|
|
||||||
|
- 配置缺失 `secret` 字段时自动补全为 `set-your-secret`
|
||||||
|
- 强制为 Mihomo 配置补全 `external-controller-cors` 字段(默认不允许跨域,限制本地访问)计划后续支持自定义 cors
|
||||||
|
- 优化窗口权限设置与状态初始化逻辑
|
||||||
|
- 网络延迟测试替换为 HTTPS 协议:`https://cp.cloudflare.com/generate_204`
|
||||||
|
- 优化 IP 信息获取流程,添加去重机制与轮询检测算法
|
||||||
|
|
||||||
|
- 同步修复翻译错误与不一致项,优化整体语言体验
|
||||||
|
- 加强语言切换后的页面稳定性,避免加载异常
|
||||||
|
|
||||||
|
### 🗑️ 移除内容
|
||||||
|
|
||||||
|
- 窗口状态管理器(上游存在缺陷)
|
||||||
|
- WebDAV 跨平台备份恢复限制
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v2.2.3
|
||||||
|
|
||||||
|
#### 已知问题
|
||||||
|
|
||||||
|
- 仅在Ubuntu 22.04/24.04,Fedora 41 **Gnome桌面环境** 做过简单测试,不保证其他其他Linux发行版可用,将在未来做进一步适配和调优
|
||||||
|
- MacOS 自定义图标与速率显示推荐图标尺寸为 256x256。其他尺寸(可能)会导致不正常图标和速率间隙
|
||||||
|
- MacOS 下 墙贴主要为浅色,Tray 图标深色时图标闪烁;彩色 Tray 速率颜色淡
|
||||||
|
- Linux 下 Clash Verge Rev 内存占用显著高于 Windows / MacOS
|
||||||
|
|
||||||
|
### 2.2.3 相对于 2.2.2
|
||||||
|
|
||||||
|
#### 修复了:
|
||||||
|
|
||||||
|
- 首页“当前代理”因为重复刷新导致的CPU占用过高的问题
|
||||||
|
- “开机自启”和“DNS覆写”开关跳动问题
|
||||||
|
- 自定义托盘图标未能应用更改
|
||||||
|
- MacOS 自定义托盘图标显示速率时图标和文本间隙过大
|
||||||
|
- MacOS 托盘速率显示不全
|
||||||
|
- Linux 在系统服务模式下无法拉起 Mihomo 内核
|
||||||
|
- 使用异步操作,避免获取系统信息和切换代理模式可能带来的崩溃
|
||||||
|
- 相同节点名称可能导致的页面渲染出错
|
||||||
|
- URL Schemes被截断的问题
|
||||||
|
- 首页流量统计卡更好的时间戳范围
|
||||||
|
- 静默启动无法触发自动轻量化计时器
|
||||||
|
|
||||||
|
#### 新增了:
|
||||||
|
|
||||||
|
- Mihomo(Meta)内核升级至 1.19.4
|
||||||
|
- Clash Verge Rev 从现在开始不再强依赖系统服务和管理权限
|
||||||
|
- 支持根据用户偏好选择Sidecar(用户空间)模式或安装服务
|
||||||
|
- 增加载入初始配置文件的错误提示,防止切换到错误的订阅配置
|
||||||
|
- 检测是否以管理员模式运行软件,如果是提示无法使用开机自启
|
||||||
|
- 代理组显示节点数量
|
||||||
|
- 统一运行模式检测,支持管理员模式下开启TUN模式
|
||||||
|
- 托盘切换代理模式会根据设置自动断开之前连接
|
||||||
|
- 如订阅获取失败回退使用Clash内核代理再次尝试
|
||||||
|
|
||||||
|
#### 移除了:
|
||||||
|
|
||||||
|
- 实时保存窗口位置和大小。这个功能可能会导致窗口异常大小和位置,还需观察。
|
||||||
|
|
||||||
|
#### 优化了:
|
||||||
|
|
||||||
|
- 重构了后端内核管理逻辑,更轻量化和有效的管理内核,提高了性能和稳定性
|
||||||
|
- 前端统一刷新应用数据,优化数据获取和刷新逻辑
|
||||||
|
- 优化首页流量图表代码,调整图表文字边距
|
||||||
|
- MacOS 托盘速率更好的显示样式和更新逻辑
|
||||||
|
- 首页仅在有流量图表时显示流量图表区域
|
||||||
|
- 更新DNS默认覆写配置
|
||||||
|
- 移除测试目录,简化资源初始化逻辑
|
||||||
|
|
||||||
|
## v2.2.2
|
||||||
|
|
||||||
|
**发行代号:拓**
|
||||||
|
|
||||||
|
感谢 Tunglies 对 Verge 后端重构,性能优化做出的重大贡献!
|
||||||
|
|
||||||
|
代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验,DNS 覆写功能增强网络控制能力,解锁测试页面助力内容访问自由度提升,轻量模式提供灵活使用选择。此外,macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。
|
||||||
|
|
||||||
|
#### 已知问题
|
||||||
|
|
||||||
|
- 仅在Ubuntu 22.04/24.04,Fedora 41 **Gnome桌面环境** 做过简单测试,不保证其他其他Linux发行版可用,将在未来做进一步适配和调优
|
||||||
|
|
||||||
|
### 2.2.2 相对于 2.2.1(已下架不再提供)
|
||||||
|
|
||||||
|
#### 修复了:
|
||||||
|
|
||||||
|
- 弹黑框的问题(原因是服务崩溃触发重装机制)
|
||||||
|
- MacOS进入轻量模式以后隐藏Dock图标
|
||||||
|
- 增加轻量模式缺失的tray翻译
|
||||||
|
- Linux下的窗口边框被削掉的问题
|
||||||
|
|
||||||
|
#### 新增了:
|
||||||
|
|
||||||
|
- 加强服务检测和重装逻辑
|
||||||
|
- 增强内核与服务保活机制
|
||||||
|
- 增加服务模式下的僵尸进程清理机制
|
||||||
|
- 新增当服务模式多次尝试失败后自动回退至用户空间模式
|
||||||
|
|
||||||
|
### 2.2.1 相对于 2.2.0(已下架不再提供)
|
||||||
|
|
||||||
|
#### 修复了:
|
||||||
|
|
||||||
|
1. **首页**
|
||||||
|
- 修复 Direct 模式首页无法渲染
|
||||||
|
- 修复 首页启用轻量模式导致 ClashVergeRev 从托盘退出
|
||||||
|
- 修复 系统代理标识判断不准的问题
|
||||||
|
- 修复 系统代理地址错误的问题
|
||||||
|
- 代理模式“多余的切换动画”
|
||||||
|
2. **系统**
|
||||||
|
- 修复 MacOS 无法使用快捷键粘贴/选择/复制订阅地址。
|
||||||
|
- 修复 代理端口设置同步问题。
|
||||||
|
- 修复 Linux 无法与 Mihomo 核心 和 ClashVergeRev 服务通信
|
||||||
|
3. **界面**
|
||||||
|
- 修复 连接详情卡没有跟随主题色
|
||||||
|
4. **轻量模式**
|
||||||
|
- 修复 MacOS 轻量模式下 Dock 栏图标无法隐藏。
|
||||||
|
|
||||||
|
#### 新增了:
|
||||||
|
|
||||||
|
1. **首页**
|
||||||
|
- 首页文本过长自动截断
|
||||||
|
2. **轻量模式**
|
||||||
|
- 新增托盘进入轻量模式支持
|
||||||
|
- 新增进入轻量模式快捷键支持
|
||||||
|
3. **系统**
|
||||||
|
- 在 ClashVergeRev 对 Mihomo 进行操作时,总是尝试确保两者运行
|
||||||
|
- 服务器模式下启动mihomo内核的时候查找并停止其他已经存在的内核进程,防止内核假死等问题带来的通信失败
|
||||||
|
4. **托盘**
|
||||||
|
- 新增 MacOS 启用托盘速率显示时,可选隐藏托盘图标显示
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2.2.0(已下架不再提供)
|
||||||
|
|
||||||
|
#### 新增功能
|
||||||
|
|
||||||
|
1. **首页**
|
||||||
|
|
||||||
|
- 新增首页功能,默认启动页面改为首页。
|
||||||
|
- 首页流量图卡片显示上传/下载名称。
|
||||||
|
- 首页支持轻量模式切换。
|
||||||
|
- 流量统计数据持久保存。
|
||||||
|
- 限制首页配置文件卡片URL长度。
|
||||||
|
|
||||||
|
2. **DNS 设置与覆写**
|
||||||
|
|
||||||
|
- 新增 DNS 覆写功能。
|
||||||
|
- 默认启用 DNS 覆写。
|
||||||
|
|
||||||
|
3. **解锁测试**
|
||||||
|
|
||||||
|
- 新增解锁测试页面。
|
||||||
|
|
||||||
|
4. **轻量模式**
|
||||||
|
|
||||||
|
- 新增轻量模式及设置。
|
||||||
|
- 添加自动轻量模式定时器。
|
||||||
|
|
||||||
|
5. **系统支持**
|
||||||
|
|
||||||
|
- Mihomo(meta)内核升级 1.19.3
|
||||||
|
- macOS 支持 CMD+W 关闭窗口。
|
||||||
|
- 新增 macOS 应用菜单。
|
||||||
|
- 添加 macOS 安装服务时候的管理员权限提示。
|
||||||
|
- 新增 sidecar(用户空间启动内核) 模式。
|
||||||
|
|
||||||
|
6. **其他**
|
||||||
|
- 增强延迟测试日志和错误处理。
|
||||||
|
- 添加诊断信息导出。
|
||||||
|
- 新增代理命令。
|
||||||
|
|
||||||
|
#### 修复
|
||||||
|
|
||||||
|
1. **系统**
|
||||||
|
|
||||||
|
- 修复 Windows 热键崩溃。
|
||||||
|
- 修复 macOS 无框标题。
|
||||||
|
- 修复 macOS 静默启动崩溃。
|
||||||
|
- 修复 macOS tray图标错位到左上角的问题。
|
||||||
|
- 修复 Windows/Linux 运行时崩溃。
|
||||||
|
- 修复 Win10 阴影和边框问题。
|
||||||
|
- 修复 升级或重装后开机自启状态检测和同步问题。
|
||||||
|
|
||||||
|
2. **构建**
|
||||||
|
- 修复构建失败问题。
|
||||||
|
|
||||||
|
#### 优化
|
||||||
|
|
||||||
|
1. **性能**
|
||||||
|
|
||||||
|
- 重构后端,巨幅性能优化。
|
||||||
|
- 优化首页组件性能。
|
||||||
|
- 优化流量图表资源使用。
|
||||||
|
- 提升代理组列表滚动性能。
|
||||||
|
- 加快应用退出速度。
|
||||||
|
- 加快进入轻量模式速度。
|
||||||
|
- 优化小数值速度更新。
|
||||||
|
- 增加请求超时至 60 秒。
|
||||||
|
- 修复代理节点选择同步。
|
||||||
|
- 优化修改verge配置性能。
|
||||||
|
|
||||||
|
2. **重构**
|
||||||
|
|
||||||
|
- 重构后端,巨幅性能优化。
|
||||||
|
- 优化定时器管理。
|
||||||
|
- 重构 MihomoManager 处理流量。
|
||||||
|
- 优化 WebSocket 连接。
|
||||||
|
|
||||||
|
3. **其他**
|
||||||
|
- 更新依赖。
|
||||||
|
- 默认 TUN 堆栈改为 gvisor。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v2.1.2
|
||||||
|
|
||||||
|
**发行代号:臻**
|
||||||
|
|
||||||
|
代号释义: 千锤百炼臻至善,集性能跃升、功能拓展、交互焕新于一体,彰显持续打磨、全方位优化的迭代精神。
|
||||||
|
|
||||||
|
感谢 Tychristine 对社区群组管理做出的重大贡献!
|
||||||
|
|
||||||
|
##### 2.1.2相对2.1.1(已下架不再提供)更新了:
|
||||||
|
|
||||||
|
- 无法更新和签名验证失败的问题(该死的CDN缓存)
|
||||||
|
- 设置菜单区分Verge基本设置和高级设置
|
||||||
|
- 增加v2 Updater的更多功能和权限
|
||||||
|
- 退出Verge后Tun代理状态仍保留的问题
|
||||||
|
|
||||||
|
##### 2.1.1相对2.1.0(已下架不再提供)更新了:
|
||||||
|
|
||||||
|
- 检测所需的Clash Verge Service版本(杀毒软件误报可能与此有关,因为检测和安装新版本Service需管理员权限)
|
||||||
|
- MacOS下支持彩色托盘图标和更好速率显示(感谢Tunglies)
|
||||||
|
- 文件类型判断不准导致脚本检测报错的问题
|
||||||
|
- 打开Win下的阴影(Win10因底层兼容性问题,可能圆角和边框显示不太完美)
|
||||||
|
- 边框去白边
|
||||||
|
- 修复Linux下编译问题
|
||||||
|
- 修复热键无法关闭面板的问题
|
||||||
|
|
||||||
|
##### 2.1.0 - 发行代号:臻
|
||||||
|
|
||||||
|
### 功能新增
|
||||||
|
|
||||||
|
- 新增窗口状态实时监控与自动保存功能
|
||||||
|
- 增强核心配置变更时的验证与错误处理机制
|
||||||
|
- 支持通过环境变量`CLASH_VERGE_REV_IP`自定义复制IP地址
|
||||||
|
- 添加连接表列宽持久化设置与进程过滤功能
|
||||||
|
- 新增代理组首字母导航与动态滚动定位功能
|
||||||
|
- 实现连接追踪暂停/恢复功能
|
||||||
|
- 支持从托盘菜单快速切换代理配置
|
||||||
|
- 添加轻量级模式开关选项
|
||||||
|
- 允许用户自定义TUN模式增强类型和FakeIP范围
|
||||||
|
- 新增系统代理状态指示器
|
||||||
|
- 增加Alpha版本自动重命名逻辑
|
||||||
|
- 优化字母导航工具提示与防抖交互机制
|
||||||
|
|
||||||
|
### 性能优化
|
||||||
|
|
||||||
|
- 重构代理列表渲染逻辑,提升布局计算效率
|
||||||
|
- 优化代理数据更新机制,采用乐观UI策略
|
||||||
|
- 改进虚拟列表渲染性能(Virtuoso)
|
||||||
|
- 提升主窗口Clash模式切换速度(感谢Tunglies)
|
||||||
|
- 加速内核关闭流程并优化管理逻辑
|
||||||
|
- 优化节点延迟刷新速率
|
||||||
|
- 改进托盘网速显示更新逻辑
|
||||||
|
- 提升配置验证错误信息的可读性
|
||||||
|
- 重构服务架构,优化代码组织结构(感谢Tunglies)
|
||||||
|
- 优化内核启动时的配置验证流程
|
||||||
|
|
||||||
|
### 问题修复
|
||||||
|
|
||||||
|
- 修复删除节点时关联组信息残留问题
|
||||||
|
- 解决菜单切换异常与重复勾选问题
|
||||||
|
- 修正连接页流量计算错误
|
||||||
|
- 修复Windows圆角显示异常问题
|
||||||
|
- 解决控制台废弃API警告
|
||||||
|
- 修复全局热键空值导致的崩溃
|
||||||
|
- 修复Alpha版本Windows打包重命名问题
|
||||||
|
- 修复MacOS端口切换崩溃问题
|
||||||
|
- 解决Linux持续集成更新器问题
|
||||||
|
- 修复静默启动后热键失效问题
|
||||||
|
- 修正TypeScript代理组类型定义
|
||||||
|
- 修复Windows托盘图标空白问题
|
||||||
|
- 优化远程目标地址显示(替换旧版IP展示)
|
||||||
|
|
||||||
|
### 交互体验
|
||||||
|
|
||||||
|
- 统一多平台托盘图标点击行为
|
||||||
|
- 优化代理列表滚动流畅度
|
||||||
|
- 改进日志搜索功能与数据管理
|
||||||
|
- 重构热键管理逻辑,修复托盘冻结问题
|
||||||
|
- 优化托盘网速显示样式
|
||||||
|
- 增强字母导航工具提示的动态响应
|
||||||
|
|
||||||
|
### 国际化
|
||||||
|
|
||||||
|
- 新增配置检查多语言支持
|
||||||
|
- 添加轻量级模式多语言文本
|
||||||
|
- 完善多语言翻译内容
|
||||||
|
|
||||||
|
### 维护更新
|
||||||
|
|
||||||
|
- 将默认TUN协议栈改为gVisor
|
||||||
|
- 更新Node.js运行版本
|
||||||
|
- 移除自动生成更新器文件
|
||||||
|
- 清理废弃代码与未使用组件
|
||||||
|
- 禁用工作流自动Alpha标签更新
|
||||||
|
- 更新依赖库版本
|
||||||
|
- 添加MacOS格式转换函数专项测试
|
||||||
|
- 优化开发模式日志输出
|
||||||
|
|
||||||
|
### 安全增强
|
||||||
|
|
||||||
|
- 强化应用启动时的配置验证机制
|
||||||
|
- 改进脚本验证与异常处理流程
|
||||||
|
- 修复编译警告(移除无用导入)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v2.0.3
|
||||||
|
|
||||||
|
### Notice
|
||||||
|
|
||||||
|
- !!使用出现异常的,打开设置-->配置目录 备份 后 删除所有文件 尝试是否正常!!
|
||||||
|
- 历时3个月的紧密开发与严格测试稳定版2.0.0终于发布了:巨量改进与性能、稳定性提升,目前Clash Verge Rev已经有了比肩cfw的健壮性;而且更强大易用!
|
||||||
|
- 由于更改了服务安装逻辑,每次更新安装需要输入系统密码卸载老版本服务和安装新版本服务,以后可以丝滑使用tun(虚拟网卡)模式
|
||||||
|
|
||||||
|
### 2.0.3相对于2.0.2改进修复了:
|
||||||
|
|
||||||
|
1. 修复VLess-URL识别网络类型错误 f400f90 #2126
|
||||||
|
2. 新增系统代理绕过文本校验 c71e18e
|
||||||
|
3. 修复脚本编辑器UI显示不正确 6197249 #2267
|
||||||
|
4. 修复Shift热键无效 589324b #2278
|
||||||
|
5. 新增nushell环境变量复制 d233a84
|
||||||
|
6. 修复全局扩展脚本无法覆写DNS d22b37c #2235
|
||||||
|
7. 切换到系统代理相对于稳定的版本 38745d4
|
||||||
|
8. 修改fake-ip-range网段 0e3b631
|
||||||
|
9. 修复窗口隐藏后WebSocket未断开连接,减小内存风险 b42d13f
|
||||||
|
10. 改进系统代理绕过设置 c5c840d
|
||||||
|
11. 修复i18n翻译文本缺失 b149084
|
||||||
|
12. 修复双击托盘图标打开面板 f839d3b #2346
|
||||||
|
13. 修复Windows10窗口白色边框 4f6ca40 #2425
|
||||||
|
14. 修复Windows窗口状态恢复 4f6ca40
|
||||||
|
15. 改进保存配置文件自动重启Mihomo内核 0669f7a
|
||||||
|
16. 改进更新托盘图标性能 d9291d4
|
||||||
|
17. 修复保存配置后代理列表未更新 542baf9 #2460
|
||||||
|
18. 新增MacOS托盘显示实时速率,可在"界面设置"中关闭 1b2f1b6
|
||||||
|
19. 新增托盘菜单显示已设置的快捷键 eeff4d4
|
||||||
|
20. 新增重载配置文件错误响应"400"时显示更多错误信息 c5989d2 #2492
|
||||||
|
21. 修复GUI代理状态与菜单显示不一致 13b63b5 #2502
|
||||||
|
22. 新增默认语言跟随系统语言(无语言支持即为英语),添加了阿拉伯语、印尼语、鞑靼语支持 9655f77 #2940
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Meta(mihomo)内核升级 1.19.1
|
||||||
|
- 增加更多语言和托盘语言跟随
|
||||||
|
- MacOS增加状态栏速率显示
|
||||||
|
- 托盘显示快捷键
|
||||||
|
- 重载配置文件错误响应"400"时显示更多错误信息
|
||||||
|
- 改进保存配置文件自动重启Mihomo内核
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- 改进更新托盘图标性能
|
||||||
|
- 窗口隐藏后WebSocket断开连接
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v2.0.2
|
||||||
|
|
||||||
|
### Notice
|
||||||
|
|
||||||
|
- !!使用出现异常的,打开设置-->配置目录 备份 后 删除所有文件 尝试是否正常!!
|
||||||
|
- 历时3个月的紧密开发与严格测试稳定版2.0.0终于发布了:巨量改进与性能、稳定性提升,目前Clash Verge Rev已经有了比肩cfw的健壮性;而且更强大易用!
|
||||||
|
- 由于更改了服务安装逻辑,Mac/Linux 首次安装需要输入系统密码卸载和安装服务,以后可以丝滑使用 tun(虚拟网卡)模式
|
||||||
|
- 因 Tauri 2.0 底层 bug,关闭窗口后保留webview进程,优点是再次打开面板更快,缺点是内存使用略有增加
|
||||||
|
|
||||||
|
### 2.0.2相对于2.0.1改进了:
|
||||||
|
|
||||||
|
- MacOS 下自定义图标可以支持彩色、单色切换
|
||||||
|
- 修正了 Linux 下多个内核僵尸进程的问题
|
||||||
|
- 修正了 DNS ipv6 强制覆盖的逻辑
|
||||||
|
- 修改了 MacOS tun 模式下覆盖设置 dns 字段的问题
|
||||||
|
- 修正了 MacOS tray 图标不会随代理模式更改的问题
|
||||||
|
- 静默启动下重复运行会出现多个实例的bug
|
||||||
|
- 安装的时候自动删除历史残留启动项
|
||||||
|
- Tun模式默认是还用内核推荐的 mixed 堆栈
|
||||||
|
- 改进了默认窗口大小(启动软件窗口不会那么小了)
|
||||||
|
- 改进了 WebDAV 备份超时时间机制
|
||||||
|
- 测试菜单添加滚动条
|
||||||
|
- 改进和修正了 Tun 模式下对设置的覆盖逻辑
|
||||||
|
- 修复了打开配置出错的问题
|
||||||
|
- 修复了配置文件无法拖拽添加的问题
|
||||||
|
- 改善了浅色模式的对比度
|
||||||
|
|
||||||
|
### 2.0.1相对于2.0.0改进了:
|
||||||
|
|
||||||
|
- 无法从 2.0rc和2.0.0 升级的问题(已经安装了2.0版本的需手动下载安装)
|
||||||
|
- MacOS 系统下少有的无法安装服务,无法启动的问题,目前更健壮了
|
||||||
|
- 当系统中没有 yaml 编辑器的情况下,打开文件程序崩溃的问题
|
||||||
|
- Windows 应用内升级和覆盖安装不会删除老执行文件的问题
|
||||||
|
- 修改优化了 mac 下 fakeip 段和 dns
|
||||||
|
- 测试菜单 svg 图标格式检查
|
||||||
|
- 应用内升级重复安装 vs runtime 的问题
|
||||||
|
- 修复外部控制下密码有特殊字符认证出错的问题
|
||||||
|
- 修复恢复 Webdav 备份设置后, Webdav 设置丢失的问题
|
||||||
|
- 代理页面增加快速回到顶部的按钮
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
- 重大框架升级:使用 Tauri 2.0(巨量改进与性能提升)
|
||||||
|
- 出现 bug 到 issues 中提出;以后不再接受1.x版本的bug反馈。
|
||||||
|
- 强烈建议完全删除 1.x 老版本再安装此版本 !!使用出现异常的,打开设置-->配置目录 备份 后 删除所有文件 尝试是否正常!!
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Meta(mihomo)内核升级 1.18.10
|
||||||
|
- Win 下的系统代理替换为 Shadowsocks/CFW/v2rayN 等成熟的 sysproxy.exe 方案,解决拨号/VPN 环境下无法设置系统代理的问题
|
||||||
|
- 服务模式改进为启动软件时自动安装,TUN 模式可自由开启不再限制于服务模式
|
||||||
|
- Mac 下可用 URL Scheme 导入订阅
|
||||||
|
- 可使用 Ctrl(cmd)+Q 快捷键退出程序
|
||||||
|
- 成功导入订阅的提示消息
|
||||||
|
- 能自动选中新导入的订阅
|
||||||
|
- 日志加入颜色区分
|
||||||
|
- 改进多处文本表述
|
||||||
|
- 加入图标 svg 格式检测
|
||||||
|
- 增加更多 app 调试日志
|
||||||
|
- 添加 MacOS 下白色桌面的 tray 黑色配色(但会代理系统代理、tun 模式图标失效的问题)
|
||||||
|
- 增加 Webdav 备份功能
|
||||||
|
- 添加统一延迟的设置开关
|
||||||
|
- 添加 Windows 下自动检测并下载 vc runtime 的功能
|
||||||
|
- 支持显示 mux 和 mptcp 的节点标识
|
||||||
|
- 延迟测试连接更换 http 的 cp.cloudflare.com/generate_204 (关闭统一延迟的情况下延迟测试结果会有所增加)
|
||||||
|
- 重构日志记录逻辑,可以收集和筛选所有日志类型了(之前无法记录debug的日志类型)
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- 优化及重构内核启动管理逻辑
|
||||||
|
- 优化 TUN 启动逻辑
|
||||||
|
- 重构和优化 app_handle
|
||||||
|
- 重构系统代理绕过逻辑
|
||||||
|
- 移除无用的 PID 创建逻辑
|
||||||
|
- 优化系统 DNS 设置逻辑
|
||||||
|
- 后端实现窗口控制
|
||||||
|
- 重构 MacOS 下的 DNS 设置逻辑
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 修复已有多个订阅导入新订阅会跳选订阅的问题
|
||||||
|
- 修复多个 Linux 下的 bug, Tun 模式在 Linux 下目前工作正常
|
||||||
|
- 修复 Linux wayland 下任务栏图标缺失的问题
|
||||||
|
- 修复 Linux KDE 桌面环境无法启动的问题
|
||||||
|
- 移除多余退出变量和钩子
|
||||||
|
- 修复 MacOS 下 tray 菜单重启 app 失效的问题
|
||||||
|
- 修复某些特定配置文件载入失败的问题
|
||||||
|
- 修复 MacOS 下 tun 模式 fakeip 不生效的问题
|
||||||
|
- 修复 Linux 下 关闭 tun 模式文件报错的问题
|
||||||
|
- 修复快捷键设置的相关 bug
|
||||||
|
- 修复 Win 下点左键菜单闪现的问题(Mac 下的操作逻辑相反,默认情况下不管点左/右键均会打开菜单,闪现不属于 bug)
|
||||||
|
|
||||||
|
### Known issues
|
||||||
|
|
||||||
|
- Windows 下窗口大小无法记忆(等待上游修复)
|
||||||
|
- Webdav 备份因为安全性和兼容性问题,暂不支持跨平台配置同步
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.7.7
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 修复导入订阅没有自动重载(不显示节点)的问题
|
||||||
|
- 英语状态下修复 Windows 工具栏提示文本超过限制的问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.7.6
|
||||||
|
|
||||||
|
### Notice
|
||||||
|
|
||||||
|
- Clash Verge Rev 目前已进入稳定周期,日后更新将着重于 bug 修复与内核常规升级
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Meta(mihomo)内核升级 1.18.7
|
||||||
|
- 界面细节调整
|
||||||
|
- 优化服务模式安装逻辑
|
||||||
|
- 移除无用的 console log
|
||||||
|
- 能自动选择第一个订阅
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 修复服务模式安装问题
|
||||||
|
- 修复 Mac 下的代理绕过 CIDR 写法过滤
|
||||||
|
- 修复 32 位升级 URL
|
||||||
|
- 修复不同分组 URL 测试地址配置无效的问题
|
||||||
|
- 修复 Web UI 下的一处 hostname 参数
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.7.5
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 展示局域网 IP 地址信息
|
||||||
|
- 在设置页面直接复制环境变量
|
||||||
|
- 优化服务模式安装逻辑
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- 优化切换订阅速度
|
||||||
|
- 优化更改端口速度
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 调整 MacOS 托盘图标大小
|
||||||
|
- Trojan URI 解析错误
|
||||||
|
- 卡片拖动显示层级错误
|
||||||
|
- 代理绕过格式检查错误
|
||||||
|
- MacOS 下编辑器最大化失败
|
||||||
|
- MacOS 服务安装失败
|
||||||
|
- 更改窗口大小导致闪退的问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.7.3
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 支持可视化编辑订阅代理组
|
||||||
|
- 支持可视化编辑订阅节点
|
||||||
|
- 支持可视化编辑订阅规则
|
||||||
|
- 扩展脚本支持订阅名称参数 `function main(config, profileName)`
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 代理绕过格式检查错误
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.7.2
|
||||||
|
|
||||||
|
### Break Changes
|
||||||
|
|
||||||
|
- 更新后请务必重新导入所有订阅,包括 Remote 和 Local
|
||||||
|
- 此版本重构了 Merge/Script,更新前请先备份好自定义 Merge 和 Script(更新并不会删除配置文件,但是旧版 Merge 和 Script 在更新后无法从前端访问,备份以防万一)
|
||||||
|
- Merge 改名为 `扩展配置`,分为 `全局扩展配置` 和 `订阅扩展配置`,全局扩展配置对所有订阅生效,订阅扩展配置只对关联的订阅生效
|
||||||
|
- Script 改名为 `扩展脚本`,同样分为 `全局扩展脚本` 和 `订阅扩展脚本`
|
||||||
|
- 订阅扩展配置在订阅右键菜单里进入
|
||||||
|
- 执行优先级为: 全局扩展配置 -> 全局扩展脚本 -> 订阅扩展配置 ->订阅扩展脚本
|
||||||
|
- 扩展配置删除了 `prepend/append` 能力,请使用 右键订阅 -> `编辑规则`/`编辑节点`/`编辑代理组` 来代替
|
||||||
|
- MacOS 用户更新后请重新安装服务模式
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 升级内核到 1.18.6
|
||||||
|
- 移除内核授权,改为服务模式实现
|
||||||
|
- 自动填充本地订阅名称
|
||||||
|
- 添加重大更新处理逻辑
|
||||||
|
- 订阅单独指定扩展配置/脚本(需要重新导入订阅)
|
||||||
|
- 添加可视化规则编辑器(需要重新导入订阅)
|
||||||
|
- 编辑器新增工具栏按钮(格式化、最大化/最小化)
|
||||||
|
- WEBUI 使用最新版 metacubex,并解决无法自动登陆问问题
|
||||||
|
- 禁用部分 Webview2 快捷键
|
||||||
|
- 热键配置新增连接符 + 号
|
||||||
|
- 新增部分悬浮提示按钮,用于解释说明
|
||||||
|
- 当日志等级为`Debug`时(更改需重启软件生效),支持点击内存主动内存回收(绿色文字)
|
||||||
|
- 设置页面右上角新增 TG 频道链接
|
||||||
|
- 各种细节优化和界面性能优化
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 修复代理绕过格式检查
|
||||||
|
- 通过进程名称关闭进程
|
||||||
|
- 退出软件时恢复 DNS 设置
|
||||||
|
- 修复创建本地订阅时更新间隔无法保存
|
||||||
|
- 连接页面列宽无法调整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.7.1
|
||||||
|
|
||||||
|
### Break Changes
|
||||||
|
|
||||||
|
- 更新后请务必重新导入所有订阅,包括 Remote 和 Local
|
||||||
|
- 此版本重构了 Merge/Script,更新前请先备份好自定义 Merge 和 Script(更新并不会删除配置文件,但是旧版 Merge 和 Script 在更新后无法从前端访问,备份以防万一)
|
||||||
|
- Merge 改名为 `扩展配置`,分为 `全局扩展配置` 和 `订阅扩展配置`,全局扩展配置对所有订阅生效,订阅扩展配置只对关联的订阅生效
|
||||||
|
- Script 改名为 `扩展脚本`,同样分为 `全局扩展脚本` 和 `订阅扩展脚本`
|
||||||
|
- 订阅扩展配置在订阅右键菜单里进入
|
||||||
|
- 执行优先级为: 全局扩展配置 -> 全局扩展脚本 -> 订阅扩展配置 ->订阅扩展脚本
|
||||||
|
- 扩展配置删除了 `prepend/append` 能力,请使用 右键订阅 -> `编辑规则`/`编辑节点`/`编辑代理组` 来代替
|
||||||
|
- MacOS 用户更新后请重新安装服务模式
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 升级内核到 1.18.6
|
||||||
|
- 移除内核授权,改为服务模式实现
|
||||||
|
- 自动填充本地订阅名称
|
||||||
|
- 添加重大更新处理逻辑
|
||||||
|
- 订阅单独指定扩展配置/脚本(需要重新导入订阅)
|
||||||
|
- 添加可视化规则编辑器(需要重新导入订阅)
|
||||||
|
- 编辑器新增工具栏按钮(格式化、最大化/最小化)
|
||||||
|
- WEBUI 使用最新版 metacubex,并解决无法自动登陆问问题
|
||||||
|
- 禁用部分 Webview2 快捷键
|
||||||
|
- 热键配置新增连接符 + 号
|
||||||
|
- 新增部分悬浮提示按钮,用于解释说明
|
||||||
|
- 当日志等级为`Debug`时(更改需重启软件生效),支持点击内存主动内存回收(绿色文字)
|
||||||
|
- 设置页面右上角新增 TG 频道链接
|
||||||
|
- 各种细节优化和界面性能优化
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 修复代理绕过格式检查
|
||||||
|
- 通过进程名称关闭进程
|
||||||
|
- 退出软件时恢复 DNS 设置
|
||||||
|
- 修复创建本地订阅时更新间隔无法保存
|
||||||
|
- 连接页面列宽无法调整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.7.0
|
||||||
|
|
||||||
|
### Break Changes
|
||||||
|
|
||||||
|
- 此版本重构了 Merge/Script,更新前请先备份好自定义 Merge 和 Script(更新并不会删除配置文件,但是旧版 Merge 和 Script 在更新后无法从前端访问,备份以防万一)
|
||||||
|
- Merge 改名为 `扩展配置`,分为 `全局扩展配置` 和 `订阅扩展配置`,全局扩展配置对所有订阅生效,订阅扩展配置只对关联的订阅生效
|
||||||
|
- Script 改名为 `扩展脚本`,同样分为 `全局扩展脚本` 和 `订阅扩展脚本`
|
||||||
|
- 执行优先级为: 全局扩展配置 -> 全局扩展脚本 -> 订阅扩展配置 ->订阅扩展脚本
|
||||||
|
- MacOS 用户更新后请重新安装服务模式
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 移除内核授权,改为服务模式实现
|
||||||
|
- 自动填充本地订阅名称
|
||||||
|
- 添加重大更新处理逻辑
|
||||||
|
- 订阅单独指定扩展配置/脚本(需要重新导入订阅)
|
||||||
|
- 添加可视化规则编辑器(需要重新导入订阅)
|
||||||
|
- 编辑器新增工具栏按钮(格式化、最大化/最小化)
|
||||||
|
- WEBUI 使用最新版 metacubex,并解决无法自动登陆问问题
|
||||||
|
- 禁用部分 Webview2 快捷键
|
||||||
|
- 热键配置新增连接符 + 号
|
||||||
|
- 新增部分悬浮提示按钮,用于解释说明
|
||||||
|
- 当日志等级为`Debug`时(更改需重启软件生效),支持点击内存主动内存回收(绿色文字)
|
||||||
|
- 设置页面右上角新增 TG 频道链接
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 修复代理绕过格式检查
|
||||||
|
- 通过进程名称关闭进程
|
||||||
|
- 退出软件时恢复 DNS 设置
|
||||||
|
- 修复创建本地订阅时更新间隔无法保存
|
||||||
|
- 连接页面列宽无法调整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.6.6
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- MacOS 应用签名
|
||||||
|
- 删除 AppImage
|
||||||
|
- 应用更新对话框添加下载按钮
|
||||||
|
- 设置系统代理绕过时保留默认值
|
||||||
|
- 系统代理绕过设置输入格式检查
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- MacOS 代理组图标无法显示
|
||||||
|
- RPM 包依赖缺失
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.6.5
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 添加 RPM 包支持
|
||||||
|
- 优化细节
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- MacOS 10.15 编辑器空白的问题
|
||||||
|
- MacOS 低版本启动白屏的问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.6.4
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 系统代理支持 PAC 模式
|
||||||
|
- 允许关闭不使用的端口
|
||||||
|
- 使用新的应用图标
|
||||||
|
- MacOS 支持切换托盘图标单色/彩色模式
|
||||||
|
- CSS 注入支持通过编辑器编辑
|
||||||
|
- 优化代理组列表性能
|
||||||
|
- 优化流量图显性能
|
||||||
|
- 支持波斯语
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- Kill 内核后 Tun 开启缓慢的问题
|
||||||
|
- 代理绕过为空时使用默认值
|
||||||
|
- 无法读取剪切板内容
|
||||||
|
- Windows 下覆盖安装无法内核占用问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.6.2
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 支持本地文件拖拽导入
|
||||||
|
- 重新支持 32 位 CPU
|
||||||
|
- 新增内置 Webview2 版本
|
||||||
|
- 优化 Merge 逻辑,支持深度合并
|
||||||
|
- 删除 Merge 配置中的 append/prepend-provider 字段
|
||||||
|
- 支持更新稳定版内核
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- MacOS DNS 还原失败
|
||||||
|
- CMD 环境变量格式错误
|
||||||
|
- Linux 下与 N 卡的兼容性问题
|
||||||
|
- 修改 Tun 设置不立即生效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v1.6.1
|
## v1.6.1
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
4
crowdin.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
files:
|
||||||
|
- source: /src/locales/en.json
|
||||||
|
translation: /src/locales
|
||||||
|
multilingual: 1
|
||||||
BIN
docs/preview.png
|
Before Width: | Height: | Size: 576 KiB |
BIN
docs/preview_dark.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
docs/preview_light.png
Normal file
|
After Width: | Height: | Size: 274 KiB |
141
package.json
@@ -1,83 +1,116 @@
|
|||||||
{
|
{
|
||||||
"name": "clash-verge",
|
"name": "clash-verge",
|
||||||
"version": "1.6.1",
|
"version": "2.3.1",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tauri dev",
|
"dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev",
|
||||||
"dev:diff": "tauri dev -f verge-dev",
|
"dev:diff": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev",
|
||||||
"build": "tauri build",
|
"build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build",
|
||||||
|
"build:fast": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build -- --profile fast-release",
|
||||||
"tauri": "tauri",
|
"tauri": "tauri",
|
||||||
"web:dev": "vite",
|
"web:dev": "vite",
|
||||||
"web:build": "tsc && vite build",
|
"web:build": "tsc --noEmit && vite build",
|
||||||
"web:serve": "vite preview",
|
"web:serve": "vite preview",
|
||||||
"check": "node scripts/check.mjs",
|
"prebuild": "node scripts/prebuild.mjs",
|
||||||
"updater": "node scripts/updater.mjs",
|
"updater": "node scripts/updater.mjs",
|
||||||
|
"updater-fixed-webview2": "node scripts/updater-fixed-webview2.mjs",
|
||||||
"portable": "node scripts/portable.mjs",
|
"portable": "node scripts/portable.mjs",
|
||||||
"prepare": "husky install"
|
"portable-fixed-webview2": "node scripts/portable-fixed-webview2.mjs",
|
||||||
|
"fix-alpha-version": "node scripts/fix-alpha_version.mjs",
|
||||||
|
"release-version": "node scripts/release-version.mjs",
|
||||||
|
"publish-version": "node scripts/publish-version.mjs",
|
||||||
|
"fmt": "cargo fmt --manifest-path ./src-tauri/Cargo.toml",
|
||||||
|
"clippy": "cargo clippy --manifest-path ./src-tauri/Cargo.toml",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"format:check": "prettier --check ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "^6.1.0",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.14.0",
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
"@mui/icons-material": "^5.15.15",
|
"@mui/icons-material": "^7.1.1",
|
||||||
"@mui/lab": "5.0.0-alpha.149",
|
"@mui/lab": "7.0.0-beta.13",
|
||||||
"@mui/material": "^5.15.15",
|
"@mui/material": "^7.1.1",
|
||||||
"@mui/x-data-grid": "^6.19.11",
|
"@mui/x-data-grid": "^8.5.2",
|
||||||
"@tauri-apps/api": "^1.5.4",
|
"@tauri-apps/api": "2.5.0",
|
||||||
|
"@tauri-apps/plugin-clipboard-manager": "^2.2.3",
|
||||||
|
"@tauri-apps/plugin-dialog": "^2.2.2",
|
||||||
|
"@tauri-apps/plugin-fs": "^2.3.0",
|
||||||
|
"@tauri-apps/plugin-global-shortcut": "^2.2.1",
|
||||||
|
"@tauri-apps/plugin-notification": "^2.2.3",
|
||||||
|
"@tauri-apps/plugin-process": "^2.2.2",
|
||||||
|
"@tauri-apps/plugin-shell": "2.2.2",
|
||||||
|
"@tauri-apps/plugin-updater": "2.8.1",
|
||||||
|
"@tauri-apps/plugin-window-state": "^2.2.3",
|
||||||
|
"@types/d3-shape": "^3.1.7",
|
||||||
"@types/json-schema": "^7.0.15",
|
"@types/json-schema": "^7.0.15",
|
||||||
"ahooks": "^3.7.11",
|
"ahooks": "^3.8.5",
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.10.0",
|
||||||
"dayjs": "1.11.5",
|
"chart.js": "^4.5.0",
|
||||||
"i18next": "^23.11.2",
|
"cli-color": "^2.0.4",
|
||||||
|
"d3-shape": "^3.2.0",
|
||||||
|
"dayjs": "1.11.13",
|
||||||
|
"foxact": "^0.2.49",
|
||||||
|
"glob": "^11.0.3",
|
||||||
|
"i18next": "^25.2.1",
|
||||||
|
"js-base64": "^3.7.7",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"meta-json-schema": "1.18.4-beta2",
|
"monaco-editor": "^0.52.2",
|
||||||
"monaco-editor": "^0.47.0",
|
"monaco-yaml": "^5.4.0",
|
||||||
"monaco-yaml": "^5.1.1",
|
"nanoid": "^5.1.5",
|
||||||
"nanoid": "^5.0.7",
|
"peggy": "^5.0.3",
|
||||||
"react": "^18.3.1",
|
"react": "19.1.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-error-boundary": "^3.1.4",
|
"react-dom": "19.1.0",
|
||||||
"react-hook-form": "^7.51.3",
|
"react-error-boundary": "6.0.0",
|
||||||
"react-i18next": "^13.5.0",
|
"react-hook-form": "^7.58.1",
|
||||||
"react-markdown": "^9.0.1",
|
"react-i18next": "15.5.3",
|
||||||
"react-router-dom": "^6.23.0",
|
"react-markdown": "10.1.0",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-monaco-editor": "0.58.0",
|
||||||
"react-virtuoso": "^4.7.10",
|
"react-router-dom": "7.6.2",
|
||||||
"recoil": "^0.7.7",
|
"react-virtuoso": "^4.13.0",
|
||||||
"swr": "^1.3.0",
|
"sockette": "^2.0.6",
|
||||||
"tar": "^6.2.1"
|
"swr": "^2.3.3",
|
||||||
|
"tar": "^7.4.3",
|
||||||
|
"types-pac": "^1.0.3",
|
||||||
|
"zustand": "^5.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/github": "^5.1.1",
|
"@actions/github": "^6.0.1",
|
||||||
"@tauri-apps/cli": "^1.5.12",
|
"@tauri-apps/cli": "2.5.0",
|
||||||
"@types/fs-extra": "^9.0.13",
|
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/react": "^18.3.1",
|
"@types/react": "19.1.8",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "19.1.6",
|
||||||
"@types/react-transition-group": "^4.4.10",
|
"@vitejs/plugin-legacy": "^6.1.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "4.5.2",
|
||||||
"adm-zip": "^0.5.12",
|
"adm-zip": "^0.5.16",
|
||||||
|
"commander": "^14.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"fs-extra": "^11.2.0",
|
"https-proxy-agent": "^7.0.6",
|
||||||
"https-proxy-agent": "^5.0.1",
|
"husky": "^9.1.7",
|
||||||
"husky": "^7.0.4",
|
"meta-json-schema": "^1.19.10",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^3.5.3",
|
||||||
"pretty-quick": "^3.3.1",
|
"pretty-quick": "^4.2.2",
|
||||||
"sass": "^1.75.0",
|
"sass": "^1.89.2",
|
||||||
"typescript": "^5.4.5",
|
"terser": "^5.43.0",
|
||||||
"vite": "^5.2.10",
|
"typescript": "^5.8.3",
|
||||||
|
"vite": "^6.3.5",
|
||||||
"vite-plugin-monaco-editor": "^1.1.0",
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
"vite-plugin-svgr": "^4.2.0"
|
"vite-plugin-svgr": "^4.3.0"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"endOfLine": "lf"
|
"endOfLine": "lf"
|
||||||
}
|
},
|
||||||
|
"type": "module",
|
||||||
|
"packageManager": "pnpm@9.13.2"
|
||||||
}
|
}
|
||||||
|
|||||||
8277
pnpm-lock.yaml
generated
42
renovate.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"extends": ["config:recommended"],
|
||||||
|
"baseBranches": ["dev"],
|
||||||
|
"enabledManagers": ["cargo", "npm"],
|
||||||
|
"labels": ["dependencies"],
|
||||||
|
"ignorePaths": [
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/bower_components/**",
|
||||||
|
"**/vendor/**",
|
||||||
|
"**/__tests__/**",
|
||||||
|
"**/test/**",
|
||||||
|
"**/tests/**",
|
||||||
|
"**/__fixtures__/**",
|
||||||
|
"**/crate/**",
|
||||||
|
"shared/**"
|
||||||
|
],
|
||||||
|
"rangeStrategy": "bump",
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"semanticCommitType": "chore",
|
||||||
|
"matchPackageNames": ["*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Disable node/pnpm version updates",
|
||||||
|
"matchPackageNames": ["node", "pnpm"],
|
||||||
|
"matchDepTypes": ["engines", "packageManager"],
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Group all cargo dependencies into a single PR",
|
||||||
|
"matchManagers": ["cargo"],
|
||||||
|
"groupName": "cargo dependencies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Group all npm dependencies into a single PR",
|
||||||
|
"matchManagers": ["npm"],
|
||||||
|
"groupName": "npm dependencies"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"postUpdateOptions": ["pnpmDedupe"],
|
||||||
|
"ignoreDeps": ["serde_yaml"]
|
||||||
|
}
|
||||||
102
scripts/check-unused-i18n.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
const LOCALES_DIR = path.resolve(__dirname, "../src/locales");
|
||||||
|
const SRC_DIRS = [
|
||||||
|
path.resolve(__dirname, "../src"),
|
||||||
|
path.resolve(__dirname, "../src-tauri"),
|
||||||
|
];
|
||||||
|
const exts = [".js", ".ts", ".tsx", ".jsx", ".vue", ".rs"];
|
||||||
|
|
||||||
|
// 递归获取所有文件
|
||||||
|
function getAllFiles(dir, exts) {
|
||||||
|
let files = [];
|
||||||
|
fs.readdirSync(dir).forEach((file) => {
|
||||||
|
const full = path.join(dir, file);
|
||||||
|
if (fs.statSync(full).isDirectory()) {
|
||||||
|
files = files.concat(getAllFiles(full, exts));
|
||||||
|
} else if (exts.includes(path.extname(full))) {
|
||||||
|
files.push(full);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取所有源码内容为一个大字符串
|
||||||
|
function getAllSourceContent() {
|
||||||
|
const files = SRC_DIRS.flatMap((dir) => getAllFiles(dir, exts));
|
||||||
|
return files.map((f) => fs.readFileSync(f, "utf8")).join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 白名单 key,不检查这些 key 是否被使用
|
||||||
|
const WHITELIST_KEYS = [
|
||||||
|
"theme.light",
|
||||||
|
"theme.dark",
|
||||||
|
"theme.system",
|
||||||
|
"Already Using Latest Core Version",
|
||||||
|
];
|
||||||
|
|
||||||
|
// 主流程
|
||||||
|
function processI18nFile(i18nPath, lang, allSource) {
|
||||||
|
const i18n = JSON.parse(fs.readFileSync(i18nPath, "utf8"));
|
||||||
|
const keys = Object.keys(i18n);
|
||||||
|
|
||||||
|
const used = {};
|
||||||
|
const unused = [];
|
||||||
|
|
||||||
|
let checked = 0;
|
||||||
|
const total = keys.length;
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if (WHITELIST_KEYS.includes(key)) {
|
||||||
|
used[key] = i18n[key];
|
||||||
|
} else {
|
||||||
|
// 只查找一次
|
||||||
|
const regex = new RegExp(`["'\`]${key}["'\`]`);
|
||||||
|
if (regex.test(allSource)) {
|
||||||
|
used[key] = i18n[key];
|
||||||
|
} else {
|
||||||
|
unused.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checked++;
|
||||||
|
if (checked % 20 === 0 || checked === total) {
|
||||||
|
const percent = ((checked / total) * 100).toFixed(1);
|
||||||
|
process.stdout.write(
|
||||||
|
`\r[${lang}] Progress: ${checked}/${total} (${percent}%)`,
|
||||||
|
);
|
||||||
|
if (checked === total) process.stdout.write("\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 输出未使用的 key
|
||||||
|
console.log(`\n[${lang}] Unused keys:`, unused);
|
||||||
|
|
||||||
|
// 备份原文件
|
||||||
|
const oldPath = i18nPath + ".old";
|
||||||
|
fs.renameSync(i18nPath, oldPath);
|
||||||
|
|
||||||
|
// 写入精简后的 i18n 文件(保留原文件名)
|
||||||
|
fs.writeFileSync(i18nPath, JSON.stringify(used, null, 2), "utf8");
|
||||||
|
console.log(
|
||||||
|
`[${lang}] Cleaned i18n file written to src/locales/${path.basename(i18nPath)}`,
|
||||||
|
);
|
||||||
|
console.log(`[${lang}] Original file backed up as ${path.basename(oldPath)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
// 支持 zhtw.json、zh-tw.json、zh_CN.json 等
|
||||||
|
const files = fs
|
||||||
|
.readdirSync(LOCALES_DIR)
|
||||||
|
.filter((f) => /^[a-z0-9\-_]+\.json$/i.test(f) && !f.endsWith(".old"));
|
||||||
|
const allSource = getAllSourceContent();
|
||||||
|
files.forEach((file) => {
|
||||||
|
const lang = path.basename(file, ".json");
|
||||||
|
processI18nFile(path.join(LOCALES_DIR, file), lang, allSource);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
67
scripts/fix-alpha_version.mjs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { exec } from "child_process";
|
||||||
|
import { promisify } from "util";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为Alpha版本重命名版本号
|
||||||
|
*/
|
||||||
|
const execPromise = promisify(exec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准输出HEAD hash
|
||||||
|
*/
|
||||||
|
async function getLatestCommitHash() {
|
||||||
|
try {
|
||||||
|
const { stdout } = await execPromise("git rev-parse HEAD");
|
||||||
|
const commitHash = stdout.trim();
|
||||||
|
// 格式化,只截取前7位字符
|
||||||
|
const formathash = commitHash.substring(0, 7);
|
||||||
|
console.log(`Found the latest commit hash code: ${commitHash}`);
|
||||||
|
return formathash;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("pnpm run fix-alpha-version ERROR", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string 传入格式化后的hash
|
||||||
|
* 将新的版本号写入文件 package.json
|
||||||
|
*/
|
||||||
|
async function updatePackageVersion(newVersion) {
|
||||||
|
// 获取内容根目录
|
||||||
|
const _dirname = process.cwd();
|
||||||
|
const packageJsonPath = path.join(_dirname, "package.json");
|
||||||
|
try {
|
||||||
|
// 读取文件
|
||||||
|
const data = await fs.readFile(packageJsonPath, "utf8");
|
||||||
|
const packageJson = JSON.parse(data);
|
||||||
|
// 获取键值替换
|
||||||
|
let result = packageJson.version.replace("alpha", newVersion);
|
||||||
|
// 检查当前版本号是否已经包含了 alpha- 后缀
|
||||||
|
if (!packageJson.version.includes(`alpha-`)) {
|
||||||
|
// 如果只有 alpha 而没有 alpha-,则替换为 alpha-newVersion
|
||||||
|
result = packageJson.version.replace("alpha", `alpha-${newVersion}`);
|
||||||
|
} else {
|
||||||
|
// 如果已经是 alpha-xxx 格式,则更新 xxx 部分
|
||||||
|
result = packageJson.version.replace(
|
||||||
|
/alpha-[^-]*/,
|
||||||
|
`alpha-${newVersion}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.log("[INFO]: Current version is: ", result);
|
||||||
|
packageJson.version = result;
|
||||||
|
// 写入版本号
|
||||||
|
await fs.writeFile(
|
||||||
|
packageJsonPath,
|
||||||
|
JSON.stringify(packageJson, null, 2),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
console.log(`[INFO]: Alpha version update to: ${newVersion}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("pnpm run fix-alpha-version ERROR", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newVersion = await getLatestCommitHash();
|
||||||
|
updatePackageVersion(newVersion).catch(console.error);
|
||||||
103
scripts/portable-fixed-webview2.mjs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import fsp from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
import AdmZip from "adm-zip";
|
||||||
|
import { createRequire } from "module";
|
||||||
|
import { getOctokit, context } from "@actions/github";
|
||||||
|
|
||||||
|
const target = process.argv.slice(2)[0];
|
||||||
|
const alpha = process.argv.slice(2)[1];
|
||||||
|
|
||||||
|
const ARCH_MAP = {
|
||||||
|
"x86_64-pc-windows-msvc": "x64",
|
||||||
|
"i686-pc-windows-msvc": "x86",
|
||||||
|
"aarch64-pc-windows-msvc": "arm64",
|
||||||
|
};
|
||||||
|
|
||||||
|
const PROCESS_MAP = {
|
||||||
|
x64: "x64",
|
||||||
|
ia32: "x86",
|
||||||
|
arm64: "arm64",
|
||||||
|
};
|
||||||
|
const arch = target ? ARCH_MAP[target] : PROCESS_MAP[process.arch];
|
||||||
|
/// Script for ci
|
||||||
|
/// 打包绿色版/便携版 (only Windows)
|
||||||
|
async function resolvePortable() {
|
||||||
|
if (process.platform !== "win32") return;
|
||||||
|
|
||||||
|
const releaseDir = target
|
||||||
|
? `./src-tauri/target/${target}/release`
|
||||||
|
: `./src-tauri/target/release`;
|
||||||
|
|
||||||
|
const configDir = path.join(releaseDir, ".config");
|
||||||
|
|
||||||
|
if (!fs.existsSync(releaseDir)) {
|
||||||
|
throw new Error("could not found the release dir");
|
||||||
|
}
|
||||||
|
|
||||||
|
await fsp.mkdir(configDir, { recursive: true });
|
||||||
|
if (!fs.existsSync(path.join(configDir, "PORTABLE"))) {
|
||||||
|
await fsp.writeFile(path.join(configDir, "PORTABLE"), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
const zip = new AdmZip();
|
||||||
|
|
||||||
|
zip.addLocalFile(path.join(releaseDir, "Clash Verge.exe"));
|
||||||
|
zip.addLocalFile(path.join(releaseDir, "verge-mihomo.exe"));
|
||||||
|
zip.addLocalFile(path.join(releaseDir, "verge-mihomo-alpha.exe"));
|
||||||
|
zip.addLocalFolder(path.join(releaseDir, "resources"), "resources");
|
||||||
|
zip.addLocalFolder(
|
||||||
|
path.join(
|
||||||
|
releaseDir,
|
||||||
|
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`,
|
||||||
|
),
|
||||||
|
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`,
|
||||||
|
);
|
||||||
|
zip.addLocalFolder(configDir, ".config");
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const packageJson = require("../package.json");
|
||||||
|
const { version } = packageJson;
|
||||||
|
|
||||||
|
const zipFile = `Clash.Verge_${version}_${arch}_fixed_webview2_portable.zip`;
|
||||||
|
zip.writeZip(zipFile);
|
||||||
|
|
||||||
|
console.log("[INFO]: create portable zip successfully");
|
||||||
|
|
||||||
|
// push release assets
|
||||||
|
if (process.env.GITHUB_TOKEN === undefined) {
|
||||||
|
throw new Error("GITHUB_TOKEN is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = { owner: context.repo.owner, repo: context.repo.repo };
|
||||||
|
const github = getOctokit(process.env.GITHUB_TOKEN);
|
||||||
|
const tag = alpha ? "alpha" : process.env.TAG_NAME || `v${version}`;
|
||||||
|
console.log("[INFO]: upload to ", tag);
|
||||||
|
|
||||||
|
const { data: release } = await github.rest.repos.getReleaseByTag({
|
||||||
|
...options,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
|
|
||||||
|
let assets = release.assets.filter((x) => {
|
||||||
|
return x.name === zipFile;
|
||||||
|
});
|
||||||
|
if (assets.length > 0) {
|
||||||
|
let id = assets[0].id;
|
||||||
|
await github.rest.repos.deleteReleaseAsset({
|
||||||
|
...options,
|
||||||
|
asset_id: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(release.name);
|
||||||
|
|
||||||
|
await github.rest.repos.uploadReleaseAsset({
|
||||||
|
...options,
|
||||||
|
release_id: release.id,
|
||||||
|
name: zipFile,
|
||||||
|
data: zip.toBuffer(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvePortable().catch(console.error);
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
import fs from "fs-extra";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import AdmZip from "adm-zip";
|
import AdmZip from "adm-zip";
|
||||||
import { createRequire } from "module";
|
import { createRequire } from "module";
|
||||||
import { getOctokit, context } from "@actions/github";
|
import fsp from "fs/promises";
|
||||||
|
|
||||||
const target = process.argv.slice(2)[0];
|
const target = process.argv.slice(2)[0];
|
||||||
const alpha = process.argv.slice(2)[1];
|
|
||||||
|
|
||||||
const ARCH_MAP = {
|
const ARCH_MAP = {
|
||||||
"x86_64-pc-windows-msvc": "x64",
|
"x86_64-pc-windows-msvc": "x64",
|
||||||
"aarch64-pc-windows-msvc": "arm64",
|
"aarch64-pc-windows-msvc": "arm64",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PROCESS_MAP = {
|
||||||
|
x64: "x64",
|
||||||
|
arm64: "arm64",
|
||||||
|
};
|
||||||
|
const arch = target ? ARCH_MAP[target] : PROCESS_MAP[process.arch];
|
||||||
/// Script for ci
|
/// Script for ci
|
||||||
/// 打包绿色版/便携版 (only Windows)
|
/// 打包绿色版/便携版 (only Windows)
|
||||||
async function resolvePortable() {
|
async function resolvePortable() {
|
||||||
@@ -22,64 +25,28 @@ async function resolvePortable() {
|
|||||||
: `./src-tauri/target/release`;
|
: `./src-tauri/target/release`;
|
||||||
const configDir = path.join(releaseDir, ".config");
|
const configDir = path.join(releaseDir, ".config");
|
||||||
|
|
||||||
if (!(await fs.pathExists(releaseDir))) {
|
if (!fs.existsSync(releaseDir)) {
|
||||||
throw new Error("could not found the release dir");
|
throw new Error("could not found the release dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.mkdir(configDir);
|
await fsp.mkdir(configDir, { recursive: true });
|
||||||
await fs.createFile(path.join(configDir, "PORTABLE"));
|
if (!fs.existsSync(path.join(configDir, "PORTABLE"))) {
|
||||||
|
await fsp.writeFile(path.join(configDir, "PORTABLE"), "");
|
||||||
|
}
|
||||||
const zip = new AdmZip();
|
const zip = new AdmZip();
|
||||||
|
|
||||||
zip.addLocalFile(path.join(releaseDir, "Clash Verge.exe"));
|
zip.addLocalFile(path.join(releaseDir, "clash-verge.exe"));
|
||||||
zip.addLocalFile(path.join(releaseDir, "clash-meta.exe"));
|
zip.addLocalFile(path.join(releaseDir, "verge-mihomo.exe"));
|
||||||
zip.addLocalFile(path.join(releaseDir, "clash-meta-alpha.exe"));
|
zip.addLocalFile(path.join(releaseDir, "verge-mihomo-alpha.exe"));
|
||||||
zip.addLocalFolder(path.join(releaseDir, "resources"), "resources");
|
zip.addLocalFolder(path.join(releaseDir, "resources"), "resources");
|
||||||
zip.addLocalFolder(configDir, ".config");
|
zip.addLocalFolder(configDir, ".config");
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
const packageJson = require("../package.json");
|
const packageJson = require("../package.json");
|
||||||
const { version } = packageJson;
|
const { version } = packageJson;
|
||||||
|
const zipFile = `Clash.Verge_${version}_${arch}_portable.zip`;
|
||||||
const zipFile = `Clash.Verge_${version}_${ARCH_MAP[target]}_portable.zip`;
|
|
||||||
zip.writeZip(zipFile);
|
zip.writeZip(zipFile);
|
||||||
|
|
||||||
console.log("[INFO]: create portable zip successfully");
|
console.log("[INFO]: create portable zip successfully");
|
||||||
|
|
||||||
// push release assets
|
|
||||||
if (process.env.GITHUB_TOKEN === undefined) {
|
|
||||||
throw new Error("GITHUB_TOKEN is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = { owner: context.repo.owner, repo: context.repo.repo };
|
|
||||||
const github = getOctokit(process.env.GITHUB_TOKEN);
|
|
||||||
const tag = alpha ? "alpha" : process.env.TAG_NAME || `v${version}`;
|
|
||||||
console.log("[INFO]: upload to ", tag);
|
|
||||||
|
|
||||||
const { data: release } = await github.rest.repos.getReleaseByTag({
|
|
||||||
...options,
|
|
||||||
tag,
|
|
||||||
});
|
|
||||||
|
|
||||||
let assets = release.assets.filter((x) => {
|
|
||||||
return x.name === zipFile;
|
|
||||||
});
|
|
||||||
if (assets.length > 0) {
|
|
||||||
let id = assets[0].id;
|
|
||||||
await github.rest.repos.deleteReleaseAsset({
|
|
||||||
...options,
|
|
||||||
asset_id: id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(release.name);
|
|
||||||
|
|
||||||
await github.rest.repos.uploadReleaseAsset({
|
|
||||||
...options,
|
|
||||||
release_id: release.id,
|
|
||||||
name: zipFile,
|
|
||||||
data: zip.toBuffer(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvePortable().catch(console.error);
|
resolvePortable().catch(console.error);
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import fs from "fs-extra";
|
import fs from "fs";
|
||||||
|
import fsp from "fs/promises";
|
||||||
import zlib from "zlib";
|
import zlib from "zlib";
|
||||||
import tar from "tar";
|
import { extract } from "tar";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import AdmZip from "adm-zip";
|
import AdmZip from "adm-zip";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import proxyAgent from "https-proxy-agent";
|
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
|
import { log_info, log_debug, log_error, log_success } from "./utils.mjs";
|
||||||
|
import { glob } from "glob";
|
||||||
|
|
||||||
const cwd = process.cwd();
|
const cwd = process.cwd();
|
||||||
const TEMP_DIR = path.join(cwd, "node_modules/.verge");
|
const TEMP_DIR = path.join(cwd, "node_modules/.verge");
|
||||||
@@ -61,7 +64,7 @@ const META_ALPHA_MAP = {
|
|||||||
"win32-x64": "mihomo-windows-amd64-compatible",
|
"win32-x64": "mihomo-windows-amd64-compatible",
|
||||||
"win32-ia32": "mihomo-windows-386",
|
"win32-ia32": "mihomo-windows-386",
|
||||||
"win32-arm64": "mihomo-windows-arm64",
|
"win32-arm64": "mihomo-windows-arm64",
|
||||||
"darwin-x64": "mihomo-darwin-amd64",
|
"darwin-x64": "mihomo-darwin-amd64-compatible",
|
||||||
"darwin-arm64": "mihomo-darwin-arm64",
|
"darwin-arm64": "mihomo-darwin-arm64",
|
||||||
"linux-x64": "mihomo-linux-amd64-compatible",
|
"linux-x64": "mihomo-linux-amd64-compatible",
|
||||||
"linux-ia32": "mihomo-linux-386",
|
"linux-ia32": "mihomo-linux-386",
|
||||||
@@ -82,7 +85,7 @@ async function getLatestAlphaVersion() {
|
|||||||
process.env.https_proxy;
|
process.env.https_proxy;
|
||||||
|
|
||||||
if (httpProxy) {
|
if (httpProxy) {
|
||||||
options.agent = proxyAgent(httpProxy);
|
options.agent = new HttpsProxyAgent(httpProxy);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch(META_ALPHA_VERSION_URL, {
|
const response = await fetch(META_ALPHA_VERSION_URL, {
|
||||||
@@ -91,9 +94,9 @@ async function getLatestAlphaVersion() {
|
|||||||
});
|
});
|
||||||
let v = await response.text();
|
let v = await response.text();
|
||||||
META_ALPHA_VERSION = v.trim(); // Trim to remove extra whitespaces
|
META_ALPHA_VERSION = v.trim(); // Trim to remove extra whitespaces
|
||||||
console.log(`Latest alpha version: ${META_ALPHA_VERSION}`);
|
log_info(`Latest alpha version: ${META_ALPHA_VERSION}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching latest alpha version:", error.message);
|
log_error("Error fetching latest alpha version:", error.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +111,7 @@ const META_MAP = {
|
|||||||
"win32-x64": "mihomo-windows-amd64-compatible",
|
"win32-x64": "mihomo-windows-amd64-compatible",
|
||||||
"win32-ia32": "mihomo-windows-386",
|
"win32-ia32": "mihomo-windows-386",
|
||||||
"win32-arm64": "mihomo-windows-arm64",
|
"win32-arm64": "mihomo-windows-arm64",
|
||||||
"darwin-x64": "mihomo-darwin-amd64",
|
"darwin-x64": "mihomo-darwin-amd64-compatible",
|
||||||
"darwin-arm64": "mihomo-darwin-arm64",
|
"darwin-arm64": "mihomo-darwin-arm64",
|
||||||
"linux-x64": "mihomo-linux-amd64-compatible",
|
"linux-x64": "mihomo-linux-amd64-compatible",
|
||||||
"linux-ia32": "mihomo-linux-386",
|
"linux-ia32": "mihomo-linux-386",
|
||||||
@@ -129,7 +132,7 @@ async function getLatestReleaseVersion() {
|
|||||||
process.env.https_proxy;
|
process.env.https_proxy;
|
||||||
|
|
||||||
if (httpProxy) {
|
if (httpProxy) {
|
||||||
options.agent = proxyAgent(httpProxy);
|
options.agent = new HttpsProxyAgent(httpProxy);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch(META_VERSION_URL, {
|
const response = await fetch(META_VERSION_URL, {
|
||||||
@@ -138,9 +141,9 @@ async function getLatestReleaseVersion() {
|
|||||||
});
|
});
|
||||||
let v = await response.text();
|
let v = await response.text();
|
||||||
META_VERSION = v.trim(); // Trim to remove extra whitespaces
|
META_VERSION = v.trim(); // Trim to remove extra whitespaces
|
||||||
console.log(`Latest release version: ${META_VERSION}`);
|
log_info(`Latest release version: ${META_VERSION}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching latest release version:", error.message);
|
log_error("Error fetching latest release version:", error.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,13 +153,13 @@ async function getLatestReleaseVersion() {
|
|||||||
*/
|
*/
|
||||||
if (!META_MAP[`${platform}-${arch}`]) {
|
if (!META_MAP[`${platform}-${arch}`]) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`clash meta alpha unsupported platform "${platform}-${arch}"`
|
`clash meta alpha unsupported platform "${platform}-${arch}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!META_ALPHA_MAP[`${platform}-${arch}`]) {
|
if (!META_ALPHA_MAP[`${platform}-${arch}`]) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`clash meta alpha unsupported platform "${platform}-${arch}"`
|
`clash meta alpha unsupported platform "${platform}-${arch}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,8 +175,8 @@ function clashMetaAlpha() {
|
|||||||
const zipFile = `${name}-${META_ALPHA_VERSION}.${urlExt}`;
|
const zipFile = `${name}-${META_ALPHA_VERSION}.${urlExt}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "clash-meta-alpha",
|
name: "verge-mihomo-alpha",
|
||||||
targetFile: `clash-meta-alpha-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
|
targetFile: `verge-mihomo-alpha-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
|
||||||
exeFile,
|
exeFile,
|
||||||
zipFile,
|
zipFile,
|
||||||
downloadURL,
|
downloadURL,
|
||||||
@@ -189,8 +192,8 @@ function clashMeta() {
|
|||||||
const zipFile = `${name}-${META_VERSION}.${urlExt}`;
|
const zipFile = `${name}-${META_VERSION}.${urlExt}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "clash-meta",
|
name: "verge-mihomo",
|
||||||
targetFile: `clash-meta-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
|
targetFile: `verge-mihomo-${SIDECAR_HOST}${isWin ? ".exe" : ""}`,
|
||||||
exeFile,
|
exeFile,
|
||||||
zipFile,
|
zipFile,
|
||||||
downloadURL,
|
downloadURL,
|
||||||
@@ -205,44 +208,44 @@ async function resolveSidecar(binInfo) {
|
|||||||
const sidecarDir = path.join(cwd, "src-tauri", "sidecar");
|
const sidecarDir = path.join(cwd, "src-tauri", "sidecar");
|
||||||
const sidecarPath = path.join(sidecarDir, targetFile);
|
const sidecarPath = path.join(sidecarDir, targetFile);
|
||||||
|
|
||||||
await fs.mkdirp(sidecarDir);
|
await fsp.mkdir(sidecarDir, { recursive: true });
|
||||||
if (!FORCE && (await fs.pathExists(sidecarPath))) return;
|
if (!FORCE && fs.existsSync(sidecarPath)) return;
|
||||||
|
|
||||||
const tempDir = path.join(TEMP_DIR, name);
|
const tempDir = path.join(TEMP_DIR, name);
|
||||||
const tempZip = path.join(tempDir, zipFile);
|
const tempZip = path.join(tempDir, zipFile);
|
||||||
const tempExe = path.join(tempDir, exeFile);
|
const tempExe = path.join(tempDir, exeFile);
|
||||||
|
|
||||||
await fs.mkdirp(tempDir);
|
await fsp.mkdir(tempDir, { recursive: true });
|
||||||
try {
|
try {
|
||||||
if (!(await fs.pathExists(tempZip))) {
|
if (!fs.existsSync(tempZip)) {
|
||||||
await downloadFile(downloadURL, tempZip);
|
await downloadFile(downloadURL, tempZip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zipFile.endsWith(".zip")) {
|
if (zipFile.endsWith(".zip")) {
|
||||||
const zip = new AdmZip(tempZip);
|
const zip = new AdmZip(tempZip);
|
||||||
zip.getEntries().forEach((entry) => {
|
zip.getEntries().forEach((entry) => {
|
||||||
console.log(`[DEBUG]: "${name}" entry name`, entry.entryName);
|
log_debug(`"${name}" entry name`, entry.entryName);
|
||||||
});
|
});
|
||||||
zip.extractAllTo(tempDir, true);
|
zip.extractAllTo(tempDir, true);
|
||||||
await fs.rename(tempExe, sidecarPath);
|
await fsp.rename(tempExe, sidecarPath);
|
||||||
console.log(`[INFO]: "${name}" unzip finished`);
|
log_success(`unzip finished: "${name}"`);
|
||||||
} else if (zipFile.endsWith(".tgz")) {
|
} else if (zipFile.endsWith(".tgz")) {
|
||||||
// tgz
|
// tgz
|
||||||
await fs.mkdirp(tempDir);
|
await fsp.mkdir(tempDir, { recursive: true });
|
||||||
await tar.extract({
|
await extract({
|
||||||
cwd: tempDir,
|
cwd: tempDir,
|
||||||
file: tempZip,
|
file: tempZip,
|
||||||
//strip: 1, // 可能需要根据实际的 .tgz 文件结构调整
|
//strip: 1, // 可能需要根据实际的 .tgz 文件结构调整
|
||||||
});
|
});
|
||||||
const files = await fs.readdir(tempDir);
|
const files = await fsp.readdir(tempDir);
|
||||||
console.log(`[DEBUG]: "${name}" files in tempDir:`, files);
|
log_debug(`"${name}" files in tempDir:`, files);
|
||||||
const extractedFile = files.find((file) => file.startsWith("虚空终端-"));
|
const extractedFile = files.find((file) => file.startsWith("虚空终端-"));
|
||||||
if (extractedFile) {
|
if (extractedFile) {
|
||||||
const extractedFilePath = path.join(tempDir, extractedFile);
|
const extractedFilePath = path.join(tempDir, extractedFile);
|
||||||
await fs.rename(extractedFilePath, sidecarPath);
|
await fsp.rename(extractedFilePath, sidecarPath);
|
||||||
console.log(`[INFO]: "${name}" file renamed to "${sidecarPath}"`);
|
log_success(`"${name}" file renamed to "${sidecarPath}"`);
|
||||||
execSync(`chmod 755 ${sidecarPath}`);
|
execSync(`chmod 755 ${sidecarPath}`);
|
||||||
console.log(`[INFO]: "${name}" chmod binary finished`);
|
log_success(`chmod binary finished: "${name}"`);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Expected file not found in ${tempDir}`);
|
throw new Error(`Expected file not found in ${tempDir}`);
|
||||||
}
|
}
|
||||||
@@ -252,16 +255,15 @@ async function resolveSidecar(binInfo) {
|
|||||||
const writeStream = fs.createWriteStream(sidecarPath);
|
const writeStream = fs.createWriteStream(sidecarPath);
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
const onError = (error) => {
|
const onError = (error) => {
|
||||||
console.error(`[ERROR]: "${name}" gz failed:`, error.message);
|
log_error(`"${name}" gz failed:`, error.message);
|
||||||
reject(error);
|
reject(error);
|
||||||
};
|
};
|
||||||
readStream
|
readStream
|
||||||
.pipe(zlib.createGunzip().on("error", onError))
|
.pipe(zlib.createGunzip().on("error", onError))
|
||||||
.pipe(writeStream)
|
.pipe(writeStream)
|
||||||
.on("finish", () => {
|
.on("finish", () => {
|
||||||
console.log(`[INFO]: "${name}" gunzip finished`);
|
|
||||||
execSync(`chmod 755 ${sidecarPath}`);
|
execSync(`chmod 755 ${sidecarPath}`);
|
||||||
console.log(`[INFO]: "${name}" chmod binary finished`);
|
log_success(`chmod binary finished: "${name}"`);
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.on("error", onError);
|
.on("error", onError);
|
||||||
@@ -269,35 +271,58 @@ async function resolveSidecar(binInfo) {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 需要删除文件
|
// 需要删除文件
|
||||||
await fs.remove(sidecarPath);
|
await fsp.rm(sidecarPath, { recursive: true, force: true });
|
||||||
throw err;
|
throw err;
|
||||||
} finally {
|
} finally {
|
||||||
// delete temp dir
|
// delete temp dir
|
||||||
await fs.remove(tempDir);
|
await fsp.rm(tempDir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resolveSetDnsScript = () =>
|
||||||
|
resolveResource({
|
||||||
|
file: "set_dns.sh",
|
||||||
|
localPath: path.join(cwd, "scripts/set_dns.sh"),
|
||||||
|
});
|
||||||
|
const resolveUnSetDnsScript = () =>
|
||||||
|
resolveResource({
|
||||||
|
file: "unset_dns.sh",
|
||||||
|
localPath: path.join(cwd, "scripts/unset_dns.sh"),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* download the file to the resources dir
|
* download the file to the resources dir
|
||||||
*/
|
*/
|
||||||
async function resolveResource(binInfo) {
|
async function resolveResource(binInfo) {
|
||||||
const { file, downloadURL } = binInfo;
|
const { file, downloadURL, localPath } = binInfo;
|
||||||
|
|
||||||
const resDir = path.join(cwd, "src-tauri/resources");
|
const resDir = path.join(cwd, "src-tauri/resources");
|
||||||
const targetPath = path.join(resDir, file);
|
const targetPath = path.join(resDir, file);
|
||||||
|
|
||||||
if (!FORCE && (await fs.pathExists(targetPath))) return;
|
if (!FORCE && fs.existsSync(targetPath)) return;
|
||||||
|
|
||||||
await fs.mkdirp(resDir);
|
if (downloadURL) {
|
||||||
await downloadFile(downloadURL, targetPath);
|
await fsp.mkdir(resDir, { recursive: true });
|
||||||
|
await downloadFile(downloadURL, targetPath);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`[INFO]: ${file} finished`);
|
if (localPath) {
|
||||||
|
await fs.copyFile(localPath, targetPath, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error("Error copying file:", err);
|
||||||
|
} else {
|
||||||
|
console.log("File was copied successfully");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log_debug(`copy file finished: "${localPath}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success(`${file} finished`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* download file and save to `path`
|
* download file and save to `path`
|
||||||
*/
|
*/ async function downloadFile(url, path) {
|
||||||
async function downloadFile(url, path) {
|
|
||||||
const options = {};
|
const options = {};
|
||||||
|
|
||||||
const httpProxy =
|
const httpProxy =
|
||||||
@@ -307,7 +332,7 @@ async function downloadFile(url, path) {
|
|||||||
process.env.https_proxy;
|
process.env.https_proxy;
|
||||||
|
|
||||||
if (httpProxy) {
|
if (httpProxy) {
|
||||||
options.agent = proxyAgent(httpProxy);
|
options.agent = new HttpsProxyAgent(httpProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
@@ -316,9 +341,9 @@ async function downloadFile(url, path) {
|
|||||||
headers: { "Content-Type": "application/octet-stream" },
|
headers: { "Content-Type": "application/octet-stream" },
|
||||||
});
|
});
|
||||||
const buffer = await response.arrayBuffer();
|
const buffer = await response.arrayBuffer();
|
||||||
await fs.writeFile(path, new Uint8Array(buffer));
|
await fsp.writeFile(path, new Uint8Array(buffer));
|
||||||
|
|
||||||
console.log(`[INFO]: download finished "${url}"`);
|
log_success(`download finished: ${url}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimpleSC.dll
|
// SimpleSC.dll
|
||||||
@@ -329,87 +354,111 @@ const resolvePlugin = async () => {
|
|||||||
const tempDir = path.join(TEMP_DIR, "SimpleSC");
|
const tempDir = path.join(TEMP_DIR, "SimpleSC");
|
||||||
const tempZip = path.join(
|
const tempZip = path.join(
|
||||||
tempDir,
|
tempDir,
|
||||||
"NSIS_Simple_Service_Plugin_Unicode_1.30.zip"
|
"NSIS_Simple_Service_Plugin_Unicode_1.30.zip",
|
||||||
);
|
);
|
||||||
const tempDll = path.join(tempDir, "SimpleSC.dll");
|
const tempDll = path.join(tempDir, "SimpleSC.dll");
|
||||||
const pluginDir = path.join(process.env.APPDATA, "Local/NSIS");
|
const pluginDir = path.join(process.env.APPDATA, "Local/NSIS");
|
||||||
const pluginPath = path.join(pluginDir, "SimpleSC.dll");
|
const pluginPath = path.join(pluginDir, "SimpleSC.dll");
|
||||||
await fs.mkdirp(pluginDir);
|
await fsp.mkdir(pluginDir, { recursive: true });
|
||||||
await fs.mkdirp(tempDir);
|
await fsp.mkdir(tempDir, { recursive: true });
|
||||||
if (!FORCE && (await fs.pathExists(pluginPath))) return;
|
if (!FORCE && fs.existsSync(pluginPath)) return;
|
||||||
try {
|
try {
|
||||||
if (!(await fs.pathExists(tempZip))) {
|
if (!fs.existsSync(tempZip)) {
|
||||||
await downloadFile(url, tempZip);
|
await downloadFile(url, tempZip);
|
||||||
}
|
}
|
||||||
const zip = new AdmZip(tempZip);
|
const zip = new AdmZip(tempZip);
|
||||||
zip.getEntries().forEach((entry) => {
|
zip.getEntries().forEach((entry) => {
|
||||||
console.log(`[DEBUG]: "SimpleSC" entry name`, entry.entryName);
|
log_debug(`"SimpleSC" entry name`, entry.entryName);
|
||||||
});
|
});
|
||||||
zip.extractAllTo(tempDir, true);
|
zip.extractAllTo(tempDir, true);
|
||||||
await fs.copyFile(tempDll, pluginPath);
|
await fsp.cp(tempDll, pluginPath, { recursive: true, force: true });
|
||||||
console.log(`[INFO]: "SimpleSC" unzip finished`);
|
log_success(`unzip finished: "SimpleSC"`);
|
||||||
} finally {
|
} finally {
|
||||||
await fs.remove(tempDir);
|
await fsp.rm(tempDir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// service chmod
|
// service chmod
|
||||||
const resolveServicePermission = async () => {
|
const resolveServicePermission = async () => {
|
||||||
const serviceExecutables = [
|
const serviceExecutables = [
|
||||||
"clash-verge-service",
|
"clash-verge-service*",
|
||||||
"install-service",
|
"install-service*",
|
||||||
"uninstall-service",
|
"uninstall-service*",
|
||||||
];
|
];
|
||||||
const resDir = path.join(cwd, "src-tauri/resources");
|
const resDir = path.join(cwd, "src-tauri/resources");
|
||||||
for (let f of serviceExecutables) {
|
for (let f of serviceExecutables) {
|
||||||
const targetPath = path.join(resDir, f);
|
// 使用glob模块来处理通配符
|
||||||
if (await fs.pathExists(targetPath)) {
|
const files = glob.sync(path.join(resDir, f));
|
||||||
execSync(`chmod 755 ${targetPath}`);
|
for (let filePath of files) {
|
||||||
console.log(`[INFO]: "${targetPath}" chmod finished`);
|
if (fs.existsSync(filePath)) {
|
||||||
|
execSync(`chmod 755 ${filePath}`);
|
||||||
|
log_success(`chmod finished: "${filePath}"`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 在 resolveResource 函数后添加新函数
|
||||||
|
async function resolveLocales() {
|
||||||
|
const srcLocalesDir = path.join(cwd, "src/locales");
|
||||||
|
const targetLocalesDir = path.join(cwd, "src-tauri/resources/locales");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 确保目标目录存在
|
||||||
|
await fsp.mkdir(targetLocalesDir, { recursive: true });
|
||||||
|
|
||||||
|
// 读取所有语言文件
|
||||||
|
const files = await fsp.readdir(srcLocalesDir);
|
||||||
|
|
||||||
|
// 复制每个文件
|
||||||
|
for (const file of files) {
|
||||||
|
const srcPath = path.join(srcLocalesDir, file);
|
||||||
|
const targetPath = path.join(targetLocalesDir, file);
|
||||||
|
|
||||||
|
await fsp.copyFile(srcPath, targetPath);
|
||||||
|
log_success(`Copied locale file: ${file}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success("All locale files copied successfully");
|
||||||
|
} catch (err) {
|
||||||
|
log_error("Error copying locale files:", err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main
|
* main
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const SERVICE_URL = `https://github.com/clash-verge-rev/clash-verge-service/releases/download/${SIDECAR_HOST}`;
|
const SERVICE_URL = `https://github.com/clash-verge-rev/clash-verge-service/releases/download/${SIDECAR_HOST}`;
|
||||||
|
|
||||||
const resolveService = () => {
|
const resolveService = () => {
|
||||||
let ext = platform === "win32" ? ".exe" : "";
|
let ext = platform === "win32" ? ".exe" : "";
|
||||||
|
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
|
||||||
resolveResource({
|
resolveResource({
|
||||||
file: "clash-verge-service" + ext,
|
file: "clash-verge-service" + suffix + ext,
|
||||||
downloadURL: `${SERVICE_URL}/clash-verge-service${ext}`,
|
downloadURL: `${SERVICE_URL}/clash-verge-service${ext}`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveInstall = () => {
|
const resolveInstall = () => {
|
||||||
let ext = platform === "win32" ? ".exe" : "";
|
let ext = platform === "win32" ? ".exe" : "";
|
||||||
|
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
|
||||||
resolveResource({
|
resolveResource({
|
||||||
file: "install-service" + ext,
|
file: "install-service" + suffix + ext,
|
||||||
downloadURL: `${SERVICE_URL}/install-service${ext}`,
|
downloadURL: `${SERVICE_URL}/install-service${ext}`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveUninstall = () => {
|
const resolveUninstall = () => {
|
||||||
let ext = platform === "win32" ? ".exe" : "";
|
let ext = platform === "win32" ? ".exe" : "";
|
||||||
|
let suffix = platform === "linux" ? "-" + SIDECAR_HOST : "";
|
||||||
|
|
||||||
resolveResource({
|
resolveResource({
|
||||||
file: "uninstall-service" + ext,
|
file: "uninstall-service" + suffix + ext,
|
||||||
downloadURL: `${SERVICE_URL}/uninstall-service${ext}`,
|
downloadURL: `${SERVICE_URL}/uninstall-service${ext}`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveSetDnsScript = () =>
|
|
||||||
resolveResource({
|
|
||||||
file: "set_dns.sh",
|
|
||||||
downloadURL: `https://github.com/clash-verge-rev/set-dns-script/releases/download/script/set_dns.sh`,
|
|
||||||
});
|
|
||||||
const resolveUnSetDnsScript = () =>
|
|
||||||
resolveResource({
|
|
||||||
file: "unset_dns.sh",
|
|
||||||
downloadURL: `https://github.com/clash-verge-rev/set-dns-script/releases/download/script/unset_dns.sh`,
|
|
||||||
});
|
|
||||||
const resolveMmdb = () =>
|
const resolveMmdb = () =>
|
||||||
resolveResource({
|
resolveResource({
|
||||||
file: "Country.mmdb",
|
file: "Country.mmdb",
|
||||||
@@ -431,16 +480,22 @@ const resolveEnableLoopback = () =>
|
|||||||
downloadURL: `https://github.com/Kuingsmile/uwp-tool/releases/download/latest/enableLoopback.exe`,
|
downloadURL: `https://github.com/Kuingsmile/uwp-tool/releases/download/latest/enableLoopback.exe`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const resolveWinSysproxy = () =>
|
||||||
|
resolveResource({
|
||||||
|
file: "sysproxy.exe",
|
||||||
|
downloadURL: `https://github.com/clash-verge-rev/sysproxy/releases/download/${arch}/sysproxy.exe`,
|
||||||
|
});
|
||||||
|
|
||||||
const tasks = [
|
const tasks = [
|
||||||
// { name: "clash", func: resolveClash, retry: 5 },
|
// { name: "clash", func: resolveClash, retry: 5 },
|
||||||
{
|
{
|
||||||
name: "clash-meta-alpha",
|
name: "verge-mihomo-alpha",
|
||||||
func: () =>
|
func: () =>
|
||||||
getLatestAlphaVersion().then(() => resolveSidecar(clashMetaAlpha())),
|
getLatestAlphaVersion().then(() => resolveSidecar(clashMetaAlpha())),
|
||||||
retry: 5,
|
retry: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "clash-meta",
|
name: "verge-mihomo",
|
||||||
func: () =>
|
func: () =>
|
||||||
getLatestReleaseVersion().then(() => resolveSidecar(clashMeta())),
|
getLatestReleaseVersion().then(() => resolveSidecar(clashMeta())),
|
||||||
retry: 5,
|
retry: 5,
|
||||||
@@ -449,8 +504,6 @@ const tasks = [
|
|||||||
{ name: "service", func: resolveService, retry: 5 },
|
{ name: "service", func: resolveService, retry: 5 },
|
||||||
{ name: "install", func: resolveInstall, retry: 5 },
|
{ name: "install", func: resolveInstall, retry: 5 },
|
||||||
{ name: "uninstall", func: resolveUninstall, retry: 5 },
|
{ name: "uninstall", func: resolveUninstall, retry: 5 },
|
||||||
{ name: "set_dns_script", func: resolveSetDnsScript, retry: 5 },
|
|
||||||
{ name: "unset_dns_script", func: resolveUnSetDnsScript, retry: 5 },
|
|
||||||
{ name: "mmdb", func: resolveMmdb, retry: 5 },
|
{ name: "mmdb", func: resolveMmdb, retry: 5 },
|
||||||
{ name: "geosite", func: resolveGeosite, retry: 5 },
|
{ name: "geosite", func: resolveGeosite, retry: 5 },
|
||||||
{ name: "geoip", func: resolveGeoIP, retry: 5 },
|
{ name: "geoip", func: resolveGeoIP, retry: 5 },
|
||||||
@@ -463,24 +516,48 @@ const tasks = [
|
|||||||
{
|
{
|
||||||
name: "service_chmod",
|
name: "service_chmod",
|
||||||
func: resolveServicePermission,
|
func: resolveServicePermission,
|
||||||
retry: 1,
|
retry: 5,
|
||||||
unixOnly: true,
|
unixOnly: platform === "linux" || platform === "darwin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "windows-sysproxy",
|
||||||
|
func: resolveWinSysproxy,
|
||||||
|
retry: 5,
|
||||||
|
winOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "set_dns_script",
|
||||||
|
func: resolveSetDnsScript,
|
||||||
|
retry: 5,
|
||||||
|
macosOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unset_dns_script",
|
||||||
|
func: resolveUnSetDnsScript,
|
||||||
|
retry: 5,
|
||||||
|
macosOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "locales",
|
||||||
|
func: resolveLocales,
|
||||||
|
retry: 2,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
async function runTask() {
|
async function runTask() {
|
||||||
const task = tasks.shift();
|
const task = tasks.shift();
|
||||||
if (!task) return;
|
if (!task) return;
|
||||||
if (task.winOnly && platform !== "win32") return runTask();
|
|
||||||
if (task.linuxOnly && platform !== "linux") return runTask();
|
|
||||||
if (task.unixOnly && platform === "win32") return runTask();
|
if (task.unixOnly && platform === "win32") return runTask();
|
||||||
|
if (task.winOnly && platform !== "win32") return runTask();
|
||||||
|
if (task.macosOnly && platform !== "darwin") return runTask();
|
||||||
|
if (task.linuxOnly && platform !== "linux") return runTask();
|
||||||
|
|
||||||
for (let i = 0; i < task.retry; i++) {
|
for (let i = 0; i < task.retry; i++) {
|
||||||
try {
|
try {
|
||||||
await task.func();
|
await task.func();
|
||||||
break;
|
break;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[ERROR]: task::${task.name} try ${i} ==`, err.message);
|
log_error(`task::${task.name} try ${i} ==`, err.message);
|
||||||
if (i === task.retry - 1) throw err;
|
if (i === task.retry - 1) throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,4 +565,3 @@ async function runTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runTask();
|
runTask();
|
||||||
runTask();
|
|
||||||
66
scripts/publish-version.mjs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// scripts/publish-version.mjs
|
||||||
|
import { spawn } from "child_process";
|
||||||
|
import { existsSync } from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const rootDir = process.cwd();
|
||||||
|
const scriptPath = path.join(rootDir, "scripts", "release-version.mjs");
|
||||||
|
|
||||||
|
if (!existsSync(scriptPath)) {
|
||||||
|
console.error("release-version.mjs not found!");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionArg = process.argv[2];
|
||||||
|
if (!versionArg) {
|
||||||
|
console.error("Usage: pnpm publish-version <version>");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 调用 release-version.mjs
|
||||||
|
const runRelease = () =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const child = spawn("node", [scriptPath, versionArg], { stdio: "inherit" });
|
||||||
|
child.on("exit", (code) => {
|
||||||
|
if (code === 0) resolve();
|
||||||
|
else reject(new Error("release-version failed"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 判断是否需要打 tag
|
||||||
|
function isSemver(version) {
|
||||||
|
return /^v?\d+\.\d+\.\d+(-[0-9A-Za-z-.]+)?$/.test(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
await runRelease();
|
||||||
|
|
||||||
|
let tag = null;
|
||||||
|
if (versionArg === "alpha") {
|
||||||
|
// 读取 package.json 里的主版本
|
||||||
|
const pkg = await import(path.join(rootDir, "package.json"), {
|
||||||
|
assert: { type: "json" },
|
||||||
|
});
|
||||||
|
tag = `v${pkg.default.version}-alpha`;
|
||||||
|
} else if (isSemver(versionArg)) {
|
||||||
|
// 1.2.3 或 v1.2.3
|
||||||
|
tag = versionArg.startsWith("v") ? versionArg : `v${versionArg}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag) {
|
||||||
|
// 打 tag 并推送
|
||||||
|
const { execSync } = await import("child_process");
|
||||||
|
try {
|
||||||
|
execSync(`git tag ${tag}`, { stdio: "inherit" });
|
||||||
|
execSync(`git push origin ${tag}`, { stdio: "inherit" });
|
||||||
|
console.log(`[INFO]: Git tag ${tag} created and pushed.`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[ERROR]: Failed to create or push git tag: ${tag}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("[INFO]: No git tag created for this version.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
253
scripts/release-version.mjs
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
/**
|
||||||
|
* CLI tool to update version numbers in package.json, src-tauri/Cargo.toml, and src-tauri/tauri.conf.json.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* pnpm release-version <version>
|
||||||
|
*
|
||||||
|
* <version> can be:
|
||||||
|
* - A full semver version (e.g., 1.2.3, v1.2.3, 1.2.3-beta, v1.2.3+build)
|
||||||
|
* - A tag: "alpha", "beta", "rc", or "autobuild"
|
||||||
|
* - "alpha", "beta", "rc": Appends the tag to the current base version (e.g., 1.2.3-beta)
|
||||||
|
* - "autobuild": Appends a timestamped autobuild tag (e.g., 1.2.3+autobuild.2406101530)
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* pnpm release-version 1.2.3
|
||||||
|
* pnpm release-version v1.2.3-beta
|
||||||
|
* pnpm release-version beta
|
||||||
|
* pnpm release-version autobuild
|
||||||
|
*
|
||||||
|
* The script will:
|
||||||
|
* - Validate and normalize the version argument
|
||||||
|
* - Update the version field in package.json
|
||||||
|
* - Update the version field in src-tauri/Cargo.toml
|
||||||
|
* - Update the version field in src-tauri/tauri.conf.json
|
||||||
|
*
|
||||||
|
* Errors are logged and the process exits with code 1 on failure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
import { program } from "commander";
|
||||||
|
import { execSync } from "child_process";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 git 短 commit hash
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getGitShortCommit() {
|
||||||
|
try {
|
||||||
|
return execSync("git rev-parse --short HEAD").toString().trim();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("[WARN]: Failed to get git short commit, fallback to 'nogit'");
|
||||||
|
return "nogit";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成短时间戳(格式:YYMMDD)或带 commit(格式:YYMMDD.cc39b27)
|
||||||
|
* @param {boolean} withCommit 是否带 commit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function generateShortTimestamp(withCommit = false) {
|
||||||
|
const now = new Date();
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(now.getDate()).padStart(2, "0");
|
||||||
|
if (withCommit) {
|
||||||
|
const gitShort = getGitShortCommit();
|
||||||
|
return `${month}${day}.${gitShort}`;
|
||||||
|
}
|
||||||
|
return `${month}${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证版本号格式
|
||||||
|
* @param {string} version
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isValidVersion(version) {
|
||||||
|
return /^v?\d+\.\d+\.\d+(-(alpha|beta|rc)(\.\d+)?)?(\+[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*)?$/i.test(
|
||||||
|
version,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准化版本号
|
||||||
|
* @param {string} version
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function normalizeVersion(version) {
|
||||||
|
return version.startsWith("v") ? version : `v${version}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取基础版本号(去掉所有 -tag 和 +build 部分)
|
||||||
|
* @param {string} version
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getBaseVersion(version) {
|
||||||
|
let base = version.replace(/-(alpha|beta|rc)(\.\d+)?/i, "");
|
||||||
|
base = base.replace(/\+[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*/g, "");
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 package.json 版本号
|
||||||
|
* @param {string} newVersion
|
||||||
|
*/
|
||||||
|
async function updatePackageVersion(newVersion) {
|
||||||
|
const _dirname = process.cwd();
|
||||||
|
const packageJsonPath = path.join(_dirname, "package.json");
|
||||||
|
try {
|
||||||
|
const data = await fs.readFile(packageJsonPath, "utf8");
|
||||||
|
const packageJson = JSON.parse(data);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"[INFO]: Current package.json version is: ",
|
||||||
|
packageJson.version,
|
||||||
|
);
|
||||||
|
packageJson.version = newVersion.startsWith("v")
|
||||||
|
? newVersion.slice(1)
|
||||||
|
: newVersion;
|
||||||
|
await fs.writeFile(
|
||||||
|
packageJsonPath,
|
||||||
|
JSON.stringify(packageJson, null, 2),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`[INFO]: package.json version updated to: ${packageJson.version}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating package.json version:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 Cargo.toml 版本号
|
||||||
|
* @param {string} newVersion
|
||||||
|
*/
|
||||||
|
async function updateCargoVersion(newVersion) {
|
||||||
|
const _dirname = process.cwd();
|
||||||
|
const cargoTomlPath = path.join(_dirname, "src-tauri", "Cargo.toml");
|
||||||
|
try {
|
||||||
|
const data = await fs.readFile(cargoTomlPath, "utf8");
|
||||||
|
const lines = data.split("\n");
|
||||||
|
const versionWithoutV = newVersion.startsWith("v")
|
||||||
|
? newVersion.slice(1)
|
||||||
|
: newVersion;
|
||||||
|
const baseVersion = getBaseVersion(versionWithoutV);
|
||||||
|
|
||||||
|
const updatedLines = lines.map((line) => {
|
||||||
|
if (line.trim().startsWith("version =")) {
|
||||||
|
return line.replace(
|
||||||
|
/version\s*=\s*"[^"]+"/,
|
||||||
|
`version = "${baseVersion}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
});
|
||||||
|
|
||||||
|
await fs.writeFile(cargoTomlPath, updatedLines.join("\n"), "utf8");
|
||||||
|
console.log(`[INFO]: Cargo.toml version updated to: ${baseVersion}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating Cargo.toml version:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 tauri.conf.json 版本号
|
||||||
|
* @param {string} newVersion
|
||||||
|
*/
|
||||||
|
async function updateTauriConfigVersion(newVersion) {
|
||||||
|
const _dirname = process.cwd();
|
||||||
|
const tauriConfigPath = path.join(_dirname, "src-tauri", "tauri.conf.json");
|
||||||
|
try {
|
||||||
|
const data = await fs.readFile(tauriConfigPath, "utf8");
|
||||||
|
const tauriConfig = JSON.parse(data);
|
||||||
|
const versionWithoutV = newVersion.startsWith("v")
|
||||||
|
? newVersion.slice(1)
|
||||||
|
: newVersion;
|
||||||
|
const baseVersion = getBaseVersion(versionWithoutV);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"[INFO]: Current tauri.conf.json version is: ",
|
||||||
|
tauriConfig.version,
|
||||||
|
);
|
||||||
|
tauriConfig.version = baseVersion;
|
||||||
|
await fs.writeFile(
|
||||||
|
tauriConfigPath,
|
||||||
|
JSON.stringify(tauriConfig, null, 2),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
console.log(`[INFO]: tauri.conf.json version updated to: ${baseVersion}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating tauri.conf.json version:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前版本号
|
||||||
|
*/
|
||||||
|
async function getCurrentVersion() {
|
||||||
|
const _dirname = process.cwd();
|
||||||
|
const packageJsonPath = path.join(_dirname, "package.json");
|
||||||
|
try {
|
||||||
|
const data = await fs.readFile(packageJsonPath, "utf8");
|
||||||
|
const packageJson = JSON.parse(data);
|
||||||
|
return packageJson.version;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error getting current version:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主函数
|
||||||
|
*/
|
||||||
|
async function main(versionArg) {
|
||||||
|
if (!versionArg) {
|
||||||
|
console.error("Error: Version argument is required");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let newVersion;
|
||||||
|
const validTags = ["alpha", "beta", "rc", "autobuild"];
|
||||||
|
|
||||||
|
if (validTags.includes(versionArg.toLowerCase())) {
|
||||||
|
const currentVersion = await getCurrentVersion();
|
||||||
|
const baseVersion = getBaseVersion(currentVersion);
|
||||||
|
|
||||||
|
if (versionArg.toLowerCase() === "autobuild") {
|
||||||
|
// 格式: 2.3.0+autobuild.250613.cc39b27
|
||||||
|
newVersion = `${baseVersion}+autobuild.${generateShortTimestamp(true)}`;
|
||||||
|
} else {
|
||||||
|
newVersion = `${baseVersion}-${versionArg.toLowerCase()}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isValidVersion(versionArg)) {
|
||||||
|
console.error("Error: Invalid version format");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
newVersion = normalizeVersion(versionArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[INFO]: Updating versions to: ${newVersion}`);
|
||||||
|
await updatePackageVersion(newVersion);
|
||||||
|
await updateCargoVersion(newVersion);
|
||||||
|
await updateTauriConfigVersion(newVersion);
|
||||||
|
console.log("[SUCCESS]: All version updates completed successfully!");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[ERROR]: Failed to update versions:", error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
program
|
||||||
|
.name("pnpm release-version")
|
||||||
|
.description("Update project version numbers")
|
||||||
|
.argument("<version>", "version tag or full version")
|
||||||
|
.action(main)
|
||||||
|
.parse(process.argv);
|
||||||
66
scripts/set_dns.sh
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 验证IPv4地址格式
|
||||||
|
function is_valid_ipv4() {
|
||||||
|
local ip=$1
|
||||||
|
local IFS='.'
|
||||||
|
local -a octets
|
||||||
|
|
||||||
|
[[ ! $ip =~ ^([0-9]+\.){3}[0-9]+$ ]] && return 1
|
||||||
|
read -r -a octets <<<"$ip"
|
||||||
|
[ "${#octets[@]}" -ne 4 ] && return 1
|
||||||
|
|
||||||
|
for octet in "${octets[@]}"; do
|
||||||
|
if ! [[ "$octet" =~ ^[0-9]+$ ]] || ((octet < 0 || octet > 255)); then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 验证IPv6地址格式
|
||||||
|
function is_valid_ipv6() {
|
||||||
|
local ip=$1
|
||||||
|
if [[ ! $ip =~ ^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$ ]] &&
|
||||||
|
[[ ! $ip =~ ^(([0-9a-fA-F]{0,4}:){0,7}:|(:[0-9a-fA-F]{0,4}:){0,6}:[0-9a-fA-F]{0,4})$ ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 验证IP地址是否为有效的IPv4或IPv6
|
||||||
|
function is_valid_ip() {
|
||||||
|
is_valid_ipv4 "$1" || is_valid_ipv6 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查参数
|
||||||
|
[ $# -lt 1 ] && echo "Usage: $0 <IP address>" && exit 1
|
||||||
|
! is_valid_ip "$1" && echo "$1 is not a valid IP address." && exit 1
|
||||||
|
|
||||||
|
# 获取网络接口和硬件端口
|
||||||
|
nic=$(route -n get default | grep "interface" | awk '{print $2}')
|
||||||
|
hardware_port=$(networksetup -listallhardwareports | awk -v dev="$nic" '
|
||||||
|
/Hardware Port:/{port=$0; gsub("Hardware Port: ", "", port)}
|
||||||
|
/Device: /{if ($2 == dev) {print port; exit}}
|
||||||
|
')
|
||||||
|
|
||||||
|
# 获取当前DNS设置
|
||||||
|
original_dns=$(networksetup -getdnsservers "$hardware_port")
|
||||||
|
|
||||||
|
# 检查当前DNS设置是否有效
|
||||||
|
is_valid_dns=false
|
||||||
|
for ip in $original_dns; do
|
||||||
|
ip=$(echo "$ip" | tr -d '[:space:]')
|
||||||
|
if [ -n "$ip" ] && (is_valid_ipv4 "$ip" || is_valid_ipv6 "$ip"); then
|
||||||
|
is_valid_dns=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 更新DNS设置
|
||||||
|
if [ "$is_valid_dns" = false ]; then
|
||||||
|
echo "empty" >.original_dns.txt
|
||||||
|
else
|
||||||
|
echo "$original_dns" >.original_dns.txt
|
||||||
|
fi
|
||||||
|
networksetup -setdnsservers "$hardware_port" "$1"
|
||||||
20
scripts/unset_dns.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
nic=$(route -n get default | grep "interface" | awk '{print $2}')
|
||||||
|
|
||||||
|
hardware_port=$(networksetup -listallhardwareports | awk -v dev="$nic" '
|
||||||
|
/Hardware Port:/{
|
||||||
|
port=$0; gsub("Hardware Port: ", "", port)
|
||||||
|
}
|
||||||
|
/Device: /{
|
||||||
|
if ($2 == dev) {
|
||||||
|
print port;
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
')
|
||||||
|
|
||||||
|
if [ -f .original_dns.txt ]; then
|
||||||
|
original_dns=$(cat .original_dns.txt)
|
||||||
|
networksetup -setdnsservers "$hardware_port" $original_dns
|
||||||
|
rm -rf .original_dns.txt
|
||||||
|
fi
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import fs from "fs-extra";
|
import fs from "fs";
|
||||||
|
import fsp from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const UPDATE_LOG = "UPDATELOG.md";
|
const UPDATE_LOG = "UPDATELOG.md";
|
||||||
@@ -12,11 +13,11 @@ export async function resolveUpdateLog(tag) {
|
|||||||
|
|
||||||
const file = path.join(cwd, UPDATE_LOG);
|
const file = path.join(cwd, UPDATE_LOG);
|
||||||
|
|
||||||
if (!(await fs.pathExists(file))) {
|
if (!fs.existsSync(file)) {
|
||||||
throw new Error("could not found UPDATELOG.md");
|
throw new Error("could not found UPDATELOG.md");
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await fs.readFile(file).then((d) => d.toString("utf8"));
|
const data = await fsp.readFile(file, "utf-8");
|
||||||
|
|
||||||
const map = {};
|
const map = {};
|
||||||
let p = "";
|
let p = "";
|
||||||
@@ -42,3 +43,42 @@ export async function resolveUpdateLog(tag) {
|
|||||||
|
|
||||||
return map[tag].join("\n").trim();
|
return map[tag].join("\n").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function resolveUpdateLogDefault() {
|
||||||
|
const cwd = process.cwd();
|
||||||
|
const file = path.join(cwd, UPDATE_LOG);
|
||||||
|
|
||||||
|
if (!fs.existsSync(file)) {
|
||||||
|
throw new Error("could not found UPDATELOG.md");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await fsp.readFile(file, "utf-8");
|
||||||
|
|
||||||
|
const reTitle = /^## v[\d\.]+/;
|
||||||
|
const reEnd = /^---/;
|
||||||
|
|
||||||
|
let isCapturing = false;
|
||||||
|
let content = [];
|
||||||
|
let firstTag = "";
|
||||||
|
|
||||||
|
for (const line of data.split("\n")) {
|
||||||
|
if (reTitle.test(line) && !isCapturing) {
|
||||||
|
isCapturing = true;
|
||||||
|
firstTag = line.slice(3).trim();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCapturing) {
|
||||||
|
if (reEnd.test(line)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
content.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!firstTag) {
|
||||||
|
throw new Error("could not found any version tag in UPDATELOG.md");
|
||||||
|
}
|
||||||
|
|
||||||
|
return content.join("\n").trim();
|
||||||
|
}
|
||||||
|
|||||||
157
scripts/updater-fixed-webview2.mjs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import fetch from "node-fetch";
|
||||||
|
import { getOctokit, context } from "@actions/github";
|
||||||
|
import { resolveUpdateLog } from "./updatelog.mjs";
|
||||||
|
|
||||||
|
const UPDATE_TAG_NAME = "updater";
|
||||||
|
const UPDATE_JSON_FILE = "update-fixed-webview2.json";
|
||||||
|
const UPDATE_JSON_PROXY = "update-fixed-webview2-proxy.json";
|
||||||
|
|
||||||
|
/// generate update.json
|
||||||
|
/// upload to update tag's release asset
|
||||||
|
async function resolveUpdater() {
|
||||||
|
if (process.env.GITHUB_TOKEN === undefined) {
|
||||||
|
throw new Error("GITHUB_TOKEN is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = { owner: context.repo.owner, repo: context.repo.repo };
|
||||||
|
const github = getOctokit(process.env.GITHUB_TOKEN);
|
||||||
|
|
||||||
|
const { data: tags } = await github.rest.repos.listTags({
|
||||||
|
...options,
|
||||||
|
per_page: 10,
|
||||||
|
page: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// get the latest publish tag
|
||||||
|
const tag = tags.find((t) => t.name.startsWith("v"));
|
||||||
|
|
||||||
|
console.log(tag);
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
const { data: latestRelease } = await github.rest.repos.getReleaseByTag({
|
||||||
|
...options,
|
||||||
|
tag: tag.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
name: tag.name,
|
||||||
|
notes: await resolveUpdateLog(tag.name), // use updatelog.md
|
||||||
|
pub_date: new Date().toISOString(),
|
||||||
|
platforms: {
|
||||||
|
"windows-x86_64": { signature: "", url: "" },
|
||||||
|
"windows-aarch64": { signature: "", url: "" },
|
||||||
|
"windows-x86": { signature: "", url: "" },
|
||||||
|
"windows-i686": { signature: "", url: "" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const promises = latestRelease.assets.map(async (asset) => {
|
||||||
|
const { name, browser_download_url } = asset;
|
||||||
|
|
||||||
|
// win64 url
|
||||||
|
if (name.endsWith("x64_fixed_webview2-setup.nsis.zip")) {
|
||||||
|
updateData.platforms["windows-x86_64"].url = browser_download_url;
|
||||||
|
}
|
||||||
|
// win64 signature
|
||||||
|
if (name.endsWith("x64_fixed_webview2-setup.nsis.zip.sig")) {
|
||||||
|
const sig = await getSignature(browser_download_url);
|
||||||
|
updateData.platforms["windows-x86_64"].signature = sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32 url
|
||||||
|
if (name.endsWith("x86_fixed_webview2-setup.nsis.zip")) {
|
||||||
|
updateData.platforms["windows-x86"].url = browser_download_url;
|
||||||
|
updateData.platforms["windows-i686"].url = browser_download_url;
|
||||||
|
}
|
||||||
|
// win32 signature
|
||||||
|
if (name.endsWith("x86_fixed_webview2-setup.nsis.zip.sig")) {
|
||||||
|
const sig = await getSignature(browser_download_url);
|
||||||
|
updateData.platforms["windows-x86"].signature = sig;
|
||||||
|
updateData.platforms["windows-i686"].signature = sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// win arm url
|
||||||
|
if (name.endsWith("arm64_fixed_webview2-setup.nsis.zip")) {
|
||||||
|
updateData.platforms["windows-aarch64"].url = browser_download_url;
|
||||||
|
}
|
||||||
|
// win arm signature
|
||||||
|
if (name.endsWith("arm64_fixed_webview2-setup.nsis.zip.sig")) {
|
||||||
|
const sig = await getSignature(browser_download_url);
|
||||||
|
updateData.platforms["windows-aarch64"].signature = sig;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.allSettled(promises);
|
||||||
|
console.log(updateData);
|
||||||
|
|
||||||
|
// maybe should test the signature as well
|
||||||
|
// delete the null field
|
||||||
|
Object.entries(updateData.platforms).forEach(([key, value]) => {
|
||||||
|
if (!value.url) {
|
||||||
|
console.log(`[Error]: failed to parse release for "${key}"`);
|
||||||
|
delete updateData.platforms[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 生成一个代理github的更新文件
|
||||||
|
// 使用 https://hub.fastgit.xyz/ 做github资源的加速
|
||||||
|
const updateDataNew = JSON.parse(JSON.stringify(updateData));
|
||||||
|
|
||||||
|
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
|
||||||
|
if (value.url) {
|
||||||
|
updateDataNew.platforms[key].url =
|
||||||
|
"https://download.clashverge.dev/" + value.url;
|
||||||
|
} else {
|
||||||
|
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// update the update.json
|
||||||
|
const { data: updateRelease } = await github.rest.repos.getReleaseByTag({
|
||||||
|
...options,
|
||||||
|
tag: UPDATE_TAG_NAME,
|
||||||
|
});
|
||||||
|
|
||||||
|
// delete the old assets
|
||||||
|
for (let asset of updateRelease.assets) {
|
||||||
|
if (asset.name === UPDATE_JSON_FILE) {
|
||||||
|
await github.rest.repos.deleteReleaseAsset({
|
||||||
|
...options,
|
||||||
|
asset_id: asset.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asset.name === UPDATE_JSON_PROXY) {
|
||||||
|
await github.rest.repos
|
||||||
|
.deleteReleaseAsset({ ...options, asset_id: asset.id })
|
||||||
|
.catch(console.error); // do not break the pipeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload new assets
|
||||||
|
await github.rest.repos.uploadReleaseAsset({
|
||||||
|
...options,
|
||||||
|
release_id: updateRelease.id,
|
||||||
|
name: UPDATE_JSON_FILE,
|
||||||
|
data: JSON.stringify(updateData, null, 2),
|
||||||
|
});
|
||||||
|
|
||||||
|
await github.rest.repos.uploadReleaseAsset({
|
||||||
|
...options,
|
||||||
|
release_id: updateRelease.id,
|
||||||
|
name: UPDATE_JSON_PROXY,
|
||||||
|
data: JSON.stringify(updateDataNew, null, 2),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the signature file content
|
||||||
|
async function getSignature(url) {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: { "Content-Type": "application/octet-stream" },
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveUpdater().catch(console.error);
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import { getOctokit, context } from "@actions/github";
|
import { getOctokit, context } from "@actions/github";
|
||||||
import { resolveUpdateLog } from "./updatelog.mjs";
|
import { resolveUpdateLog, resolveUpdateLogDefault } from "./updatelog.mjs";
|
||||||
|
|
||||||
|
// Add stable update JSON filenames
|
||||||
const UPDATE_TAG_NAME = "updater";
|
const UPDATE_TAG_NAME = "updater";
|
||||||
const UPDATE_JSON_FILE = "update.json";
|
const UPDATE_JSON_FILE = "update.json";
|
||||||
const UPDATE_JSON_PROXY = "update-proxy.json";
|
const UPDATE_JSON_PROXY = "update-proxy.json";
|
||||||
|
// Add alpha update JSON filenames
|
||||||
|
const ALPHA_TAG_NAME = "updater-alpha";
|
||||||
|
const ALPHA_UPDATE_JSON_FILE = "update.json";
|
||||||
|
const ALPHA_UPDATE_JSON_PROXY = "update-proxy.json";
|
||||||
|
|
||||||
/// generate update.json
|
/// generate update.json
|
||||||
/// upload to update tag's release asset
|
/// upload to update tag's release asset
|
||||||
@@ -16,171 +21,293 @@ async function resolveUpdater() {
|
|||||||
const options = { owner: context.repo.owner, repo: context.repo.repo };
|
const options = { owner: context.repo.owner, repo: context.repo.repo };
|
||||||
const github = getOctokit(process.env.GITHUB_TOKEN);
|
const github = getOctokit(process.env.GITHUB_TOKEN);
|
||||||
|
|
||||||
const { data: tags } = await github.rest.repos.listTags({
|
// Fetch all tags using pagination
|
||||||
...options,
|
let allTags = [];
|
||||||
per_page: 10,
|
let page = 1;
|
||||||
page: 1,
|
const perPage = 100;
|
||||||
});
|
|
||||||
|
|
||||||
// get the latest publish tag
|
while (true) {
|
||||||
const tag = tags.find((t) => t.name.startsWith("v"));
|
const { data: pageTags } = await github.rest.repos.listTags({
|
||||||
|
...options,
|
||||||
|
per_page: perPage,
|
||||||
|
page: page,
|
||||||
|
});
|
||||||
|
|
||||||
console.log(tag);
|
allTags = allTags.concat(pageTags);
|
||||||
console.log();
|
|
||||||
|
|
||||||
const { data: latestRelease } = await github.rest.repos.getReleaseByTag({
|
// Break if we received fewer tags than requested (last page)
|
||||||
...options,
|
if (pageTags.length < perPage) {
|
||||||
tag: tag.name,
|
break;
|
||||||
});
|
|
||||||
|
|
||||||
const updateData = {
|
|
||||||
name: tag.name,
|
|
||||||
notes: await resolveUpdateLog(tag.name), // use updatelog.md
|
|
||||||
pub_date: new Date().toISOString(),
|
|
||||||
platforms: {
|
|
||||||
win64: { signature: "", url: "" }, // compatible with older formats
|
|
||||||
linux: { signature: "", url: "" }, // compatible with older formats
|
|
||||||
darwin: { signature: "", url: "" }, // compatible with older formats
|
|
||||||
"darwin-aarch64": { signature: "", url: "" },
|
|
||||||
"darwin-intel": { signature: "", url: "" },
|
|
||||||
"darwin-x86_64": { signature: "", url: "" },
|
|
||||||
"linux-x86_64": { signature: "", url: "" },
|
|
||||||
"linux-aarch64": { signature: "", url: "" },
|
|
||||||
"linux-armv7": { signature: "", url: "" },
|
|
||||||
"windows-x86_64": { signature: "", url: "" },
|
|
||||||
"windows-aarch64": { signature: "", url: "" },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const promises = latestRelease.assets.map(async (asset) => {
|
|
||||||
const { name, browser_download_url } = asset;
|
|
||||||
|
|
||||||
// win64 url
|
|
||||||
if (name.endsWith("x64-setup.nsis.zip")) {
|
|
||||||
updateData.platforms.win64.url = browser_download_url;
|
|
||||||
updateData.platforms["windows-x86_64"].url = browser_download_url;
|
|
||||||
}
|
|
||||||
// win64 signature
|
|
||||||
if (name.endsWith("x64-setup.nsis.zip.sig")) {
|
|
||||||
const sig = await getSignature(browser_download_url);
|
|
||||||
updateData.platforms.win64.signature = sig;
|
|
||||||
updateData.platforms["windows-x86_64"].signature = sig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// win arm url
|
page++;
|
||||||
if (name.endsWith("arm64-setup.nsis.zip")) {
|
|
||||||
updateData.platforms["windows-aarch64"].url = browser_download_url;
|
|
||||||
}
|
|
||||||
// win arm signature
|
|
||||||
if (name.endsWith("arm64-setup.nsis.zip.sig")) {
|
|
||||||
const sig = await getSignature(browser_download_url);
|
|
||||||
updateData.platforms["windows-aarch64"].signature = sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// darwin url (intel)
|
|
||||||
if (name.endsWith(".app.tar.gz") && !name.includes("aarch")) {
|
|
||||||
updateData.platforms.darwin.url = browser_download_url;
|
|
||||||
updateData.platforms["darwin-intel"].url = browser_download_url;
|
|
||||||
updateData.platforms["darwin-x86_64"].url = browser_download_url;
|
|
||||||
}
|
|
||||||
// darwin signature (intel)
|
|
||||||
if (name.endsWith(".app.tar.gz.sig") && !name.includes("aarch")) {
|
|
||||||
const sig = await getSignature(browser_download_url);
|
|
||||||
updateData.platforms.darwin.signature = sig;
|
|
||||||
updateData.platforms["darwin-intel"].signature = sig;
|
|
||||||
updateData.platforms["darwin-x86_64"].signature = sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// darwin url (aarch)
|
|
||||||
if (name.endsWith("aarch64.app.tar.gz")) {
|
|
||||||
updateData.platforms["darwin-aarch64"].url = browser_download_url;
|
|
||||||
}
|
|
||||||
// darwin signature (aarch)
|
|
||||||
if (name.endsWith("aarch64.app.tar.gz.sig")) {
|
|
||||||
const sig = await getSignature(browser_download_url);
|
|
||||||
updateData.platforms["darwin-aarch64"].signature = sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// linux x64 url
|
|
||||||
if (name.endsWith("amd64.AppImage.tar.gz")) {
|
|
||||||
updateData.platforms.linux.url = browser_download_url;
|
|
||||||
updateData.platforms["linux-x86_64"].url = browser_download_url;
|
|
||||||
// 暂时使用x64版本的url和sig,使得可以检查更新,但aarch64版本还不支持构建appimage
|
|
||||||
updateData.platforms["linux-aarch64"].url = browser_download_url;
|
|
||||||
updateData.platforms["linux-armv7"].url = browser_download_url;
|
|
||||||
}
|
|
||||||
// linux x64 signature
|
|
||||||
if (name.endsWith("amd64.AppImage.tar.gz.sig")) {
|
|
||||||
const sig = await getSignature(browser_download_url);
|
|
||||||
updateData.platforms.linux.signature = sig;
|
|
||||||
updateData.platforms["linux-x86_64"].signature = sig;
|
|
||||||
// 暂时使用x64版本的url和sig,使得可以检查更新,但aarch64版本还不支持构建appimage
|
|
||||||
updateData.platforms["linux-aarch64"].signature = sig;
|
|
||||||
updateData.platforms["linux-armv7"].signature = sig;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.allSettled(promises);
|
|
||||||
console.log(updateData);
|
|
||||||
|
|
||||||
// maybe should test the signature as well
|
|
||||||
// delete the null field
|
|
||||||
Object.entries(updateData.platforms).forEach(([key, value]) => {
|
|
||||||
if (!value.url) {
|
|
||||||
console.log(`[Error]: failed to parse release for "${key}"`);
|
|
||||||
delete updateData.platforms[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 生成一个代理github的更新文件
|
|
||||||
// 使用 https://hub.fastgit.xyz/ 做github资源的加速
|
|
||||||
const updateDataNew = JSON.parse(JSON.stringify(updateData));
|
|
||||||
|
|
||||||
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
|
|
||||||
if (value.url) {
|
|
||||||
updateDataNew.platforms[key].url =
|
|
||||||
"https://mirror.ghproxy.com/" + value.url;
|
|
||||||
} else {
|
|
||||||
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// update the update.json
|
|
||||||
const { data: updateRelease } = await github.rest.repos.getReleaseByTag({
|
|
||||||
...options,
|
|
||||||
tag: UPDATE_TAG_NAME,
|
|
||||||
});
|
|
||||||
|
|
||||||
// delete the old assets
|
|
||||||
for (let asset of updateRelease.assets) {
|
|
||||||
if (asset.name === UPDATE_JSON_FILE) {
|
|
||||||
await github.rest.repos.deleteReleaseAsset({
|
|
||||||
...options,
|
|
||||||
asset_id: asset.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asset.name === UPDATE_JSON_PROXY) {
|
|
||||||
await github.rest.repos
|
|
||||||
.deleteReleaseAsset({ ...options, asset_id: asset.id })
|
|
||||||
.catch(console.error); // do not break the pipeline
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload new assets
|
const tags = allTags;
|
||||||
await github.rest.repos.uploadReleaseAsset({
|
console.log(`Retrieved ${tags.length} tags in total`);
|
||||||
...options,
|
|
||||||
release_id: updateRelease.id,
|
|
||||||
name: UPDATE_JSON_FILE,
|
|
||||||
data: JSON.stringify(updateData, null, 2),
|
|
||||||
});
|
|
||||||
|
|
||||||
await github.rest.repos.uploadReleaseAsset({
|
// More flexible tag detection with regex patterns
|
||||||
...options,
|
const stableTagRegex = /^v\d+\.\d+\.\d+$/; // Matches vX.Y.Z format
|
||||||
release_id: updateRelease.id,
|
// const preReleaseRegex = /^v\d+\.\d+\.\d+-(alpha|beta|rc|pre)/i; // Matches vX.Y.Z-alpha/beta/rc format
|
||||||
name: UPDATE_JSON_PROXY,
|
const preReleaseRegex = /^(alpha|beta|rc|pre)$/i; // Matches exact alpha/beta/rc/pre tags
|
||||||
data: JSON.stringify(updateDataNew, null, 2),
|
|
||||||
});
|
// Get the latest stable tag and pre-release tag
|
||||||
|
const stableTag = tags.find((t) => stableTagRegex.test(t.name));
|
||||||
|
const preReleaseTag = tags.find((t) => preReleaseRegex.test(t.name));
|
||||||
|
|
||||||
|
console.log("All tags:", tags.map((t) => t.name).join(", "));
|
||||||
|
console.log("Stable tag:", stableTag ? stableTag.name : "None found");
|
||||||
|
console.log(
|
||||||
|
"Pre-release tag:",
|
||||||
|
preReleaseTag ? preReleaseTag.name : "None found",
|
||||||
|
);
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
// Process stable release
|
||||||
|
if (stableTag) {
|
||||||
|
await processRelease(github, options, stableTag, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process pre-release if found
|
||||||
|
if (preReleaseTag) {
|
||||||
|
await processRelease(github, options, preReleaseTag, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process a release (stable or alpha) and generate update files
|
||||||
|
async function processRelease(github, options, tag, isAlpha) {
|
||||||
|
if (!tag) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data: release } = await github.rest.repos.getReleaseByTag({
|
||||||
|
...options,
|
||||||
|
tag: tag.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
name: tag.name,
|
||||||
|
notes: await resolveUpdateLog(tag.name).catch(() =>
|
||||||
|
resolveUpdateLogDefault().catch(() => "No changelog available"),
|
||||||
|
),
|
||||||
|
pub_date: new Date().toISOString(),
|
||||||
|
platforms: {
|
||||||
|
win64: { signature: "", url: "" }, // compatible with older formats
|
||||||
|
linux: { signature: "", url: "" }, // compatible with older formats
|
||||||
|
darwin: { signature: "", url: "" }, // compatible with older formats
|
||||||
|
"darwin-aarch64": { signature: "", url: "" },
|
||||||
|
"darwin-intel": { signature: "", url: "" },
|
||||||
|
"darwin-x86_64": { signature: "", url: "" },
|
||||||
|
"linux-x86_64": { signature: "", url: "" },
|
||||||
|
"linux-x86": { signature: "", url: "" },
|
||||||
|
"linux-i686": { signature: "", url: "" },
|
||||||
|
"linux-aarch64": { signature: "", url: "" },
|
||||||
|
"linux-armv7": { signature: "", url: "" },
|
||||||
|
"windows-x86_64": { signature: "", url: "" },
|
||||||
|
"windows-aarch64": { signature: "", url: "" },
|
||||||
|
"windows-x86": { signature: "", url: "" },
|
||||||
|
"windows-i686": { signature: "", url: "" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const promises = release.assets.map(async (asset) => {
|
||||||
|
const { name, browser_download_url } = asset;
|
||||||
|
|
||||||
|
// Process all the platform URL and signature data
|
||||||
|
// win64 url
|
||||||
|
if (name.endsWith("x64-setup.exe")) {
|
||||||
|
updateData.platforms.win64.url = browser_download_url;
|
||||||
|
updateData.platforms["windows-x86_64"].url = browser_download_url;
|
||||||
|
}
|
||||||
|
// win64 signature
|
||||||
|
if (name.endsWith("x64-setup.exe.sig")) {
|
||||||
|
const sig = await getSignature(browser_download_url);
|
||||||
|
updateData.platforms.win64.signature = sig;
|
||||||
|
updateData.platforms["windows-x86_64"].signature = sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32 url
|
||||||
|
if (name.endsWith("x86-setup.exe")) {
|
||||||
|
updateData.platforms["windows-x86"].url = browser_download_url;
|
||||||
|
updateData.platforms["windows-i686"].url = browser_download_url;
|
||||||
|
}
|
||||||
|
// win32 signature
|
||||||
|
if (name.endsWith("x86-setup.exe.sig")) {
|
||||||
|
const sig = await getSignature(browser_download_url);
|
||||||
|
updateData.platforms["windows-x86"].signature = sig;
|
||||||
|
updateData.platforms["windows-i686"].signature = sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// win arm url
|
||||||
|
if (name.endsWith("arm64-setup.exe")) {
|
||||||
|
updateData.platforms["windows-aarch64"].url = browser_download_url;
|
||||||
|
}
|
||||||
|
// win arm signature
|
||||||
|
if (name.endsWith("arm64-setup.exe.sig")) {
|
||||||
|
const sig = await getSignature(browser_download_url);
|
||||||
|
updateData.platforms["windows-aarch64"].signature = sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// darwin url (intel)
|
||||||
|
if (name.endsWith(".app.tar.gz") && !name.includes("aarch")) {
|
||||||
|
updateData.platforms.darwin.url = browser_download_url;
|
||||||
|
updateData.platforms["darwin-intel"].url = browser_download_url;
|
||||||
|
updateData.platforms["darwin-x86_64"].url = browser_download_url;
|
||||||
|
}
|
||||||
|
// darwin signature (intel)
|
||||||
|
if (name.endsWith(".app.tar.gz.sig") && !name.includes("aarch")) {
|
||||||
|
const sig = await getSignature(browser_download_url);
|
||||||
|
updateData.platforms.darwin.signature = sig;
|
||||||
|
updateData.platforms["darwin-intel"].signature = sig;
|
||||||
|
updateData.platforms["darwin-x86_64"].signature = sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// darwin url (aarch)
|
||||||
|
if (name.endsWith("aarch64.app.tar.gz")) {
|
||||||
|
updateData.platforms["darwin-aarch64"].url = browser_download_url;
|
||||||
|
// 使linux可以检查更新
|
||||||
|
updateData.platforms.linux.url = browser_download_url;
|
||||||
|
updateData.platforms["linux-x86_64"].url = browser_download_url;
|
||||||
|
updateData.platforms["linux-x86"].url = browser_download_url;
|
||||||
|
updateData.platforms["linux-i686"].url = browser_download_url;
|
||||||
|
updateData.platforms["linux-aarch64"].url = browser_download_url;
|
||||||
|
updateData.platforms["linux-armv7"].url = browser_download_url;
|
||||||
|
}
|
||||||
|
// darwin signature (aarch)
|
||||||
|
if (name.endsWith("aarch64.app.tar.gz.sig")) {
|
||||||
|
const sig = await getSignature(browser_download_url);
|
||||||
|
updateData.platforms["darwin-aarch64"].signature = sig;
|
||||||
|
updateData.platforms.linux.signature = sig;
|
||||||
|
updateData.platforms["linux-x86_64"].signature = sig;
|
||||||
|
updateData.platforms["linux-x86"].url = browser_download_url;
|
||||||
|
updateData.platforms["linux-i686"].url = browser_download_url;
|
||||||
|
updateData.platforms["linux-aarch64"].signature = sig;
|
||||||
|
updateData.platforms["linux-armv7"].signature = sig;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.allSettled(promises);
|
||||||
|
console.log(updateData);
|
||||||
|
|
||||||
|
// maybe should test the signature as well
|
||||||
|
// delete the null field
|
||||||
|
Object.entries(updateData.platforms).forEach(([key, value]) => {
|
||||||
|
if (!value.url) {
|
||||||
|
console.log(`[Error]: failed to parse release for "${key}"`);
|
||||||
|
delete updateData.platforms[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate a proxy update file for accelerated GitHub resources
|
||||||
|
const updateDataNew = JSON.parse(JSON.stringify(updateData));
|
||||||
|
|
||||||
|
Object.entries(updateDataNew.platforms).forEach(([key, value]) => {
|
||||||
|
if (value.url) {
|
||||||
|
updateDataNew.platforms[key].url =
|
||||||
|
"https://download.clashverge.dev/" + value.url;
|
||||||
|
} else {
|
||||||
|
console.log(`[Error]: updateDataNew.platforms.${key} is null`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the appropriate updater release based on isAlpha flag
|
||||||
|
const releaseTag = isAlpha ? ALPHA_TAG_NAME : UPDATE_TAG_NAME;
|
||||||
|
console.log(
|
||||||
|
`Processing ${isAlpha ? "alpha" : "stable"} release:`,
|
||||||
|
releaseTag,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let updateRelease;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to get the existing release
|
||||||
|
const response = await github.rest.repos.getReleaseByTag({
|
||||||
|
...options,
|
||||||
|
tag: releaseTag,
|
||||||
|
});
|
||||||
|
updateRelease = response.data;
|
||||||
|
console.log(
|
||||||
|
`Found existing ${releaseTag} release with ID: ${updateRelease.id}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
// If release doesn't exist, create it
|
||||||
|
if (error.status === 404) {
|
||||||
|
console.log(
|
||||||
|
`Release with tag ${releaseTag} not found, creating new release...`,
|
||||||
|
);
|
||||||
|
const createResponse = await github.rest.repos.createRelease({
|
||||||
|
...options,
|
||||||
|
tag_name: releaseTag,
|
||||||
|
name: isAlpha
|
||||||
|
? "Auto-update Alpha Channel"
|
||||||
|
: "Auto-update Stable Channel",
|
||||||
|
body: `This release contains the update information for ${isAlpha ? "alpha" : "stable"} channel.`,
|
||||||
|
prerelease: isAlpha,
|
||||||
|
});
|
||||||
|
updateRelease = createResponse.data;
|
||||||
|
console.log(
|
||||||
|
`Created new ${releaseTag} release with ID: ${updateRelease.id}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// If it's another error, throw it
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File names based on release type
|
||||||
|
const jsonFile = isAlpha ? ALPHA_UPDATE_JSON_FILE : UPDATE_JSON_FILE;
|
||||||
|
const proxyFile = isAlpha ? ALPHA_UPDATE_JSON_PROXY : UPDATE_JSON_PROXY;
|
||||||
|
|
||||||
|
// Delete existing assets with these names
|
||||||
|
for (let asset of updateRelease.assets) {
|
||||||
|
if (asset.name === jsonFile) {
|
||||||
|
await github.rest.repos.deleteReleaseAsset({
|
||||||
|
...options,
|
||||||
|
asset_id: asset.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asset.name === proxyFile) {
|
||||||
|
await github.rest.repos
|
||||||
|
.deleteReleaseAsset({ ...options, asset_id: asset.id })
|
||||||
|
.catch(console.error); // do not break the pipeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload new assets
|
||||||
|
await github.rest.repos.uploadReleaseAsset({
|
||||||
|
...options,
|
||||||
|
release_id: updateRelease.id,
|
||||||
|
name: jsonFile,
|
||||||
|
data: JSON.stringify(updateData, null, 2),
|
||||||
|
});
|
||||||
|
|
||||||
|
await github.rest.repos.uploadReleaseAsset({
|
||||||
|
...options,
|
||||||
|
release_id: updateRelease.id,
|
||||||
|
name: proxyFile,
|
||||||
|
data: JSON.stringify(updateDataNew, null, 2),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Successfully uploaded ${isAlpha ? "alpha" : "stable"} update files to ${releaseTag}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to process ${isAlpha ? "alpha" : "stable"} release:`,
|
||||||
|
error.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.status === 404) {
|
||||||
|
console.log(`Release not found for tag: ${tag.name}, skipping...`);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
`Failed to get release for tag: ${tag.name}`,
|
||||||
|
error.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the signature file content
|
// get the signature file content
|
||||||
|
|||||||
11
scripts/utils.mjs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import clc from "cli-color";
|
||||||
|
|
||||||
|
export const log_success = (msg, ...optionalParams) =>
|
||||||
|
console.log(clc.green(msg), ...optionalParams);
|
||||||
|
export const log_error = (msg, ...optionalParams) =>
|
||||||
|
console.log(clc.red(msg), ...optionalParams);
|
||||||
|
export const log_info = (msg, ...optionalParams) =>
|
||||||
|
console.log(clc.bgBlue(msg), ...optionalParams);
|
||||||
|
var debugMsg = clc.xterm(245);
|
||||||
|
export const log_debug = (msg, ...optionalParams) =>
|
||||||
|
console.log(debugMsg(msg), ...optionalParams);
|
||||||
1
src-tauri/.clippy.toml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
avoid-breaking-exported-api = true
|
||||||
2
src-tauri/.gitignore
vendored
@@ -1,6 +1,8 @@
|
|||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
/target/
|
/target/
|
||||||
|
gen/
|
||||||
WixTools
|
WixTools
|
||||||
resources
|
resources
|
||||||
sidecar
|
sidecar
|
||||||
|
|
||||||
|
|||||||
6140
src-tauri/Cargo.lock
generated
139
src-tauri/Cargo.toml
Normal file → Executable file
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "clash-verge"
|
name = "clash-verge"
|
||||||
version = "1.6.1"
|
version = "2.3.1"
|
||||||
description = "clash verge"
|
description = "clash verge"
|
||||||
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
@@ -9,44 +9,99 @@ default-run = "clash-verge"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
[package.metadata.bundle]
|
||||||
|
identifier = "io.github.clash-verge-rev.clash-verge-rev"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1", features = [] }
|
tauri-build = { version = "2.2.0", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
warp = "0.3"
|
warp = "0.3.7"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0.98"
|
||||||
dirs = "5.0"
|
dirs = "6.0"
|
||||||
open = "5.0"
|
open = "5.3.2"
|
||||||
log = "0.4"
|
log = "0.4.27"
|
||||||
dunce = "1.0"
|
dunce = "1.0.5"
|
||||||
log4rs = "1"
|
log4rs = "1.3.0"
|
||||||
nanoid = "0.4"
|
nanoid = "0.4"
|
||||||
chrono = "0.4"
|
chrono = "0.4.41"
|
||||||
sysinfo = "0.30"
|
sysinfo = "0.35.2"
|
||||||
boa_engine = "0.18"
|
boa_engine = "0.20.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0.140"
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9.34-deprecated"
|
||||||
once_cell = "1.18"
|
once_cell = "1.21.3"
|
||||||
|
lazy_static = "1.5.0"
|
||||||
port_scanner = "0.1.5"
|
port_scanner = "0.1.5"
|
||||||
delay_timer = "0.11.5"
|
delay_timer = "0.11.6"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12.4"
|
||||||
percent-encoding = "2.3.1"
|
percent-encoding = "2.3.1"
|
||||||
window-shadows = { version = "0.2" }
|
tokio = { version = "1.45.1", features = [
|
||||||
tokio = { version = "1", features = ["full"] }
|
"rt-multi-thread",
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
"macros",
|
||||||
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
|
"time",
|
||||||
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
|
"sync",
|
||||||
auto-launch = { git="https://github.com/zzzgydi/auto-launch", branch = "main" }
|
] }
|
||||||
tauri = { version = "1.6", features = [ "fs-read-file", "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
reqwest = { version = "0.12.20", features = ["json", "rustls-tls", "cookies"] }
|
||||||
|
regex = "1.11.1"
|
||||||
|
sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs" }
|
||||||
|
image = "0.25.6"
|
||||||
|
imageproc = "0.25.0"
|
||||||
|
tauri = { version = "2.5.1", features = [
|
||||||
|
"protocol-asset",
|
||||||
|
"devtools",
|
||||||
|
"tray-icon",
|
||||||
|
"image-ico",
|
||||||
|
"image-png",
|
||||||
|
] }
|
||||||
|
network-interface = { version = "2.0.1", features = ["serde"] }
|
||||||
|
tauri-plugin-shell = "2.2.2"
|
||||||
|
tauri-plugin-dialog = "2.2.2"
|
||||||
|
tauri-plugin-fs = "2.3.0"
|
||||||
|
tauri-plugin-process = "2.2.2"
|
||||||
|
tauri-plugin-clipboard-manager = "2.2.3"
|
||||||
|
tauri-plugin-deep-link = "2.3.0"
|
||||||
|
tauri-plugin-devtools = "2.0.0"
|
||||||
|
tauri-plugin-window-state = "2.2.3"
|
||||||
|
zip = "4.1.0"
|
||||||
|
reqwest_dav = "0.2.1"
|
||||||
|
aes-gcm = { version = "0.10.3", features = ["std"] }
|
||||||
|
base64 = "0.22.1"
|
||||||
|
getrandom = "0.3.3"
|
||||||
|
tokio-tungstenite = "0.27.0"
|
||||||
|
futures = "0.3.31"
|
||||||
|
sys-locale = "0.3.2"
|
||||||
|
async-trait = "0.1.88"
|
||||||
|
mihomo_api = { path = "src_crates/crate_mihomo_api" }
|
||||||
|
ab_glyph = "0.2.29"
|
||||||
|
tungstenite = "0.27.0"
|
||||||
|
libc = "0.2.173"
|
||||||
|
gethostname = "1.0.2"
|
||||||
|
hmac = "0.12.1"
|
||||||
|
sha2 = "0.10.9"
|
||||||
|
hex = "0.4.3"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
runas = "=1.2.0"
|
runas = "=1.2.0"
|
||||||
deelevate = "0.2.0"
|
deelevate = "0.2.0"
|
||||||
winreg = "0.52.0"
|
winreg = "0.55.0"
|
||||||
|
winapi = { version = "0.3.9", features = [
|
||||||
|
"winbase",
|
||||||
|
"fileapi",
|
||||||
|
"winnt",
|
||||||
|
"handleapi",
|
||||||
|
"errhandlingapi",
|
||||||
|
"minwindef",
|
||||||
|
"winerror",
|
||||||
|
] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
#openssl
|
|
||||||
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||||
|
tauri-plugin-autostart = "2.4.0"
|
||||||
|
tauri-plugin-global-shortcut = "2.2.1"
|
||||||
|
tauri-plugin-updater = "2.8.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["custom-protocol"]
|
default = ["custom-protocol"]
|
||||||
@@ -58,3 +113,35 @@ panic = "abort"
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
lto = true
|
lto = true
|
||||||
opt-level = "s"
|
opt-level = "s"
|
||||||
|
strip = true
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
incremental = true
|
||||||
|
codegen-units = 256 # 增加编译单元,提升编译速度
|
||||||
|
opt-level = 0 # 禁用优化,进一步提升编译速度
|
||||||
|
debug = true # 保留调试信息
|
||||||
|
strip = false # 不剥离符号,保留调试信息
|
||||||
|
|
||||||
|
[profile.fast-release]
|
||||||
|
inherits = "release" # 继承 release 的配置
|
||||||
|
panic = "abort" # 与 release 相同
|
||||||
|
codegen-units = 256 # 增加编译单元,提升编译速度
|
||||||
|
lto = false # 禁用 LTO,提升编译速度
|
||||||
|
opt-level = 0 # 禁用优化,大幅提升编译速度
|
||||||
|
debug = true # 保留调试信息
|
||||||
|
strip = false # 不剥离符号,保留调试信息
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "app_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3.20.0"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["src_crates/crate_mihomo_api"]
|
||||||
|
|
||||||
|
# [patch.crates-io]
|
||||||
|
# bitflags = { git = "https://github.com/bitflags/bitflags", rev = "2.9.0" }
|
||||||
|
# zerocopy = { git = "https://github.com/google/zerocopy", rev = "v0.8.24" }
|
||||||
|
# tungstenite = { git = "https://github.com/snapview/tungstenite-rs", rev = "v0.26.2" }
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>Clash Verge</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>clash</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
BIN
src-tauri/assets/fonts/SF-Pro.ttf
Executable file
9
src-tauri/capabilities/desktop-windows.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"identifier": "desktop-windows-capability",
|
||||||
|
"description": "permissions for desktop windows applications",
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": [
|
||||||
|
"core:webview:allow-create-webview",
|
||||||
|
"core:webview:allow-create-webview-window"
|
||||||
|
]
|
||||||
|
}
|
||||||
22
src-tauri/capabilities/desktop.json
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"identifier": "desktop-capability",
|
||||||
|
"platforms": ["macOS", "windows", "linux"],
|
||||||
|
"webviews": ["main"],
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": [
|
||||||
|
"global-shortcut:default",
|
||||||
|
"updater:default",
|
||||||
|
"dialog:default",
|
||||||
|
"dialog:allow-ask",
|
||||||
|
"dialog:allow-message",
|
||||||
|
"updater:default",
|
||||||
|
"updater:allow-check",
|
||||||
|
"updater:allow-download-and-install",
|
||||||
|
"process:allow-restart",
|
||||||
|
"deep-link:default",
|
||||||
|
"autostart:allow-enable",
|
||||||
|
"autostart:allow-disable",
|
||||||
|
"autostart:allow-is-enabled",
|
||||||
|
"core:window:allow-set-theme"
|
||||||
|
]
|
||||||
|
}
|
||||||
83
src-tauri/capabilities/migrated.json
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"identifier": "migrated",
|
||||||
|
"description": "permissions that were migrated from v1",
|
||||||
|
"local": true,
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": [
|
||||||
|
"core:default",
|
||||||
|
"fs:allow-read-file",
|
||||||
|
"fs:allow-exists",
|
||||||
|
{
|
||||||
|
"identifier": "fs:scope",
|
||||||
|
"allow": ["$APPDATA/**", "$RESOURCE/../**", "**"]
|
||||||
|
},
|
||||||
|
"fs:allow-write-file",
|
||||||
|
{
|
||||||
|
"identifier": "fs:scope",
|
||||||
|
"allow": ["$APPDATA/**", "$RESOURCE/../**", "**"]
|
||||||
|
},
|
||||||
|
"fs:allow-app-read",
|
||||||
|
"fs:allow-app-read-recursive",
|
||||||
|
"fs:allow-appcache-read",
|
||||||
|
"fs:allow-appcache-read-recursive",
|
||||||
|
"fs:allow-appconfig-read",
|
||||||
|
"fs:allow-appconfig-read-recursive",
|
||||||
|
"core:window:allow-create",
|
||||||
|
"core:window:allow-center",
|
||||||
|
"core:window:allow-request-user-attention",
|
||||||
|
"core:window:allow-set-resizable",
|
||||||
|
"core:window:allow-set-maximizable",
|
||||||
|
"core:window:allow-set-minimizable",
|
||||||
|
"core:window:allow-set-closable",
|
||||||
|
"core:window:allow-set-title",
|
||||||
|
"core:window:allow-maximize",
|
||||||
|
"core:window:allow-unmaximize",
|
||||||
|
"core:window:allow-minimize",
|
||||||
|
"core:window:allow-unminimize",
|
||||||
|
"core:window:allow-show",
|
||||||
|
"core:window:allow-hide",
|
||||||
|
"core:window:allow-close",
|
||||||
|
"core:window:allow-set-decorations",
|
||||||
|
"core:window:allow-set-always-on-top",
|
||||||
|
"core:window:allow-set-content-protected",
|
||||||
|
"core:window:allow-set-size",
|
||||||
|
"core:window:allow-set-min-size",
|
||||||
|
"core:window:allow-set-max-size",
|
||||||
|
"core:window:allow-set-position",
|
||||||
|
"core:window:allow-set-fullscreen",
|
||||||
|
"core:window:allow-set-focus",
|
||||||
|
"core:window:allow-set-icon",
|
||||||
|
"core:window:allow-set-skip-taskbar",
|
||||||
|
"core:window:allow-set-cursor-grab",
|
||||||
|
"core:window:allow-set-cursor-visible",
|
||||||
|
"core:window:allow-set-cursor-icon",
|
||||||
|
"core:window:allow-set-cursor-position",
|
||||||
|
"core:window:allow-set-ignore-cursor-events",
|
||||||
|
"core:window:allow-start-dragging",
|
||||||
|
"core:window:allow-maximize",
|
||||||
|
"core:window:allow-toggle-maximize",
|
||||||
|
"core:window:allow-unmaximize",
|
||||||
|
"core:window:allow-minimize",
|
||||||
|
"core:window:allow-unminimize",
|
||||||
|
"core:window:allow-set-maximizable",
|
||||||
|
"core:window:allow-set-minimizable",
|
||||||
|
"core:webview:allow-print",
|
||||||
|
"shell:allow-execute",
|
||||||
|
"shell:allow-open",
|
||||||
|
"shell:allow-kill",
|
||||||
|
"shell:allow-spawn",
|
||||||
|
"shell:allow-stdin-write",
|
||||||
|
"dialog:allow-open",
|
||||||
|
"global-shortcut:allow-is-registered",
|
||||||
|
"global-shortcut:allow-register",
|
||||||
|
"global-shortcut:allow-register-all",
|
||||||
|
"global-shortcut:allow-unregister",
|
||||||
|
"global-shortcut:allow-unregister-all",
|
||||||
|
"process:allow-restart",
|
||||||
|
"process:allow-exit",
|
||||||
|
"clipboard-manager:allow-read-text",
|
||||||
|
"clipboard-manager:allow-write-text",
|
||||||
|
"shell:default",
|
||||||
|
"dialog:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
BIN
src-tauri/icons/tray-icon-mono.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src-tauri/icons/tray-icon-sys-mono.ico
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
src-tauri/icons/tray-icon-sys.ico
Normal file
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
src-tauri/icons/tray-icon-tun-mono.ico
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src-tauri/icons/tray-icon-tun.ico
Normal file
|
After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
BIN
src-tauri/images/background.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
@@ -2,8 +2,9 @@
|
|||||||
Categories={{{categories}}}
|
Categories={{{categories}}}
|
||||||
Comment={{{comment}}}
|
Comment={{{comment}}}
|
||||||
Exec={{{exec}}} %u
|
Exec={{{exec}}} %u
|
||||||
|
StartupWMClass={{{exec}}}
|
||||||
Icon={{{icon}}}
|
Icon={{{icon}}}
|
||||||
Name={{{name}}}
|
Name={{{name}}}
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
MimeType=x-scheme-handler/clash;
|
MimeType=x-scheme-handler/clash;
|
||||||
4
src-tauri/packages/linux/post-install.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
chmod +x /usr/bin/install-service
|
||||||
|
chmod +x /usr/bin/uninstall-service
|
||||||
|
chmod +x /usr/bin/clash-verge-service
|
||||||
2
src-tauri/packages/linux/pre-remove.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
/usr/bin/uninstall-service
|
||||||
14
src-tauri/packages/macos/entitlements.plist
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<false/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>io.github.clash-verge-rev.clash-verge-rev</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.inherit</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
; This file is copied from https://github.com/tauri-apps/tauri/blob/tauri-v1.5/tooling/bundler/src/bundle/windows/templates/installer.nsi
|
; This file is copied from https://github.com/tauri-apps/tauri/blob/tauri-v1.5/tooling/bundler/src/bundle/windows/templates/installer.nsi
|
||||||
; and edit to fit the needs of the project. the latest tauri 2.x has a different base nsi script.
|
; and edit to fit the needs of the project. the latest tauri 2.x has a different base nsi script.
|
||||||
|
RequestExecutionLevel admin
|
||||||
|
|
||||||
Unicode true
|
Unicode true
|
||||||
; Set the compression algorithm. Default is LZMA.
|
; Set the compression algorithm. Default is LZMA.
|
||||||
@@ -13,8 +14,11 @@ Unicode true
|
|||||||
!include FileFunc.nsh
|
!include FileFunc.nsh
|
||||||
!include x64.nsh
|
!include x64.nsh
|
||||||
!include WordFunc.nsh
|
!include WordFunc.nsh
|
||||||
!include "LogicLib.nsh"
|
|
||||||
!include "StrFunc.nsh"
|
!include "StrFunc.nsh"
|
||||||
|
!include "Win\COM.nsh"
|
||||||
|
!include "Win\Propkey.nsh"
|
||||||
|
!include "WinVer.nsh"
|
||||||
|
!include "LogicLib.nsh"
|
||||||
!addplugindir "$%AppData%\Local\NSIS\"
|
!addplugindir "$%AppData%\Local\NSIS\"
|
||||||
${StrCase}
|
${StrCase}
|
||||||
${StrLoc}
|
${StrLoc}
|
||||||
@@ -149,7 +153,6 @@ Function PageReinstall
|
|||||||
; however, this should be fine since the user will have to confirm the uninstallation
|
; however, this should be fine since the user will have to confirm the uninstallation
|
||||||
; and they can chose to abort it if doesn't make sense.
|
; and they can chose to abort it if doesn't make sense.
|
||||||
StrCpy $0 0
|
StrCpy $0 0
|
||||||
|
|
||||||
wix_loop:
|
wix_loop:
|
||||||
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $0
|
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $0
|
||||||
StrCmp $1 "" wix_done ; Exit loop if there is no more keys to loop on
|
StrCmp $1 "" wix_done ; Exit loop if there is no more keys to loop on
|
||||||
@@ -323,10 +326,15 @@ Var AppStartMenuFolder
|
|||||||
!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(createDesktop)"
|
!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(createDesktop)"
|
||||||
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateDesktopShortcut
|
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateDesktopShortcut
|
||||||
; Show run app after installation.
|
; Show run app after installation.
|
||||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAINBINARYNAME}.exe"
|
!define MUI_FINISHPAGE_RUN
|
||||||
|
!define MUI_FINISHPAGE_RUN_FUNCTION RunMainBinary
|
||||||
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
|
||||||
!insertmacro MUI_PAGE_FINISH
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
|
Function RunMainBinary
|
||||||
|
nsis_tauri_utils::RunAsUser "$INSTDIR\${MAINBINARYNAME}.exe" ""
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
; Uninstaller Pages
|
; Uninstaller Pages
|
||||||
; 1. Confirm uninstall page
|
; 1. Confirm uninstall page
|
||||||
Var DeleteAppDataCheckbox
|
Var DeleteAppDataCheckbox
|
||||||
@@ -420,55 +428,85 @@ Function .onInit
|
|||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
!macro CheckAllVergeProcesses
|
!macro CheckAllVergeProcesses
|
||||||
; Check if Clash Verge.exe is running
|
; Check if clash-verge-service.exe is running
|
||||||
nsis_tauri_utils::FindProcess "Clash Verge.exe"
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
${If} $R0 != 0
|
nsis_tauri_utils::FindProcessCurrentUser "clash-verge-service.exe"
|
||||||
; Kill the process
|
!else
|
||||||
DetailPrint "Kill Clash Verge.exe..."
|
|
||||||
!if "${INSTALLMODE}" == "currentUser"
|
|
||||||
nsis_tauri_utils::KillProcessCurrentUser "Clash Verge.exe"
|
|
||||||
!else
|
|
||||||
nsis_tauri_utils::KillProcess "Clash Verge.exe"
|
|
||||||
!endif
|
|
||||||
${EndIf}
|
|
||||||
|
|
||||||
|
|
||||||
; Check if clash-verge-service.exe is running
|
|
||||||
nsis_tauri_utils::FindProcess "clash-verge-service.exe"
|
nsis_tauri_utils::FindProcess "clash-verge-service.exe"
|
||||||
${If} $R0 != 0
|
!endif
|
||||||
; Kill the process
|
Pop $R0
|
||||||
DetailPrint "Kill clash-verge-service.exe..."
|
${If} $R0 = 0
|
||||||
!if "${INSTALLMODE}" == "currentUser"
|
DetailPrint "Kill clash-verge-service.exe..."
|
||||||
nsis_tauri_utils::KillProcessCurrentUser "clash-verge-service.exe"
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
!else
|
nsis_tauri_utils::KillProcessCurrentUser "clash-verge-service.exe"
|
||||||
nsis_tauri_utils::KillProcess "clash-verge-service.exe"
|
!else
|
||||||
!endif
|
nsis_tauri_utils::KillProcess "clash-verge-service.exe"
|
||||||
${EndIf}
|
!endif
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; Check if verge-mihomo-alpha.exe is running
|
||||||
; Check if clash-meta-alpha.exe is running
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
|
nsis_tauri_utils::FindProcessCurrentUser "verge-mihomo-alpha.exe"
|
||||||
|
!else
|
||||||
|
nsis_tauri_utils::FindProcess "verge-mihomo-alpha.exe"
|
||||||
|
!endif
|
||||||
|
Pop $R0
|
||||||
|
${If} $R0 = 0
|
||||||
|
DetailPrint "Kill verge-mihomo-alpha.exe..."
|
||||||
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
|
nsis_tauri_utils::KillProcessCurrentUser "verge-mihomo-alpha.exe"
|
||||||
|
!else
|
||||||
|
nsis_tauri_utils::KillProcess "verge-mihomo-alpha.exe"
|
||||||
|
!endif
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; Check if verge-mihomo.exe is running
|
||||||
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
|
nsis_tauri_utils::FindProcessCurrentUser "verge-mihomo.exe"
|
||||||
|
!else
|
||||||
|
nsis_tauri_utils::FindProcess "verge-mihomo.exe"
|
||||||
|
!endif
|
||||||
|
Pop $R0
|
||||||
|
${If} $R0 = 0
|
||||||
|
DetailPrint "Kill verge-mihomo.exe..."
|
||||||
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
|
nsis_tauri_utils::KillProcessCurrentUser "verge-mihomo.exe"
|
||||||
|
!else
|
||||||
|
nsis_tauri_utils::KillProcess "verge-mihomo.exe"
|
||||||
|
!endif
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; Check if clash-meta-alpha.exe is running
|
||||||
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
|
nsis_tauri_utils::FindProcessCurrentUser "clash-meta-alpha.exe"
|
||||||
|
!else
|
||||||
nsis_tauri_utils::FindProcess "clash-meta-alpha.exe"
|
nsis_tauri_utils::FindProcess "clash-meta-alpha.exe"
|
||||||
${If} $R0 != 0
|
!endif
|
||||||
; Kill the process
|
Pop $R0
|
||||||
DetailPrint "Kill clash-meta-alpha.exe..."
|
${If} $R0 = 0
|
||||||
!if "${INSTALLMODE}" == "currentUser"
|
DetailPrint "Kill clash-meta-alpha.exe..."
|
||||||
nsis_tauri_utils::KillProcessCurrentUser "clash-meta-alpha.exe"
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
!else
|
nsis_tauri_utils::KillProcessCurrentUser "clash-meta-alpha.exe"
|
||||||
nsis_tauri_utils::KillProcess "clash-meta-alpha.exe"
|
!else
|
||||||
!endif
|
nsis_tauri_utils::KillProcess "clash-meta-alpha.exe"
|
||||||
${EndIf}
|
!endif
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
; Check if clash-meta.exe is running
|
; Check if clash-meta.exe is running
|
||||||
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
|
nsis_tauri_utils::FindProcessCurrentUser "clash-meta.exe"
|
||||||
|
!else
|
||||||
nsis_tauri_utils::FindProcess "clash-meta.exe"
|
nsis_tauri_utils::FindProcess "clash-meta.exe"
|
||||||
${If} $R0 != 0
|
!endif
|
||||||
; Kill the process
|
Pop $R0
|
||||||
DetailPrint "Kill clash-meta.exe..."
|
${If} $R0 = 0
|
||||||
!if "${INSTALLMODE}" == "currentUser"
|
DetailPrint "Kill clash-meta.exe..."
|
||||||
nsis_tauri_utils::KillProcessCurrentUser "clash-meta.exe"
|
!if "${INSTALLMODE}" == "currentUser"
|
||||||
!else
|
nsis_tauri_utils::KillProcessCurrentUser "clash-meta.exe"
|
||||||
nsis_tauri_utils::KillProcess "clash-meta.exe"
|
!else
|
||||||
!endif
|
nsis_tauri_utils::KillProcess "clash-meta.exe"
|
||||||
${EndIf}
|
!endif
|
||||||
|
${EndIf}
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro StartVergeService
|
!macro StartVergeService
|
||||||
@@ -571,7 +609,7 @@ Section WebView2
|
|||||||
!if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper"
|
!if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper"
|
||||||
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
||||||
DetailPrint "$(webview2Downloading)"
|
DetailPrint "$(webview2Downloading)"
|
||||||
nsis_tauri_utils::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
NSISdl::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
||||||
Pop $0
|
Pop $0
|
||||||
${If} $0 == 0
|
${If} $0 == 0
|
||||||
DetailPrint "$(webview2DownloadSuccess)"
|
DetailPrint "$(webview2DownloadSuccess)"
|
||||||
@@ -653,11 +691,114 @@ SectionEnd
|
|||||||
app_check_done:
|
app_check_done:
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Var VC_REDIST_URL
|
||||||
|
Var VC_REDIST_EXE
|
||||||
|
|
||||||
|
Section CheckAndInstallVSRuntime
|
||||||
|
; 检查是否已安装 Visual C++ Redistributable
|
||||||
|
${If} ${IsNativeARM64}
|
||||||
|
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.arm64.exe"
|
||||||
|
StrCpy $VC_REDIST_EXE "vc_redist.arm64.exe"
|
||||||
|
|
||||||
|
; 检查关键DLL
|
||||||
|
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
|
||||||
|
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
|
||||||
|
|
||||||
|
${ElseIf} ${RunningX64}
|
||||||
|
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.x64.exe"
|
||||||
|
StrCpy $VC_REDIST_EXE "vc_redist.x64.exe"
|
||||||
|
|
||||||
|
; 检查关键DLL
|
||||||
|
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
|
||||||
|
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
|
||||||
|
|
||||||
|
${Else}
|
||||||
|
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.x86.exe"
|
||||||
|
StrCpy $VC_REDIST_EXE "vc_redist.x86.exe"
|
||||||
|
|
||||||
|
; 检查关键DLL
|
||||||
|
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
|
||||||
|
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
checkInstall:
|
||||||
|
; 检查注册表
|
||||||
|
${If} ${RunningX64}
|
||||||
|
SetRegView 64
|
||||||
|
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${ARCH}" "Installed"
|
||||||
|
${If} $R0 == "1"
|
||||||
|
Goto Done
|
||||||
|
${EndIf}
|
||||||
|
${Else}
|
||||||
|
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" "Installed"
|
||||||
|
${If} $R0 == "1"
|
||||||
|
Goto Done
|
||||||
|
${EndIf}
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; 如果没有安装,则下载并安装
|
||||||
|
DetailPrint "正在下载 Visual C++ Redistributable..."
|
||||||
|
nsisdl::download "$VC_REDIST_URL" "$TEMP\$VC_REDIST_EXE"
|
||||||
|
Pop $0
|
||||||
|
${If} $0 == "success"
|
||||||
|
DetailPrint "正在安装 Visual C++ Redistributable..."
|
||||||
|
ExecWait '"$TEMP\$VC_REDIST_EXE" /quiet /norestart' $0
|
||||||
|
${If} $0 == 0
|
||||||
|
DetailPrint "Visual C++ Redistributable 安装成功"
|
||||||
|
${Else}
|
||||||
|
DetailPrint "Visual C++ Redistributable 安装失败"
|
||||||
|
${EndIf}
|
||||||
|
Delete "$TEMP\$VC_REDIST_EXE"
|
||||||
|
${Else}
|
||||||
|
DetailPrint "Visual C++ Redistributable 下载失败"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
Done:
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Section Install
|
Section Install
|
||||||
SetOutPath $INSTDIR
|
SetOutPath $INSTDIR
|
||||||
|
nsExec::Exec 'netsh int tcp res'
|
||||||
!insertmacro CheckIfAppIsRunning
|
!insertmacro CheckIfAppIsRunning
|
||||||
!insertmacro CheckAllVergeProcesses
|
!insertmacro CheckAllVergeProcesses
|
||||||
|
|
||||||
|
; 清理自启动注册表项
|
||||||
|
DetailPrint "Cleaning auto-launch registry entries..."
|
||||||
|
|
||||||
|
StrCpy $R1 "Software\Microsoft\Windows\CurrentVersion\Run"
|
||||||
|
|
||||||
|
SetRegView 64
|
||||||
|
; 清理旧版本的注册表项 (Clash Verge)
|
||||||
|
ReadRegStr $R2 HKCU "$R1" "Clash Verge"
|
||||||
|
${If} $R2 != ""
|
||||||
|
DeleteRegValue HKCU "$R1" "Clash Verge"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
ReadRegStr $R2 HKLM "$R1" "Clash Verge"
|
||||||
|
${If} $R2 != ""
|
||||||
|
DeleteRegValue HKLM "$R1" "Clash Verge"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; 清理新版本的注册表项 (clash-verge)
|
||||||
|
ReadRegStr $R2 HKCU "$R1" "clash-verge"
|
||||||
|
${If} $R2 != ""
|
||||||
|
DeleteRegValue HKCU "$R1" "clash-verge"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
ReadRegStr $R2 HKLM "$R1" "clash-verge"
|
||||||
|
${If} $R2 != ""
|
||||||
|
DeleteRegValue HKLM "$R1" "clash-verge"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; Delete old files before installation
|
||||||
|
; Delete clash-verge.desktop
|
||||||
|
IfFileExists "$INSTDIR\Clash Verge.exe" 0 +2
|
||||||
|
Delete "$INSTDIR\Clash Verge.exe"
|
||||||
|
|
||||||
; Copy main executable
|
; Copy main executable
|
||||||
File "${MAINBINARYSRCPATH}"
|
File "${MAINBINARYSRCPATH}"
|
||||||
|
|
||||||
@@ -730,7 +871,8 @@ Function .onInstSuccess
|
|||||||
check_r_flag:
|
check_r_flag:
|
||||||
${GetOptions} $CMDLINE "/R" $R0
|
${GetOptions} $CMDLINE "/R" $R0
|
||||||
IfErrors run_done 0
|
IfErrors run_done 0
|
||||||
Exec '"$INSTDIR\${MAINBINARYNAME}.exe"'
|
${GetOptions} $CMDLINE "/ARGS" $R0
|
||||||
|
nsis_tauri_utils::RunAsUser "$INSTDIR\${MAINBINARYNAME}.exe" "$R0"
|
||||||
run_done:
|
run_done:
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
@@ -743,35 +885,73 @@ Function un.onInit
|
|||||||
|
|
||||||
!insertmacro MUI_UNGETLANGUAGE
|
!insertmacro MUI_UNGETLANGUAGE
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
Function un.isDirectoryEmpty
|
!macro DeleteAppUserModelId
|
||||||
Exch $0
|
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_DestinationList} ${IID_ICustomDestinationList} r1 ""
|
||||||
Push $1
|
${If} $1 P<> 0
|
||||||
Push $2
|
${ICustomDestinationList::DeleteList} $1 '("${BUNDLEID}")'
|
||||||
StrCpy $2 0
|
${IUnknown::Release} $1 ""
|
||||||
FindFirst $1 $2 "$0\*.*"
|
${EndIf}
|
||||||
loop:
|
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ApplicationDestinations} ${IID_IApplicationDestinations} r1 ""
|
||||||
StrCmp $2 "" done
|
${If} $1 P<> 0
|
||||||
StrCmp $2 "." next
|
${IApplicationDestinations::SetAppID} $1 '("${BUNDLEID}")i.r0'
|
||||||
StrCmp $2 ".." next
|
${If} $0 >= 0
|
||||||
StrCpy $0 0
|
${IApplicationDestinations::RemoveAllDestinations} $1 ''
|
||||||
goto done
|
${EndIf}
|
||||||
next:
|
${IUnknown::Release} $1 ""
|
||||||
FindNext $1 $2
|
${EndIf}
|
||||||
goto loop
|
!macroend
|
||||||
done:
|
|
||||||
FindClose $1
|
; From https://stackoverflow.com/a/42816728/16993372
|
||||||
StrCmp $2 "" 0 +2
|
!macro UnpinShortcut shortcut
|
||||||
StrCpy $0 1
|
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_StartMenuPin} ${IID_IStartMenuPinnedList} r0 ""
|
||||||
Pop $2
|
${If} $0 P<> 0
|
||||||
Pop $1
|
System::Call 'SHELL32::SHCreateItemFromParsingName(ws, p0, g "${IID_IShellItem}", *p0r1)' "${shortcut}"
|
||||||
Exch $0
|
${If} $1 P<> 0
|
||||||
FunctionEnd
|
${IStartMenuPinnedList::RemoveFromList} $0 '(r1)'
|
||||||
|
${IUnknown::Release} $1 ""
|
||||||
|
${EndIf}
|
||||||
|
${IUnknown::Release} $0 ""
|
||||||
|
${EndIf}
|
||||||
|
!macroend
|
||||||
|
|
||||||
Section Uninstall
|
Section Uninstall
|
||||||
|
;删除 window-state.json 文件
|
||||||
|
SetShellVarContext current
|
||||||
|
Delete "$APPDATA\io.github.clash-verge-rev.clash-verge-rev\window-state.json"
|
||||||
|
|
||||||
!insertmacro CheckIfAppIsRunning
|
!insertmacro CheckIfAppIsRunning
|
||||||
!insertmacro CheckAllVergeProcesses
|
!insertmacro CheckAllVergeProcesses
|
||||||
!insertmacro RemoveVergeService
|
!insertmacro RemoveVergeService
|
||||||
|
|
||||||
|
; 清理自启动注册表项
|
||||||
|
DetailPrint "Cleaning auto-launch registry entries..."
|
||||||
|
|
||||||
|
StrCpy $R1 "Software\Microsoft\Windows\CurrentVersion\Run"
|
||||||
|
|
||||||
|
SetRegView 64
|
||||||
|
; 清理旧版本的注册表项 (Clash Verge)
|
||||||
|
ReadRegStr $R2 HKCU "$R1" "Clash Verge"
|
||||||
|
${If} $R2 != ""
|
||||||
|
DeleteRegValue HKCU "$R1" "Clash Verge"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
ReadRegStr $R2 HKLM "$R1" "Clash Verge"
|
||||||
|
${If} $R2 != ""
|
||||||
|
DeleteRegValue HKLM "$R1" "Clash Verge"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; 清理新版本的注册表项 (clash-verge)
|
||||||
|
ReadRegStr $R2 HKCU "$R1" "clash-verge"
|
||||||
|
${If} $R2 != ""
|
||||||
|
DeleteRegValue HKCU "$R1" "clash-verge"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
ReadRegStr $R2 HKLM "$R1" "clash-verge"
|
||||||
|
${If} $R2 != ""
|
||||||
|
DeleteRegValue HKLM "$R1" "clash-verge"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
; Delete the app directory and its content from disk
|
; Delete the app directory and its content from disk
|
||||||
; Copy main executable
|
; Copy main executable
|
||||||
Delete "$INSTDIR\${MAINBINARYNAME}.exe"
|
Delete "$INSTDIR\${MAINBINARYNAME}.exe"
|
||||||
@@ -786,26 +966,36 @@ Section Uninstall
|
|||||||
Delete "$INSTDIR\\{{this}}"
|
Delete "$INSTDIR\\{{this}}"
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
|
; Delete clash-verge.desktop
|
||||||
|
IfFileExists "$INSTDIR\Clash Verge.exe" 0 +2
|
||||||
|
Delete "$INSTDIR\Clash Verge.exe"
|
||||||
|
|
||||||
; Delete uninstaller
|
; Delete uninstaller
|
||||||
Delete "$INSTDIR\uninstall.exe"
|
Delete "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
; Remove InstallDir
|
{{#each resources_ancestors}}
|
||||||
Push "$INSTDIR"
|
RMDir /REBOOTOK "$INSTDIR\\{{this}}"
|
||||||
Call un.isDirectoryEmpty
|
{{/each}}
|
||||||
Pop $0
|
RMDir "$INSTDIR"
|
||||||
${If} $0 == 1
|
|
||||||
RMDir /R /REBOOTOK "$INSTDIR"
|
!insertmacro DeleteAppUserModelId
|
||||||
${Else}
|
!insertmacro UnpinShortcut "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk"
|
||||||
MessageBox MB_OK "Install Directory is not Empty, Please remove it manually."
|
!insertmacro UnpinShortcut "$DESKTOP\${PRODUCTNAME}.lnk"
|
||||||
${EndIf}
|
; 兼容旧名称快捷方式
|
||||||
|
!insertmacro UnpinShortcut "$SMPROGRAMS\$AppStartMenuFolder\clash-verge.lnk"
|
||||||
|
!insertmacro UnpinShortcut "$DESKTOP\clash-verge.lnk"
|
||||||
|
|
||||||
; Remove start menu shortcut
|
; Remove start menu shortcut
|
||||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder
|
!insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder
|
||||||
Delete "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk"
|
Delete "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk"
|
||||||
|
; 兼容旧名称快捷方式
|
||||||
|
Delete "$SMPROGRAMS\$AppStartMenuFolder\clash-verge.lnk"
|
||||||
RMDir "$SMPROGRAMS\$AppStartMenuFolder"
|
RMDir "$SMPROGRAMS\$AppStartMenuFolder"
|
||||||
|
|
||||||
; Remove desktop shortcuts
|
; Remove desktop shortcuts
|
||||||
Delete "$DESKTOP\${MAINBINARYNAME}.lnk"
|
Delete "$DESKTOP\${PRODUCTNAME}.lnk"
|
||||||
|
; 兼容旧名称快捷方式
|
||||||
|
Delete "$DESKTOP\clash-verge.lnk"
|
||||||
|
|
||||||
; Remove registry information for add/remove programs
|
; Remove registry information for add/remove programs
|
||||||
!if "${INSTALLMODE}" == "both"
|
!if "${INSTALLMODE}" == "both"
|
||||||
@@ -825,6 +1015,10 @@ Section Uninstall
|
|||||||
RmDir /r "$LOCALAPPDATA\${BUNDLEID}"
|
RmDir /r "$LOCALAPPDATA\${BUNDLEID}"
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
;删除 window-state.json 文件
|
||||||
|
SetShellVarContext current
|
||||||
|
Delete "$APPDATA\io.github.clash-verge-rev.clash-verge-rev\window-state.json"
|
||||||
|
|
||||||
${GetOptions} $CMDLINE "/P" $R0
|
${GetOptions} $CMDLINE "/P" $R0
|
||||||
IfErrors +2 0
|
IfErrors +2 0
|
||||||
SetAutoClose true
|
SetAutoClose true
|
||||||
@@ -840,13 +1034,39 @@ Function SkipIfPassive
|
|||||||
${IfThen} $PassiveMode == 1 ${|} Abort ${|}
|
${IfThen} $PassiveMode == 1 ${|} Abort ${|}
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
|
!macro SetLnkAppUserModelId shortcut
|
||||||
|
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ShellLink} ${IID_IShellLink} r0 ""
|
||||||
|
${If} $0 P<> 0
|
||||||
|
${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)'
|
||||||
|
${If} $1 P<> 0
|
||||||
|
${IPersistFile::Load} $1 '("${shortcut}", ${STGM_READWRITE})'
|
||||||
|
${IUnknown::QueryInterface} $0 '("${IID_IPropertyStore}",.r2)'
|
||||||
|
${If} $2 P<> 0
|
||||||
|
System::Call 'Oleaut32::SysAllocString(w "${BUNDLEID}") i.r3'
|
||||||
|
System::Call '*${SYSSTRUCT_PROPERTYKEY}(${PKEY_AppUserModel_ID})p.r4'
|
||||||
|
System::Call '*${SYSSTRUCT_PROPVARIANT}(${VT_BSTR},,&i4 $3)p.r5'
|
||||||
|
${IPropertyStore::SetValue} $2 '($4,$5)'
|
||||||
|
|
||||||
|
System::Call 'Oleaut32::SysFreeString($3)'
|
||||||
|
System::Free $4
|
||||||
|
System::Free $5
|
||||||
|
${IPropertyStore::Commit} $2 ""
|
||||||
|
${IUnknown::Release} $2 ""
|
||||||
|
${IPersistFile::Save} $1 '("${shortcut}",1)'
|
||||||
|
${EndIf}
|
||||||
|
${IUnknown::Release} $1 ""
|
||||||
|
${EndIf}
|
||||||
|
${IUnknown::Release} $0 ""
|
||||||
|
${EndIf}
|
||||||
|
!macroend
|
||||||
|
|
||||||
Function CreateDesktopShortcut
|
Function CreateDesktopShortcut
|
||||||
CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
CreateShortcut "$DESKTOP\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
||||||
ApplicationID::Set "$DESKTOP\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
|
!insertmacro SetLnkAppUserModelId "$DESKTOP\${PRODUCTNAME}.lnk"
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
Function CreateStartMenuShortcut
|
Function CreateStartMenuShortcut
|
||||||
CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder"
|
CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder"
|
||||||
CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
||||||
ApplicationID::Set "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
|
!insertmacro SetLnkAppUserModelId "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk"
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
@@ -11,4 +11,3 @@ merge_derives = true
|
|||||||
use_try_shorthand = false
|
use_try_shorthand = false
|
||||||
use_field_init_shorthand = false
|
use_field_init_shorthand = false
|
||||||
force_explicit_abi = true
|
force_explicit_abi = true
|
||||||
imports_granularity = "Crate"
|
|
||||||
|
|||||||
246
src-tauri/src/cmd/app.rs
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
use super::CmdResult;
|
||||||
|
use crate::{
|
||||||
|
feat, logging,
|
||||||
|
utils::{dirs, logging::Type},
|
||||||
|
wrap_err,
|
||||||
|
};
|
||||||
|
use tauri::Manager;
|
||||||
|
|
||||||
|
/// 打开应用程序所在目录
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn open_app_dir() -> CmdResult<()> {
|
||||||
|
let app_dir = wrap_err!(dirs::app_home_dir())?;
|
||||||
|
wrap_err!(open::that(app_dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 打开核心所在目录
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn open_core_dir() -> CmdResult<()> {
|
||||||
|
let core_dir = wrap_err!(tauri::utils::platform::current_exe())?;
|
||||||
|
let core_dir = core_dir.parent().ok_or("failed to get core dir")?;
|
||||||
|
wrap_err!(open::that(core_dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 打开日志目录
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn open_logs_dir() -> CmdResult<()> {
|
||||||
|
let log_dir = wrap_err!(dirs::app_logs_dir())?;
|
||||||
|
wrap_err!(open::that(log_dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 打开网页链接
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn open_web_url(url: String) -> CmdResult<()> {
|
||||||
|
wrap_err!(open::that(url))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 打开/关闭开发者工具
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn open_devtools(app_handle: tauri::AppHandle) {
|
||||||
|
if let Some(window) = app_handle.get_webview_window("main") {
|
||||||
|
if !window.is_devtools_open() {
|
||||||
|
window.open_devtools();
|
||||||
|
} else {
|
||||||
|
window.close_devtools();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 退出应用
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn exit_app() {
|
||||||
|
feat::quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 重启应用
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn restart_app() -> CmdResult<()> {
|
||||||
|
feat::restart_app();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取便携版标识
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_portable_flag() -> CmdResult<bool> {
|
||||||
|
Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取应用目录
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_app_dir() -> CmdResult<String> {
|
||||||
|
let app_home_dir = wrap_err!(dirs::app_home_dir())?
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
Ok(app_home_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取当前自启动状态
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_auto_launch_status() -> CmdResult<bool> {
|
||||||
|
use crate::core::sysopt::Sysopt;
|
||||||
|
wrap_err!(Sysopt::global().get_launch_status())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 下载图标缓存
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String> {
|
||||||
|
let icon_cache_dir = wrap_err!(dirs::app_home_dir())?.join("icons").join("cache");
|
||||||
|
let icon_path = icon_cache_dir.join(&name);
|
||||||
|
|
||||||
|
if icon_path.exists() {
|
||||||
|
return Ok(icon_path.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !icon_cache_dir.exists() {
|
||||||
|
let _ = std::fs::create_dir_all(&icon_cache_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp_path = icon_cache_dir.join(format!("{}.downloading", &name));
|
||||||
|
|
||||||
|
let response = wrap_err!(reqwest::get(&url).await)?;
|
||||||
|
|
||||||
|
let content_type = response
|
||||||
|
.headers()
|
||||||
|
.get(reqwest::header::CONTENT_TYPE)
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
.unwrap_or("");
|
||||||
|
|
||||||
|
let is_image = content_type.starts_with("image/");
|
||||||
|
|
||||||
|
let content = wrap_err!(response.bytes().await)?;
|
||||||
|
|
||||||
|
let is_html = content.len() > 15
|
||||||
|
&& (content.starts_with(b"<!DOCTYPE html")
|
||||||
|
|| content.starts_with(b"<html")
|
||||||
|
|| content.starts_with(b"<?xml"));
|
||||||
|
|
||||||
|
if is_image && !is_html {
|
||||||
|
{
|
||||||
|
let mut file = match std::fs::File::create(&temp_path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(_) => {
|
||||||
|
if icon_path.exists() {
|
||||||
|
return Ok(icon_path.to_string_lossy().to_string());
|
||||||
|
} else {
|
||||||
|
return Err("Failed to create temporary file".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !icon_path.exists() {
|
||||||
|
match std::fs::rename(&temp_path, &icon_path) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => {
|
||||||
|
let _ = std::fs::remove_file(&temp_path);
|
||||||
|
if icon_path.exists() {
|
||||||
|
return Ok(icon_path.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let _ = std::fs::remove_file(&temp_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(icon_path.to_string_lossy().to_string())
|
||||||
|
} else {
|
||||||
|
let _ = std::fs::remove_file(&temp_path);
|
||||||
|
Err(format!("下载的内容不是有效图片: {}", url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct IconInfo {
|
||||||
|
name: String,
|
||||||
|
previous_t: String,
|
||||||
|
current_t: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 复制图标文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<String> {
|
||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
|
let file_path = Path::new(&path);
|
||||||
|
|
||||||
|
let icon_dir = wrap_err!(dirs::app_home_dir())?.join("icons");
|
||||||
|
if !icon_dir.exists() {
|
||||||
|
let _ = fs::create_dir_all(&icon_dir);
|
||||||
|
}
|
||||||
|
let ext = match file_path.extension() {
|
||||||
|
Some(e) => e.to_string_lossy().to_string(),
|
||||||
|
None => "ico".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dest_path = icon_dir.join(format!(
|
||||||
|
"{0}-{1}.{ext}",
|
||||||
|
icon_info.name, icon_info.current_t
|
||||||
|
));
|
||||||
|
if file_path.exists() {
|
||||||
|
if icon_info.previous_t.trim() != "" {
|
||||||
|
fs::remove_file(
|
||||||
|
icon_dir.join(format!("{0}-{1}.png", icon_info.name, icon_info.previous_t)),
|
||||||
|
)
|
||||||
|
.unwrap_or_default();
|
||||||
|
fs::remove_file(
|
||||||
|
icon_dir.join(format!("{0}-{1}.ico", icon_info.name, icon_info.previous_t)),
|
||||||
|
)
|
||||||
|
.unwrap_or_default();
|
||||||
|
}
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"Copying icon file path: {:?} -> file dist: {:?}",
|
||||||
|
path,
|
||||||
|
dest_path
|
||||||
|
);
|
||||||
|
match fs::copy(file_path, &dest_path) {
|
||||||
|
Ok(_) => Ok(dest_path.to_string_lossy().to_string()),
|
||||||
|
Err(err) => Err(err.to_string()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("file not found".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 通知UI已准备就绪
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn notify_ui_ready() -> CmdResult<()> {
|
||||||
|
log::info!(target: "app", "前端UI已准备就绪");
|
||||||
|
crate::utils::resolve::mark_ui_ready();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UI加载阶段
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn update_ui_stage(stage: String) -> CmdResult<()> {
|
||||||
|
log::info!(target: "app", "UI加载阶段更新: {}", stage);
|
||||||
|
|
||||||
|
use crate::utils::resolve::UiReadyStage;
|
||||||
|
|
||||||
|
let stage_enum = match stage.as_str() {
|
||||||
|
"NotStarted" => UiReadyStage::NotStarted,
|
||||||
|
"Loading" => UiReadyStage::Loading,
|
||||||
|
"DomReady" => UiReadyStage::DomReady,
|
||||||
|
"ResourcesLoaded" => UiReadyStage::ResourcesLoaded,
|
||||||
|
"Ready" => UiReadyStage::Ready,
|
||||||
|
_ => {
|
||||||
|
log::warn!(target: "app", "未知的UI加载阶段: {}", stage);
|
||||||
|
return Err(format!("未知的UI加载阶段: {}", stage));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
crate::utils::resolve::update_ui_ready_stage(stage_enum);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 重置UI就绪状态
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn reset_ui_ready_state() -> CmdResult<()> {
|
||||||
|
log::info!(target: "app", "重置UI就绪状态");
|
||||||
|
crate::utils::resolve::reset_ui_ready();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
269
src-tauri/src/cmd/clash.rs
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
use super::CmdResult;
|
||||||
|
use crate::{
|
||||||
|
config::*, core::*, feat, module::mihomo::MihomoManager, process::AsyncHandler, wrap_err,
|
||||||
|
};
|
||||||
|
use serde_yaml::Mapping;
|
||||||
|
|
||||||
|
/// 复制Clash环境变量
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn copy_clash_env() -> CmdResult {
|
||||||
|
feat::copy_clash_env();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取Clash信息
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_clash_info() -> CmdResult<ClashInfo> {
|
||||||
|
Ok(Config::clash().latest().get_client_info())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改Clash配置
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn patch_clash_config(payload: Mapping) -> CmdResult {
|
||||||
|
wrap_err!(feat::patch_clash(payload).await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改Clash模式
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn patch_clash_mode(payload: String) -> CmdResult {
|
||||||
|
feat::change_clash_mode(payload);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 切换Clash核心
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>> {
|
||||||
|
log::info!(target: "app", "changing core to {clash_core}");
|
||||||
|
|
||||||
|
match CoreManager::global()
|
||||||
|
.change_core(Some(clash_core.clone()))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
// 切换内核后重启内核
|
||||||
|
match CoreManager::global().restart_core().await {
|
||||||
|
Ok(_) => {
|
||||||
|
log::info!(target: "app", "core changed and restarted to {clash_core}");
|
||||||
|
handle::Handle::notice_message("config_core::change_success", &clash_core);
|
||||||
|
handle::Handle::refresh_clash();
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let error_msg = format!("Core changed but failed to restart: {}", err);
|
||||||
|
log::error!(target: "app", "{}", error_msg);
|
||||||
|
handle::Handle::notice_message("config_core::change_error", &error_msg);
|
||||||
|
Ok(Some(error_msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let error_msg = err.to_string();
|
||||||
|
log::error!(target: "app", "failed to change core: {error_msg}");
|
||||||
|
handle::Handle::notice_message("config_core::change_error", &error_msg);
|
||||||
|
Ok(Some(error_msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 启动核心
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn start_core() -> CmdResult {
|
||||||
|
wrap_err!(CoreManager::global().start_core().await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 关闭核心
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn stop_core() -> CmdResult {
|
||||||
|
wrap_err!(CoreManager::global().stop_core().await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 重启核心
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn restart_core() -> CmdResult {
|
||||||
|
wrap_err!(CoreManager::global().restart_core().await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取代理延迟
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn clash_api_get_proxy_delay(
|
||||||
|
name: String,
|
||||||
|
url: Option<String>,
|
||||||
|
timeout: i32,
|
||||||
|
) -> CmdResult<serde_json::Value> {
|
||||||
|
MihomoManager::global()
|
||||||
|
.test_proxy_delay(&name, url, timeout)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 测试URL延迟
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn test_delay(url: String) -> CmdResult<u32> {
|
||||||
|
Ok(feat::test_delay(url).await.unwrap_or(10000u32))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 保存DNS配置到单独文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
|
||||||
|
use crate::utils::dirs;
|
||||||
|
use serde_yaml;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
// 获取DNS配置文件路径
|
||||||
|
let dns_path = dirs::app_home_dir()
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.join("dns_config.yaml");
|
||||||
|
|
||||||
|
// 保存DNS配置到文件
|
||||||
|
let yaml_str = serde_yaml::to_string(&dns_config).map_err(|e| e.to_string())?;
|
||||||
|
fs::write(&dns_path, yaml_str).map_err(|e| e.to_string())?;
|
||||||
|
log::info!(target: "app", "DNS config saved to {:?}", dns_path);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 应用或撤销DNS配置
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn apply_dns_config(apply: bool) -> CmdResult {
|
||||||
|
use crate::{
|
||||||
|
config::Config,
|
||||||
|
core::{handle, CoreManager},
|
||||||
|
utils::dirs,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用spawn来处理异步操作
|
||||||
|
AsyncHandler::spawn(move || async move {
|
||||||
|
if apply {
|
||||||
|
// 读取DNS配置文件
|
||||||
|
let dns_path = match dirs::app_home_dir() {
|
||||||
|
Ok(path) => path.join("dns_config.yaml"),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(target: "app", "Failed to get home dir: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !dns_path.exists() {
|
||||||
|
log::warn!(target: "app", "DNS config file not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dns_yaml = match std::fs::read_to_string(&dns_path) {
|
||||||
|
Ok(content) => content,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(target: "app", "Failed to read DNS config: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 解析DNS配置并创建patch
|
||||||
|
let patch_config = match serde_yaml::from_str::<serde_yaml::Mapping>(&dns_yaml) {
|
||||||
|
Ok(config) => {
|
||||||
|
let mut patch = serde_yaml::Mapping::new();
|
||||||
|
patch.insert("dns".into(), config.into());
|
||||||
|
patch
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(target: "app", "Failed to parse DNS config: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log::info!(target: "app", "Applying DNS config from file");
|
||||||
|
|
||||||
|
// 重新生成配置,确保DNS配置被正确应用
|
||||||
|
// 这里不调用patch_clash以避免将DNS配置写入config.yaml
|
||||||
|
Config::runtime()
|
||||||
|
.latest()
|
||||||
|
.patch_config(patch_config.clone());
|
||||||
|
|
||||||
|
// 首先重新生成配置
|
||||||
|
if let Err(err) = Config::generate().await {
|
||||||
|
log::error!(target: "app", "Failed to regenerate config with DNS: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 然后应用新配置
|
||||||
|
if let Err(err) = CoreManager::global().update_config().await {
|
||||||
|
log::error!(target: "app", "Failed to apply config with DNS: {}", err);
|
||||||
|
} else {
|
||||||
|
log::info!(target: "app", "DNS config successfully applied");
|
||||||
|
handle::Handle::refresh_clash();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 当关闭DNS设置时,不需要对配置进行任何修改
|
||||||
|
// 直接重新生成配置,让enhance函数自动跳过DNS配置的加载
|
||||||
|
log::info!(target: "app", "DNS settings disabled, regenerating config");
|
||||||
|
|
||||||
|
// 重新生成配置
|
||||||
|
if let Err(err) = Config::generate().await {
|
||||||
|
log::error!(target: "app", "Failed to regenerate config: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用新配置
|
||||||
|
match CoreManager::global().update_config().await {
|
||||||
|
Ok(_) => {
|
||||||
|
log::info!(target: "app", "Config regenerated successfully");
|
||||||
|
handle::Handle::refresh_clash();
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!(target: "app", "Failed to apply regenerated config: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查DNS配置文件是否存在
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn check_dns_config_exists() -> CmdResult<bool> {
|
||||||
|
use crate::utils::dirs;
|
||||||
|
|
||||||
|
let dns_path = dirs::app_home_dir()
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.join("dns_config.yaml");
|
||||||
|
|
||||||
|
Ok(dns_path.exists())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取DNS配置文件内容
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_dns_config_content() -> CmdResult<String> {
|
||||||
|
use crate::utils::dirs;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
let dns_path = dirs::app_home_dir()
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.join("dns_config.yaml");
|
||||||
|
|
||||||
|
if !dns_path.exists() {
|
||||||
|
return Err("DNS config file not found".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = fs::read_to_string(&dns_path).map_err(|e| e.to_string())?;
|
||||||
|
Ok(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 验证DNS配置文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn validate_dns_config() -> CmdResult<(bool, String)> {
|
||||||
|
use crate::{core::CoreManager, utils::dirs};
|
||||||
|
|
||||||
|
let app_dir = dirs::app_home_dir().map_err(|e| e.to_string())?;
|
||||||
|
let dns_path = app_dir.join("dns_config.yaml");
|
||||||
|
let dns_path_str = dns_path.to_str().unwrap_or_default();
|
||||||
|
|
||||||
|
if !dns_path.exists() {
|
||||||
|
return Ok((false, "DNS config file not found".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
match CoreManager::global()
|
||||||
|
.validate_config_file(dns_path_str, None)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(result) => Ok(result),
|
||||||
|
Err(e) => Err(e.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src-tauri/src/cmd/lightweight.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use crate::module::lightweight;
|
||||||
|
|
||||||
|
use super::CmdResult;
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn entry_lightweight_mode() -> CmdResult {
|
||||||
|
lightweight::entry_lightweight_mode();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn exit_lightweight_mode() -> CmdResult {
|
||||||
|
lightweight::exit_lightweight_mode();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
1297
src-tauri/src/cmd/media_unlock_checker.rs
Normal file
38
src-tauri/src/cmd/mod.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
// Common result type used by command functions
|
||||||
|
pub type CmdResult<T = ()> = Result<T, String>;
|
||||||
|
|
||||||
|
// Command modules
|
||||||
|
pub mod app;
|
||||||
|
pub mod clash;
|
||||||
|
pub mod lightweight;
|
||||||
|
pub mod media_unlock_checker;
|
||||||
|
pub mod network;
|
||||||
|
pub mod profile;
|
||||||
|
pub mod proxy;
|
||||||
|
pub mod runtime;
|
||||||
|
pub mod save_profile;
|
||||||
|
pub mod service;
|
||||||
|
pub mod system;
|
||||||
|
pub mod uwp;
|
||||||
|
pub mod validate;
|
||||||
|
pub mod verge;
|
||||||
|
pub mod webdav;
|
||||||
|
|
||||||
|
// Re-export all command functions for backwards compatibility
|
||||||
|
pub use app::*;
|
||||||
|
pub use clash::*;
|
||||||
|
pub use lightweight::*;
|
||||||
|
pub use media_unlock_checker::*;
|
||||||
|
pub use network::*;
|
||||||
|
pub use profile::*;
|
||||||
|
pub use proxy::*;
|
||||||
|
pub use runtime::*;
|
||||||
|
pub use save_profile::*;
|
||||||
|
pub use service::*;
|
||||||
|
pub use system::*;
|
||||||
|
pub use uwp::*;
|
||||||
|
pub use validate::*;
|
||||||
|
pub use verge::*;
|
||||||
|
pub use webdav::*;
|
||||||
90
src-tauri/src/cmd/network.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
use super::CmdResult;
|
||||||
|
use crate::wrap_err;
|
||||||
|
use network_interface::NetworkInterface;
|
||||||
|
use serde_yaml::Mapping;
|
||||||
|
use sysproxy::{Autoproxy, Sysproxy};
|
||||||
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
|
/// get the system proxy
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_sys_proxy() -> CmdResult<Mapping> {
|
||||||
|
let current = spawn_blocking(Sysproxy::get_system_proxy)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to spawn blocking task for sysproxy: {}", e))?
|
||||||
|
.map_err(|e| format!("Failed to get system proxy: {}", e))?;
|
||||||
|
|
||||||
|
let mut map = Mapping::new();
|
||||||
|
map.insert("enable".into(), current.enable.into());
|
||||||
|
map.insert(
|
||||||
|
"server".into(),
|
||||||
|
format!("{}:{}", current.host, current.port).into(),
|
||||||
|
);
|
||||||
|
map.insert("bypass".into(), current.bypass.into());
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get the system proxy
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_auto_proxy() -> CmdResult<Mapping> {
|
||||||
|
let current = spawn_blocking(Autoproxy::get_auto_proxy)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to spawn blocking task for autoproxy: {}", e))?
|
||||||
|
.map_err(|e| format!("Failed to get auto proxy: {}", e))?;
|
||||||
|
|
||||||
|
let mut map = Mapping::new();
|
||||||
|
map.insert("enable".into(), current.enable.into());
|
||||||
|
map.insert("url".into(), current.url.into());
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取系统主机名
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_system_hostname() -> CmdResult<String> {
|
||||||
|
use gethostname::gethostname;
|
||||||
|
|
||||||
|
// 获取系统主机名,处理可能的非UTF-8字符
|
||||||
|
let hostname = match gethostname().into_string() {
|
||||||
|
Ok(name) => name,
|
||||||
|
Err(os_string) => {
|
||||||
|
// 对于包含非UTF-8的主机名,使用调试格式化
|
||||||
|
let fallback = format!("{:?}", os_string);
|
||||||
|
// 去掉可能存在的引号
|
||||||
|
fallback.trim_matches('"').to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取网络接口列表
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_network_interfaces() -> Vec<String> {
|
||||||
|
use sysinfo::Networks;
|
||||||
|
let mut result = Vec::new();
|
||||||
|
let networks = Networks::new_with_refreshed_list();
|
||||||
|
for (interface_name, _) in &networks {
|
||||||
|
result.push(interface_name.clone());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取网络接口详细信息
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_network_interfaces_info() -> CmdResult<Vec<NetworkInterface>> {
|
||||||
|
use network_interface::{NetworkInterface, NetworkInterfaceConfig};
|
||||||
|
|
||||||
|
let names = get_network_interfaces();
|
||||||
|
let interfaces = wrap_err!(NetworkInterface::show())?;
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
for interface in interfaces {
|
||||||
|
if names.contains(&interface.name) {
|
||||||
|
result.push(interface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
472
src-tauri/src/cmd/profile.rs
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
use super::CmdResult;
|
||||||
|
use crate::{
|
||||||
|
config::{Config, IProfiles, PrfItem, PrfOption},
|
||||||
|
core::{handle, timer::Timer, tray::Tray, CoreManager},
|
||||||
|
feat, logging, ret_err,
|
||||||
|
utils::{dirs, help, logging::Type},
|
||||||
|
wrap_err,
|
||||||
|
};
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
// 添加全局互斥锁防止并发配置更新
|
||||||
|
static PROFILE_UPDATE_MUTEX: Mutex<()> = Mutex::const_new(());
|
||||||
|
|
||||||
|
/// 获取配置文件避免锁竞争
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_profiles() -> CmdResult<IProfiles> {
|
||||||
|
// 策略1: 尝试快速获取latest数据
|
||||||
|
let latest_result = tokio::time::timeout(
|
||||||
|
Duration::from_millis(500),
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
let profiles = Config::profiles();
|
||||||
|
let latest = profiles.latest();
|
||||||
|
IProfiles {
|
||||||
|
current: latest.current.clone(),
|
||||||
|
items: latest.items.clone(),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match latest_result {
|
||||||
|
Ok(Ok(profiles)) => {
|
||||||
|
logging!(info, Type::Cmd, false, "快速获取配置列表成功");
|
||||||
|
return Ok(profiles);
|
||||||
|
}
|
||||||
|
Ok(Err(join_err)) => {
|
||||||
|
logging!(warn, Type::Cmd, true, "快速获取配置任务失败: {}", join_err);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
logging!(warn, Type::Cmd, true, "快速获取配置超时(500ms)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略2: 如果快速获取失败,尝试获取data()
|
||||||
|
let data_result = tokio::time::timeout(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
let profiles = Config::profiles();
|
||||||
|
let data = profiles.data();
|
||||||
|
IProfiles {
|
||||||
|
current: data.current.clone(),
|
||||||
|
items: data.items.clone(),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match data_result {
|
||||||
|
Ok(Ok(profiles)) => {
|
||||||
|
logging!(info, Type::Cmd, false, "获取draft配置列表成功");
|
||||||
|
return Ok(profiles);
|
||||||
|
}
|
||||||
|
Ok(Err(join_err)) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"获取draft配置任务失败: {}",
|
||||||
|
join_err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
logging!(error, Type::Cmd, true, "获取draft配置超时(2秒)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略3: fallback,尝试重新创建配置
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"所有获取配置策略都失败,尝试fallback"
|
||||||
|
);
|
||||||
|
|
||||||
|
match tokio::task::spawn_blocking(IProfiles::new).await {
|
||||||
|
Ok(profiles) => {
|
||||||
|
logging!(info, Type::Cmd, true, "使用fallback配置成功");
|
||||||
|
Ok(profiles)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
logging!(error, Type::Cmd, true, "fallback配置也失败: {}", err);
|
||||||
|
// 返回空配置避免崩溃
|
||||||
|
Ok(IProfiles {
|
||||||
|
current: None,
|
||||||
|
items: Some(vec![]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 增强配置文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn enhance_profiles() -> CmdResult {
|
||||||
|
wrap_err!(feat::enhance_profiles().await)?;
|
||||||
|
handle::Handle::refresh_clash();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 导入配置文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
|
||||||
|
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
|
||||||
|
wrap_err!(Config::profiles().data().append_item(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 重新排序配置文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult {
|
||||||
|
wrap_err!(Config::profiles().data().reorder(active_id, over_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 创建配置文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResult {
|
||||||
|
let item = wrap_err!(PrfItem::from(item, file_data).await)?;
|
||||||
|
wrap_err!(Config::profiles().data().append_item(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新配置文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResult {
|
||||||
|
wrap_err!(feat::update_profile(index, option, Some(true)).await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除配置文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn delete_profile(index: String) -> CmdResult {
|
||||||
|
let should_update = wrap_err!({ Config::profiles().data().delete_item(index) })?;
|
||||||
|
|
||||||
|
// 删除后自动清理冗余文件
|
||||||
|
let _ = Config::profiles().latest().auto_cleanup();
|
||||||
|
|
||||||
|
if should_update {
|
||||||
|
wrap_err!(CoreManager::global().update_config().await)?;
|
||||||
|
handle::Handle::refresh_clash();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改profiles的配置
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
|
||||||
|
// 获取互斥锁,防止并发执行
|
||||||
|
let _guard = PROFILE_UPDATE_MUTEX.lock().await;
|
||||||
|
|
||||||
|
logging!(info, Type::Cmd, true, "开始修改配置文件");
|
||||||
|
|
||||||
|
// 保存当前配置,以便在验证失败时恢复
|
||||||
|
let current_profile = Config::profiles().latest().current.clone();
|
||||||
|
logging!(info, Type::Cmd, true, "当前配置: {:?}", current_profile);
|
||||||
|
|
||||||
|
// 如果要切换配置,先检查目标配置文件是否有语法错误
|
||||||
|
if let Some(new_profile) = profiles.current.as_ref() {
|
||||||
|
if current_profile.as_ref() != Some(new_profile) {
|
||||||
|
logging!(info, Type::Cmd, true, "正在切换到新配置: {}", new_profile);
|
||||||
|
|
||||||
|
// 获取目标配置文件路径
|
||||||
|
let config_file_result = {
|
||||||
|
let profiles_config = Config::profiles();
|
||||||
|
let profiles_data = profiles_config.latest();
|
||||||
|
match profiles_data.get_item(new_profile) {
|
||||||
|
Ok(item) => {
|
||||||
|
if let Some(file) = &item.file {
|
||||||
|
let path = dirs::app_profiles_dir().map(|dir| dir.join(file));
|
||||||
|
path.ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
logging!(error, Type::Cmd, true, "获取目标配置信息失败: {}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果获取到文件路径,检查YAML语法
|
||||||
|
if let Some(file_path) = config_file_result {
|
||||||
|
if !file_path.exists() {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"目标配置文件不存在: {}",
|
||||||
|
file_path.display()
|
||||||
|
);
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"config_validate::file_not_found",
|
||||||
|
format!("{}", file_path.display()),
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 超时保护
|
||||||
|
let file_read_result = tokio::time::timeout(
|
||||||
|
Duration::from_secs(5),
|
||||||
|
tokio::fs::read_to_string(&file_path),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match file_read_result {
|
||||||
|
Ok(Ok(content)) => {
|
||||||
|
let yaml_parse_result = tokio::task::spawn_blocking(move || {
|
||||||
|
serde_yaml::from_str::<serde_yaml::Value>(&content)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match yaml_parse_result {
|
||||||
|
Ok(Ok(_)) => {
|
||||||
|
logging!(info, Type::Cmd, true, "目标配置文件语法正确");
|
||||||
|
}
|
||||||
|
Ok(Err(err)) => {
|
||||||
|
let error_msg = format!(" {}", err);
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"目标配置文件存在YAML语法错误:{}",
|
||||||
|
error_msg
|
||||||
|
);
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"config_validate::yaml_syntax_error",
|
||||||
|
&error_msg,
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
Err(join_err) => {
|
||||||
|
let error_msg = format!("YAML解析任务失败: {}", join_err);
|
||||||
|
logging!(error, Type::Cmd, true, "{}", error_msg);
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"config_validate::yaml_parse_error",
|
||||||
|
&error_msg,
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Err(err)) => {
|
||||||
|
let error_msg = format!("无法读取目标配置文件: {}", err);
|
||||||
|
logging!(error, Type::Cmd, true, "{}", error_msg);
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"config_validate::file_read_error",
|
||||||
|
&error_msg,
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let error_msg = "读取配置文件超时(5秒)".to_string();
|
||||||
|
logging!(error, Type::Cmd, true, "{}", error_msg);
|
||||||
|
handle::Handle::notice_message(
|
||||||
|
"config_validate::file_read_timeout",
|
||||||
|
&error_msg,
|
||||||
|
);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新profiles配置
|
||||||
|
logging!(info, Type::Cmd, true, "正在更新配置草稿");
|
||||||
|
|
||||||
|
let current_value = profiles.current.clone();
|
||||||
|
|
||||||
|
let _ = Config::profiles().draft().patch_config(profiles);
|
||||||
|
|
||||||
|
// 为配置更新添加超时保护
|
||||||
|
let update_result = tokio::time::timeout(
|
||||||
|
Duration::from_secs(30), // 30秒超时
|
||||||
|
CoreManager::global().update_config(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// 更新配置并进行验证
|
||||||
|
match update_result {
|
||||||
|
Ok(Ok((true, _))) => {
|
||||||
|
logging!(info, Type::Cmd, true, "配置更新成功");
|
||||||
|
Config::profiles().apply();
|
||||||
|
handle::Handle::refresh_clash();
|
||||||
|
|
||||||
|
// 强制刷新代理缓存,确保profile切换后立即获取最新节点数据
|
||||||
|
crate::process::AsyncHandler::spawn(|| async move {
|
||||||
|
if let Err(e) = super::proxy::force_refresh_proxies().await {
|
||||||
|
log::warn!(target: "app", "强制刷新代理缓存失败: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
crate::process::AsyncHandler::spawn(|| async move {
|
||||||
|
if let Err(e) = Tray::global().update_tooltip() {
|
||||||
|
log::warn!(target: "app", "异步更新托盘提示失败: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = Tray::global().update_menu() {
|
||||||
|
log::warn!(target: "app", "异步更新托盘菜单失败: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存配置文件
|
||||||
|
if let Err(e) = Config::profiles().data().save_file() {
|
||||||
|
log::warn!(target: "app", "异步保存配置文件失败: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 立即通知前端配置变更
|
||||||
|
if let Some(current) = ¤t_value {
|
||||||
|
logging!(info, Type::Cmd, true, "向前端发送配置变更事件: {}", current);
|
||||||
|
handle::Handle::notify_profile_changed(current.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
Ok(Ok((false, error_msg))) => {
|
||||||
|
logging!(warn, Type::Cmd, true, "配置验证失败: {}", error_msg);
|
||||||
|
Config::profiles().discard();
|
||||||
|
// 如果验证失败,恢复到之前的配置
|
||||||
|
if let Some(prev_profile) = current_profile {
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"尝试恢复到之前的配置: {}",
|
||||||
|
prev_profile
|
||||||
|
);
|
||||||
|
let restore_profiles = IProfiles {
|
||||||
|
current: Some(prev_profile),
|
||||||
|
items: None,
|
||||||
|
};
|
||||||
|
// 静默恢复,不触发验证
|
||||||
|
wrap_err!({ Config::profiles().draft().patch_config(restore_profiles) })?;
|
||||||
|
Config::profiles().apply();
|
||||||
|
|
||||||
|
crate::process::AsyncHandler::spawn(|| async move {
|
||||||
|
if let Err(e) = Config::profiles().data().save_file() {
|
||||||
|
log::warn!(target: "app", "异步保存恢复配置文件失败: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
logging!(info, Type::Cmd, true, "成功恢复到之前的配置");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送验证错误通知
|
||||||
|
handle::Handle::notice_message("config_validate::error", &error_msg);
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
logging!(warn, Type::Cmd, true, "更新过程发生错误: {}", e);
|
||||||
|
Config::profiles().discard();
|
||||||
|
handle::Handle::notice_message("config_validate::boot_error", e.to_string());
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// 超时处理
|
||||||
|
let timeout_msg = "配置更新超时(30秒),可能是配置验证或核心通信阻塞";
|
||||||
|
logging!(error, Type::Cmd, true, "{}", timeout_msg);
|
||||||
|
Config::profiles().discard();
|
||||||
|
|
||||||
|
if let Some(prev_profile) = current_profile {
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Cmd,
|
||||||
|
true,
|
||||||
|
"超时后尝试恢复到之前的配置: {}",
|
||||||
|
prev_profile
|
||||||
|
);
|
||||||
|
let restore_profiles = IProfiles {
|
||||||
|
current: Some(prev_profile),
|
||||||
|
items: None,
|
||||||
|
};
|
||||||
|
wrap_err!({ Config::profiles().draft().patch_config(restore_profiles) })?;
|
||||||
|
Config::profiles().apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
handle::Handle::notice_message("config_validate::timeout", timeout_msg);
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 根据profile name修改profiles
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn patch_profiles_config_by_profile_index(
|
||||||
|
_app_handle: tauri::AppHandle,
|
||||||
|
profile_index: String,
|
||||||
|
) -> CmdResult<bool> {
|
||||||
|
logging!(info, Type::Cmd, true, "切换配置到: {}", profile_index);
|
||||||
|
|
||||||
|
let profiles = IProfiles {
|
||||||
|
current: Some(profile_index),
|
||||||
|
items: None,
|
||||||
|
};
|
||||||
|
patch_profiles_config(profiles).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改某个profile item的
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
|
||||||
|
// 保存修改前检查是否有更新 update_interval
|
||||||
|
let update_interval_changed =
|
||||||
|
if let Ok(old_profile) = Config::profiles().latest().get_item(&index) {
|
||||||
|
let old_interval = old_profile.option.as_ref().and_then(|o| o.update_interval);
|
||||||
|
let new_interval = profile.option.as_ref().and_then(|o| o.update_interval);
|
||||||
|
old_interval != new_interval
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存修改
|
||||||
|
wrap_err!(Config::profiles().data().patch_item(index.clone(), profile))?;
|
||||||
|
|
||||||
|
// 如果更新间隔变更,异步刷新定时器
|
||||||
|
if update_interval_changed {
|
||||||
|
let index_clone = index.clone();
|
||||||
|
crate::process::AsyncHandler::spawn(move || async move {
|
||||||
|
logging!(info, Type::Timer, "定时器更新间隔已变更,正在刷新定时器...");
|
||||||
|
if let Err(e) = crate::core::Timer::global().refresh() {
|
||||||
|
logging!(error, Type::Timer, "刷新定时器失败: {}", e);
|
||||||
|
} else {
|
||||||
|
// 刷新成功后发送自定义事件,不触发配置重载
|
||||||
|
crate::core::handle::Handle::notify_timer_updated(index_clone);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查看配置文件
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn view_profile(app_handle: tauri::AppHandle, index: String) -> CmdResult {
|
||||||
|
let file = {
|
||||||
|
wrap_err!(Config::profiles().latest().get_item(&index))?
|
||||||
|
.file
|
||||||
|
.clone()
|
||||||
|
.ok_or("the file field is null")
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let path = wrap_err!(dirs::app_profiles_dir())?.join(file);
|
||||||
|
if !path.exists() {
|
||||||
|
ret_err!("the file not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap_err!(help::open_file(app_handle, path))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 读取配置文件内容
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn read_profile_file(index: String) -> CmdResult<String> {
|
||||||
|
let profiles = Config::profiles();
|
||||||
|
let profiles = profiles.latest();
|
||||||
|
let item = wrap_err!(profiles.get_item(&index))?;
|
||||||
|
let data = wrap_err!(item.read_file())?;
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取下一次更新时间
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_next_update_time(uid: String) -> CmdResult<Option<i64>> {
|
||||||
|
let timer = Timer::global();
|
||||||
|
let next_time = timer.get_next_update_time(&uid);
|
||||||
|
Ok(next_time)
|
||||||
|
}
|
||||||
99
src-tauri/src/cmd/proxy.rs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
use super::CmdResult;
|
||||||
|
use crate::{core::handle, module::mihomo::MihomoManager, state::proxy::CmdProxyState};
|
||||||
|
use std::{
|
||||||
|
sync::Mutex,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
use tauri::Manager;
|
||||||
|
|
||||||
|
const PROVIDERS_REFRESH_INTERVAL: Duration = Duration::from_secs(3);
|
||||||
|
const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_proxies() -> CmdResult<serde_json::Value> {
|
||||||
|
let manager = MihomoManager::global();
|
||||||
|
|
||||||
|
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||||
|
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
|
||||||
|
|
||||||
|
let should_refresh = {
|
||||||
|
let mut state = cmd_proxy_state.lock().unwrap();
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(state.last_refresh_time) > PROXIES_REFRESH_INTERVAL {
|
||||||
|
state.need_refresh = true;
|
||||||
|
state.last_refresh_time = now;
|
||||||
|
}
|
||||||
|
state.need_refresh
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_refresh {
|
||||||
|
let proxies = manager.get_refresh_proxies().await?;
|
||||||
|
{
|
||||||
|
let mut state = cmd_proxy_state.lock().unwrap();
|
||||||
|
state.proxies = Box::new(proxies);
|
||||||
|
state.need_refresh = false;
|
||||||
|
}
|
||||||
|
log::debug!(target: "app", "proxies刷新成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
let proxies = {
|
||||||
|
let state = cmd_proxy_state.lock().unwrap();
|
||||||
|
state.proxies.clone()
|
||||||
|
};
|
||||||
|
Ok(*proxies)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 强制刷新代理缓存用于profile切换
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn force_refresh_proxies() -> CmdResult<serde_json::Value> {
|
||||||
|
let manager = MihomoManager::global();
|
||||||
|
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||||
|
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
|
||||||
|
|
||||||
|
log::debug!(target: "app", "强制刷新代理缓存");
|
||||||
|
|
||||||
|
let proxies = manager.get_refresh_proxies().await?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut state = cmd_proxy_state.lock().unwrap();
|
||||||
|
state.proxies = Box::new(proxies.clone());
|
||||||
|
state.need_refresh = false;
|
||||||
|
state.last_refresh_time = Instant::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!(target: "app", "强制刷新代理缓存完成");
|
||||||
|
Ok(proxies)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
|
||||||
|
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||||
|
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
|
||||||
|
|
||||||
|
let should_refresh = {
|
||||||
|
let mut state = cmd_proxy_state.lock().unwrap();
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(state.last_refresh_time) > PROVIDERS_REFRESH_INTERVAL {
|
||||||
|
state.need_refresh = true;
|
||||||
|
state.last_refresh_time = now;
|
||||||
|
}
|
||||||
|
state.need_refresh
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_refresh {
|
||||||
|
let manager = MihomoManager::global();
|
||||||
|
let providers = manager.get_providers_proxies().await?;
|
||||||
|
{
|
||||||
|
let mut state = cmd_proxy_state.lock().unwrap();
|
||||||
|
state.providers_proxies = Box::new(providers);
|
||||||
|
state.need_refresh = false;
|
||||||
|
}
|
||||||
|
log::debug!(target: "app", "providers_proxies刷新成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
let providers_proxies = {
|
||||||
|
let state = cmd_proxy_state.lock().unwrap();
|
||||||
|
state.providers_proxies.clone()
|
||||||
|
};
|
||||||
|
Ok(*providers_proxies)
|
||||||
|
}
|
||||||
36
src-tauri/src/cmd/runtime.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use super::CmdResult;
|
||||||
|
use crate::{config::*, wrap_err};
|
||||||
|
use anyhow::Context;
|
||||||
|
use serde_yaml::Mapping;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// 获取运行时配置
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_runtime_config() -> CmdResult<Option<Mapping>> {
|
||||||
|
Ok(Config::runtime().latest().config.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取运行时YAML配置
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_runtime_yaml() -> CmdResult<String> {
|
||||||
|
let runtime = Config::runtime();
|
||||||
|
let runtime = runtime.latest();
|
||||||
|
let config = runtime.config.as_ref();
|
||||||
|
wrap_err!(config
|
||||||
|
.ok_or(anyhow::anyhow!("failed to parse config to yaml file"))
|
||||||
|
.and_then(
|
||||||
|
|config| serde_yaml::to_string(config).context("failed to convert config to yaml")
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取运行时存在的键
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_runtime_exists() -> CmdResult<Vec<String>> {
|
||||||
|
Ok(Config::runtime().latest().exists_keys.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取运行时日志
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> {
|
||||||
|
Ok(Config::runtime().latest().chain_logs.clone())
|
||||||
|
}
|
||||||
165
src-tauri/src/cmd/save_profile.rs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
use super::CmdResult;
|
||||||
|
use crate::{
|
||||||
|
config::*,
|
||||||
|
core::*,
|
||||||
|
logging,
|
||||||
|
utils::{dirs, logging::Type},
|
||||||
|
wrap_err,
|
||||||
|
};
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
/// 保存profiles的配置
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdResult {
|
||||||
|
if file_data.is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在异步操作前完成所有文件操作
|
||||||
|
let (file_path, original_content, is_merge_file) = {
|
||||||
|
let profiles = Config::profiles();
|
||||||
|
let profiles_guard = profiles.latest();
|
||||||
|
let item = wrap_err!(profiles_guard.get_item(&index))?;
|
||||||
|
// 确定是否为merge类型文件
|
||||||
|
let is_merge = item.itype.as_ref().is_some_and(|t| t == "merge");
|
||||||
|
let content = wrap_err!(item.read_file())?;
|
||||||
|
let path = item.file.clone().ok_or("file field is null")?;
|
||||||
|
let profiles_dir = wrap_err!(dirs::app_profiles_dir())?;
|
||||||
|
(profiles_dir.join(path), content, is_merge)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存新的配置文件
|
||||||
|
wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?;
|
||||||
|
|
||||||
|
let file_path_str = file_path.to_string_lossy().to_string();
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[cmd配置save] 开始验证配置文件: {}, 是否为merge文件: {}",
|
||||||
|
file_path_str,
|
||||||
|
is_merge_file
|
||||||
|
);
|
||||||
|
|
||||||
|
// 对于 merge 文件,只进行语法验证,不进行后续内核验证
|
||||||
|
if is_merge_file {
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[cmd配置save] 检测到merge文件,只进行语法验证"
|
||||||
|
);
|
||||||
|
match CoreManager::global()
|
||||||
|
.validate_config_file(&file_path_str, Some(true))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok((true, _)) => {
|
||||||
|
logging!(
|
||||||
|
info,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[cmd配置save] merge文件语法验证通过"
|
||||||
|
);
|
||||||
|
// 成功后尝试更新整体配置
|
||||||
|
if let Err(e) = CoreManager::global().update_config().await {
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[cmd配置save] 更新整体配置时发生错误: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Ok((false, error_msg)) => {
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[cmd配置save] merge文件语法验证失败: {}",
|
||||||
|
error_msg
|
||||||
|
);
|
||||||
|
// 恢复原始配置文件
|
||||||
|
wrap_err!(fs::write(&file_path, original_content))?;
|
||||||
|
// 发送合并文件专用错误通知
|
||||||
|
let result = (false, error_msg.clone());
|
||||||
|
crate::cmd::validate::handle_yaml_validation_notice(&result, "合并配置文件");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[cmd配置save] 验证过程发生错误: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
// 恢复原始配置文件
|
||||||
|
wrap_err!(fs::write(&file_path, original_content))?;
|
||||||
|
return Err(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非merge文件使用完整验证流程
|
||||||
|
match CoreManager::global()
|
||||||
|
.validate_config_file(&file_path_str, None)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok((true, _)) => {
|
||||||
|
logging!(info, Type::Config, true, "[cmd配置save] 验证成功");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok((false, error_msg)) => {
|
||||||
|
logging!(
|
||||||
|
warn,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[cmd配置save] 验证失败: {}",
|
||||||
|
error_msg
|
||||||
|
);
|
||||||
|
// 恢复原始配置文件
|
||||||
|
wrap_err!(fs::write(&file_path, original_content))?;
|
||||||
|
|
||||||
|
// 智能判断错误类型
|
||||||
|
let is_script_error = file_path_str.ends_with(".js")
|
||||||
|
|| error_msg.contains("Script syntax error")
|
||||||
|
|| error_msg.contains("Script must contain a main function")
|
||||||
|
|| error_msg.contains("Failed to read script file");
|
||||||
|
|
||||||
|
if error_msg.contains("YAML syntax error")
|
||||||
|
|| error_msg.contains("Failed to read file:")
|
||||||
|
|| (!file_path_str.ends_with(".js") && !is_script_error)
|
||||||
|
{
|
||||||
|
// 普通YAML错误使用YAML通知处理
|
||||||
|
log::info!(target: "app", "[cmd配置save] YAML配置文件验证失败,发送通知");
|
||||||
|
let result = (false, error_msg.clone());
|
||||||
|
crate::cmd::validate::handle_yaml_validation_notice(&result, "YAML配置文件");
|
||||||
|
} else if is_script_error {
|
||||||
|
// 脚本错误使用专门的通知处理
|
||||||
|
log::info!(target: "app", "[cmd配置save] 脚本文件验证失败,发送通知");
|
||||||
|
let result = (false, error_msg.clone());
|
||||||
|
crate::cmd::validate::handle_script_validation_notice(&result, "脚本文件");
|
||||||
|
} else {
|
||||||
|
// 普通配置错误使用一般通知
|
||||||
|
log::info!(target: "app", "[cmd配置save] 其他类型验证失败,发送一般通知");
|
||||||
|
handle::Handle::notice_message("config_validate::error", &error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Config,
|
||||||
|
true,
|
||||||
|
"[cmd配置save] 验证过程发生错误: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
// 恢复原始配置文件
|
||||||
|
wrap_err!(fs::write(&file_path, original_content))?;
|
||||||
|
Err(e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||