Compare commits
591 Commits
v10.3.3
...
renovate/b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c6dc9b29a | ||
|
|
50e55dea77 | ||
|
|
f7c9e42066 | ||
|
|
ef3ee7bea7 | ||
|
|
8eb300c4fb | ||
|
|
b7b99a725c | ||
|
|
df090a89c4 | ||
|
|
856fa0ed5b | ||
|
|
9bb5a680ac | ||
|
|
2d40f7bdb3 | ||
|
|
06b210d1c9 | ||
|
|
e0e714e7b5 | ||
|
|
dfb8342d8b | ||
|
|
9d662e36a1 | ||
|
|
45159c29fe | ||
|
|
a27c68a1dd | ||
|
|
cdee1c14d9 | ||
|
|
8de6a36d86 | ||
|
|
b03a8d53bb | ||
|
|
b0510cdade | ||
|
|
7945311b4b | ||
|
|
75de4dfd3b | ||
|
|
6a838bbcb7 | ||
|
|
ab2bab8c9b | ||
|
|
9a55278d32 | ||
|
|
f9735f3b31 | ||
|
|
50be214b56 | ||
|
|
9ec8e83f41 | ||
|
|
c70984d458 | ||
|
|
a3503c0c70 | ||
|
|
ca2d16edfd | ||
|
|
722b1ad09e | ||
|
|
743845a66b | ||
|
|
2594f4c0fb | ||
|
|
639d055f9a | ||
|
|
ea2ccdd69f | ||
|
|
84a50afa83 | ||
|
|
b13c1bd2d7 | ||
|
|
7749f41d56 | ||
|
|
593a2a33d9 | ||
|
|
4f693aeaf3 | ||
|
|
c3d34184d0 | ||
|
|
4aa224de87 | ||
|
|
320b13c06b | ||
|
|
907d778c55 | ||
|
|
f3fccb86c0 | ||
|
|
bb4afb71e9 | ||
|
|
ec8d30f634 | ||
|
|
50d318641a | ||
|
|
c5267f6087 | ||
|
|
d80e8f64d1 | ||
|
|
c6f2e0cc44 | ||
|
|
99c3e8af26 | ||
|
|
30d3537c0e | ||
|
|
90cb16e3d0 | ||
|
|
5192a0f1dc | ||
|
|
bec7edf1fc | ||
|
|
051784ac0d | ||
|
|
17d715479a | ||
|
|
39a90f5ebe | ||
|
|
80c4bd5065 | ||
|
|
222d800a32 | ||
|
|
b29699fc55 | ||
|
|
fc0e5461eb | ||
|
|
75da4a709c | ||
|
|
02e388122b | ||
|
|
02fe1087de | ||
|
|
34b7943fd1 | ||
|
|
b61886f0f9 | ||
|
|
d9a8ecfd33 | ||
|
|
6c68bfaf64 | ||
|
|
5866a0570f | ||
|
|
28f5754efd | ||
|
|
ea1b286c98 | ||
|
|
22ab77de6d | ||
|
|
410bd61c75 | ||
|
|
07b422915c | ||
|
|
5b9d387ef3 | ||
|
|
5b5dd27834 | ||
|
|
79f65981a5 | ||
|
|
a52c775247 | ||
|
|
549111db3a | ||
|
|
1572974ec4 | ||
|
|
8387468607 | ||
|
|
94979d6b7a | ||
|
|
6652a2aa90 | ||
|
|
f943b220d9 | ||
|
|
0fc7016d68 | ||
|
|
9e9e6c9d55 | ||
|
|
bafa15c8f1 | ||
|
|
f669de8272 | ||
|
|
ff26835406 | ||
|
|
99892359c7 | ||
|
|
8fc25d7fd4 | ||
|
|
942bfaa708 | ||
|
|
a1fd324e82 | ||
|
|
62bf2c6e90 | ||
|
|
9f1e0c8eef | ||
|
|
027de7c865 | ||
|
|
266adabd13 | ||
|
|
7d2e43f83b | ||
|
|
294a90a7c3 | ||
|
|
af5def1551 | ||
|
|
8c63ee6a18 | ||
|
|
ee8ae8623d | ||
|
|
222e6b55c0 | ||
|
|
2050a80665 | ||
|
|
e01be14041 | ||
|
|
dd6bc580fa | ||
|
|
22ef36a185 | ||
|
|
b57ceccbe1 | ||
|
|
e9d430a4e4 | ||
|
|
8a247fba95 | ||
|
|
5b6c31bd89 | ||
|
|
c316e2af69 | ||
|
|
7270aa96f0 | ||
|
|
dd823eb489 | ||
|
|
cb29305385 | ||
|
|
2dfa37dd0c | ||
|
|
b3d3284f18 | ||
|
|
73e3e133c6 | ||
|
|
9828af9f03 | ||
|
|
48aa6b5ac5 | ||
|
|
e5b3ed1461 | ||
|
|
5fad9f0ec6 | ||
|
|
f4a5507716 | ||
|
|
2ea9d1d6fd | ||
|
|
e393b1f90b | ||
|
|
2259e81bb0 | ||
|
|
65a30292a3 | ||
|
|
494eef3472 | ||
|
|
bee1e865b2 | ||
|
|
c92b049bbc | ||
|
|
c9985480fe | ||
|
|
e1b5b76d8e | ||
|
|
762a74f5f9 | ||
|
|
a7a2d8493e | ||
|
|
bc6538d209 | ||
|
|
93d841310e | ||
|
|
2ac679f17e | ||
|
|
9a3ef463f9 | ||
|
|
affc5fcd75 | ||
|
|
96bff5e974 | ||
|
|
e828e14fa5 | ||
|
|
4c6dc8ff82 | ||
|
|
75bd7c90d3 | ||
|
|
2aa3d94a98 | ||
|
|
f4ac809dff | ||
|
|
0dee534f84 | ||
|
|
ad9f2c2ccb | ||
|
|
a886d20a7b | ||
|
|
791993795a | ||
|
|
a2afdb821f | ||
|
|
47b51a8be0 | ||
|
|
7c7e7c3ce4 | ||
|
|
fec08a5ad1 | ||
|
|
3961ef61c8 | ||
|
|
84692da9a2 | ||
|
|
fb7ba52e39 | ||
|
|
898823abb2 | ||
|
|
ccefd0a43a | ||
|
|
98f0be61ed | ||
|
|
6bb1d54cb0 | ||
|
|
69a76e32b7 | ||
|
|
99d989d486 | ||
|
|
456d62224e | ||
|
|
b662fae11e | ||
|
|
0926bd2f6c | ||
|
|
3fb473ae95 | ||
|
|
603ed18a4c | ||
|
|
0307fdd296 | ||
|
|
c10dcdbfdb | ||
|
|
31d8e579c6 | ||
|
|
7b3fec0349 | ||
|
|
e32a58f6ff | ||
|
|
36cd726676 | ||
|
|
8dc08de628 | ||
|
|
0361954919 | ||
|
|
29a62575f4 | ||
|
|
547a6df2ae | ||
|
|
53d08cdf28 | ||
|
|
a033152c60 | ||
|
|
9472aaca7a | ||
|
|
6254b99e02 | ||
|
|
3d17bdb747 | ||
|
|
d2eeeb9129 | ||
|
|
1d626e0add | ||
|
|
9f4cb8c1b6 | ||
|
|
91fc5e3902 | ||
|
|
9048cd8f47 | ||
|
|
4f5e8a8836 | ||
|
|
29b05fa50f | ||
|
|
dbe1a5c988 | ||
|
|
b6c1290934 | ||
|
|
306ff3c7c5 | ||
|
|
0e43e0d7fc | ||
|
|
4da696321a | ||
|
|
a95dd1e037 | ||
|
|
012a6bbde3 | ||
|
|
32197f79f3 | ||
|
|
257d202646 | ||
|
|
c00365c19d | ||
|
|
3f9fe845e5 | ||
|
|
b166aae835 | ||
|
|
31f0097862 | ||
|
|
f78514dbd8 | ||
|
|
9fc5fe9798 | ||
|
|
6d14ac1693 | ||
|
|
b8ab573c00 | ||
|
|
75ac6808a1 | ||
|
|
6719ff93d8 | ||
|
|
6b8327faad | ||
|
|
85c8bd2277 | ||
|
|
23fff2a09f | ||
|
|
689db93c99 | ||
|
|
1114556661 | ||
|
|
f8c910a3c2 | ||
|
|
f18ae089ff | ||
|
|
4a64992054 | ||
|
|
9fefb47242 | ||
|
|
fc5cc3c43b | ||
|
|
27464b795e | ||
|
|
845558c1da | ||
|
|
31fe5aa452 | ||
|
|
b354e07ef3 | ||
|
|
50a74dac4b | ||
|
|
7558bbfe9b | ||
|
|
7518676ac9 | ||
|
|
b7b665ff48 | ||
|
|
1be941e815 | ||
|
|
d1b7eba44e | ||
|
|
38e2d5663a | ||
|
|
3db95a3e67 | ||
|
|
ef0a0d69bb | ||
|
|
4b3a3e74f8 | ||
|
|
2c4751c7b2 | ||
|
|
30941ed26d | ||
|
|
c7163b63db | ||
|
|
6e6b3dcbfe | ||
|
|
1d136a6635 | ||
|
|
0ee67d78ef | ||
|
|
7356b920d4 | ||
|
|
ce8a325c1f | ||
|
|
a2f57e4769 | ||
|
|
751f41bc5e | ||
|
|
fd406f0f82 | ||
|
|
801dddacd4 | ||
|
|
397a537eef | ||
|
|
0423c836eb | ||
|
|
3250337e70 | ||
|
|
9dcd7fffe2 | ||
|
|
30b727b138 | ||
|
|
b86d6981ab | ||
|
|
2bf6a2b100 | ||
|
|
3dc8d31d57 | ||
|
|
b308fb92c0 | ||
|
|
bc9746455e | ||
|
|
109a9c76e3 | ||
|
|
4488f3d5d3 | ||
|
|
5a7958d20e | ||
|
|
481a942b76 | ||
|
|
a601d8429d | ||
|
|
a4a2d52a6d | ||
|
|
47fa3ba7de | ||
|
|
e6bb6709b3 | ||
|
|
c421742c4f | ||
|
|
1312cc8f6e | ||
|
|
ed37763d30 | ||
|
|
583bbf65e2 | ||
|
|
5770a5caa7 | ||
|
|
722903fec3 | ||
|
|
30f1c3c1b4 | ||
|
|
ef7d146282 | ||
|
|
20667a23d3 | ||
|
|
26f05827ae | ||
|
|
b1ffe7d553 | ||
|
|
368a060529 | ||
|
|
b40bffb1f2 | ||
|
|
488ae149f7 | ||
|
|
fa3e4726b7 | ||
|
|
66a12cc8bf | ||
|
|
3e0c21e981 | ||
|
|
da270ae7d9 | ||
|
|
4624f11ba5 | ||
|
|
224bb96a98 | ||
|
|
9a6fe8eea9 | ||
|
|
aebc035ec0 | ||
|
|
bd348c328e | ||
|
|
c5f2d7b473 | ||
|
|
dc9d8d55f2 | ||
|
|
b172ba7f03 | ||
|
|
8227890808 | ||
|
|
a0963fe3fc | ||
|
|
4df30c2587 | ||
|
|
305a5fbcae | ||
|
|
4f4dcbb643 | ||
|
|
202897ba35 | ||
|
|
444689c899 | ||
|
|
98ec13f8db | ||
|
|
39f76a3a71 | ||
|
|
f181a795a6 | ||
|
|
ea2f3e07e9 | ||
|
|
8aad6eae0d | ||
|
|
e86e5fe3e7 | ||
|
|
2c2569c4f8 | ||
|
|
9ffdc9649e | ||
|
|
a5d4f2eec9 | ||
|
|
a5df40e01d | ||
|
|
0573fc97c6 | ||
|
|
1ae95f41a1 | ||
|
|
8a7af2e14d | ||
|
|
c36da89933 | ||
|
|
bbb84c2ee7 | ||
|
|
36fd4b13c0 | ||
|
|
49327000fc | ||
|
|
9c25cd7426 | ||
|
|
9767e4169c | ||
|
|
0854f9c559 | ||
|
|
e4a068d808 | ||
|
|
4c793b0df8 | ||
|
|
a021441135 | ||
|
|
29c555c394 | ||
|
|
c33d396489 | ||
|
|
f6d2ba4dae | ||
|
|
a88574204d | ||
|
|
9435bc4b7d | ||
|
|
27245cbd7b | ||
|
|
21751aa8a5 | ||
|
|
ad41948450 | ||
|
|
e32246f172 | ||
|
|
25d3a816b4 | ||
|
|
05b1a565e0 | ||
|
|
7b2623ea3c | ||
|
|
983c5243ba | ||
|
|
1958fe1e5b | ||
|
|
ca8558d9b4 | ||
|
|
1b534800a9 | ||
|
|
e91c00c9c0 | ||
|
|
a2375b4820 | ||
|
|
2e0c8e9e17 | ||
|
|
dc0ddcf9f0 | ||
|
|
a1f3c86a39 | ||
|
|
55f672eff7 | ||
|
|
8ece0346d8 | ||
|
|
b1fe1d201a | ||
|
|
5010abdc22 | ||
|
|
e4441d5021 | ||
|
|
5af0c6a7e5 | ||
|
|
b8da17106a | ||
|
|
fdf40dbf43 | ||
|
|
f3b6530969 | ||
|
|
cbc5fc94f9 | ||
|
|
dceb697355 | ||
|
|
07118fa0d2 | ||
|
|
16e6db0def | ||
|
|
64d8f6d632 | ||
|
|
180b5cba58 | ||
|
|
bac416e907 | ||
|
|
cb674a1572 | ||
|
|
960b14fa20 | ||
|
|
a9f57d4205 | ||
|
|
13330b6950 | ||
|
|
1ebcc9beee | ||
|
|
55e1bbf2b9 | ||
|
|
f2dfa1e475 | ||
|
|
fcd53e772a | ||
|
|
8b9d7ef8f3 | ||
|
|
d8406a8cfe | ||
|
|
4a9ef581e5 | ||
|
|
a52db1f261 | ||
|
|
8e16174ce7 | ||
|
|
c748bb5d7a | ||
|
|
3cc8f0d818 | ||
|
|
f96eeeda6b | ||
|
|
d1d8904376 | ||
|
|
3b329fe687 | ||
|
|
9eb1b4ac9f | ||
|
|
c4c0bd7383 | ||
|
|
1e9de5832d | ||
|
|
f2b17cdd9d | ||
|
|
7bfd6c2439 | ||
|
|
0e8d5f0266 | ||
|
|
32add8f046 | ||
|
|
f661f00277 | ||
|
|
2a1999fe20 | ||
|
|
4d66431aad | ||
|
|
767f0d91f4 | ||
|
|
a3428e3477 | ||
|
|
614131b7bf | ||
|
|
9b0681f3b8 | ||
|
|
ecf8fb7a47 | ||
|
|
04bfb45a97 | ||
|
|
d90ce30452 | ||
|
|
ab21600ca6 | ||
|
|
728ea26204 | ||
|
|
373cd3b3ae | ||
|
|
f4e0258b09 | ||
|
|
d50360a69a | ||
|
|
351922c81f | ||
|
|
9518f43866 | ||
|
|
2c1ce3d4e6 | ||
|
|
12116c3261 | ||
|
|
fbc84e8aa1 | ||
|
|
6dab1e4f37 | ||
|
|
650a143602 | ||
|
|
9b6027fe78 | ||
|
|
0e30e05ce8 | ||
|
|
eea952fa78 | ||
|
|
6071a1ee3b | ||
|
|
a801b7b9f4 | ||
|
|
c6e3f0ae0a | ||
|
|
a43b03d3db | ||
|
|
12b0fa57ad | ||
|
|
d9e304f0ef | ||
|
|
842b92cca7 | ||
|
|
485f0ec9c8 | ||
|
|
5e3b5fc9a7 | ||
|
|
7c63541cad | ||
|
|
238e089d74 | ||
|
|
8991bc9f62 | ||
|
|
7a3f3a8905 | ||
|
|
e4085e03eb | ||
|
|
4b0c366e5f | ||
|
|
ea97240d09 | ||
|
|
12de531abb | ||
|
|
c3876ce3bf | ||
|
|
cbbfc3a114 | ||
|
|
ad2bfc9abd | ||
|
|
528461412e | ||
|
|
64db679390 | ||
|
|
77a8b3b7d2 | ||
|
|
7007e76ab5 | ||
|
|
3c970063a9 | ||
|
|
b70830015e | ||
|
|
b43f2c8b3a | ||
|
|
c311da16f3 | ||
|
|
37608a338c | ||
|
|
b07288e674 | ||
|
|
707698faab | ||
|
|
2e70d132d0 | ||
|
|
30c5b31e21 | ||
|
|
77ff6cb714 | ||
|
|
ea13c51b7d | ||
|
|
3ed763b884 | ||
|
|
10e1e170b7 | ||
|
|
ffa62afc66 | ||
|
|
f794329913 | ||
|
|
f9a35c7661 | ||
|
|
ed496f3462 | ||
|
|
6accdae232 | ||
|
|
96efcc6c0d | ||
|
|
bf72d7bb5a | ||
|
|
dadffb1081 | ||
|
|
78dc567226 | ||
|
|
362ce4f4f9 | ||
|
|
ab35cd7b10 | ||
|
|
15f4ad7cd1 | ||
|
|
cbfb92041f | ||
|
|
a506c67cac | ||
|
|
788e0412f6 | ||
|
|
18b37ce3e3 | ||
|
|
a15e6748c7 | ||
|
|
c6d0539fd2 | ||
|
|
3eb3867944 | ||
|
|
810315b0e2 | ||
|
|
b461fc2536 | ||
|
|
7e63977ba0 | ||
|
|
78dec892cf | ||
|
|
9ea6628b5c | ||
|
|
465df2e9be | ||
|
|
61ef926849 | ||
|
|
7fa38c593e | ||
|
|
41c6d1cd9a | ||
|
|
cf3893dc49 | ||
|
|
a2fbe92a25 | ||
|
|
e1754707d8 | ||
|
|
cd380a53b3 | ||
|
|
a8c29fd1a2 | ||
|
|
6b871e7949 | ||
|
|
1b5fdb6645 | ||
|
|
fe9d877cdf | ||
|
|
60e7aa8f03 | ||
|
|
18e2d3e59c | ||
|
|
d68fcb08b2 | ||
|
|
1f6baefdc3 | ||
|
|
71efce32c1 | ||
|
|
3626c9cdc8 | ||
|
|
a23b761304 | ||
|
|
3fd27e4913 | ||
|
|
b3f152b716 | ||
|
|
df381f3a79 | ||
|
|
2dec9db310 | ||
|
|
d50dc4c9f6 | ||
|
|
ed8b563f20 | ||
|
|
2a73aa731d | ||
|
|
4dd1c13bd8 | ||
|
|
c1c9fe22df | ||
|
|
06a6b7a2eb | ||
|
|
b814dd824f | ||
|
|
ce234bdb59 | ||
|
|
13a46a44a8 | ||
|
|
dc78b00c3c | ||
|
|
48ae4bf813 | ||
|
|
a50040e2d5 | ||
|
|
2c9a56a8df | ||
|
|
021320b292 | ||
|
|
9d3662c3ea | ||
|
|
8e580457a5 | ||
|
|
5350658dab | ||
|
|
1ec0ac50a5 | ||
|
|
635bfce198 | ||
|
|
1307d2d7e8 | ||
|
|
d21141fefe | ||
|
|
0ec0e5a9dd | ||
|
|
9415d7c61f | ||
|
|
42188af02b | ||
|
|
e9581bcf15 | ||
|
|
6afe4f51c6 | ||
|
|
f623746d6c | ||
|
|
1ce4d66e74 | ||
|
|
3735d5c537 | ||
|
|
f3b1d2dfb3 | ||
|
|
7f7d2633cd | ||
|
|
afd95e3d5c | ||
|
|
8f72545894 | ||
|
|
d0d447deac | ||
|
|
53a8683788 | ||
|
|
81491a8d03 | ||
|
|
83504754ac | ||
|
|
2068c2c169 | ||
|
|
dbac121a90 | ||
|
|
b974938a33 | ||
|
|
06cb88a1a1 | ||
|
|
a6195d284c | ||
|
|
5b8850e8a3 | ||
|
|
57546a07fc | ||
|
|
d7709490ce | ||
|
|
3e6c6e513b | ||
|
|
30858780cf | ||
|
|
a7ddf4575a | ||
|
|
470231c9d1 | ||
|
|
282e336ac4 | ||
|
|
658829e4ff | ||
|
|
a0ff565220 | ||
|
|
7e48c5dedc | ||
|
|
03436b7f8f | ||
|
|
3f5eedb83d | ||
|
|
234ad4bdd7 | ||
|
|
c7923393be | ||
|
|
d4548b2f9a | ||
|
|
f6e8af186c | ||
|
|
58153635da | ||
|
|
5358509825 | ||
|
|
1ab0232d96 | ||
|
|
66860f1848 | ||
|
|
625f823f46 | ||
|
|
6263ab7e10 | ||
|
|
7db991db9d | ||
|
|
d75782892e | ||
|
|
cb7adc8ced | ||
|
|
7c3ba80270 | ||
|
|
76c39edc8b | ||
|
|
c20a300eea | ||
|
|
de3902a9c9 | ||
|
|
8bca671e9f | ||
|
|
54301a6a17 | ||
|
|
f06b7c0807 | ||
|
|
43c02cf7a7 | ||
|
|
3a1568e884 | ||
|
|
14753a14e7 | ||
|
|
227e8dcc8d | ||
|
|
97fd2b2718 | ||
|
|
f30e36d7bb | ||
|
|
d640bc66f5 | ||
|
|
a2331a2575 | ||
|
|
26a2c3c266 | ||
|
|
ceafcba88f | ||
|
|
d7182b5a6e | ||
|
|
93ec1172fe | ||
|
|
609477a373 | ||
|
|
1d49af10a7 | ||
|
|
327ed837c2 | ||
|
|
d406e2aeab | ||
|
|
0991cc8a6f | ||
|
|
ac6330fac8 | ||
|
|
29f0d229d3 | ||
|
|
3dd11f7b52 | ||
|
|
ddb1a021bb | ||
|
|
565aa405be | ||
|
|
907465f891 | ||
|
|
250485c826 |
98
.github/ISSUE_TEMPLATE/bug_report.md
vendored
98
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,32 +2,106 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
about: Topgrade is misbehaving
|
about: Topgrade is misbehaving
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
type: Bug
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- If you're here to report about a "No asset found" error, please make sure that an hour has been passed since the last release was made. -->
|
<!--
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
Please make sure to
|
||||||
|
[search for existing issues](https://github.com/topgrade-rs/topgrade/issues)
|
||||||
|
before filing a new one!
|
||||||
|
|
||||||
## What did you expect to happen?
|
Questions labeled with `Optional` can be skipped.
|
||||||
|
|
||||||
|
If you're here to report about a "No asset found" error, please make sure that
|
||||||
|
an hour has been passed since the last release was made.
|
||||||
|
-->
|
||||||
|
|
||||||
## What actually happened?
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] I have searched the issue tracker for relevant or duplicate issues.
|
||||||
|
|
||||||
## Additional Details
|
## Erroneous Behavior
|
||||||
- Which operating system or Linux distribution are you using?
|
|
||||||
- How did you install Topgrade?
|
|
||||||
- Which version are you running? <!-- Check with `topgrade -V` -->
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Run `topgrade --dry-run` to see which commands Topgrade is running.
|
What actually happened?
|
||||||
If the command seems wrong and you know why please tell us so.
|
-->
|
||||||
If the command seems fine try to run it yourself and tell us if you got a different result from Topgrade.
|
|
||||||
|
## Expected Behavior
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Describe the expected behavior.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Steps to reproduce
|
||||||
|
|
||||||
|
<!--
|
||||||
|
A minimal example to reproduce the issue.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Possible Cause (Optional)
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If you know the possible cause of the issue, please tell us.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Problem persists without calling from topgrade
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Execute the erroneous command directly to see if the problem persists.
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] No
|
||||||
|
|
||||||
|
## Ran through `Remote Execution`
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Did you run topgrade through `Remote Execution`?
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] No
|
||||||
|
|
||||||
|
If yes, does the issue still occur when you run topgrade directly in your
|
||||||
|
remote host?
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] No
|
||||||
|
|
||||||
|
## Configuration file (Optional)
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Paste your configuration file inside the code block if you think this issue is
|
||||||
|
related to configuration.
|
||||||
|
-->
|
||||||
|
|
||||||
|
```toml
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Additional Details
|
||||||
|
|
||||||
|
- Operation System/Version
|
||||||
|
<!-- For example, Fedora Linux 38 -->
|
||||||
|
|
||||||
|
- Installation
|
||||||
|
<!--
|
||||||
|
How did you install topgrade: build from repo / crates.io (cargo install topgrade)
|
||||||
|
/ package manager (which one) / other (describe)
|
||||||
|
-->
|
||||||
|
|
||||||
|
- Topgrade version (`topgrade -V`)
|
||||||
|
|
||||||
|
## Verbose Output (`topgrade -v`)
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Paste the verbose output into the pre-tags
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<!-- Paste the output of the problematic command with `-v` into the pre-tags -->
|
|
||||||
<pre>
|
<pre>
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
18
.github/ISSUE_TEMPLATE/feature_request.md
vendored
18
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,21 +1,23 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: General feature request
|
||||||
about: Can you please support...?
|
about: Suggest a general feature, or feature within an already existing step
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
type: Feature
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## I want to suggest a new step
|
## Checklist
|
||||||
### Which tool is this about? Where is its repository?
|
|
||||||
### Which operating systems are supported by this tool?
|
- [ ] I have searched the issue tracker for relevant or duplicate issues.
|
||||||
### What should Topgrade do to figure out if the tool needs to be invoked?
|
|
||||||
### Which exact commands should Topgrade run?
|
|
||||||
|
|
||||||
## I want to suggest some general feature
|
## I want to suggest some general feature
|
||||||
|
|
||||||
Topgrade should...
|
Topgrade should...
|
||||||
|
|
||||||
## More information
|
## More information
|
||||||
|
|
||||||
<!-- Assuming that someone else implements the feature,
|
<!-- Assuming that someone else implements the feature,
|
||||||
please state if you know how to test it from a side branch of Topgrade. -->
|
please state if you know how to test it from a side branch of Topgrade. -->
|
||||||
|
|
||||||
|
- [ ] I am able and willing to implement this feature myself
|
||||||
|
|||||||
30
.github/ISSUE_TEMPLATE/step_request.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/step_request.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: New step request
|
||||||
|
about: Suggest a new step/package manager to update
|
||||||
|
title: ''
|
||||||
|
type: Feature
|
||||||
|
labels: request step
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] I have searched the issue tracker for relevant or duplicate issues.
|
||||||
|
|
||||||
|
## I want to suggest a new step
|
||||||
|
|
||||||
|
* Which tool is this about? Where is its repository?
|
||||||
|
* Which operating systems are supported by this tool?
|
||||||
|
* What should Topgrade do to figure out if the tool needs to be invoked?
|
||||||
|
* Which exact commands should Topgrade run?
|
||||||
|
* Does it have a `--dry-run` option? i.e., print what should be done and exit
|
||||||
|
* Does it need the user to confirm the execution? And does it provide a `--yes`
|
||||||
|
option to skip this?
|
||||||
|
|
||||||
|
## More information
|
||||||
|
|
||||||
|
<!-- Assuming that someone else implements the step,
|
||||||
|
please state if you know how to test it from a side branch of Topgrade. -->
|
||||||
|
|
||||||
|
- [ ] I am able and willing to implement this step myself
|
||||||
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,12 +1,18 @@
|
|||||||
## Standards checklist:
|
## What does this PR do
|
||||||
|
|
||||||
- [ ] The PR title is descriptive.
|
## Standards checklist
|
||||||
- [ ] The code compiles (`cargo build`)
|
|
||||||
- [ ] The code passes rustfmt (`cargo fmt`)
|
- [ ] The PR title is descriptive
|
||||||
- [ ] The code passes clippy (`cargo clippy`)
|
- [ ] I have read `CONTRIBUTING.md`
|
||||||
- [ ] The code passes tests (`cargo test`)
|
|
||||||
- [ ] *Optional:* I have tested the code myself
|
- [ ] *Optional:* I have tested the code myself
|
||||||
- [ ] I also tested that Topgrade skips the step where needed
|
- [ ] If this PR introduces new user-facing messages they are translated
|
||||||
|
|
||||||
|
## For new steps
|
||||||
|
|
||||||
|
- [ ] *Optional:* Topgrade skips this step where needed
|
||||||
|
- [ ] *Optional:* The `--dry-run` option works with this step
|
||||||
|
- [ ] *Optional:* The `--yes` option works with this step if it is supported by
|
||||||
|
the underlying command
|
||||||
|
|
||||||
If you developed a feature or a bug fix for someone else and you do not have the
|
If you developed a feature or a bug fix for someone else and you do not have the
|
||||||
means to test it, please tag this person here.
|
means to test it, please tag this person here.
|
||||||
|
|||||||
91
.github/workflows/check-and-lint.yaml
vendored
91
.github/workflows/check-and-lint.yaml
vendored
@@ -1,91 +0,0 @@
|
|||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
name: CI
|
|
||||||
|
|
||||||
env:
|
|
||||||
RUST_VER: '1.68.0'
|
|
||||||
CROSS_VER: '0.2.5'
|
|
||||||
CARGO_NET_RETRY: 3
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
fmt:
|
|
||||||
name: Rustfmt
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Rust
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: '${{ env.RUST_VER }}'
|
|
||||||
components: rustfmt
|
|
||||||
|
|
||||||
- name: Run cargo fmt
|
|
||||||
env:
|
|
||||||
TERM: xterm-256color
|
|
||||||
run: |
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
|
|
||||||
main:
|
|
||||||
needs: fmt
|
|
||||||
name: ${{ matrix.target_name }} (check, clippy)
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: x86_64-linux-android
|
|
||||||
target_name: Android
|
|
||||||
use_cross: true
|
|
||||||
os: ubuntu-20.04
|
|
||||||
|
|
||||||
- target: x86_64-unknown-freebsd
|
|
||||||
target_name: FreeBSD
|
|
||||||
use_cross: true
|
|
||||||
os: ubuntu-20.04
|
|
||||||
|
|
||||||
- target: x86_64-unknown-linux-gnu
|
|
||||||
target_name: Linux
|
|
||||||
os: ubuntu-20.04
|
|
||||||
|
|
||||||
- target: x86_64-apple-darwin
|
|
||||||
target_name: macOS
|
|
||||||
os: macos-11
|
|
||||||
|
|
||||||
- target: x86_64-unknown-netbsd
|
|
||||||
target_name: NetBSD
|
|
||||||
use_cross: true
|
|
||||||
os: ubuntu-20.04
|
|
||||||
|
|
||||||
- target: x86_64-pc-windows-msvc
|
|
||||||
target_name: Windows
|
|
||||||
os: windows-2019
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Rust
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: '${{ env.RUST_VER }}'
|
|
||||||
components: clippy
|
|
||||||
|
|
||||||
- name: Setup Rust Cache
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
prefix-key: ${{ matrix.target }}
|
|
||||||
|
|
||||||
- name: Setup cross
|
|
||||||
if: matrix.use_cross == true
|
|
||||||
run: curl -fL --retry 3 https://github.com/cross-rs/cross/releases/download/v${{ env.CROSS_VER }}/cross-x86_64-unknown-linux-musl.tar.gz | tar vxz -C /usr/local/bin
|
|
||||||
|
|
||||||
- name: Run cargo check
|
|
||||||
run: ${{ matrix.use_cross == true && 'cross' || 'cargo' }} check --locked --target ${{ matrix.target }}
|
|
||||||
|
|
||||||
- name: Run cargo clippy
|
|
||||||
run: ${{ matrix.use_cross == true && 'cross' || 'cargo' }} clippy --locked --target ${{ matrix.target }} --all-features -- -D warnings
|
|
||||||
27
.github/workflows/check-semver.yml
vendored
27
.github/workflows/check-semver.yml
vendored
@@ -1,27 +0,0 @@
|
|||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published, edited]
|
|
||||||
|
|
||||||
name: Check SemVer compliance
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
prepare:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: nightly-2022-08-03
|
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
|
||||||
|
|
||||||
semver:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: install
|
|
||||||
args: --git https://github.com/rust-lang/rust-semverver
|
|
||||||
- run: eval "current_version=$(grep -e '^version = .*$' Cargo.toml | cut -d ' ' -f 3)"
|
|
||||||
- run: cargo semver | tee semver_out
|
|
||||||
- run: (head -n 1 semver_out | grep "\-> $current_version") || (echo "versioning mismatch" && return 1)
|
|
||||||
27
.github/workflows/check_config_creation_if_not_exists.yml
vendored
Normal file
27
.github/workflows/check_config_creation_if_not_exists.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Check config file creation if not exists
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
TestConfig:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
CONFIG_PATH=~/.config/topgrade.toml;
|
||||||
|
if [ -f "$CONFIG_PATH" ]; then rm $CONFIG_PATH; fi
|
||||||
|
cargo build;
|
||||||
|
TOPGRADE_SKIP_BRKC_NOTIFY=true ./target/debug/topgrade --dry-run --only system;
|
||||||
|
stat $CONFIG_PATH;
|
||||||
27
.github/workflows/check_i18n.yml
vendored
Normal file
27
.github/workflows/check_i18n.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
name: Check i18n
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_locale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Install checker
|
||||||
|
# Build it with the dev profile as this is faster and the checker still works
|
||||||
|
run: |
|
||||||
|
cargo install --git https://github.com/topgrade-rs/topgrade_i18n_locale_checker --profile dev
|
||||||
|
|
||||||
|
- name: Run the checker
|
||||||
|
run: topgrade_i18n_locale_checker --locale-file ./locales/app.yml --rust-src-to-check ./src
|
||||||
37
.github/workflows/check_security_vulnerability.yml
vendored
Normal file
37
.github/workflows/check_security_vulnerability.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
name: Check Security Vulnerability
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: DevSkim
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Run DevSkim scanner
|
||||||
|
uses: microsoft/DevSkim-Action@4b5047945a44163b94642a1cecc0d93a3f428cc6 # v1.0.16
|
||||||
|
|
||||||
|
- name: Upload DevSkim scan results to GitHub Security tab
|
||||||
|
uses: github/codeql-action/upload-sarif@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
||||||
|
with:
|
||||||
|
sarif_file: devskim-results.sarif
|
||||||
165
.github/workflows/ci.yml
vendored
Normal file
165
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
env:
|
||||||
|
CROSS_VER: '0.2.5'
|
||||||
|
CARGO_NET_RETRY: 3
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
name: Rustfmt
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Run cargo fmt
|
||||||
|
env:
|
||||||
|
TERM: xterm-256color
|
||||||
|
run: |
|
||||||
|
rustup component add rustfmt
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
|
||||||
|
custom-checks:
|
||||||
|
name: Custom checks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Check if `Step` enum is sorted
|
||||||
|
run: |
|
||||||
|
ENUM_NAME="Step"
|
||||||
|
FILE="src/step.rs"
|
||||||
|
awk "/enum $ENUM_NAME/,/}/" "$FILE" | \
|
||||||
|
grep -E '^\s*[A-Za-z_][A-Za-z0-9_]*\s*,?$' | \
|
||||||
|
sed 's/[, ]//g' > original.txt
|
||||||
|
sort original.txt > sorted.txt
|
||||||
|
diff original.txt sorted.txt
|
||||||
|
|
||||||
|
- name: Check if `Step::run()`'s match is sorted
|
||||||
|
run: |
|
||||||
|
FILE="src/step.rs"
|
||||||
|
awk '/[[:alpha:]] =>/{print $1}' $FILE > original.txt
|
||||||
|
sort original.txt > sorted.txt
|
||||||
|
diff original.txt sorted.txt
|
||||||
|
|
||||||
|
- name: Check if `default_steps` contains every step
|
||||||
|
run: |
|
||||||
|
# Extract all variants from enum Step
|
||||||
|
all_variants=$(sed -n '/^pub enum Step {/,/^}/p' src/step.rs | grep -Po '^\s*\K[A-Z][A-Za-z0-9_]*' | sort)
|
||||||
|
|
||||||
|
# Extract variants used inside default_steps
|
||||||
|
used_variants=$(sed -n '/^pub(crate) fn default_steps()/,/^}/p' src/step.rs | \
|
||||||
|
grep -Po '\b[A-Z][A-Za-z0-9_]*\b' | \
|
||||||
|
grep -Fx -f <(echo "$all_variants") | \
|
||||||
|
sort)
|
||||||
|
|
||||||
|
# Check for missing variants
|
||||||
|
missing=$(comm -23 <(echo "$all_variants") <(echo "$used_variants"))
|
||||||
|
if [[ -z "$missing" ]]; then
|
||||||
|
echo "All variants are used."
|
||||||
|
else
|
||||||
|
echo "Missing variants:"
|
||||||
|
echo "$missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for duplicates
|
||||||
|
duplicates=$(echo "$used_variants" | uniq -c | awk '$1 > 1 {print $2}')
|
||||||
|
if [[ -z "$duplicates" ]]; then
|
||||||
|
echo "No duplicates found."
|
||||||
|
else
|
||||||
|
echo "Duplicates found:"
|
||||||
|
echo "$duplicates"
|
||||||
|
# We allow duplicates, but lets keep this check for potential future usefulness
|
||||||
|
# exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
main:
|
||||||
|
needs: [ fmt, custom-checks ]
|
||||||
|
name: ${{ matrix.target_name }} (check, clippy)
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- target: x86_64-linux-android
|
||||||
|
target_name: Android
|
||||||
|
use_cross: true
|
||||||
|
os: ubuntu-latest
|
||||||
|
|
||||||
|
- target: x86_64-unknown-freebsd
|
||||||
|
target_name: FreeBSD
|
||||||
|
use_cross: true
|
||||||
|
os: ubuntu-latest
|
||||||
|
|
||||||
|
- target: x86_64-unknown-linux-gnu
|
||||||
|
target_name: Linux
|
||||||
|
os: ubuntu-latest
|
||||||
|
|
||||||
|
- target: x86_64-apple-darwin
|
||||||
|
target_name: macOS-x86_64
|
||||||
|
os: macos-15-intel
|
||||||
|
|
||||||
|
- target: aarch64-apple-darwin
|
||||||
|
target_name: macOS-aarch64
|
||||||
|
os: macos-latest
|
||||||
|
|
||||||
|
- target: x86_64-unknown-netbsd
|
||||||
|
target_name: NetBSD
|
||||||
|
use_cross: true
|
||||||
|
os: ubuntu-latest
|
||||||
|
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
target_name: Windows
|
||||||
|
os: windows-latest
|
||||||
|
env:
|
||||||
|
cargo_cmd: ${{ matrix.use_cross == true && 'cross' || 'cargo' }}
|
||||||
|
matrix_target: ${{ matrix.target }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||||
|
with:
|
||||||
|
prefix-key: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Setup cross
|
||||||
|
if: matrix.use_cross == true
|
||||||
|
run: |
|
||||||
|
curl -fL --retry 3 "https://github.com/cross-rs/cross/releases/download/v${CROSS_VER}/cross-x86_64-unknown-linux-musl.tar.gz" | tar vxz -C /usr/local/bin
|
||||||
|
|
||||||
|
- name: Run cargo/cross check
|
||||||
|
run: |
|
||||||
|
"${cargo_cmd}" check --locked --target "${matrix_target}"
|
||||||
|
|
||||||
|
- name: Run cargo/cross clippy
|
||||||
|
run: |
|
||||||
|
rustup component add clippy
|
||||||
|
"${cargo_cmd}" clippy --locked --target "${matrix_target}" --all-features -- -D warnings
|
||||||
|
|
||||||
|
- name: Run cargo test
|
||||||
|
# ONLY run test with cargo
|
||||||
|
if: matrix.use_cross == false
|
||||||
|
run: |
|
||||||
|
cargo test --locked --target "${matrix_target}"
|
||||||
29
.github/workflows/crates-publish.yml
vendored
29
.github/workflows/crates-publish.yml
vendored
@@ -1,29 +0,0 @@
|
|||||||
on:
|
|
||||||
# workflow_run:
|
|
||||||
# workflows: ["Check SemVer compliance"]
|
|
||||||
# types:
|
|
||||||
# - completed
|
|
||||||
release:
|
|
||||||
types: [published, edited]
|
|
||||||
|
|
||||||
name: Publish to crates.io on release
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
prepare:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
|
|
||||||
publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: katyo/publish-crates@v1
|
|
||||||
with:
|
|
||||||
dry-run: true
|
|
||||||
check-repo: ${{ github.event_name == 'push' }}
|
|
||||||
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
|
||||||
ignore-unpublished-changes: true
|
|
||||||
303
.github/workflows/create_release_assets.yml
vendored
Normal file
303
.github/workflows/create_release_assets.yml
vendored
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
name: Publish release files for CD native and non-cd-native environments
|
||||||
|
|
||||||
|
on:
|
||||||
|
repository_dispatch:
|
||||||
|
types: [ release-created ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
# Write permissions to call the repository dispatch
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Publish release files for CD native environments
|
||||||
|
native_build:
|
||||||
|
permissions:
|
||||||
|
# Use to sign the release artifacts
|
||||||
|
id-token: write
|
||||||
|
# Used to upload release artifacts
|
||||||
|
contents: write
|
||||||
|
# Used to generate artifact attestations
|
||||||
|
attestations: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# Use the Ubuntu 22.04 image to link with a low version of glibc
|
||||||
|
#
|
||||||
|
# https://github.com/topgrade-rs/topgrade/issues/1095
|
||||||
|
platform: [ ubuntu-22.04, macos-latest, macos-15-intel, windows-latest ]
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
env:
|
||||||
|
tag: ${{ github.event.client_payload.tag }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Install needed components
|
||||||
|
run: |
|
||||||
|
rustup component add rustfmt
|
||||||
|
rustup component add clippy
|
||||||
|
|
||||||
|
- name: Install cargo-deb
|
||||||
|
run: cargo install cargo-deb
|
||||||
|
if: ${{ startsWith(matrix.platform, 'ubuntu-') }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Check format
|
||||||
|
run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
- name: Run clippy
|
||||||
|
run: cargo clippy --all-targets --locked -- -D warnings
|
||||||
|
|
||||||
|
- name: Run clippy (All features)
|
||||||
|
run: cargo clippy --all-targets --locked --all-features -- -D warnings
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test
|
||||||
|
|
||||||
|
# Used `https://github.com/BurntSushi/ripgrep/blob/master/.github/workflows/release.yml`
|
||||||
|
# as a reference.
|
||||||
|
- name: Build debug binary to create release assets
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo build --all-features
|
||||||
|
bin="target/debug/topgrade"
|
||||||
|
echo "BIN=$bin" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create deployment directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
dir=deployment/deb
|
||||||
|
mkdir -p "$dir"
|
||||||
|
echo "DEPLOY_DIR=$dir" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Generate man page and shell completions
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
"$BIN" --gen-manpage > "$DEPLOY_DIR/topgrade.1"
|
||||||
|
"$BIN" --gen-completion bash > "$DEPLOY_DIR/topgrade.bash"
|
||||||
|
"$BIN" --gen-completion fish > "$DEPLOY_DIR/topgrade.fish"
|
||||||
|
"$BIN" --gen-completion zsh > "$DEPLOY_DIR/_topgrade"
|
||||||
|
|
||||||
|
- name: Build in Release profile with all features enabled
|
||||||
|
run: cargo build --release --all-features
|
||||||
|
|
||||||
|
- name: Rename Release (Unix)
|
||||||
|
run: |
|
||||||
|
cargo install default-target
|
||||||
|
mkdir -p assets
|
||||||
|
FILENAME=topgrade-${tag}-$(default-target)
|
||||||
|
mv target/release/topgrade assets
|
||||||
|
cd assets
|
||||||
|
tar --format=ustar -czf $FILENAME.tar.gz topgrade
|
||||||
|
rm topgrade
|
||||||
|
ls .
|
||||||
|
if: ${{ matrix.platform != 'windows-latest' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Build Debian-based system binary and create package
|
||||||
|
# First remove the binary built by previous steps
|
||||||
|
# because we don't want the auto-update feature,
|
||||||
|
# then build the new binary without auto-updating.
|
||||||
|
run: |
|
||||||
|
rm -rf target/release
|
||||||
|
cargo build --release
|
||||||
|
cargo deb --no-build --no-strip
|
||||||
|
if: ${{ startsWith(matrix.platform, 'ubuntu-') }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Move Debian-based system package
|
||||||
|
run: |
|
||||||
|
mkdir -p assets
|
||||||
|
mv target/debian/*.deb assets
|
||||||
|
if: ${{ startsWith(matrix.platform, 'ubuntu-') }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Rename Release (Windows)
|
||||||
|
run: |
|
||||||
|
cargo install default-target
|
||||||
|
mkdir assets
|
||||||
|
FILENAME=topgrade-${tag}-$(default-target)
|
||||||
|
mv target/release/topgrade.exe assets/topgrade.exe
|
||||||
|
cd assets
|
||||||
|
powershell Compress-Archive -Path * -Destination ${FILENAME}.zip
|
||||||
|
rm topgrade.exe
|
||||||
|
ls .
|
||||||
|
if: ${{ matrix.platform == 'windows-latest' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Upload assets
|
||||||
|
run: |
|
||||||
|
gh release upload "${tag}" assets/*
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate artifact attestations
|
||||||
|
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
|
||||||
|
with:
|
||||||
|
subject-path: assets/*
|
||||||
|
|
||||||
|
# Publish release files for non-CD-native environments
|
||||||
|
cross_build:
|
||||||
|
permissions:
|
||||||
|
# Use to sign the release artifacts
|
||||||
|
id-token: write
|
||||||
|
# Used to upload release artifacts
|
||||||
|
contents: write
|
||||||
|
# Used to generate artifact attestations
|
||||||
|
attestations: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
[
|
||||||
|
"aarch64-unknown-linux-gnu",
|
||||||
|
"armv7-unknown-linux-gnueabihf",
|
||||||
|
"x86_64-unknown-linux-musl",
|
||||||
|
"aarch64-unknown-linux-musl",
|
||||||
|
"x86_64-unknown-freebsd",
|
||||||
|
]
|
||||||
|
# Run this one on an older version as well, to limit glibc to 2.34 instead of 2.39.
|
||||||
|
# Even though this is cross-compiled, it links to the libc6-<arch>-cross installed on the host
|
||||||
|
# (see the apt-get install calls below)
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
env:
|
||||||
|
matrix_target: ${{ matrix.target }}
|
||||||
|
tag: ${{ github.event.client_payload.tag }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Install needed components
|
||||||
|
run: |
|
||||||
|
rustup component add rustfmt
|
||||||
|
rustup component add clippy
|
||||||
|
|
||||||
|
- name: Install cargo-deb cross compilation dependencies
|
||||||
|
run: sudo apt-get install libc6-arm64-cross libgcc-s1-arm64-cross
|
||||||
|
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Install cargo-deb cross compilation dependencies for armv7
|
||||||
|
run: sudo apt-get install libc6-armhf-cross libgcc-s1-armhf-cross
|
||||||
|
if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Install cargo-deb
|
||||||
|
run: cargo install cargo-deb
|
||||||
|
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'armv7-unknown-linux-gnueabihf' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: install targets
|
||||||
|
run: rustup target add "${matrix_target}"
|
||||||
|
|
||||||
|
- name: install cross
|
||||||
|
# Install from source to fix `ld: cannot find -lgeom` for freebsd build
|
||||||
|
run: cargo +stable install --git https://github.com/cross-rs/cross cross
|
||||||
|
|
||||||
|
- name: Run clippy
|
||||||
|
run: cross clippy --all-targets --locked --target "${matrix_target}" -- -D warnings
|
||||||
|
|
||||||
|
- name: Run clippy (All features)
|
||||||
|
run: cross clippy --locked --all-features --target "${matrix_target}" -- -D warnings
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: cross test --target "${matrix_target}"
|
||||||
|
# Running tests on FreeBSD is impossible; see https://github.com/cross-rs/cross/wiki/FAQ#running-bsd-tests
|
||||||
|
# Not that this is *NOT* the same as the original issue with `ld: cannot find -lgeom`, but a new issue:
|
||||||
|
# error: test failed, to rerun pass `--lib`
|
||||||
|
# Caused by:
|
||||||
|
# could not execute process `/target/x86_64-unknown-freebsd/debug/deps/topgrade-9b1670d87ca863dd` (never executed)
|
||||||
|
# Caused by:
|
||||||
|
# No such file or directory (os error 2)
|
||||||
|
# TODO: I have not tested this in GHA yet, only locally
|
||||||
|
if: ${{ matrix.target != 'x86_64-unknown-freebsd' }}
|
||||||
|
|
||||||
|
# Used `https://github.com/BurntSushi/ripgrep/blob/master/.github/workflows/release.yml`
|
||||||
|
# as a reference.
|
||||||
|
- name: Build debug binary to create release assets
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# This build is not using the target arch since this binary is only needed in CI. It needs
|
||||||
|
# to be the compiled for the runner since it has the run the binary to generate completion
|
||||||
|
# scripts.
|
||||||
|
cargo build --all-features
|
||||||
|
bin="target/debug/topgrade"
|
||||||
|
echo "BIN=$bin" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create deployment directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
dir=deployment/deb
|
||||||
|
mkdir -p "$dir"
|
||||||
|
echo "DEPLOY_DIR=$dir" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Generate man page and shell completions
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
"$BIN" --gen-manpage > "$DEPLOY_DIR/topgrade.1"
|
||||||
|
"$BIN" --gen-completion bash > "$DEPLOY_DIR/topgrade.bash"
|
||||||
|
"$BIN" --gen-completion fish > "$DEPLOY_DIR/topgrade.fish"
|
||||||
|
"$BIN" --gen-completion zsh > "$DEPLOY_DIR/_topgrade"
|
||||||
|
|
||||||
|
- name: Build in Release profile with all features enabled
|
||||||
|
run: cross build --release --all-features --target "${matrix_target}"
|
||||||
|
|
||||||
|
- name: Rename Release
|
||||||
|
run: |
|
||||||
|
mkdir -p assets
|
||||||
|
FILENAME=topgrade-${tag}-${matrix_target}
|
||||||
|
mv "target/${matrix_target}/release/topgrade" assets
|
||||||
|
cd assets
|
||||||
|
tar --format=ustar -czf "$FILENAME.tar.gz" topgrade
|
||||||
|
rm topgrade
|
||||||
|
ls .
|
||||||
|
|
||||||
|
- name: Build Debian-based system package without autoupdate feature
|
||||||
|
# First remove the binary built by previous steps
|
||||||
|
# because we don't want the auto-update feature,
|
||||||
|
# then build the new binary without auto-updating.
|
||||||
|
run: |
|
||||||
|
rm -rf "target/${matrix_target}"
|
||||||
|
cross build --release --target "${matrix_target}"
|
||||||
|
cargo deb --target="${matrix_target}" --no-build --no-strip
|
||||||
|
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'armv7-unknown-linux-gnueabihf' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Move Debian-based system package
|
||||||
|
run: |
|
||||||
|
mkdir -p assets
|
||||||
|
mv target/"${matrix_target}"/debian/*.deb assets
|
||||||
|
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'armv7-unknown-linux-gnueabihf' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
|
- name: Upload assets
|
||||||
|
run:
|
||||||
|
gh release upload "${tag}" assets/*
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate artifact attestations
|
||||||
|
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
|
||||||
|
with:
|
||||||
|
subject-path: assets/*
|
||||||
|
|
||||||
|
triggers:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ native_build, cross_build ]
|
||||||
|
env:
|
||||||
|
tag: ${{ github.event.client_payload.tag }}
|
||||||
|
steps:
|
||||||
|
- name: Trigger workflows
|
||||||
|
run: |
|
||||||
|
gh api "repos/${GITHUB_REPOSITORY}/dispatches" \
|
||||||
|
-f "event_type=release-assets-built" \
|
||||||
|
-F "client_payload[tag]=${tag}"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
25
.github/workflows/dependency-review.yml
vendored
Normal file
25
.github/workflows/dependency-review.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Dependency Review Action
|
||||||
|
#
|
||||||
|
# This Action will scan dependency manifest files that change as part of a Pull Request,
|
||||||
|
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
|
||||||
|
# Once installed, if the workflow run is marked as required,
|
||||||
|
# PRs introducing known-vulnerable packages will be blocked from merging.
|
||||||
|
#
|
||||||
|
# Source repository: https://github.com/actions/dependency-review-action
|
||||||
|
name: 'Dependency Review'
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dependency-review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: 'Checkout Repository'
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: 'Dependency Review'
|
||||||
|
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
|
||||||
20
.github/workflows/lint_pr.yml
vendored
Normal file
20
.github/workflows/lint_pr.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: 'Lint PR'
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target: # zizmor: ignore[dangerous-triggers] this is the only way, and we're not running user code
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
name: Validate PR title
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
steps:
|
||||||
|
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
70
.github/workflows/release-cross.yml
vendored
70
.github/workflows/release-cross.yml
vendored
@@ -1,70 +0,0 @@
|
|||||||
name: Publish release files for non-cd-native environments
|
|
||||||
|
|
||||||
on:
|
|
||||||
# workflow_run:
|
|
||||||
# workflows: ["Check SemVer compliance"]
|
|
||||||
# types:
|
|
||||||
# - completed
|
|
||||||
release:
|
|
||||||
types: [ created ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
target: [ "aarch64-unknown-linux-gnu", "armv7-unknown-linux-gnueabihf", "x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl", "x86_64-unknown-freebsd", ]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
profile: minimal
|
|
||||||
default: true
|
|
||||||
override: true
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
components: rustfmt, clippy
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Check format
|
|
||||||
with:
|
|
||||||
use-cross: true
|
|
||||||
command: fmt
|
|
||||||
args: --all -- --check
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Run clippy
|
|
||||||
with:
|
|
||||||
command: clippy
|
|
||||||
use-cross: true
|
|
||||||
args: --all-targets --locked --target ${{matrix.target}} -- -D warnings
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Run clippy (All features)
|
|
||||||
with:
|
|
||||||
command: clippy
|
|
||||||
use-cross: true
|
|
||||||
args: --locked --all-features --target ${{matrix.target}} -- -D warnings
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Run tests
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
use-cross: true
|
|
||||||
args: --target ${{matrix.target}}
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Build
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
use-cross: true
|
|
||||||
args: --release --all-features --target ${{matrix.target}}
|
|
||||||
- name: Rename Release
|
|
||||||
run: |
|
|
||||||
mkdir assets
|
|
||||||
FILENAME=topgrade-${{github.event.release.tag_name}}-${{matrix.target}}
|
|
||||||
mv target/${{matrix.target}}/release/topgrade assets
|
|
||||||
cd assets
|
|
||||||
tar --format=ustar -czf $FILENAME.tar.gz topgrade
|
|
||||||
rm topgrade
|
|
||||||
ls .
|
|
||||||
- name: Release
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
files: assets/*
|
|
||||||
67
.github/workflows/release-plz.yml
vendored
Normal file
67
.github/workflows/release-plz.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
name: Release-plz
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
# Release unpublished packages.
|
||||||
|
release-plz-release:
|
||||||
|
name: Release-plz release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: crates_io
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
id-token: write # For trusted publishing
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Run release-plz
|
||||||
|
id: release-plz
|
||||||
|
uses: release-plz/action@d529f731ae3e89610ada96eda34e5c6ba3b12214 # v0.5
|
||||||
|
with:
|
||||||
|
command: release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Trigger workflows
|
||||||
|
if: steps.release-plz.outputs.releases_created == 'true'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag: ${{ fromJSON(steps.release-plz.outputs.releases)[0].tag }}
|
||||||
|
run: |
|
||||||
|
gh api "repos/${GITHUB_REPOSITORY}/dispatches" \
|
||||||
|
-f "event_type=release-created" \
|
||||||
|
-F "client_payload[tag]=${tag}"
|
||||||
|
|
||||||
|
# Create a PR with the new versions and changelog, preparing the next release.
|
||||||
|
release-plz-pr:
|
||||||
|
name: Release-plz PR
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
concurrency:
|
||||||
|
group: release-plz-${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Run release-plz
|
||||||
|
uses: release-plz/action@d529f731ae3e89610ada96eda34e5c6ba3b12214 # v0.5
|
||||||
|
with:
|
||||||
|
command: release-pr
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
77
.github/workflows/release.yml
vendored
77
.github/workflows/release.yml
vendored
@@ -1,77 +0,0 @@
|
|||||||
name: Publish release files for CD native environments
|
|
||||||
|
|
||||||
on:
|
|
||||||
# workflow_run:
|
|
||||||
# workflows: ["Check SemVer compliance"]
|
|
||||||
# types:
|
|
||||||
# - completed
|
|
||||||
release:
|
|
||||||
types: [ created ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: [ ubuntu-latest, macos-latest, windows-latest ]
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
profile: minimal
|
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Check format
|
|
||||||
with:
|
|
||||||
command: fmt
|
|
||||||
args: --all -- --check
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Run clippy
|
|
||||||
with:
|
|
||||||
command: clippy
|
|
||||||
args: --all-targets --locked -- -D warnings
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Run clippy (All features)
|
|
||||||
with:
|
|
||||||
command: clippy
|
|
||||||
args: --all-targets --locked --all-features -- -D warnings
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Run tests
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
- uses: actions-rs/cargo@v1.0.1
|
|
||||||
name: Build
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
args: --release --all-features
|
|
||||||
- name: Rename Release (Unix)
|
|
||||||
run: |
|
|
||||||
cargo install default-target
|
|
||||||
mkdir assets
|
|
||||||
FILENAME=topgrade-${{github.event.release.tag_name}}-$(default-target)
|
|
||||||
mv target/release/topgrade assets
|
|
||||||
cd assets
|
|
||||||
tar --format=ustar -czf $FILENAME.tar.gz topgrade
|
|
||||||
rm topgrade
|
|
||||||
ls .
|
|
||||||
if: ${{ matrix.platform != 'windows-latest' }}
|
|
||||||
shell: bash
|
|
||||||
- name: Rename Release (Windows)
|
|
||||||
run: |
|
|
||||||
cargo install default-target
|
|
||||||
mkdir assets
|
|
||||||
FILENAME=topgrade-${{github.event.release.tag_name}}-$(default-target)
|
|
||||||
mv target/release/topgrade.exe assets/topgrade.exe
|
|
||||||
cd assets
|
|
||||||
powershell Compress-Archive -Path * -Destination ${FILENAME}.zip
|
|
||||||
rm topgrade.exe
|
|
||||||
ls .
|
|
||||||
if: ${{ matrix.platform == 'windows-latest' }}
|
|
||||||
shell: bash
|
|
||||||
- name: Release
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
files: assets/*
|
|
||||||
38
.github/workflows/release_to_aur.yml
vendored
Normal file
38
.github/workflows/release_to_aur.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: Publish to AUR
|
||||||
|
|
||||||
|
on:
|
||||||
|
repository_dispatch:
|
||||||
|
types: [ release-assets-built ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
aur-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Determine version
|
||||||
|
id: determine_version
|
||||||
|
env:
|
||||||
|
tag: ${{ github.event.client_payload.tag }}
|
||||||
|
run: |
|
||||||
|
# tag should be something like "v16.0.4", remove the prefix v here
|
||||||
|
echo "version=${tag#v}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Publish source AUR package
|
||||||
|
uses: varabyte/update-aur-package@572e31b1972fa289a27b1926c06a489eb89c7fd7
|
||||||
|
with:
|
||||||
|
version: ${{ steps.determine_version.outputs.version }}
|
||||||
|
package_name: topgrade
|
||||||
|
commit_username: "Thomas Schönauer"
|
||||||
|
commit_email: t.schoenauer@hgs-wt.at
|
||||||
|
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- name: Publish binary AUR package
|
||||||
|
uses: varabyte/update-aur-package@572e31b1972fa289a27b1926c06a489eb89c7fd7
|
||||||
|
with:
|
||||||
|
version: ${{ steps.determine_version.outputs.version }}
|
||||||
|
package_name: topgrade-bin
|
||||||
|
commit_username: "Thomas Schönauer"
|
||||||
|
commit_email: t.schoenauer@hgs-wt.at
|
||||||
|
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||||
22
.github/workflows/release_to_homebrew.yml
vendored
Normal file
22
.github/workflows/release_to_homebrew.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
name: Publish to Homebrew
|
||||||
|
|
||||||
|
on:
|
||||||
|
repository_dispatch:
|
||||||
|
types: [ release-created ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
homebrew-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Bump formulae
|
||||||
|
uses: dawidd6/action-homebrew-bump-formula@c5ddc585e75f0f750a8b4f610688b4bec9e80915 # v6
|
||||||
|
with:
|
||||||
|
# Custom GitHub access token with only the 'public_repo' scope enabled
|
||||||
|
token: ${{secrets.HOMEBREW_ACCESS_TOKEN}}
|
||||||
|
formula: topgrade
|
||||||
|
tag: ${{ github.event.client_payload.tag }}
|
||||||
|
# We cannot use an org because org forks cannot give push access to maintainers, which Homebrew requires.
|
||||||
|
# org: topgrade-rs
|
||||||
119
.github/workflows/release_to_pypi.yml
vendored
Normal file
119
.github/workflows/release_to_pypi.yml
vendored
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
name: Update PyPi
|
||||||
|
|
||||||
|
on:
|
||||||
|
repository_dispatch:
|
||||||
|
types: [ release-created ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# TODO: make linux/windows/macos/sdist a matrix. See how other workflows do it.
|
||||||
|
linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target: [x86_64, x86, aarch64]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Build wheels
|
||||||
|
uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
args: --release --out dist
|
||||||
|
manylinux: auto
|
||||||
|
- name: Upload wheels
|
||||||
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
|
with:
|
||||||
|
name: wheels-linux-${{ matrix.target }}
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target: [x64, x86]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Build wheels
|
||||||
|
uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
args: --release --out dist
|
||||||
|
- name: Upload wheels
|
||||||
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
|
with:
|
||||||
|
name: wheels-windows-${{ matrix.target }}
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
macos:
|
||||||
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target: [x86_64, aarch64]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Build wheels
|
||||||
|
uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
args: --release --out dist
|
||||||
|
- name: Upload wheels
|
||||||
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
|
with:
|
||||||
|
name: wheels-macos-${{ matrix.target }}
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
sdist:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Build sdist
|
||||||
|
uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4
|
||||||
|
with:
|
||||||
|
command: sdist
|
||||||
|
args: --out dist
|
||||||
|
- name: Upload sdist
|
||||||
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
|
with:
|
||||||
|
name: wheels-sdist
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [linux, windows, macos, sdist]
|
||||||
|
permissions:
|
||||||
|
# Use to sign the release artifacts
|
||||||
|
id-token: write
|
||||||
|
# Used to upload release artifacts
|
||||||
|
contents: write
|
||||||
|
# Used to generate artifact attestation
|
||||||
|
attestations: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||||
|
|
||||||
|
- name: Generate artifact attestation
|
||||||
|
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
|
||||||
|
with:
|
||||||
|
subject-path: 'wheels-*/*'
|
||||||
|
|
||||||
|
- name: Publish to PyPI
|
||||||
|
uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4
|
||||||
|
env:
|
||||||
|
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||||
|
with:
|
||||||
|
command: upload
|
||||||
|
args: --non-interactive --skip-existing wheels-*/*
|
||||||
19
.github/workflows/release_to_winget.yml
vendored
Normal file
19
.github/workflows/release_to_winget.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Publish to WinGet
|
||||||
|
|
||||||
|
on:
|
||||||
|
repository_dispatch:
|
||||||
|
types: [ release-created ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # main
|
||||||
|
with:
|
||||||
|
release-tag: ${{ github.event.client_payload.tag }}
|
||||||
|
identifier: topgrade-rs.topgrade
|
||||||
|
max-versions-to-keep: 5 # keep only latest 5 versions
|
||||||
|
token: ${{ secrets.WINGET_TOKEN }}
|
||||||
76
.github/workflows/scorecards.yml
vendored
Normal file
76
.github/workflows/scorecards.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# This workflow uses actions that are not certified by GitHub. They are provided
|
||||||
|
# by a third-party and are governed by separate terms of service, privacy
|
||||||
|
# policy, and support documentation.
|
||||||
|
|
||||||
|
name: Scorecard supply-chain security
|
||||||
|
on:
|
||||||
|
# For Branch-Protection check. Only the default branch is supported. See
|
||||||
|
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||||
|
branch_protection_rule:
|
||||||
|
# To guarantee Maintained check is occasionally updated. See
|
||||||
|
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||||
|
schedule:
|
||||||
|
- cron: '20 7 * * 2'
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
|
||||||
|
# Declare default permissions as read only.
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analysis:
|
||||||
|
name: Scorecard analysis
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
# Needed to upload the results to code-scanning dashboard.
|
||||||
|
security-events: write
|
||||||
|
# Needed to publish results and get a badge (see publish_results below).
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
# To allow GraphQL ListCommits to work
|
||||||
|
issues: read
|
||||||
|
pull-requests: read
|
||||||
|
# To detect SAST tools
|
||||||
|
checks: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout code"
|
||||||
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: "Run analysis"
|
||||||
|
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||||
|
with:
|
||||||
|
results_file: results.sarif
|
||||||
|
results_format: sarif
|
||||||
|
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
||||||
|
# - you want to enable the Branch-Protection check on a *public* repository, or
|
||||||
|
# - you are installing Scorecards on a *private* repository
|
||||||
|
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
|
||||||
|
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
||||||
|
|
||||||
|
# Public repositories:
|
||||||
|
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||||
|
# - Allows the repository to include the Scorecard badge.
|
||||||
|
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||||
|
# For private repositories:
|
||||||
|
# - `publish_results` will always be set to `false`, regardless
|
||||||
|
# of the value entered here.
|
||||||
|
publish_results: true
|
||||||
|
|
||||||
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
|
# format to the repository Actions tab.
|
||||||
|
- name: "Upload artifact"
|
||||||
|
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||||
|
with:
|
||||||
|
name: SARIF file
|
||||||
|
path: results.sarif
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
|
- name: "Upload to code-scanning"
|
||||||
|
uses: github/codeql-action/upload-sarif@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
||||||
|
with:
|
||||||
|
sarif_file: results.sarif
|
||||||
59
.github/workflows/test.yaml
vendored
59
.github/workflows/test.yaml
vendored
@@ -1,59 +0,0 @@
|
|||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
name: Test with Code Coverage
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Test
|
|
||||||
env:
|
|
||||||
PROJECT_NAME_UNDERSCORE: topgrade
|
|
||||||
CARGO_INCREMENTAL: 0
|
|
||||||
RUSTFLAGS: -Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort
|
|
||||||
RUSTDOCFLAGS: -Cpanic=abort
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly
|
|
||||||
override: true
|
|
||||||
- name: Cache dependencies
|
|
||||||
uses: actions/cache@v2
|
|
||||||
env:
|
|
||||||
cache-name: cache-dependencies
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/.crates.toml
|
|
||||||
~/.cargo/.crates2.json
|
|
||||||
~/.cargo/bin
|
|
||||||
~/.cargo/registry/index
|
|
||||||
~/.cargo/registry/cache
|
|
||||||
target
|
|
||||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Cargo.lock') }}
|
|
||||||
- name: Generate test result and coverage report
|
|
||||||
run: |
|
|
||||||
cargo install cargo2junit grcov;
|
|
||||||
cargo test $CARGO_OPTIONS -- -Z unstable-options --format json | cargo2junit > results.xml;
|
|
||||||
zip -0 ccov.zip `find . \( -name "$PROJECT_NAME_UNDERSCORE*.gc*" \) -print`;
|
|
||||||
grcov ccov.zip -s . -t lcov --llvm --ignore-not-existing --ignore "/*" --ignore "tests/*" -o lcov.info;
|
|
||||||
- name: Upload test results
|
|
||||||
uses: EnricoMi/publish-unit-test-result-action@v1
|
|
||||||
with:
|
|
||||||
check_name: Test Results
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
files: results.xml
|
|
||||||
- name: Upload to CodeCov
|
|
||||||
uses: codecov/codecov-action@v1
|
|
||||||
with:
|
|
||||||
# required for private repositories:
|
|
||||||
# token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
files: ./lcov.info
|
|
||||||
fail_ci_if_error: true
|
|
||||||
22
.github/workflows/update_aur.yml
vendored
22
.github/workflows/update_aur.yml
vendored
@@ -1,22 +0,0 @@
|
|||||||
name: Publish to AUR
|
|
||||||
|
|
||||||
on:
|
|
||||||
# workflow_run:
|
|
||||||
# workflows: ["Check SemVer compliance"]
|
|
||||||
# types:
|
|
||||||
# - completed
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
aur-publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Publish AUR package
|
|
||||||
uses: ATiltedTree/create-aur-release@v1
|
|
||||||
with:
|
|
||||||
package_name: topgrade
|
|
||||||
commit_username: "Thomas Schönauer"
|
|
||||||
commit_email: t.schoenauer@hgs-wt.at
|
|
||||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
|
||||||
38
.github/workflows/update_homebrew.yml
vendored
38
.github/workflows/update_homebrew.yml
vendored
@@ -1,38 +0,0 @@
|
|||||||
name: Publish to Homebrew
|
|
||||||
|
|
||||||
on:
|
|
||||||
# workflow_run:
|
|
||||||
# workflows: ["Check SemVer compliance"]
|
|
||||||
# types:
|
|
||||||
# - completed
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
homebrew-publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up Homebrew
|
|
||||||
id: set-up-homebrew
|
|
||||||
uses: Homebrew/actions/setup-homebrew@master
|
|
||||||
- name: Cache Homebrew Bundler RubyGems
|
|
||||||
id: cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
|
|
||||||
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
|
|
||||||
restore-keys: ${{ runner.os }}-rubygems-
|
|
||||||
|
|
||||||
- name: Install Homebrew Bundler RubyGems
|
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
run: brew install-bundler-gems
|
|
||||||
- name: Bump formulae
|
|
||||||
uses: Homebrew/actions/bump-formulae@master
|
|
||||||
with:
|
|
||||||
# Custom GitHub access token with only the 'public_repo' scope enabled
|
|
||||||
token: ${{secrets.HOMEBREW_ACCESS_TOKEN}}
|
|
||||||
# Bump only these formulae if outdated
|
|
||||||
formulae: |
|
|
||||||
topgrade
|
|
||||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,4 +1,20 @@
|
|||||||
|
# JetBrains IDEs
|
||||||
|
.idea/
|
||||||
|
|
||||||
/target
|
# Visual Studio
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Generic build outputs
|
||||||
/build
|
/build
|
||||||
|
|
||||||
|
# Specific for some languages like Rust
|
||||||
|
/target
|
||||||
|
|
||||||
|
# LLVM profiling output
|
||||||
|
*.profraw
|
||||||
|
|
||||||
|
# Backup files for any .rs files in the project
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|||||||
25
.pre-commit-config.yaml
Normal file
25
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/gitleaks/gitleaks
|
||||||
|
rev: v8.29.0
|
||||||
|
hooks:
|
||||||
|
- id: gitleaks
|
||||||
|
|
||||||
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||||
|
rev: v0.11.0.1
|
||||||
|
hooks:
|
||||||
|
- id: shellcheck
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v6.0.0
|
||||||
|
hooks:
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: trailing-whitespace
|
||||||
|
|
||||||
|
- repo: https://github.com/crate-ci/typos
|
||||||
|
rev: v1.39.2
|
||||||
|
hooks:
|
||||||
|
- id: typos
|
||||||
|
|
||||||
|
|
||||||
|
ci:
|
||||||
|
autoupdate_commit_msg: "chore(pre-commit): autoupdate"
|
||||||
20
.typos.toml
Normal file
20
.typos.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Typos configuration (minimal, conservative)
|
||||||
|
# Exclude locales and OS fingerprint data to avoid false positives
|
||||||
|
# - Recognize a few project-specific proper nouns
|
||||||
|
|
||||||
|
[files]
|
||||||
|
extend-exclude = [
|
||||||
|
"src/steps/os/os_release/**",
|
||||||
|
"locales/**",
|
||||||
|
# Include only English locale files - TODO: Split locales/app.yml into a Separate english File
|
||||||
|
# "!locales/en/**"
|
||||||
|
]
|
||||||
|
|
||||||
|
[default]
|
||||||
|
# Mark specific words as always valid by mapping them to themselves
|
||||||
|
check-file = true
|
||||||
|
check-filename = true
|
||||||
|
|
||||||
|
[default.extend-words]
|
||||||
|
# Add project-specific terms that should not be flagged as typos
|
||||||
|
# Example: topgrade = "topgrade"
|
||||||
38
.vscode/launch.json
vendored
38
.vscode/launch.json
vendored
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Topgrade",
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"cargo": {
|
|
||||||
"args": [
|
|
||||||
"build",
|
|
||||||
"--bin=topgrade-rs",
|
|
||||||
"--package=topgrade-rs"
|
|
||||||
],
|
|
||||||
"filter": {
|
|
||||||
"name": "topgrade-rs",
|
|
||||||
"kind": "bin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"args": [
|
|
||||||
"--only",
|
|
||||||
"${input:step}",
|
|
||||||
"-v"
|
|
||||||
],
|
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"type": "promptString",
|
|
||||||
"id": "step",
|
|
||||||
"description": "step name",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
14
.vscode/tasks.json
vendored
14
.vscode/tasks.json
vendored
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"type": "cargo",
|
|
||||||
"command": "clippy",
|
|
||||||
"problemMatcher": [
|
|
||||||
"$rustc"
|
|
||||||
],
|
|
||||||
"group": "test",
|
|
||||||
"label": "rust: cargo clippy"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
50
.vscode/topgrade.code-snippets
vendored
50
.vscode/topgrade.code-snippets
vendored
@@ -1,50 +0,0 @@
|
|||||||
{
|
|
||||||
// Place your topgrade workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
|
||||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
|
||||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
|
||||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
|
||||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
|
||||||
// Placeholders with the same ids are connected.
|
|
||||||
// Example:
|
|
||||||
// "Print to console": {
|
|
||||||
// "scope": "javascript,typescript",
|
|
||||||
// "prefix": "log",
|
|
||||||
// "body": [
|
|
||||||
// "console.log('$1');",
|
|
||||||
// "$2"
|
|
||||||
// ],
|
|
||||||
// "description": "Log output to console"
|
|
||||||
// }
|
|
||||||
"Skip Step": {
|
|
||||||
"scope": "rust",
|
|
||||||
"prefix": "skipstep",
|
|
||||||
"body": [
|
|
||||||
"return Err(SkipStep(format!(\"$1\")).into());"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Step": {
|
|
||||||
"scope": "rust",
|
|
||||||
"prefix": "step",
|
|
||||||
"body": [
|
|
||||||
"pub fn $1(ctx: &ExecutionContext) -> Result<()> {",
|
|
||||||
" $0",
|
|
||||||
" Ok(())",
|
|
||||||
"}"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Require Binary": {
|
|
||||||
"scope": "rust",
|
|
||||||
"prefix": "req",
|
|
||||||
"description": "Require a binary to be installed",
|
|
||||||
"body": [
|
|
||||||
"let ${1:binary} = require(\"${1:binary}\")?;"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"macos": {
|
|
||||||
"scope": "rust",
|
|
||||||
"prefix": "macos",
|
|
||||||
"body": [
|
|
||||||
"#[cfg(target_os = \"macos\")]"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0
BREAKINGCHANGES.md
Normal file
0
BREAKINGCHANGES.md
Normal file
3
BREAKINGCHANGES_dev.md
Normal file
3
BREAKINGCHANGES_dev.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
1. The `jet_brains_toolbox` step was renamed to `jetbrains_toolbox`. If you're
|
||||||
|
using the old name in your configuration file in the `disable` or `only`
|
||||||
|
fields, simply change it to `jetbrains_toolbox`.
|
||||||
296
CHANGELOG.md
Normal file
296
CHANGELOG.md
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [16.4.2](https://github.com/topgrade-rs/topgrade/compare/v16.4.1...v16.4.2) - 2025-11-20
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- *(deps)* update dawidd6/action-homebrew-bump-formula action to v6 ([#1543](https://github.com/topgrade-rs/topgrade/pull/1543))
|
||||||
|
|
||||||
|
## [16.4.1](https://github.com/topgrade-rs/topgrade/compare/v16.4.0...v16.4.1) - 2025-11-20
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- refactor run_containers error handling ([#1541](https://github.com/topgrade-rs/topgrade/pull/1541))
|
||||||
|
|
||||||
|
## [16.4.0](https://github.com/topgrade-rs/topgrade/compare/v16.3.0...v16.4.0) - 2025-11-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- *(os)* add Origami Linux support ([#1530](https://github.com/topgrade-rs/topgrade/pull/1530))
|
||||||
|
- *(containers)* add option to run `system prune` ([#1523](https://github.com/topgrade-rs/topgrade/pull/1523))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(deps)* restore custom commands order ([#1535](https://github.com/topgrade-rs/topgrade/pull/1535))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- *(deps)* update clap, clap_builder, clap_complete ([#1540](https://github.com/topgrade-rs/topgrade/pull/1540))
|
||||||
|
- *(deps)* update github/codeql-action action to v4.31.4 ([#1531](https://github.com/topgrade-rs/topgrade/pull/1531))
|
||||||
|
- *(config)* add custom commands order test ([#1536](https://github.com/topgrade-rs/topgrade/pull/1536))
|
||||||
|
- make Config methods more consistent by utilizing `#[derive(Default)]` ([#1534](https://github.com/topgrade-rs/topgrade/pull/1534))
|
||||||
|
- *(issue templates)* use issue types ([#1533](https://github.com/topgrade-rs/topgrade/pull/1533))
|
||||||
|
- *(deps)* lock file maintenance ([#1505](https://github.com/topgrade-rs/topgrade/pull/1505))
|
||||||
|
- *(deps)* update actions/checkout digest to 93cb6ef ([#1526](https://github.com/topgrade-rs/topgrade/pull/1526))
|
||||||
|
- *(deps)* update actions/checkout action to v5.0.1 ([#1527](https://github.com/topgrade-rs/topgrade/pull/1527))
|
||||||
|
|
||||||
|
## [16.3.0](https://github.com/topgrade-rs/topgrade/compare/v16.2.1...v16.3.0) - 2025-11-16
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- print summary and run post commands when (q)uit is used ([#1254](https://github.com/topgrade-rs/topgrade/pull/1254))
|
||||||
|
- run pre_sudo before pre_commands ([#1469](https://github.com/topgrade-rs/topgrade/pull/1469))
|
||||||
|
- *(chezmoi)* add `exclude_encrypted` config ([#1453](https://github.com/topgrade-rs/topgrade/pull/1453))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(elan)* skip running elan update on elan >=4.0.0 ([#1507](https://github.com/topgrade-rs/topgrade/pull/1507))
|
||||||
|
- *(deps)* Fix non-locked install on older version of Rust ([#1485](https://github.com/topgrade-rs/topgrade/pull/1485))
|
||||||
|
- *(deps)* Fix non-locked install on older version of Rust ([#1482](https://github.com/topgrade-rs/topgrade/pull/1482))
|
||||||
|
- *(bun)* skip self-update if not installed via official script ([#1476](https://github.com/topgrade-rs/topgrade/pull/1476))
|
||||||
|
- *(openbsd)* fix compilation on OpenBSD ([#1473](https://github.com/topgrade-rs/topgrade/pull/1473))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- *(license)* switch license variant to GPL-3.0-or-later ([#1518](https://github.com/topgrade-rs/topgrade/pull/1518))
|
||||||
|
- *(deps)* update some dependencies ([#1512](https://github.com/topgrade-rs/topgrade/pull/1512))
|
||||||
|
- *(deps)* update github/codeql-action action to v4.31.3 ([#1483](https://github.com/topgrade-rs/topgrade/pull/1483))
|
||||||
|
- remove unnecessary cfg-if dependency ([#1509](https://github.com/topgrade-rs/topgrade/pull/1509))
|
||||||
|
- *(lint_pr)* run on synchronize, and add zizmor ignore ([#1508](https://github.com/topgrade-rs/topgrade/pull/1508))
|
||||||
|
- *(pre-commit)* autoupdate ([#1464](https://github.com/topgrade-rs/topgrade/pull/1464))
|
||||||
|
- improve issue templates ([#1235](https://github.com/topgrade-rs/topgrade/pull/1235))
|
||||||
|
- *(deps)* bump mac-notification-sys, use main branch temporarily ([#1506](https://github.com/topgrade-rs/topgrade/pull/1506))
|
||||||
|
- *(deps)* lock file maintenance ([#1481](https://github.com/topgrade-rs/topgrade/pull/1481))
|
||||||
|
- *(deps)* pin dependencies ([#1478](https://github.com/topgrade-rs/topgrade/pull/1478))
|
||||||
|
- *(deps)* update actions/dependency-review-action action to v4.8.2 ([#1479](https://github.com/topgrade-rs/topgrade/pull/1479))
|
||||||
|
- Add Renovate ([#1477](https://github.com/topgrade-rs/topgrade/pull/1477))
|
||||||
|
- Replace main's self update with a proper step call ([#1470](https://github.com/topgrade-rs/topgrade/pull/1470))
|
||||||
|
- *(release)* Fix homebrew releases ([#1468](https://github.com/topgrade-rs/topgrade/pull/1468))
|
||||||
|
|
||||||
|
## [16.2.1](https://github.com/topgrade-rs/topgrade/compare/v16.2.0...v16.2.1) - 2025-11-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(release)* Use bash in Windows to fix powershell issues ([#1461](https://github.com/topgrade-rs/topgrade/pull/1461))
|
||||||
|
- *(release)* Fix .deb distribution ([#1460](https://github.com/topgrade-rs/topgrade/pull/1460))
|
||||||
|
- *(release)* Fix .deb distribution ([#1458](https://github.com/topgrade-rs/topgrade/pull/1458))
|
||||||
|
|
||||||
|
## [16.2.0](https://github.com/topgrade-rs/topgrade/compare/v16.1.2...v16.2.0) - 2025-11-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- *(mise)* run `mise self-update` ([#1450](https://github.com/topgrade-rs/topgrade/pull/1450))
|
||||||
|
- *(falconf)* add falconf step ([#1219](https://github.com/topgrade-rs/topgrade/pull/1219))
|
||||||
|
- *(hyprpm)* add hyprpm step ([#1213](https://github.com/topgrade-rs/topgrade/pull/1213))
|
||||||
|
- *(doom)* add doom.aot option ([#1214](https://github.com/topgrade-rs/topgrade/pull/1214))
|
||||||
|
- add show_distribution_summary config option ([#1259](https://github.com/topgrade-rs/topgrade/pull/1259))
|
||||||
|
- *(rustup)* add rustup.channels config ([#1206](https://github.com/topgrade-rs/topgrade/pull/1206))
|
||||||
|
- *(os)* add AOSC OS support ([#1424](https://github.com/topgrade-rs/topgrade/pull/1424))
|
||||||
|
- add damp run type ([#1217](https://github.com/topgrade-rs/topgrade/pull/1217))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(release)* fix homebrew releases by migrating to dawidd6/action-homebrew-bump-formula ([#1457](https://github.com/topgrade-rs/topgrade/pull/1457))
|
||||||
|
- *(mise)* fix mise self-update failing when installed via a package manager ([#1456](https://github.com/topgrade-rs/topgrade/pull/1456))
|
||||||
|
- *(release)* Add man page to .deb distribution ([#1455](https://github.com/topgrade-rs/topgrade/pull/1455))
|
||||||
|
- *(self-update)* fix windows self-update reporting failure on successful self-update ([#1452](https://github.com/topgrade-rs/topgrade/pull/1452))
|
||||||
|
- *(pkgfile)* make pkgfile opt-in ([#1449](https://github.com/topgrade-rs/topgrade/pull/1449))
|
||||||
|
- *(vcpkg)* fix permission denied when updating vcpkg if it's installed as root ([#1447](https://github.com/topgrade-rs/topgrade/pull/1447))
|
||||||
|
- *(zh_TW)* fixed zh_TW strings ([#1446](https://github.com/topgrade-rs/topgrade/pull/1446))
|
||||||
|
- *(git)* fix shellexpand::tilde in git_repos in topgrade.d/* ([#1223](https://github.com/topgrade-rs/topgrade/pull/1223))
|
||||||
|
- *(auto-cpufreq)* skip when install script is not used ([#1215](https://github.com/topgrade-rs/topgrade/pull/1215))
|
||||||
|
- *(vim)* change nvimrc base_dir for windows ([#1433](https://github.com/topgrade-rs/topgrade/pull/1433))
|
||||||
|
- *(guix)* fix overcomplicated Guix step ([#1290](https://github.com/topgrade-rs/topgrade/pull/1290))
|
||||||
|
- *(gem)* fix incorrectly placed debug message in `gem` step ([#1212](https://github.com/topgrade-rs/topgrade/pull/1212))
|
||||||
|
- *(conda)* replace deprecated `auto_activate_base` ([#1158](https://github.com/topgrade-rs/topgrade/pull/1158))
|
||||||
|
- *(containers)* fix panic in `containers` step ([#1150](https://github.com/topgrade-rs/topgrade/pull/1150))
|
||||||
|
- *(jetbrains-toolbox)* fix step not dry running ([#1253](https://github.com/topgrade-rs/topgrade/pull/1253))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- comment run_config_update ([#1448](https://github.com/topgrade-rs/topgrade/pull/1448))
|
||||||
|
- Expand LLM guidelines in CONTRIBUTING.md ([#1445](https://github.com/topgrade-rs/topgrade/pull/1445))
|
||||||
|
- Add AI guidelines to CONTRIBUTING.md ([#1444](https://github.com/topgrade-rs/topgrade/pull/1444))
|
||||||
|
- add comments to Config::allowed_steps ([#1291](https://github.com/topgrade-rs/topgrade/pull/1291))
|
||||||
|
- *(nix)* Deduplicate run_nix and run_nix_self_upgrade nix --version checking ([#1376](https://github.com/topgrade-rs/topgrade/pull/1376))
|
||||||
|
- remove commented-out library code and unnecessary bin declaration ([#1373](https://github.com/topgrade-rs/topgrade/pull/1373))
|
||||||
|
- Simplify target cfgs ([#1346](https://github.com/topgrade-rs/topgrade/pull/1346))
|
||||||
|
- tidy up binary-conflict code ([#1329](https://github.com/topgrade-rs/topgrade/pull/1329))
|
||||||
|
- Improve installation section ([#1442](https://github.com/topgrade-rs/topgrade/pull/1442))
|
||||||
|
- *(deps)* Update jetbrains-toolbox-updater ([#1438](https://github.com/topgrade-rs/topgrade/pull/1438))
|
||||||
|
- remove template expansion in code contexts ([#1434](https://github.com/topgrade-rs/topgrade/pull/1434))
|
||||||
|
- *(deps)* bump github/codeql-action from 4.31.0 to 4.31.2 ([#1427](https://github.com/topgrade-rs/topgrade/pull/1427))
|
||||||
|
- don't persist credentials in actions/checkout ([#1422](https://github.com/topgrade-rs/topgrade/pull/1422))
|
||||||
|
- Improve CONTRIBUTING.md ([#1420](https://github.com/topgrade-rs/topgrade/pull/1420))
|
||||||
|
- Update SECURITY.md ([#1421](https://github.com/topgrade-rs/topgrade/pull/1421))
|
||||||
|
- Enforce conventional commits in PR titles ([#1418](https://github.com/topgrade-rs/topgrade/pull/1418))
|
||||||
|
- Improve contributing section
|
||||||
|
- Remove roadmap
|
||||||
|
- Reformat README.md
|
||||||
|
- Update installation methods
|
||||||
|
- *(release)* Fix dispatch error in create_release_assets.yml ([#1406](https://github.com/topgrade-rs/topgrade/pull/1406))
|
||||||
|
|
||||||
|
## [16.1.2](https://github.com/topgrade-rs/topgrade/compare/v16.1.1...v16.1.2) - 2025-11-01
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(release)* Fix cross-compilation for arm requiring glibc>=2.39 ([#1405](https://github.com/topgrade-rs/topgrade/pull/1405))
|
||||||
|
- *(release)* Fix FreeBSD build ([#1404](https://github.com/topgrade-rs/topgrade/pull/1404))
|
||||||
|
- *(release)* Fix FreeBSD build ([#1402](https://github.com/topgrade-rs/topgrade/pull/1402))
|
||||||
|
- *(release)* Fix manual workflow trigger ([#1401](https://github.com/topgrade-rs/topgrade/pull/1401))
|
||||||
|
- *(release)* Fix FreeBSD build and add manual workflow trigger ([#1399](https://github.com/topgrade-rs/topgrade/pull/1399))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- *(release)* Fix cross trying to fmt ([#1403](https://github.com/topgrade-rs/topgrade/pull/1403))
|
||||||
|
|
||||||
|
## [16.1.1](https://github.com/topgrade-rs/topgrade/compare/v16.1.0...v16.1.1) - 2025-11-01
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(typst)* Skip typst when self-update is disabled ([#1397](https://github.com/topgrade-rs/topgrade/pull/1397))
|
||||||
|
- *(release)* Fix winget release workflow ([#1395](https://github.com/topgrade-rs/topgrade/pull/1395))
|
||||||
|
- *(release)* Fix FreeBSD release ([#1393](https://github.com/topgrade-rs/topgrade/pull/1393))
|
||||||
|
- *(release)* Fix FreeBSD release ([#1391](https://github.com/topgrade-rs/topgrade/pull/1391))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- Update from deprecated macos-13 to macos-15-intel ([#1394](https://github.com/topgrade-rs/topgrade/pull/1394))
|
||||||
|
|
||||||
|
## [16.1.0](https://github.com/topgrade-rs/topgrade/compare/v16.0.4...v16.1.0) - 2025-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- *(deb-get)* Skip non-deb-get packages by passing --dg-only ([#1386](https://github.com/topgrade-rs/topgrade/pull/1386))
|
||||||
|
- *(typst)* add typst step ([#1374](https://github.com/topgrade-rs/topgrade/pull/1374))
|
||||||
|
- *(step)* Add atuin step ([#1367](https://github.com/topgrade-rs/topgrade/pull/1367))
|
||||||
|
- *(nix)* support upgrading Determinate Nix ([#1366](https://github.com/topgrade-rs/topgrade/pull/1366))
|
||||||
|
- *(sudo)* print warning if Windows Sudo is misconfigured
|
||||||
|
- *(sudo)* print warning if steps were skipped due to missing sudo
|
||||||
|
- *(sudo)* add SudoKind::Null
|
||||||
|
- detect and warn if running as root
|
||||||
|
- add `--no-tmux` flag ([#1328](https://github.com/topgrade-rs/topgrade/pull/1328))
|
||||||
|
- add step for mandb - user and system (update man entries) ([#1319](https://github.com/topgrade-rs/topgrade/pull/1319))
|
||||||
|
- support for pkgfile ([#1306](https://github.com/topgrade-rs/topgrade/pull/1306))
|
||||||
|
- add "show_skipped" option in config file #1280 ([#1286](https://github.com/topgrade-rs/topgrade/pull/1286))
|
||||||
|
- fix typos ([#1221](https://github.com/topgrade-rs/topgrade/pull/1221))
|
||||||
|
- *(conda)* allow configuring additional envs to update ([#1048](https://github.com/topgrade-rs/topgrade/pull/1048))
|
||||||
|
- *(step)* nix-helper ([#1045](https://github.com/topgrade-rs/topgrade/pull/1045))
|
||||||
|
- *(winget)* winget uses sudo when `[windows] winget_use_sudo = true` ([#1061](https://github.com/topgrade-rs/topgrade/pull/1061))
|
||||||
|
- suppress pixi release notes by default ([#1225](https://github.com/topgrade-rs/topgrade/pull/1225))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(freshclam)* run with sudo when running without sudo fails ([#1118](https://github.com/topgrade-rs/topgrade/pull/1118))
|
||||||
|
- *(tldr)* move tldr to be a generic step ([#1370](https://github.com/topgrade-rs/topgrade/pull/1370))
|
||||||
|
- *(nix)* fix nix upgrade command selection for profiles in XDG_STATE_HOME ([#1354](https://github.com/topgrade-rs/topgrade/pull/1354))
|
||||||
|
- *(containers)* Docker update fails on M Macs due to platform / ([#1360](https://github.com/topgrade-rs/topgrade/pull/1360))
|
||||||
|
- *(sudo)* reorder require_sudo() after print_separator()
|
||||||
|
- *(sudo)* use require_sudo for windows commands
|
||||||
|
- *(sudo)* prevent sudo_command = "sudo" finding gsudo
|
||||||
|
- *(sudo)* set sudo flags depending on kind
|
||||||
|
- skip gcloud update step if component manager is disabled ([#1237](https://github.com/topgrade-rs/topgrade/pull/1237))
|
||||||
|
- *(i18n)* use double-quotes for translations with newlines
|
||||||
|
- *(powershell)* run microsoft_store command directly
|
||||||
|
- *(powershell)* remove mentions of USOClient
|
||||||
|
- *(powershell)* execution policy check breaks when run in pwsh
|
||||||
|
- *(powershell)* don't use sudo with Update-Module for pwsh
|
||||||
|
- *(powershell)* add -Command to module update cmdline
|
||||||
|
- *(tmux)* support all default `tpm` locations (xdg and both hardcoded locations) ([#1146](https://github.com/topgrade-rs/topgrade/pull/1146))
|
||||||
|
- fixed the German translation for "y/n/s/q" ([#1220](https://github.com/topgrade-rs/topgrade/pull/1220))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- *(release)* switch to release-plz ([#1333](https://github.com/topgrade-rs/topgrade/pull/1333))
|
||||||
|
- *(pre-commit)* Make pre-commit.ci use conventional commits ([#1388](https://github.com/topgrade-rs/topgrade/pull/1388))
|
||||||
|
- *(pre-commit)* pre-commit autoupdate ([#1383](https://github.com/topgrade-rs/topgrade/pull/1383))
|
||||||
|
- *(deps)* bump actions/upload-artifact from 4.6.2 to 5.0.0 ([#1382](https://github.com/topgrade-rs/topgrade/pull/1382))
|
||||||
|
- *(deps)* bump github/codeql-action from 4.30.9 to 4.31.0 ([#1379](https://github.com/topgrade-rs/topgrade/pull/1379))
|
||||||
|
- *(deps)* bump actions/download-artifact from 5.0.0 to 6.0.0 ([#1380](https://github.com/topgrade-rs/topgrade/pull/1380))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.62.33 to 2.62.38 ([#1381](https://github.com/topgrade-rs/topgrade/pull/1381))
|
||||||
|
- *(pre-commit)* Fix pre-commit-config.yaml ([#1378](https://github.com/topgrade-rs/topgrade/pull/1378))
|
||||||
|
- *(release)* Add .deb auto completion script ([#1353](https://github.com/topgrade-rs/topgrade/pull/1353))
|
||||||
|
- *(deps)* bump github/codeql-action from 4.30.8 to 4.30.9 ([#1369](https://github.com/topgrade-rs/topgrade/pull/1369))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.62.28 to 2.62.33 ([#1368](https://github.com/topgrade-rs/topgrade/pull/1368))
|
||||||
|
- *(deps)* bump actions/dependency-review-action from 4.8.0 to 4.8.1 ([#1362](https://github.com/topgrade-rs/topgrade/pull/1362))
|
||||||
|
- *(deps)* bump softprops/action-gh-release from 2.3.4 to 2.4.1 ([#1364](https://github.com/topgrade-rs/topgrade/pull/1364))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.62.21 to 2.62.28 ([#1363](https://github.com/topgrade-rs/topgrade/pull/1363))
|
||||||
|
- *(deps)* bump github/codeql-action from 3.30.6 to 4.30.8 ([#1365](https://github.com/topgrade-rs/topgrade/pull/1365))
|
||||||
|
- *(deps)* bump github/codeql-action from 3.30.5 to 3.30.6 ([#1355](https://github.com/topgrade-rs/topgrade/pull/1355))
|
||||||
|
- *(deps)* bump softprops/action-gh-release from 2.3.3 to 2.3.4 ([#1356](https://github.com/topgrade-rs/topgrade/pull/1356))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.62.13 to 2.62.21 ([#1357](https://github.com/topgrade-rs/topgrade/pull/1357))
|
||||||
|
- *(deps)* bump ossf/scorecard-action from 2.4.2 to 2.4.3 ([#1358](https://github.com/topgrade-rs/topgrade/pull/1358))
|
||||||
|
- *(deps)* bump actions/dependency-review-action from 4.7.3 to 4.8.0 ([#1350](https://github.com/topgrade-rs/topgrade/pull/1350))
|
||||||
|
- *(deps)* bump github/codeql-action from 3.30.3 to 3.30.5 ([#1349](https://github.com/topgrade-rs/topgrade/pull/1349))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.62.1 to 2.62.13 ([#1351](https://github.com/topgrade-rs/topgrade/pull/1351))
|
||||||
|
- *(deps)* bump actions/cache from 4.2.4 to 4.3.0 ([#1352](https://github.com/topgrade-rs/topgrade/pull/1352))
|
||||||
|
- Fix WSL distribution name cleanup ([#1348](https://github.com/topgrade-rs/topgrade/pull/1348))
|
||||||
|
- *(pyproject)* mark version as dynamic ([#1347](https://github.com/topgrade-rs/topgrade/pull/1347))
|
||||||
|
- *(deps)* replace winapi with windows
|
||||||
|
- *(sudo)* rename interactive to login_shell
|
||||||
|
- Fix "WSL already reported" panic ([#1344](https://github.com/topgrade-rs/topgrade/pull/1344))
|
||||||
|
- Move step logic out of Powershell struct ([#1345](https://github.com/topgrade-rs/topgrade/pull/1345))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.61.5 to 2.62.1 ([#1335](https://github.com/topgrade-rs/topgrade/pull/1335))
|
||||||
|
- *(deps)* bump Swatinem/rust-cache from 2.8.0 to 2.8.1 ([#1336](https://github.com/topgrade-rs/topgrade/pull/1336))
|
||||||
|
- Fixes for #1188; custom_commands broken ([#1332](https://github.com/topgrade-rs/topgrade/pull/1332))
|
||||||
|
- use login shell when executing topgrade ([#1327](https://github.com/topgrade-rs/topgrade/pull/1327))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.60.0 to 2.61.5 ([#1325](https://github.com/topgrade-rs/topgrade/pull/1325))
|
||||||
|
- *(deps)* bump github/codeql-action from 3.30.1 to 3.30.3 ([#1324](https://github.com/topgrade-rs/topgrade/pull/1324))
|
||||||
|
- *(pre-commit)* add typos with conservative excludes; no content changes ([#1317](https://github.com/topgrade-rs/topgrade/pull/1317))
|
||||||
|
- fix simple typos in code and comments (split var, whether, Extensions) ([#1318](https://github.com/topgrade-rs/topgrade/pull/1318))
|
||||||
|
- *(deps)* bump github/codeql-action from 3.29.11 to 3.30.1 ([#1301](https://github.com/topgrade-rs/topgrade/pull/1301))
|
||||||
|
- *(deps)* bump softprops/action-gh-release from 2.3.2 to 2.3.3 ([#1302](https://github.com/topgrade-rs/topgrade/pull/1302))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.58.21 to 2.60.0 ([#1303](https://github.com/topgrade-rs/topgrade/pull/1303))
|
||||||
|
- *(deps)* bump actions/dependency-review-action from 4.7.2 to 4.7.3 ([#1304](https://github.com/topgrade-rs/topgrade/pull/1304))
|
||||||
|
- *(deps)* bump actions/attest-build-provenance from 2.4.0 to 3.0.0 ([#1305](https://github.com/topgrade-rs/topgrade/pull/1305))
|
||||||
|
- update tracing-subscriber to ~0.3.20 (ANSI escape injection fix, GHSA-xwfj-jgwm-7wp5) ([#1288](https://github.com/topgrade-rs/topgrade/pull/1288))
|
||||||
|
- *(deps)* bump github/codeql-action from 3.29.8 to 3.29.11 ([#1281](https://github.com/topgrade-rs/topgrade/pull/1281))
|
||||||
|
- *(deps)* bump actions/dependency-review-action from 4.7.1 to 4.7.2 ([#1282](https://github.com/topgrade-rs/topgrade/pull/1282))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.58.9 to 2.58.21 ([#1283](https://github.com/topgrade-rs/topgrade/pull/1283))
|
||||||
|
- *(deps)* bump PyO3/maturin-action from 1.49.3 to 1.49.4 ([#1285](https://github.com/topgrade-rs/topgrade/pull/1285))
|
||||||
|
- *(deps)* bump actions/cache from 4.2.3 to 4.2.4 ([#1284](https://github.com/topgrade-rs/topgrade/pull/1284))
|
||||||
|
- Support "Insiders" versions of VSCode and VSCodium ([#1279](https://github.com/topgrade-rs/topgrade/pull/1279))
|
||||||
|
- Sudo preserve env list argument is `--preserve-env` ([#1276](https://github.com/topgrade-rs/topgrade/pull/1276))
|
||||||
|
- Clippy fixes from rust 1.91 nightly ([#1267](https://github.com/topgrade-rs/topgrade/pull/1267))
|
||||||
|
- *(deps)* bump actions/checkout from 4.2.2 to 5.0.0 ([#1264](https://github.com/topgrade-rs/topgrade/pull/1264))
|
||||||
|
- *(deps)* bump actions/download-artifact from 4.3.0 to 5.0.0 ([#1263](https://github.com/topgrade-rs/topgrade/pull/1263))
|
||||||
|
- *(deps)* bump taiki-e/install-action from 2.58.0 to 2.58.9 ([#1261](https://github.com/topgrade-rs/topgrade/pull/1261))
|
||||||
|
- *(deps)* bump ossf/scorecard-action from 2.4.0 to 2.4.2 ([#1262](https://github.com/topgrade-rs/topgrade/pull/1262))
|
||||||
|
- *(deps)* bump github/codeql-action from 3.29.5 to 3.29.8 ([#1265](https://github.com/topgrade-rs/topgrade/pull/1265))
|
||||||
|
- *(ci)* Dependabot, workflow security ([#1257](https://github.com/topgrade-rs/topgrade/pull/1257))
|
||||||
|
- replace once_cell crate with std equivalent ([#1260](https://github.com/topgrade-rs/topgrade/pull/1260))
|
||||||
|
- *(deps)* bump tokio from 1.38 to 1.47 ([#1256](https://github.com/topgrade-rs/topgrade/pull/1256))
|
||||||
|
- *(app.yml)* fix fr language #1248
|
||||||
|
- *(sudo)* add SudoKind::WinSudo
|
||||||
|
- *(sudo)* add SudoExecuteOpts builder functions and preserve_env enum
|
||||||
|
- *(yarn)* remove unnecessary Yarn::yarn field
|
||||||
|
- *(apt)* extract detect_apt() function
|
||||||
|
- route sudo usage through Sudo::execute*
|
||||||
|
- move RunType::execute to ExecutionContext
|
||||||
|
- *(powershell)* store powershell path directly
|
||||||
|
- *(powershell)* cleanup and simplify code
|
||||||
|
- Move step running into enum for dynamic ordering ([#1188](https://github.com/topgrade-rs/topgrade/pull/1188))
|
||||||
|
- Generate artifact attestations for release assets ([#1216](https://github.com/topgrade-rs/topgrade/pull/1216))
|
||||||
|
- windows update, use explicit reboot policy ([#1143](https://github.com/topgrade-rs/topgrade/pull/1143))
|
||||||
|
- add Discord invite link to README ([#1203](https://github.com/topgrade-rs/topgrade/pull/1203))
|
||||||
|
- Catch secondary uv self-update error ([#1201](https://github.com/topgrade-rs/topgrade/pull/1201))
|
||||||
|
- Handle another format change in asdf version ([#1194](https://github.com/topgrade-rs/topgrade/pull/1194))
|
||||||
|
- Preserve custom command order from config instead of sorting alphabetically ([#1182](https://github.com/topgrade-rs/topgrade/pull/1182))
|
||||||
|
- Add support for multiple binary names and idea having multiple binaries ([#1167](https://github.com/topgrade-rs/topgrade/pull/1167))
|
||||||
|
- fix the invalid action version ([#1185](https://github.com/topgrade-rs/topgrade/pull/1185))
|
||||||
|
- allow us to re-run AUR CI ([#1184](https://github.com/topgrade-rs/topgrade/pull/1184))
|
||||||
|
- Update Yazi upgrade step to use ya pkg. ([#1163](https://github.com/topgrade-rs/topgrade/pull/1163))
|
||||||
|
- use the new tag name and specify shell to bash ([#1183](https://github.com/topgrade-rs/topgrade/pull/1183))
|
||||||
|
- allow specifying tag when manually run 'create_release_assets.yml' ([#1180](https://github.com/topgrade-rs/topgrade/pull/1180))
|
||||||
|
- fix homebrew ci, remove duplicate trigger event ([#1179](https://github.com/topgrade-rs/topgrade/pull/1179))
|
||||||
|
- fix PyPI pipeline duplicate wheel name ([#1178](https://github.com/topgrade-rs/topgrade/pull/1178))
|
||||||
|
- add event workflow_dispatch to release pipelines ([#1177](https://github.com/topgrade-rs/topgrade/pull/1177))
|
||||||
|
- fix pipeline release to PyPI ([#1176](https://github.com/topgrade-rs/topgrade/pull/1176))
|
||||||
|
- Install rustfmt and clippy where necessary ([#1171](https://github.com/topgrade-rs/topgrade/pull/1171))
|
||||||
182
CONTRIBUTING.md
Normal file
182
CONTRIBUTING.md
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
## Contributing to `topgrade`
|
||||||
|
|
||||||
|
Thank you for your interest in contributing to `topgrade`!
|
||||||
|
We welcome and encourage contributions of all kinds, such as:
|
||||||
|
|
||||||
|
1. Issue reports or feature requests
|
||||||
|
2. Documentation improvements
|
||||||
|
3. Code (PR or PR Review)
|
||||||
|
|
||||||
|
### LLM/AI guidelines
|
||||||
|
|
||||||
|
You may use LLMs (AI tools) for:
|
||||||
|
|
||||||
|
* Inspiration, problem solving, help with Rust, translation, etc.
|
||||||
|
* Generating small and self-contained snippets of code (e.g., shell scripts or utility functions)
|
||||||
|
|
||||||
|
Do **not** use LLMs to:
|
||||||
|
|
||||||
|
* Generate ("vibe code") entire pull requests
|
||||||
|
* Write or generate issue or pull request descriptions
|
||||||
|
|
||||||
|
### General guidelines
|
||||||
|
|
||||||
|
**Please use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) for your PR title**.
|
||||||
|
|
||||||
|
We use [pre-commit](https://github.com/pre-commit/pre-commit). It runs in CI, but you can optionally install the hook
|
||||||
|
locally with `pre-commit install`. If you don't want to use pre-commit, make sure the following pass before submitting
|
||||||
|
your PR:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ cargo fmt
|
||||||
|
$ cargo clippy
|
||||||
|
$ cargo test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a new step
|
||||||
|
|
||||||
|
In `topgrade`'s terms, a package manager (or something else that can be upgraded) is called a step.
|
||||||
|
To add a new step to `topgrade`:
|
||||||
|
|
||||||
|
1. Add a new variant to
|
||||||
|
[`enum Step`](https://github.com/topgrade-rs/topgrade/blob/main/src/step.rs)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub enum Step {
|
||||||
|
// Existing steps
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Your new step here!
|
||||||
|
// Make sure it stays sorted alphabetically because that looks great :)
|
||||||
|
Xxx,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Implement the update function
|
||||||
|
|
||||||
|
You need to find the appropriate location where this update function goes, it should be
|
||||||
|
a file under [`src/steps`](https://github.com/topgrade-rs/topgrade/tree/main/src/steps),
|
||||||
|
the file names are self-explanatory, for example, steps related to `zsh` are
|
||||||
|
placed in [`steps/zsh.rs`](https://github.com/topgrade-rs/topgrade/blob/main/src/steps/zsh.rs), and steps that run on
|
||||||
|
Linux only are placed in [`steps/linux.rs`](https://github.com/topgrade-rs/topgrade/blob/main/src/steps/linux.rs).
|
||||||
|
|
||||||
|
Then you implement the update function, and put it in the file where it belongs.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub fn run_xxx(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
// Check if this step is installed, if not, then this update will be skipped.
|
||||||
|
let xxx = require("xxx")?;
|
||||||
|
|
||||||
|
// Print the separator
|
||||||
|
print_separator("xxx");
|
||||||
|
|
||||||
|
// Invoke the new step to get things updated!
|
||||||
|
ctx.execute(xxx)
|
||||||
|
.arg(/* args required by this step */)
|
||||||
|
.status_checked()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Such an update function would be conventionally named `run_xxx()`, where `xxx`
|
||||||
|
is the name of the new step, and it should take an argument of type
|
||||||
|
`&ExecutionContext`.
|
||||||
|
|
||||||
|
The update function should usually do 3 things:
|
||||||
|
1. Check if the step is installed
|
||||||
|
2. Output the separator
|
||||||
|
3. Execute commands
|
||||||
|
|
||||||
|
This is sufficient for most tools, but you may need some extra stuff
|
||||||
|
for complicated steps.
|
||||||
|
|
||||||
|
3. Add a match arm to `Step::run()`
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Xxx => runner.execute(*self, "xxx", || ItsModule::run_xxx(ctx))?
|
||||||
|
```
|
||||||
|
|
||||||
|
We use [conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html)
|
||||||
|
to separate the steps. For example, for steps that are Linux-only, it goes
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
// Xxx is Linux-only
|
||||||
|
runner.execute(Step::Xxx, "xxx", || ItsModule::run_xxx(&ctx))?;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Finally, add the step to `default_steps()` in `step.rs`
|
||||||
|
```rust
|
||||||
|
steps.push(Xxx)
|
||||||
|
```
|
||||||
|
Keep the conditional compilation the same as in the above step 3.
|
||||||
|
|
||||||
|
Congrats, you just added a new step :)
|
||||||
|
|
||||||
|
### Modification to the configuration entries
|
||||||
|
|
||||||
|
If your PR has the configuration options
|
||||||
|
(in [`src/config.rs`](https://github.com/topgrade-rs/topgrade/blob/main/src/config.rs))
|
||||||
|
modified:
|
||||||
|
|
||||||
|
1. Adding new options
|
||||||
|
2. Changing the existing options
|
||||||
|
|
||||||
|
Be sure to apply your changes to
|
||||||
|
[`config.example.toml`](https://github.com/topgrade-rs/topgrade/blob/main/config.example.toml),
|
||||||
|
and have some basic documentations guiding user how to use these options.
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
If your PR introduces a breaking change, document it in [`BREAKINGCHANGES_dev.md`][bc_dev].
|
||||||
|
It should be written in Markdown and wrapped at 80, for example:
|
||||||
|
|
||||||
|
```md
|
||||||
|
1. The configuration location has been updated to x.
|
||||||
|
|
||||||
|
2. The step x has been removed.
|
||||||
|
|
||||||
|
3. ...
|
||||||
|
```
|
||||||
|
|
||||||
|
[bc_dev]: https://github.com/topgrade-rs/topgrade/blob/main/BREAKINGCHANGES_dev.md
|
||||||
|
|
||||||
|
### I18n
|
||||||
|
|
||||||
|
If your PR introduces user-facing messages, we need to ensure they are translated.
|
||||||
|
Please add the translations to [`locales/app.yml`][app_yml]. For simple messages
|
||||||
|
without arguments (e.g., "hello world"), we can simply translate them according
|
||||||
|
(Tip: LLMs are good at translation). If a message contains
|
||||||
|
arguments, e.g., "hello <NAME>", please follow this convention:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
"hello {name}": # key
|
||||||
|
en: "hello %{name}" # translation
|
||||||
|
```
|
||||||
|
|
||||||
|
Arguments in the key should be in format `{argument_name}`, and they will have
|
||||||
|
a preceding `%` when used in translations.
|
||||||
|
|
||||||
|
[app_yml]: https://github.com/topgrade-rs/topgrade/blob/main/locales/app.yml
|
||||||
|
|
||||||
|
### Locales
|
||||||
|
|
||||||
|
Some steps respect locale, which means their output can be in language other
|
||||||
|
than English. In those cases, we cannot rely on the output of a command.
|
||||||
|
|
||||||
|
For example, one may want to check if a tool works by doing this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let output = Command::new("xxx").arg("--help").output().unwrap();
|
||||||
|
let stdout = from_utf8(output.stdout).expect("Assume it is UTF-8 encoded");
|
||||||
|
|
||||||
|
if stdout.contains("help") {
|
||||||
|
// xxx works
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If `xxx` respects locale, then the above code should work on English system,
|
||||||
|
on a system that does not use English, e.g., it uses Chinese, that `"help"` may be
|
||||||
|
translated to `"帮助"`, and the above code won't work.
|
||||||
3528
Cargo.lock
generated
3528
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
101
Cargo.toml
101
Cargo.toml
@@ -3,72 +3,97 @@ name = "topgrade"
|
|||||||
description = "Upgrade all the things"
|
description = "Upgrade all the things"
|
||||||
categories = ["os"]
|
categories = ["os"]
|
||||||
keywords = ["upgrade", "update"]
|
keywords = ["upgrade", "update"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0-or-later"
|
||||||
repository = "https://github.com/topgrade-rs/topgrade"
|
repository = "https://github.com/topgrade-rs/topgrade"
|
||||||
version = "10.3.3"
|
rust-version = "1.84.1"
|
||||||
|
version = "16.4.2"
|
||||||
authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"]
|
authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"]
|
||||||
exclude = ["doc/screenshot.gif"]
|
exclude = ["doc/screenshot.gif", "BREAKINGCHANGES_dev.md"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "topgrade"
|
|
||||||
path = "src/main.rs"
|
|
||||||
|
|
||||||
##[lib]
|
|
||||||
##name = "topgrade_lib"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
home = "~0.5"
|
home = "=0.5.11"
|
||||||
directories = "~4.0"
|
etcetera = "=0.10.0"
|
||||||
serde = { version = "~1.0", features = ["derive"] }
|
serde = { version = "~1.0", features = ["derive"] }
|
||||||
toml = "0.5"
|
toml = { version = "=0.9.8", features = ["preserve_order"] }
|
||||||
which_crate = { version = "~4.1", package = "which" }
|
which_crate = { version = "~8.0", package = "which" }
|
||||||
shellexpand = "~2.1"
|
shellexpand = "~3.1"
|
||||||
clap = { version = "~3.1", features = ["cargo", "derive"] }
|
clap = { version = "~4.5", features = ["cargo", "derive"] }
|
||||||
clap_complete = "~3.1"
|
clap_complete = "~4.5"
|
||||||
clap_mangen = "~0.1"
|
clap_mangen = "~0.2"
|
||||||
walkdir = "~2.3"
|
walkdir = "~2.5"
|
||||||
console = "~0.15"
|
console = "~0.16"
|
||||||
lazy_static = "~1.4"
|
|
||||||
chrono = "~0.4"
|
chrono = "~0.4"
|
||||||
glob = "~0.3"
|
glob = "~0.3"
|
||||||
strum = { version = "~0.24", features = ["derive"] }
|
strum = { version = "~0.27", features = ["derive"] }
|
||||||
thiserror = "~1.0"
|
thiserror = "~2.0"
|
||||||
tempfile = "~3.2"
|
tempfile = "~3.23"
|
||||||
cfg-if = "~1.0"
|
tokio = { version = "~1.48", features = ["process", "rt-multi-thread"] }
|
||||||
tokio = { version = "~1.18", features = ["process", "rt-multi-thread"] }
|
|
||||||
futures = "~0.3"
|
futures = "~0.3"
|
||||||
regex = "~1.5"
|
regex = "~1.12"
|
||||||
semver = "~1.0"
|
semver = "~1.0"
|
||||||
shell-words = "~1.1"
|
shell-words = "~1.1"
|
||||||
color-eyre = "~0.6"
|
color-eyre = "~0.6"
|
||||||
tracing = { version = "~0.1", features = ["attributes", "log"] }
|
tracing = { version = "~0.1", features = ["attributes", "log"] }
|
||||||
tracing-subscriber = { version = "~0.3", features = ["env-filter", "time"] }
|
tracing-subscriber = { version = "~0.3.20", features = ["env-filter", "time"] }
|
||||||
|
merge = "~0.1"
|
||||||
|
regex-split = "~0.1"
|
||||||
|
notify-rust = "~4.11"
|
||||||
|
wildmatch = "2.3.0"
|
||||||
|
rust-i18n = "3.0.1"
|
||||||
|
sys-locale = "0.3.1"
|
||||||
|
jetbrains-toolbox-updater = "5.0.0"
|
||||||
|
indexmap = { version = "2.9.0", features = ["serde"] }
|
||||||
|
serde_json = "1.0.145"
|
||||||
|
# Temporary transitive dependency pins
|
||||||
|
ignore = "=0.4.23"
|
||||||
|
globset = "=0.4.16"
|
||||||
|
base64ct = "<1.9.0"
|
||||||
|
clap-cargo = "0.15.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[patch.crates-io]
|
||||||
notify-rust = "~4.5"
|
mac-notification-sys = { git = "https://github.com/h4llow3En/mac-notification-sys" }
|
||||||
|
|
||||||
[package.metadata.generate-rpm]
|
[package.metadata.generate-rpm]
|
||||||
assets = [{source = "target/release/topgrade", dest="/usr/bin/topgrade"}]
|
assets = [{ source = "target/release/topgrade", dest = "/usr/bin/topgrade" }]
|
||||||
|
|
||||||
[package.metadata.generate-rpm.requires]
|
[package.metadata.generate-rpm.requires]
|
||||||
git = "*"
|
git = "*"
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
depends = "$auto,git"
|
name = "topgrade"
|
||||||
|
maintainer = "Chris Gelatt <kreeblah@gmail.com>"
|
||||||
|
copyright = "2024, Topgrade Team"
|
||||||
|
license-file = ["LICENSE", "0"]
|
||||||
|
depends = "$auto"
|
||||||
|
extended-description = "Keeping your system up to date usually involves invoking multiple package managers. This results in big, non-portable shell one-liners saved in your shell. To remedy this, Topgrade detects which tools you use and runs the appropriate commands to update them."
|
||||||
|
section = "utils"
|
||||||
|
priority = "optional"
|
||||||
|
default-features = true
|
||||||
|
assets = [
|
||||||
|
["target/release/topgrade", "usr/bin/", "755"],
|
||||||
|
["README.md", "usr/share/doc/topgrade/README.md", "644"],
|
||||||
|
# The man page and shell completions are automatically generated by topgrade's build process in CI,
|
||||||
|
# so these files aren't actually committed.
|
||||||
|
["deployment/deb/topgrade.1", "usr/share/man/man1/topgrade.1", "644"],
|
||||||
|
["deployment/deb/topgrade.bash", "usr/share/bash-completion/completions/topgrade", "644"],
|
||||||
|
["deployment/deb/topgrade.fish", "usr/share/fish/vendor_completions.d/topgrade.fish", "644"],
|
||||||
|
["deployment/deb/_topgrade", "usr/share/zsh/vendor-completions/", "644"],
|
||||||
|
]
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "~0.2"
|
nix = { version = "~0.30", features = ["hostname", "signal", "user"] }
|
||||||
nix = "~0.24"
|
rust-ini = "~0.21"
|
||||||
rust-ini = "~0.18"
|
self_update_crate = { version = "~0.42", default-features = false, optional = true, package = "self_update", features = ["archive-tar", "compression-flate2", "rustls"] }
|
||||||
self_update_crate = { version = "~0.30", default-features = false, optional = true, package = "self_update", features = ["archive-tar", "compression-flate2", "rustls"] }
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
self_update_crate = { version = "~0.30", default-features = false, optional = true, package = "self_update", features = ["archive-zip", "compression-zip-deflate", "rustls"] }
|
is_elevated = "~0.1"
|
||||||
winapi = "~0.3"
|
|
||||||
parselnk = "~0.1"
|
parselnk = "~0.1"
|
||||||
|
self_update_crate = { version = "~0.42", default-features = false, optional = true, package = "self_update", features = ["archive-zip", "compression-zip-deflate", "rustls"] }
|
||||||
|
windows = { version = "~0.62", features = ["Win32_System_Console"] }
|
||||||
|
windows-registry = "~0.6"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
102
README.md
102
README.md
@@ -3,12 +3,12 @@
|
|||||||
<img alt="Topgrade" src="doc/topgrade_transparent.png" width="850px">
|
<img alt="Topgrade" src="doc/topgrade_transparent.png" width="850px">
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<a href="https://github.com/topgrade-rs/topgrade/releases"><img alt="GitHub Release" src="https://img.shields.io/github/release/topgrade-rs/topgrade.svg"></a>
|
<a href="https://github.com/topgrade-rs/topgrade/releases"><img alt="GitHub Release" src="https://img.shields.io/github/release/topgrade-rs/topgrade.svg"></a>
|
||||||
<a href="https://crates.io/crates/topgrade"><img alt="crates.io" src="https://img.shields.io/crates/v/topgrade.svg"></a>
|
<a href="https://crates.io/crates/topgrade"><img alt="crates.io" src="https://img.shields.io/crates/v/topgrade.svg"></a>
|
||||||
<a href="https://aur.archlinux.org/packages/topgrade"><img alt="AUR" src="https://img.shields.io/aur/version/topgrade.svg"></a>
|
<a href="https://aur.archlinux.org/packages/topgrade"><img alt="AUR" src="https://img.shields.io/aur/version/topgrade.svg"></a>
|
||||||
<a href="https://formulae.brew.sh/formula/topgrade"><img alt="Homebrew" src="https://img.shields.io/homebrew/v/topgrade.svg"></a>
|
<a href="https://formulae.brew.sh/formula/topgrade"><img alt="Homebrew" src="https://img.shields.io/homebrew/v/topgrade.svg"></a>
|
||||||
|
|
||||||
<img alt="Demo" src="doc/screenshot.gif" width="550px">
|
<img alt="Demo" src="doc/topgrade_demo.gif">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
@@ -24,35 +24,77 @@ To remedy this, **Topgrade** detects which tools you use and runs the appropriat
|
|||||||
|
|
||||||
[](https://repology.org/project/topgrade/versions)
|
[](https://repology.org/project/topgrade/versions)
|
||||||
|
|
||||||
- Arch Linux: [AUR](https://aur.archlinux.org/packages/topgrade)
|
### Official
|
||||||
- NixOS: [Nixpkgs](https://search.nixos.org/packages?show=topgrade)
|
|
||||||
- Void Linux: [XBPS](https://voidlinux.org/packages/?arch=x86_64&q=topgrade)
|
|
||||||
- macOS: [Homebrew](https://formulae.brew.sh/formula/topgrade) or [MacPorts](https://ports.macports.org/port/topgrade/)
|
|
||||||
|
|
||||||
Other systems users can either use `cargo install` or the compiled binaries from the release page.
|
- Self-updating binary (all platforms): [releases](https://github.com/topgrade-rs/topgrade/releases)
|
||||||
The compiled binaries contain a self-upgrading feature.
|
- Install from source (all platforms): [`cargo install topgrade`](https://crates.io/crates/topgrade)
|
||||||
|
- Debian/Ubuntu ([deb-get](https://github.com/wimpysworld/deb-get)):
|
||||||
|
[`deb-get install topgrade`](https://github.com/wimpysworld/deb-get/blob/main/01-main/packages/topgrade)
|
||||||
|
- Arch Linux (AUR): [topgrade](https://aur.archlinux.org/packages/topgrade)
|
||||||
|
or [topgrade-bin](https://aur.archlinux.org/packages/topgrade-bin)
|
||||||
|
- [PyPi](https://pypi.org/): `pip`, `pipx`, or `uv tool` [
|
||||||
|
`install topgrade`](https://pypi.org/project/topgrade/)
|
||||||
|
- Windows ([Winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/)): [
|
||||||
|
`winget install --id=topgrade-rs.topgrade -e`](https://winstall.app/apps/topgrade-rs.topgrade)
|
||||||
|
- macOS or Linux ([Homebrew](https://brew.sh/)): [`brew install topgrade`](https://formulae.brew.sh/formula/topgrade)
|
||||||
|
- Fedora/RHEL/AlmaLinux/CentOS-Stream ([Copr](https://copr.fedorainfracloud.org/)): [
|
||||||
|
`sudo dnf copr enable lilay/topgrade && sudo dnf install topgrade`](https://copr.fedorainfracloud.org/coprs/lilay/topgrade/)
|
||||||
|
|
||||||
Topgrade requires Rust 1.60 or above.
|
### Community-maintained
|
||||||
|
|
||||||
|
- Windows ([Chocolatey](https://chocolatey.org/)): [
|
||||||
|
`choco install topgrade`](https://community.chocolatey.org/packages/topgrade)
|
||||||
|
- Windows ([Scoop](https://scoop.sh/)): [
|
||||||
|
`scoop bucket add main && scoop install main/topgrade`](https://scoop.sh/#/apps?q=topgrade)
|
||||||
|
- macOS ([MacPorts](https://www.macports.org/)): [
|
||||||
|
`sudo port install topgrade`](https://ports.macports.org/port/topgrade/)
|
||||||
|
- NixOS or Nix (nixpkgs): [topgrade](https://search.nixos.org/packages?show=topgrade)
|
||||||
|
- Void Linux: [`sudo xbps-install -S topgrade`](https://voidlinux.org/packages/?arch=x86_64&q=topgrade)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Just run `topgrade`.
|
Just run `topgrade`.
|
||||||
|
|
||||||
Visit the documentation at [topgrade-rs.github.io](https://topgrade-rs.github.io/) for more information.
|
## Configuration
|
||||||
|
|
||||||
> **Warning**
|
|
||||||
> Work in Progress
|
|
||||||
|
|
||||||
## Customization
|
|
||||||
|
|
||||||
See `config.example.toml` for an example configuration file.
|
See `config.example.toml` for an example configuration file.
|
||||||
|
|
||||||
|
## Migration and Breaking Changes
|
||||||
|
|
||||||
|
Whenever there is a **breaking change**, the major version number will be bumped,
|
||||||
|
and we will document these changes in the release note, please take a look at
|
||||||
|
it when updated to a major release.
|
||||||
|
|
||||||
|
> Got a question? Feel free to open an issue or discussion!
|
||||||
|
|
||||||
### Configuration Path
|
### Configuration Path
|
||||||
|
|
||||||
The configuration should be placed in the following paths depending on the operating system:
|
#### `CONFIG_DIR` on each platform
|
||||||
|
|
||||||
- **Windows** - `%APPDATA%/topgrade.toml`
|
- **Windows**: `%APPDATA%`
|
||||||
- **macOS** and **other Unix systems** - `${XDG_CONFIG_HOME:-~/.config}/topgrade.toml`
|
- **macOS** and **other Unix systems**: `${XDG_CONFIG_HOME:-~/.config}`
|
||||||
|
|
||||||
|
`topgrade` will look for the configuration file in the following places, in order of priority:
|
||||||
|
|
||||||
|
1. `CONFIG_DIR/topgrade.toml`
|
||||||
|
2. `CONFIG_DIR/topgrade/topgrade.toml`
|
||||||
|
|
||||||
|
If the file with higher priority is present, no matter it is valid or not, the other configuration files will be
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
On the first run(no configuration file exists), `topgrade` will create a configuration file at
|
||||||
|
`CONFIG_DIR/topgrade.toml` for you.
|
||||||
|
|
||||||
|
### Custom Commands
|
||||||
|
|
||||||
|
Custom commands can be defined in the config file which can be run before, during, or after the inbuilt commands, as
|
||||||
|
required.
|
||||||
|
By default, the custom commands are run using a new shell according to the `$SHELL` environment variable on unix (falls
|
||||||
|
back to `sh`) or `pwsh` on windows (falls back to `powershell`).
|
||||||
|
|
||||||
|
On unix, if you want to run your command using an interactive shell, for example to source your shell's rc files, you
|
||||||
|
can add `-i` at the start of your custom command.
|
||||||
|
But note that this requires the command to exit the shell correctly or else the shell will hang indefinitely.
|
||||||
|
|
||||||
## Remote Execution
|
## Remote Execution
|
||||||
|
|
||||||
@@ -70,18 +112,14 @@ Open a new issue describing your problem and if possible provide a solution.
|
|||||||
### Missing a feature or found an unsupported tool/distro?
|
### Missing a feature or found an unsupported tool/distro?
|
||||||
|
|
||||||
Just let us now what you are missing by opening an issue.
|
Just let us now what you are missing by opening an issue.
|
||||||
For tools, please open an issue describing the tool, which platforms it supports and if possible, give us an example of its usage.
|
For tools, please open an issue describing the tool, which platforms it supports and if possible, give us an example of
|
||||||
|
its usage.
|
||||||
|
|
||||||
### Want to contribute to the code?
|
### Want to contribute?
|
||||||
|
|
||||||
Just fork the repository and start coding.
|
See [CONTRIBUTING.md](https://github.com/topgrade-rs/topgrade/blob/master/CONTRIBUTING.md)
|
||||||
|
|
||||||
### Contribution Guidelines
|
## Discord server
|
||||||
|
|
||||||
- Check if your code passes `cargo fmt` and `cargo clippy`.
|
Welcome to [join](https://discord.gg/Q8HGGWundY) our Discord server if you want
|
||||||
- Check if your code is self explanatory, if not it should be documented by comments.
|
to discuss Topgrade!
|
||||||
## Roadmap
|
|
||||||
|
|
||||||
- [ ] Add a proper testing framework to the code base.
|
|
||||||
- [ ] Add unit tests for package managers.
|
|
||||||
- [ ] Split up code into more maintainable parts, eg. putting every linux package manager in a own submodule of linux.rs.
|
|
||||||
|
|||||||
21
RELEASE_PROCEDURE.md
Normal file
21
RELEASE_PROCEDURE.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
Non-major versions go via release-plz.
|
||||||
|
|
||||||
|
1. bumps the version number.
|
||||||
|
|
||||||
|
> If there are breaking changes, the major version number should be increased.
|
||||||
|
|
||||||
|
2. If the major versioin number gets bumped, update [SECURITY.md][SECURITY_file_link].
|
||||||
|
|
||||||
|
[SECURITY_file_link]: https://github.com/topgrade-rs/topgrade/blob/main/SECURITY.md
|
||||||
|
|
||||||
|
3. Overwrite [`BREAKINGCHANGES`][breaking_changes] with
|
||||||
|
[`BREAKINGCHANGES_dev`][breaking_changes_dev], and create a new dev file:
|
||||||
|
|
||||||
|
```sh'
|
||||||
|
$ cd topgrade
|
||||||
|
$ mv BREAKINGCHANGES_dev.md BREAKINGCHANGES.md
|
||||||
|
$ touch BREAKINGCHANGES_dev.md
|
||||||
|
```
|
||||||
|
|
||||||
|
[breaking_changes_dev]: https://github.com/topgrade-rs/topgrade/blob/main/BREAKINGCHANGES_dev.md
|
||||||
|
[breaking_changes]: https://github.com/topgrade-rs/topgrade/blob/main/BREAKINGCHANGES.md
|
||||||
12
SECURITY.md
12
SECURITY.md
@@ -1,11 +1,9 @@
|
|||||||
# Security Policy
|
# Security Policy
|
||||||
|
|
||||||
|
## Reporting a vulnerability
|
||||||
|
|
||||||
|
To report a security vulnerability, go to [the security tab](https://github.com/topgrade-rs/topgrade/security) and click "Report a vulnerability".
|
||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
We only support the latest major version and each subversion.
|
We only support the latest version of Topgrade. Fixes are not backported.
|
||||||
|
|
||||||
| Version | Supported |
|
|
||||||
| -------- | ------------------ |
|
|
||||||
| 10.0.x | :white_check_mark: |
|
|
||||||
| < 10.0 | :x: |
|
|
||||||
|
|
||||||
|
|||||||
35
build-all.sh
35
build-all.sh
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
build_function() {
|
build_function() {
|
||||||
rustup update
|
rustup update
|
||||||
cargo install cross
|
cargo install cross
|
||||||
@@ -20,7 +21,7 @@ build_function() {
|
|||||||
|
|
||||||
package_function() {
|
package_function() {
|
||||||
|
|
||||||
cd build
|
cd build || exit 1
|
||||||
mkdir x86_64-unknown-linux-gnu/
|
mkdir x86_64-unknown-linux-gnu/
|
||||||
mkdir x86_64-unknown-linux-musl/
|
mkdir x86_64-unknown-linux-musl/
|
||||||
mkdir x86_64-unknown-freebsd/
|
mkdir x86_64-unknown-freebsd/
|
||||||
@@ -35,28 +36,28 @@ package_function() {
|
|||||||
cp ../target/aarch64-unknown-linux-musl/release/topgrade aarch64-unknown-linux-musl/topgrade
|
cp ../target/aarch64-unknown-linux-musl/release/topgrade aarch64-unknown-linux-musl/topgrade
|
||||||
cp ../target/x86_64-pc-windows-gnu/release/topgrade.exe x86_64-pc-windows-gnu/topgrade.exe
|
cp ../target/x86_64-pc-windows-gnu/release/topgrade.exe x86_64-pc-windows-gnu/topgrade.exe
|
||||||
|
|
||||||
cd x86_64-unknown-linux-gnu/
|
cd x86_64-unknown-linux-gnu/ || exit 1
|
||||||
tar -czf ../topgrade-${ans}-x86_64-linux-gnu.tar.gz topgrade
|
tar -czf "../topgrade-${ans}-x86_64-linux-gnu.tar.gz" topgrade
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd x86_64-unknown-linux-musl
|
cd x86_64-unknown-linux-musl/ || exit 1
|
||||||
tar -czf ../topgrade-${ans}-x86_64-linux-musl.tar.gz topgrade
|
tar -czf "../topgrade-${ans}-x86_64-linux-musl.tar.gz" topgrade
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd x86_64-unknown-freebsd/
|
cd x86_64-unknown-freebsd/ || exit 1
|
||||||
tar -czf ../topgrade-${ans}-x86_64-freebsd.tar.gz topgrade
|
tar -czf "../topgrade-${ans}-x86_64-freebsd.tar.gz" topgrade
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd aarch64-unknown-linux-gnu/
|
cd aarch64-unknown-linux-gnu/ || exit 1
|
||||||
tar -czf ../topgrade-${ans}-aarch64-linux-gnu.tar.gz topgrade
|
tar -czf "../topgrade-${ans}-aarch64-linux-gnu.tar.gz" topgrade
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd aarch64-unknown-linux-musl/
|
cd aarch64-unknown-linux-musl/ || exit 1
|
||||||
tar -czf ../topgrade-${ans}-aarch64-linux-musl.tar.gz topgrade
|
tar -czf "../topgrade-${ans}-aarch64-linux-musl.tar.gz" topgrade
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd x86_64-pc-windows-gnu/
|
cd x86_64-pc-windows-gnu/ || exit 1
|
||||||
zip -q ../topgrade-${ans}-x86_64-windows.zip topgrade.exe
|
zip -q "../topgrade-${ans}-x86_64-windows.zip" topgrade.exe
|
||||||
cd ..
|
cd ..
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
@@ -65,17 +66,19 @@ package_function() {
|
|||||||
print_checksums() {
|
print_checksums() {
|
||||||
|
|
||||||
|
|
||||||
cd build/
|
cd build/ || exit 1
|
||||||
sha256sum topgrade-${ans}-*
|
sha256sum topgrade-"${ans}"-*
|
||||||
cd ../
|
cd ../
|
||||||
}
|
}
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
|
|
||||||
echo "You should always have a look on scripts you download from the internet."
|
echo "You should always have a look on scripts you download from the internet."
|
||||||
|
# shellcheck disable=SC2162
|
||||||
read -p "Do you still want to proceed? (y/n) " yn
|
read -p "Do you still want to proceed? (y/n) " yn
|
||||||
|
|
||||||
echo -n "Input version number: "
|
echo -n "Input version number: "
|
||||||
|
# shellcheck disable=SC2162
|
||||||
read ans
|
read ans
|
||||||
mkdir build
|
mkdir build
|
||||||
|
|
||||||
|
|||||||
@@ -1,133 +1,424 @@
|
|||||||
# Don't ask for confirmations
|
# Include any additional configuration file(s)
|
||||||
#assume_yes = true
|
# [include] sections are processed in the order you write them
|
||||||
|
# Files in $CONFIG_DIR/topgrade.d/ are automatically included before this file
|
||||||
|
[include]
|
||||||
|
# paths = ["/etc/topgrade.toml"]
|
||||||
|
|
||||||
# Disable specific steps - same options as the command line flag
|
|
||||||
#disable = ["system", "emacs"]
|
|
||||||
|
|
||||||
# Ignore failures for these steps
|
[misc]
|
||||||
#ignore_failures = ["powershell"]
|
# On Unix systems, Topgrade should not be run as root, it
|
||||||
|
# will run commands with sudo or equivalent where needed.
|
||||||
|
# Set this to true to suppress the warning and confirmation
|
||||||
|
# prompt if Topgrade detects it is being run as root.
|
||||||
|
# (default: false)
|
||||||
|
# allow_root = false
|
||||||
|
|
||||||
# Run specific steps - same options as the command line flag
|
# Run `sudo -v` to cache credentials at the start of the run
|
||||||
#only = ["system", "emacs"]
|
# This avoids a blocking password prompt in the middle of an unattended run
|
||||||
|
# (default: false)
|
||||||
# Do not ask to retry failed steps (default: false)
|
# pre_sudo = false
|
||||||
#no_retry = true
|
|
||||||
|
|
||||||
# Sudo command to be used
|
# Sudo command to be used
|
||||||
#sudo_command = "sudo"
|
# sudo_command = "sudo"
|
||||||
|
|
||||||
# Run `sudo -v` to cache credentials at the start of the run; this avoids a
|
# Disable specific steps - same options as the command line flag
|
||||||
# blocking password prompt in the middle of a possibly-unattended run.
|
# disable = ["system", "emacs"]
|
||||||
#pre_sudo = false
|
|
||||||
|
|
||||||
# Run inside tmux
|
# Ignore failures for these steps
|
||||||
#run_in_tmux = true
|
# ignore_failures = ["powershell"]
|
||||||
|
|
||||||
# List of remote machines with Topgrade installed on them
|
# List of remote machines with Topgrade installed on them
|
||||||
#remote_topgrades = ["toothless", "pi", "parnas"]
|
# remote_topgrades = ["toothless", "pi", "parnas"]
|
||||||
|
|
||||||
# Arguments to pass SSH when upgrading remote systems
|
|
||||||
#ssh_arguments = "-o ConnectTimeout=2"
|
|
||||||
|
|
||||||
# Path to Topgrade executable on remote machines
|
# Path to Topgrade executable on remote machines
|
||||||
#remote_topgrade_path = ".cargo/bin/topgrade"
|
# remote_topgrade_path = ".cargo/bin/topgrade"
|
||||||
|
|
||||||
|
# Arguments to pass to SSH when upgrading remote systems
|
||||||
|
# ssh_arguments = "-o ConnectTimeout=2"
|
||||||
|
|
||||||
# Arguments to pass tmux when pulling Repositories
|
# Arguments to pass tmux when pulling Repositories
|
||||||
#tmux_arguments = "-S /var/tmux.sock"
|
# tmux_arguments = "-S /var/tmux.sock"
|
||||||
|
|
||||||
# Do not set the terminal title
|
# Do not set the terminal title (default: true)
|
||||||
#set_title = false
|
# set_title = true
|
||||||
|
|
||||||
# Display the time in step titles
|
# Display the time in step titles (default: true)
|
||||||
# display_time = true
|
# display_time = true
|
||||||
|
|
||||||
# Cleanup temporary or old files
|
# Don't ask for confirmations (no default value)
|
||||||
#cleanup = true
|
# assume_yes = true
|
||||||
|
|
||||||
# Skip sending a notification at the end of a run
|
# Do not ask to retry failed steps (default: false)
|
||||||
#skip_notify = true
|
# no_retry = true
|
||||||
|
|
||||||
# Skip the preamble displayed when topgrade is run
|
# Show the reason for skipped steps (default: false)
|
||||||
#display_preamble = false
|
# This has no effect if the "only" option is specified
|
||||||
|
# show_skipped = true
|
||||||
|
|
||||||
[git]
|
# Run inside tmux (default: false)
|
||||||
#max_concurrency = 5
|
# run_in_tmux = true
|
||||||
# Additional git repositories to pull
|
|
||||||
#repos = [
|
|
||||||
# "~/src/*/",
|
|
||||||
# "~/.config/something"
|
|
||||||
#]
|
|
||||||
|
|
||||||
# Don't pull the predefined git repos
|
# Changes the way topgrade interacts with
|
||||||
#pull_predefined = false
|
# the tmux session, creating the session
|
||||||
|
# and only attaching to it if not inside tmux
|
||||||
|
# (default: "attach_if_not_in_session", allowed values: "attach_if_not_in_session", "attach_always")
|
||||||
|
# tmux_session_mode = "attach_if_not_in_session"
|
||||||
|
|
||||||
# Arguments to pass Git when pulling Repositories
|
# Cleanup temporary or old files (default: false)
|
||||||
#arguments = "--rebase --autostash"
|
# cleanup = true
|
||||||
|
|
||||||
|
# Send a notification for every step (default: false)
|
||||||
|
# notify_each_step = false
|
||||||
|
|
||||||
|
# Skip sending a notification at the end of a run (default: false)
|
||||||
|
# skip_notify = true
|
||||||
|
|
||||||
|
# The Bash-it branch to update (default: "stable")
|
||||||
|
# bashit_branch = "stable"
|
||||||
|
|
||||||
|
# Run specific steps - same options as the command line flag
|
||||||
|
# only = ["system", "emacs"]
|
||||||
|
|
||||||
|
# Whether to self update
|
||||||
|
#
|
||||||
|
# this will be ignored if the binary is built without self update support
|
||||||
|
#
|
||||||
|
# available also via setting the environment variable TOPGRADE_NO_SELF_UPGRADE)
|
||||||
|
# no_self_update = true
|
||||||
|
|
||||||
|
# Extra tracing filter directives
|
||||||
|
# These are prepended to the `--log-filter` argument
|
||||||
|
# See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
|
||||||
|
# log_filters = ["topgrade::command=debug", "warn"]
|
||||||
|
|
||||||
|
# Whether to show a distribution-specific summary if applicable, e.g. listing
|
||||||
|
# Pacman backup configuration files (*.pacsave and *.pacnew)
|
||||||
|
# (default: true)
|
||||||
|
# show_distribution_summary = false
|
||||||
|
|
||||||
[composer]
|
|
||||||
#self_update = true
|
|
||||||
|
|
||||||
# Commands to run before anything
|
# Commands to run before anything
|
||||||
[pre_commands]
|
[pre_commands]
|
||||||
#"Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak"
|
# "Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak"
|
||||||
|
|
||||||
|
|
||||||
|
# Commands to run after anything
|
||||||
|
[post_commands]
|
||||||
|
# "Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak"
|
||||||
|
|
||||||
|
|
||||||
# Custom commands
|
# Custom commands
|
||||||
[commands]
|
[commands]
|
||||||
#"Python Environment" = "~/dev/.env/bin/pip install -i https://pypi.python.org/simple -U --upgrade-strategy eager jupyter"
|
# "Python Environment" = "~/dev/.env/bin/pip install -i https://pypi.python.org/simple -U --upgrade-strategy eager jupyter"
|
||||||
|
# "Custom command using interactive shell (unix)" = "-i vim_upgrade"
|
||||||
|
|
||||||
[brew]
|
|
||||||
#greedy_cask = true
|
|
||||||
#autoremove = true
|
|
||||||
|
|
||||||
[linux]
|
|
||||||
# Arch Package Manager to use. Allowed values: autodetect, aura, garuda_update, pacman, pamac, paru, pikaur, trizen, yay.
|
|
||||||
#arch_package_manager = "pacman"
|
|
||||||
# Arguments to pass yay (or paru) when updating packages
|
|
||||||
#yay_arguments = "--nodevel"
|
|
||||||
# Arguments to pass dnf when updating packages
|
|
||||||
#dnf_arguments = "--refresh"
|
|
||||||
#aura_aur_arguments = "-kx"
|
|
||||||
#aura_pacman_arguments = ""
|
|
||||||
#garuda_update_arguments = ""
|
|
||||||
#show_arch_news = true
|
|
||||||
#trizen_arguments = "--devel"
|
|
||||||
#pikaur_arguments = ""
|
|
||||||
#pamac_arguments = "--no-devel"
|
|
||||||
#enable_tlmgr = true
|
|
||||||
#emerge_sync_flags = "-q"
|
|
||||||
#emerge_update_flags = "-uDNa --with-bdeps=y world"
|
|
||||||
#redhat_distro_sync = false
|
|
||||||
#rpm_ostree = false
|
|
||||||
#nix_arguments = "--flake"
|
|
||||||
|
|
||||||
[python]
|
[python]
|
||||||
#enable_pip_review = true ###disabled by default
|
# enable_pip_review = true ###disabled by default
|
||||||
#enable_pipupgrade = true ###disabled by default
|
# enable_pip_review_local = true ###disabled by default
|
||||||
|
# enable_pipupgrade = true ###disabled by default
|
||||||
|
# pipupgrade_arguments = "-y -u --pip-path pip" ###disabled by default
|
||||||
|
|
||||||
|
# For the poetry step, by default, Topgrade skips its update if poetry is not
|
||||||
|
# installed with the official script. This configuration entry forces Topgrade
|
||||||
|
# to run the update in this case.
|
||||||
|
#
|
||||||
|
# (default: false)
|
||||||
|
# poetry_force_self_update = true
|
||||||
|
|
||||||
|
|
||||||
|
[conda]
|
||||||
|
# Additional named conda environments to update (`conda env update -n env_name`)
|
||||||
|
# env_names = [
|
||||||
|
# "Toolbox",
|
||||||
|
# "PyTorch"
|
||||||
|
# ]
|
||||||
|
# Additional conda environment paths to update (`conda env update -p env_path`)
|
||||||
|
# env_paths = [
|
||||||
|
# "~/webserver/.conda/",
|
||||||
|
# "~/experiments/.conda/"
|
||||||
|
# ]
|
||||||
|
|
||||||
|
|
||||||
|
[composer]
|
||||||
|
# self_update = true
|
||||||
|
|
||||||
|
|
||||||
|
[brew]
|
||||||
|
# For the BrewCask step
|
||||||
|
# If `Repo Cask Upgrade` exists, then use the `-a` option.
|
||||||
|
# Otherwise, use the `--greedy` option.
|
||||||
|
# greedy_cask = true
|
||||||
|
|
||||||
|
# For the BrewCask step
|
||||||
|
# If `Repo Cask Upgrade` does not exist, then use the `--greedy_latest` option.
|
||||||
|
# NOTE: the above entry `greedy_cask` contains this entry, though you can enable
|
||||||
|
# both of them, they won't clash with each other.
|
||||||
|
# greedy_latest = true
|
||||||
|
|
||||||
|
# For the BrewCask step
|
||||||
|
# If `Repo Cask Upgrade` does not exist, then use the `--greedy_auto_updates` option.
|
||||||
|
# NOTE: the above entry `greedy_cask` contains this entry, though you can enable
|
||||||
|
# both of them, they won't clash with each other.
|
||||||
|
# greedy_auto_updates = true
|
||||||
|
|
||||||
|
# For the BrewFormula step
|
||||||
|
# Execute `brew autoremove` after the step.
|
||||||
|
# autoremove = true
|
||||||
|
|
||||||
|
# For the BrewFormula step
|
||||||
|
# Upgrade formulae built from the HEAD branch; `brew upgrade --fetch-HEAD`
|
||||||
|
# fetch_head = true
|
||||||
|
|
||||||
|
|
||||||
|
[linux]
|
||||||
|
# Arch Package Manager to use.
|
||||||
|
# Allowed values:
|
||||||
|
# autodetect, aura, garuda_update, pacman, pamac, paru, pikaur, trizen, yay
|
||||||
|
# arch_package_manager = "pacman"
|
||||||
|
|
||||||
|
# Arguments to pass yay (or paru) when updating packages
|
||||||
|
# yay_arguments = "--nodevel"
|
||||||
|
|
||||||
|
# Arguments to pass dnf when updating packages
|
||||||
|
# dnf_arguments = "--refresh"
|
||||||
|
|
||||||
|
# aura_aur_arguments = "-kx"
|
||||||
|
|
||||||
|
# aura_pacman_arguments = ""
|
||||||
|
# garuda_update_arguments = ""
|
||||||
|
|
||||||
|
# show_arch_news = true
|
||||||
|
|
||||||
|
# trizen_arguments = "--devel"
|
||||||
|
|
||||||
|
# pikaur_arguments = ""
|
||||||
|
|
||||||
|
# pamac_arguments = "--no-devel"
|
||||||
|
|
||||||
|
# enable_tlmgr = true
|
||||||
|
|
||||||
|
# emerge_sync_flags = "-q"
|
||||||
|
|
||||||
|
# emerge_update_flags = "-uDNa --with-bdeps=y world"
|
||||||
|
|
||||||
|
# redhat_distro_sync = false
|
||||||
|
|
||||||
|
# suse_dup = false
|
||||||
|
|
||||||
|
# rpm_ostree = false
|
||||||
|
|
||||||
|
# For Fedora/CentOS/RHEL Atomic variants, if `bootc` is available and this configuration entry is set to true, use
|
||||||
|
# it to do the update - Will also supersede rpm-ostree if enabled
|
||||||
|
# (default: false)
|
||||||
|
# bootc = false
|
||||||
|
|
||||||
|
# nix_arguments = "--flake"
|
||||||
|
|
||||||
|
# nix_env_arguments = "--prebuilt-only"
|
||||||
|
|
||||||
|
# Extra Home Manager arguments
|
||||||
|
# home_manager_arguments = ["--flake", "file"]
|
||||||
|
|
||||||
|
|
||||||
|
[mandb]
|
||||||
|
# Enable the mandb step (to update manual entries).
|
||||||
|
# Mandb is updated in the background by a service on most systems by default.
|
||||||
|
# (default: false)
|
||||||
|
# enable = true
|
||||||
|
|
||||||
|
|
||||||
|
[pkgfile]
|
||||||
|
# Enable the pkgfile step (to update the pkgfile database).
|
||||||
|
# Pkgfile is sometimes installed by default, but often not used and heavy to update.
|
||||||
|
# (default: false)
|
||||||
|
# enable = true
|
||||||
|
|
||||||
|
|
||||||
|
[git]
|
||||||
|
# How many repos to pull at max in parallel
|
||||||
|
# max_concurrency = 5
|
||||||
|
|
||||||
|
# Additional git repositories to pull
|
||||||
|
# repos = [
|
||||||
|
# "~/src/*/",
|
||||||
|
# "~/.config/something"
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# Don't pull the predefined git repos
|
||||||
|
# pull_predefined = false
|
||||||
|
|
||||||
|
# Arguments to pass Git when pulling Repositories
|
||||||
|
# arguments = "--rebase --autostash"
|
||||||
|
|
||||||
|
|
||||||
[windows]
|
[windows]
|
||||||
# Manually select Windows updates
|
# Manually select Windows updates
|
||||||
#accept_all_updates = false
|
# accept_all_updates = false
|
||||||
#open_remotes_in_new_terminal = true
|
|
||||||
#wsl_update_pre_release = true
|
# Controls whether to automatically reboot the computer when updates are
|
||||||
#wsl_update_use_web_download = true
|
# installed that request it. (default: "no", allowed values: "yes", "no", "ask")
|
||||||
|
# updates_auto_reboot = "yes"
|
||||||
|
|
||||||
|
# open_remotes_in_new_terminal = true
|
||||||
|
|
||||||
|
# wsl_update_pre_release = true
|
||||||
|
|
||||||
|
# wsl_update_use_web_download = true
|
||||||
|
|
||||||
|
# The default for winget_install_silently is true,
|
||||||
|
# this example turns off silent install.
|
||||||
|
# winget_install_silently = false
|
||||||
|
|
||||||
# Causes Topgrade to rename itself during the run to allow package managers
|
# Causes Topgrade to rename itself during the run to allow package managers
|
||||||
# to upgrade it. Use this only if you installed Topgrade by using a package
|
# to upgrade it. Use this only if you installed Topgrade by using a package
|
||||||
# manager such as Scoop or Cargo
|
# manager such as Scoop or Cargo
|
||||||
#self_rename = true
|
# self_rename = true
|
||||||
|
|
||||||
|
# Use sudo to elevate privileges for the Windows Package Manager (winget)
|
||||||
|
# Only use this option if you want to run the Winget step in sudo-mode.
|
||||||
|
# Running winget in sudo-mode is generally not recommended, as not every
|
||||||
|
# package supports installing / upgrading in sudo-mode and it may cause issues
|
||||||
|
# with some packages or may even cause the Winget-step to fail.
|
||||||
|
# If any problems occur, please try running Topgrade without this option first
|
||||||
|
# before reporting an issue.
|
||||||
|
# (default: false)
|
||||||
|
# winget_use_sudo = true
|
||||||
|
|
||||||
|
|
||||||
|
[chezmoi]
|
||||||
|
# Exclude encrypted files from update
|
||||||
|
# (default: false)
|
||||||
|
# exclude_encrypted = false
|
||||||
|
|
||||||
|
[mise]
|
||||||
|
# Upgrades to the latest version available, bumping the version in mise.toml
|
||||||
|
# (default: false)
|
||||||
|
# bump = false
|
||||||
|
|
||||||
|
# Number of jobs to run in parallel
|
||||||
|
# (default: 4)
|
||||||
|
# jobs = 4
|
||||||
|
|
||||||
|
# Run interactively
|
||||||
|
# (default: false)
|
||||||
|
# interactive = false
|
||||||
|
|
||||||
|
|
||||||
[npm]
|
[npm]
|
||||||
# Use sudo if the NPM directory isn't owned by the current user
|
# Use sudo if the NPM directory isn't owned by the current user
|
||||||
#use_sudo = true
|
# use_sudo = true
|
||||||
|
|
||||||
|
|
||||||
|
[yarn]
|
||||||
|
# Run `yarn global upgrade` with `sudo`
|
||||||
|
# use_sudo = true
|
||||||
|
|
||||||
|
|
||||||
|
[deno]
|
||||||
|
# Upgrade deno executable to the given version.
|
||||||
|
# version = "stable"
|
||||||
|
|
||||||
|
|
||||||
|
[vim]
|
||||||
|
# For `vim-plug`, execute `PlugUpdate!` instead of `PlugUpdate`
|
||||||
|
# force_plug_update = true
|
||||||
|
|
||||||
|
|
||||||
[firmware]
|
[firmware]
|
||||||
# Offer to update firmware; if false just check for and display available updates
|
# Offer to update firmware; if false just check for and display available updates
|
||||||
#upgrade = true
|
# upgrade = true
|
||||||
|
|
||||||
|
|
||||||
|
[vagrant]
|
||||||
|
# Vagrant directories
|
||||||
|
# directories = []
|
||||||
|
|
||||||
|
# power on vagrant boxes if needed
|
||||||
|
# power_on = true
|
||||||
|
|
||||||
|
# Always suspend vagrant boxes instead of powering off
|
||||||
|
# always_suspend = true
|
||||||
|
|
||||||
|
|
||||||
[flatpak]
|
[flatpak]
|
||||||
# Use sudo for updating the system-wide installation
|
# Use sudo for updating the system-wide installation
|
||||||
#use_sudo = true
|
# use_sudo = true
|
||||||
|
|
||||||
|
|
||||||
[distrobox]
|
[distrobox]
|
||||||
#use_root = false
|
# use_root = false
|
||||||
#containers = ["archlinux-latest"]
|
|
||||||
|
# containers = ["archlinux-latest"]
|
||||||
|
[containers]
|
||||||
|
# Specify the containers to ignore while updating (Wildcard supported)
|
||||||
|
# ignored_containers = ["ghcr.io/rancher-sandbox/rancher-desktop/rdx-proxy:latest", "docker.io*"]
|
||||||
|
# Specify the runtime to use for containers (default: "docker", allowed values: "docker", "podman")
|
||||||
|
# runtime = "podman"
|
||||||
|
|
||||||
|
# Run 'docker system prune' to clean up unused containers, networks, and build cache
|
||||||
|
# (default: false)
|
||||||
|
# system_prune = false
|
||||||
|
|
||||||
|
[lensfun]
|
||||||
|
# If disabled, Topgrade invokes `lensfun‑update‑data` without root privilege,
|
||||||
|
# then the update will be only available to you. Otherwise, `sudo` is required,
|
||||||
|
# and the update will be installed system-wide, i.e., available to all users.
|
||||||
|
# (default: false)
|
||||||
|
# use_sudo = false
|
||||||
|
|
||||||
|
[julia]
|
||||||
|
# If disabled, Topgrade invokes julia with the --startup-file=no CLI option.
|
||||||
|
#
|
||||||
|
# This may be desirable to avoid loading outdated packages with "using" directives
|
||||||
|
# in the startup file, which might cause the update run to fail.
|
||||||
|
# (default: true)
|
||||||
|
# startup_file = true
|
||||||
|
|
||||||
|
[zigup]
|
||||||
|
# Version strings passed to zigup.
|
||||||
|
# These may be pinned versions such as "0.13.0" or branches such as "master".
|
||||||
|
# Each one will be updated in its own zigup invocation.
|
||||||
|
# (default: ["master"])
|
||||||
|
# target_versions = ["master", "0.13.0"]
|
||||||
|
|
||||||
|
# Specifies the directory that the zig files will be installed to.
|
||||||
|
# If defined, passed with the --install-dir command line flag.
|
||||||
|
# If not defined, zigup will use its default behaviour.
|
||||||
|
# (default: not defined)
|
||||||
|
# install_dir = "~/.zig"
|
||||||
|
|
||||||
|
# Specifies the path of the symlink which will be set to point at the default compiler version.
|
||||||
|
# If defined, passed with the --path-link command line flag.
|
||||||
|
# If not defined, zigup will use its default behaviour.
|
||||||
|
# This is not meaningful if set_default is not enabled.
|
||||||
|
# (default: not defined)
|
||||||
|
# path_link = "~/.bin/zig"
|
||||||
|
|
||||||
|
# If enabled, run `zigup clean` after updating all versions.
|
||||||
|
# If enabled, each updated version above will be marked with `zigup keep`.
|
||||||
|
# (default: false)
|
||||||
|
# cleanup = false
|
||||||
|
|
||||||
|
[vscode]
|
||||||
|
# If this is set and is a non-empty string, it specifies the profile the
|
||||||
|
# extensions should be updated for.
|
||||||
|
# (default: this won't be set by default)
|
||||||
|
# profile = ""
|
||||||
|
|
||||||
|
[pixi]
|
||||||
|
# Show the release notes of the latest pixi release
|
||||||
|
# during the pixi step
|
||||||
|
# (default: false)
|
||||||
|
# include_release_notes = false
|
||||||
|
|
||||||
|
[doom]
|
||||||
|
# If this is set to true, the `--aot` flag is added to `doom upgrade`,
|
||||||
|
# which enables ahead-of-time native compilation of packages.
|
||||||
|
# (default: false)
|
||||||
|
# aot = true
|
||||||
|
|
||||||
|
[rustup]
|
||||||
|
# If set, updates only these channels.
|
||||||
|
# (default: [] (all channels))
|
||||||
|
# channels = ["stable"]
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 718 KiB |
BIN
doc/topgrade_demo.gif
Normal file
BIN
doc/topgrade_demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 MiB |
1420
locales/app.yml
Normal file
1420
locales/app.yml
Normal file
File diff suppressed because it is too large
Load Diff
24
pyproject.toml
Normal file
24
pyproject.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["maturin>=1.0,<2.0"]
|
||||||
|
build-backend = "maturin"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "topgrade"
|
||||||
|
dynamic = ["version"]
|
||||||
|
description = "Upgrade all the things"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
requires-python = ">=3.7"
|
||||||
|
classifiers = [
|
||||||
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
|
"Programming Language :: Rust",
|
||||||
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
|
]
|
||||||
|
|
||||||
|
urls.bugs = "https://github.com/topgrade-rs/topgrade/issues"
|
||||||
|
urls.homepage = "https://github.com/topgrade-rs/topgrade"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.maturin]
|
||||||
|
bindings = "bin"
|
||||||
10
renovate.json
Normal file
10
renovate.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:best-practices",
|
||||||
|
":semanticCommits"
|
||||||
|
],
|
||||||
|
"lockFileMaintenance": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "1.84.1"
|
||||||
171
src/breaking_changes.rs
Normal file
171
src/breaking_changes.rs
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
//! Inform the users of the breaking changes introduced in this major release.
|
||||||
|
//!
|
||||||
|
//! Print the breaking changes and possibly a migration guide when:
|
||||||
|
//! 1. The Topgrade being executed is a new major release
|
||||||
|
//! 2. This is the first launch of that major release
|
||||||
|
|
||||||
|
use crate::terminal::print_separator;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use crate::WINDOWS_DIRS;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use crate::XDG_DIRS;
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use etcetera::base_strategy::BaseStrategy;
|
||||||
|
use rust_i18n::t;
|
||||||
|
use std::{
|
||||||
|
env::var,
|
||||||
|
fs::{read_to_string, OpenOptions},
|
||||||
|
io::Write,
|
||||||
|
path::PathBuf,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Version string x.y.z
|
||||||
|
static VERSION_STR: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
/// Version info
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Version {
|
||||||
|
_major: u64,
|
||||||
|
minor: u64,
|
||||||
|
patch: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Version {
|
||||||
|
type Err = std::convert::Infallible;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
const NOT_SEMVER: &str = "Topgrade version is not semantic";
|
||||||
|
const NOT_NUMBER: &str = "Topgrade version is not dot-separated numbers";
|
||||||
|
|
||||||
|
let mut iter = s.split('.').take(3);
|
||||||
|
let major = iter.next().expect(NOT_SEMVER).parse().expect(NOT_NUMBER);
|
||||||
|
let minor = iter.next().expect(NOT_SEMVER).parse().expect(NOT_NUMBER);
|
||||||
|
let patch = iter.next().expect(NOT_SEMVER).parse().expect(NOT_NUMBER);
|
||||||
|
|
||||||
|
// They cannot be all 0s
|
||||||
|
assert!(
|
||||||
|
!(major == 0 && minor == 0 && patch == 0),
|
||||||
|
"Version numbers cannot be all 0s"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
_major: major,
|
||||||
|
minor,
|
||||||
|
patch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
/// True if this version is a new major release.
|
||||||
|
pub(crate) fn is_new_major_release(&self) -> bool {
|
||||||
|
// We have already checked that they cannot all be zeros, so `self.major`
|
||||||
|
// is guaranteed to be non-zero.
|
||||||
|
self.minor == 0 && self.patch == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Topgrade's breaking changes
|
||||||
|
///
|
||||||
|
/// We store them in the compiled binary.
|
||||||
|
pub(crate) static BREAKINGCHANGES: &str = include_str!("../BREAKINGCHANGES.md");
|
||||||
|
|
||||||
|
/// Return platform's data directory.
|
||||||
|
fn data_dir() -> PathBuf {
|
||||||
|
#[cfg(unix)]
|
||||||
|
return XDG_DIRS.data_dir();
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
return WINDOWS_DIRS.data_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return Topgrade's keep file path.
|
||||||
|
///
|
||||||
|
/// keep file is a file under the data directory containing a major version
|
||||||
|
/// number, it will be created on first run and is used to check if an execution
|
||||||
|
/// of Topgrade is the first run of a major release, for more details, see
|
||||||
|
/// `first_run_of_major_release()`.
|
||||||
|
fn keep_file_path() -> PathBuf {
|
||||||
|
let keep_file = "topgrade_keep";
|
||||||
|
data_dir().join(keep_file)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If environment variable `TOPGRADE_SKIP_BRKC_NOTIFY` is set to `true`, then
|
||||||
|
/// we won't notify the user of the breaking changes.
|
||||||
|
pub(crate) fn should_skip() -> bool {
|
||||||
|
if let Ok(var) = var("TOPGRADE_SKIP_BRKC_NOTIFY") {
|
||||||
|
return var.as_str() == "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if this is the first execution of a major release.
|
||||||
|
pub(crate) fn first_run_of_major_release() -> Result<bool> {
|
||||||
|
let version = VERSION_STR.parse::<Version>().expect("should be a valid version");
|
||||||
|
let keep_file = keep_file_path();
|
||||||
|
|
||||||
|
// disable this lint here as the current code has better readability
|
||||||
|
#[allow(clippy::collapsible_if)]
|
||||||
|
if version.is_new_major_release() {
|
||||||
|
if !keep_file.exists() || read_to_string(&keep_file)? != VERSION_STR {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print breaking changes to the user.
|
||||||
|
pub(crate) fn print_breaking_changes() {
|
||||||
|
let header = format!(
|
||||||
|
"{}",
|
||||||
|
t!("Topgrade {version_str} Breaking Changes", version_str = VERSION_STR)
|
||||||
|
);
|
||||||
|
print_separator(header);
|
||||||
|
let contents = if BREAKINGCHANGES.is_empty() {
|
||||||
|
t!("No Breaking changes").to_string()
|
||||||
|
} else {
|
||||||
|
BREAKINGCHANGES.to_string()
|
||||||
|
};
|
||||||
|
println!("{contents}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function will be ONLY executed when the user has confirmed the breaking
|
||||||
|
/// changes, once confirmed, we write the keep file, which means the first run
|
||||||
|
/// of this major release is finished.
|
||||||
|
pub(crate) fn write_keep_file() -> Result<()> {
|
||||||
|
std::fs::create_dir_all(data_dir())?;
|
||||||
|
let keep_file = keep_file_path();
|
||||||
|
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(keep_file)?;
|
||||||
|
let _ = file.write(VERSION_STR.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_new_major_release_works() {
|
||||||
|
let first_major_release: Version = "1.0.0".parse().unwrap();
|
||||||
|
let under_dev: Version = "0.1.0".parse().unwrap();
|
||||||
|
|
||||||
|
assert!(first_major_release.is_new_major_release());
|
||||||
|
assert!(!under_dev.is_new_major_release());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Version numbers cannot be all 0s")]
|
||||||
|
fn invalid_version() {
|
||||||
|
let all_0 = "0.0.0";
|
||||||
|
all_0.parse::<Version>().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ use color_eyre::eyre::Context;
|
|||||||
|
|
||||||
use crate::error::TopgradeError;
|
use crate::error::TopgradeError;
|
||||||
|
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
/// Like [`Output`], but UTF-8 decoded.
|
/// Like [`Output`], but UTF-8 decoded.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Utf8Output {
|
pub struct Utf8Output {
|
||||||
@@ -43,13 +45,13 @@ impl TryFrom<&Output> for Utf8Output {
|
|||||||
type Error = eyre::Error;
|
type Error = eyre::Error;
|
||||||
|
|
||||||
fn try_from(Output { status, stdout, stderr }: &Output) -> Result<Self, Self::Error> {
|
fn try_from(Output { status, stdout, stderr }: &Output) -> Result<Self, Self::Error> {
|
||||||
let stdout = String::from_utf8(stdout.to_vec()).map_err(|err| {
|
let stdout = String::from_utf8(stdout.clone()).map_err(|err| {
|
||||||
eyre!(
|
eyre!(
|
||||||
"Stdout contained invalid UTF-8: {}",
|
"Stdout contained invalid UTF-8: {}",
|
||||||
String::from_utf8_lossy(err.as_bytes())
|
String::from_utf8_lossy(err.as_bytes())
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let stderr = String::from_utf8(stderr.to_vec()).map_err(|err| {
|
let stderr = String::from_utf8(stderr.clone()).map_err(|err| {
|
||||||
eyre!(
|
eyre!(
|
||||||
"Stderr contained invalid UTF-8: {}",
|
"Stderr contained invalid UTF-8: {}",
|
||||||
String::from_utf8_lossy(err.as_bytes())
|
String::from_utf8_lossy(err.as_bytes())
|
||||||
@@ -113,6 +115,9 @@ pub trait CommandExt {
|
|||||||
///
|
///
|
||||||
/// Returns an `Err` if the command failed to execute, if `succeeded` returns an `Err`, or if
|
/// Returns an `Err` if the command failed to execute, if `succeeded` returns an `Err`, or if
|
||||||
/// the output contains invalid UTF-8.
|
/// the output contains invalid UTF-8.
|
||||||
|
// This function is currently unused, but is useful and makes sense with `output_checked_with`
|
||||||
|
// and `output_checked_utf8` existing.
|
||||||
|
#[allow(dead_code)]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn output_checked_with_utf8(
|
fn output_checked_with_utf8(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -147,6 +152,7 @@ pub trait CommandExt {
|
|||||||
/// Like [`Command::spawn`], but gives a nice error message if the command fails to
|
/// Like [`Command::spawn`], but gives a nice error message if the command fails to
|
||||||
/// execute.
|
/// execute.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
#[allow(dead_code)]
|
||||||
fn spawn_checked(&mut self) -> eyre::Result<Self::Child>;
|
fn spawn_checked(&mut self) -> eyre::Result<Self::Child>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +189,7 @@ impl CommandExt for Command {
|
|||||||
let err = TopgradeError::ProcessFailedWithOutput(program, output.status, stderr.into_owned());
|
let err = TopgradeError::ProcessFailedWithOutput(program, output.status, stderr.into_owned());
|
||||||
|
|
||||||
let ret = Err(err).with_context(|| message);
|
let ret = Err(err).with_context(|| message);
|
||||||
tracing::debug!("Command failed: {ret:?}");
|
debug!("Command failed: {ret:?}");
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +209,7 @@ impl CommandExt for Command {
|
|||||||
let (program, _) = get_program_and_args(self);
|
let (program, _) = get_program_and_args(self);
|
||||||
let err = TopgradeError::ProcessFailed(program, status);
|
let err = TopgradeError::ProcessFailed(program, status);
|
||||||
let ret = Err(err).with_context(|| format!("Command failed: `{command}`"));
|
let ret = Err(err).with_context(|| format!("Command failed: `{command}`"));
|
||||||
tracing::debug!("Command failed: {ret:?}");
|
debug!("Command failed: {ret:?}");
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,6 +245,6 @@ fn format_program_and_args(cmd: &Command) -> String {
|
|||||||
|
|
||||||
fn log(cmd: &Command) -> String {
|
fn log(cmd: &Command) -> String {
|
||||||
let command = format_program_and_args(cmd);
|
let command = format_program_and_args(cmd);
|
||||||
tracing::debug!("Executing command `{command}`");
|
debug!("Executing command `{command}`");
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|||||||
1480
src/config.rs
1480
src/config.rs
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,9 @@ pub fn interrupted() -> bool {
|
|||||||
/// Clears the interrupted flag
|
/// Clears the interrupted flag
|
||||||
pub fn unset_interrupted() {
|
pub fn unset_interrupted() {
|
||||||
debug_assert!(INTERRUPTED.load(Ordering::SeqCst));
|
debug_assert!(INTERRUPTED.load(Ordering::SeqCst));
|
||||||
INTERRUPTED.store(false, Ordering::SeqCst)
|
INTERRUPTED.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_interrupted() {
|
pub fn set_interrupted() {
|
||||||
INTERRUPTED.store(true, Ordering::SeqCst)
|
INTERRUPTED.store(true, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,17 @@
|
|||||||
//! SIGINT handling in Unix systems.
|
//! SIGINT handling in Unix systems.
|
||||||
use crate::ctrlc::interrupted::set_interrupted;
|
use crate::ctrlc::interrupted::set_interrupted;
|
||||||
use nix::sys::signal;
|
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal};
|
||||||
|
|
||||||
/// Handle SIGINT. Set the interruption flag.
|
/// Handle SIGINT. Set the interruption flag.
|
||||||
extern "C" fn handle_sigint(_: i32) {
|
extern "C" fn handle_sigint(_: i32) {
|
||||||
set_interrupted()
|
set_interrupted();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the necessary signal handlers.
|
/// Set the necessary signal handlers.
|
||||||
/// The function panics on failure.
|
/// The function panics on failure.
|
||||||
pub fn set_handler() {
|
pub fn set_handler() {
|
||||||
let sig_action = signal::SigAction::new(
|
let sig_action = SigAction::new(SigHandler::Handler(handle_sigint), SaFlags::empty(), SigSet::empty());
|
||||||
signal::SigHandler::Handler(handle_sigint),
|
|
||||||
signal::SaFlags::empty(),
|
|
||||||
signal::SigSet::empty(),
|
|
||||||
);
|
|
||||||
unsafe {
|
unsafe {
|
||||||
signal::sigaction(signal::SIGINT, &sig_action).unwrap();
|
sigaction(Signal::SIGINT, &sig_action).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
//! A stub for Ctrl + C handling.
|
//! A stub for Ctrl + C handling.
|
||||||
use crate::ctrlc::interrupted::set_interrupted;
|
use crate::ctrlc::interrupted::set_interrupted;
|
||||||
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
|
use tracing::error;
|
||||||
use winapi::um::consoleapi::SetConsoleCtrlHandler;
|
use windows::core::BOOL;
|
||||||
use winapi::um::wincon::CTRL_C_EVENT;
|
use windows::Win32::System::Console::{SetConsoleCtrlHandler, CTRL_C_EVENT};
|
||||||
|
|
||||||
extern "system" fn handler(ctrl_type: DWORD) -> BOOL {
|
extern "system" fn handler(ctrl_type: u32) -> BOOL {
|
||||||
match ctrl_type {
|
match ctrl_type {
|
||||||
CTRL_C_EVENT => {
|
CTRL_C_EVENT => {
|
||||||
set_interrupted();
|
set_interrupted();
|
||||||
TRUE
|
true.into()
|
||||||
}
|
}
|
||||||
_ => FALSE,
|
_ => false.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_handler() {
|
pub fn set_handler() {
|
||||||
if 0 == unsafe { SetConsoleCtrlHandler(Some(handler), TRUE) } {
|
if let Err(e) = unsafe { SetConsoleCtrlHandler(Some(handler), true) } {
|
||||||
tracing::error!("Cannot set a control C handler")
|
error!("Cannot set a control C handler: {e}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
109
src/error.rs
109
src/error.rs
@@ -1,41 +1,118 @@
|
|||||||
use std::process::ExitStatus;
|
use std::{fmt::Display, process::ExitStatus};
|
||||||
|
|
||||||
|
use rust_i18n::t;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::sudo::SudoKind;
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
pub enum TopgradeError {
|
pub enum TopgradeError {
|
||||||
#[error("`{0}` failed: {1}")]
|
|
||||||
ProcessFailed(String, ExitStatus),
|
ProcessFailed(String, ExitStatus),
|
||||||
|
|
||||||
#[error("`{0}` failed: {1}")]
|
|
||||||
ProcessFailedWithOutput(String, ExitStatus, String),
|
ProcessFailedWithOutput(String, ExitStatus, String),
|
||||||
|
|
||||||
#[error("Sudo is required for this step")]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
SudoRequired,
|
|
||||||
|
|
||||||
#[error("Unknown Linux Distribution")]
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
UnknownLinuxDistribution,
|
UnknownLinuxDistribution,
|
||||||
|
|
||||||
#[error("Failed getting the system package manager")]
|
#[cfg(target_os = "linux")]
|
||||||
|
EmptyOSReleaseFile,
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
FailedGettingPackageManager,
|
FailedGettingPackageManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for TopgradeError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
TopgradeError::ProcessFailed(process, exit_status) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
t!(
|
||||||
|
"`{process}` failed: {exit_status}",
|
||||||
|
process = process,
|
||||||
|
exit_status = exit_status
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TopgradeError::ProcessFailedWithOutput(process, exit_status, output) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
t!(
|
||||||
|
"`{process}` failed: {exit_status} with {output}",
|
||||||
|
process = process,
|
||||||
|
exit_status = exit_status,
|
||||||
|
output = output
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
TopgradeError::UnknownLinuxDistribution => write!(f, "{}", t!("Unknown Linux Distribution")),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
TopgradeError::EmptyOSReleaseFile => {
|
||||||
|
write!(f, "{}", t!("File \"/etc/os-release\" does not exist or is empty"))
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
TopgradeError::FailedGettingPackageManager => {
|
||||||
|
write!(f, "{}", t!("Failed getting the system package manager"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("A step failed")]
|
|
||||||
pub struct StepFailed;
|
pub struct StepFailed;
|
||||||
|
|
||||||
|
impl Display for StepFailed {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", t!("A step failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub struct UnsupportedSudo<'a> {
|
||||||
|
pub sudo_kind: SudoKind,
|
||||||
|
pub option: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for UnsupportedSudo<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
t!(
|
||||||
|
"{sudo_kind} does not support the {option} option",
|
||||||
|
sudo_kind = self.sudo_kind,
|
||||||
|
option = self.option
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub struct MissingSudo();
|
||||||
|
|
||||||
|
impl Display for MissingSudo {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", t!("Could not find sudo"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("Dry running")]
|
|
||||||
pub struct DryRun();
|
pub struct DryRun();
|
||||||
|
|
||||||
|
impl Display for DryRun {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", t!("Dry running"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("{0}")]
|
|
||||||
pub struct SkipStep(pub String);
|
pub struct SkipStep(pub String);
|
||||||
|
|
||||||
#[cfg(all(windows, feature = "self-update"))]
|
impl Display for SkipStep {
|
||||||
#[derive(Error, Debug)]
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
#[error("Topgrade Upgraded")]
|
write!(f, "{}", self.0)
|
||||||
pub struct Upgraded(pub ExitStatus);
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,67 +1,115 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use crate::executor::RunType;
|
use std::env::var;
|
||||||
use crate::git::Git;
|
use std::ffi::OsStr;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::sync::{LazyLock, Mutex};
|
||||||
|
|
||||||
|
use clap::ValueEnum;
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use strum::EnumString;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::error::MissingSudo;
|
||||||
|
use crate::executor::{DryCommand, Executor};
|
||||||
|
use crate::powershell::Powershell;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use crate::steps::linux::Distribution;
|
||||||
use crate::sudo::Sudo;
|
use crate::sudo::Sudo;
|
||||||
use crate::utils::require_option;
|
use crate::utils::require_option;
|
||||||
use crate::{config::Config, executor::Executor};
|
|
||||||
use color_eyre::eyre::Result;
|
/// An enum telling whether Topgrade should perform dry runs or actually perform the steps.
|
||||||
use directories::BaseDirs;
|
#[derive(Clone, Copy, Debug, Deserialize, Default, EnumString, ValueEnum)]
|
||||||
use std::path::Path;
|
pub enum RunType {
|
||||||
use std::sync::Mutex;
|
/// Executing commands will just print the command with its argument.
|
||||||
|
Dry,
|
||||||
|
|
||||||
|
/// Executing commands will perform actual execution.
|
||||||
|
#[default]
|
||||||
|
Wet,
|
||||||
|
|
||||||
|
/// Executing commands will print the command and perform actual execution.
|
||||||
|
Damp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunType {
|
||||||
|
/// Tells whether we're performing a dry run.
|
||||||
|
pub fn dry(self) -> bool {
|
||||||
|
match self {
|
||||||
|
RunType::Dry => true,
|
||||||
|
RunType::Wet => false,
|
||||||
|
RunType::Damp => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ExecutionContext<'a> {
|
pub struct ExecutionContext<'a> {
|
||||||
run_type: RunType,
|
run_type: RunType,
|
||||||
sudo: Option<Sudo>,
|
sudo: Option<Sudo>,
|
||||||
git: &'a Git,
|
|
||||||
config: &'a Config,
|
config: &'a Config,
|
||||||
base_dirs: &'a BaseDirs,
|
|
||||||
/// Name of a tmux session to execute commands in, if any.
|
/// Name of a tmux session to execute commands in, if any.
|
||||||
/// This is used in `./steps/remote/ssh.rs`, where we want to run `topgrade` in a new
|
/// This is used in `./steps/remote/ssh.rs`, where we want to run `topgrade` in a new
|
||||||
/// tmux window for each remote.
|
/// tmux window for each remote.
|
||||||
tmux_session: Mutex<Option<String>>,
|
tmux_session: Mutex<Option<String>>,
|
||||||
|
/// True if topgrade is running under ssh.
|
||||||
|
under_ssh: bool,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
distribution: &'a Result<Distribution>,
|
||||||
|
powershell: LazyLock<Option<Powershell>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExecutionContext<'a> {
|
impl<'a> ExecutionContext<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
run_type: RunType,
|
run_type: RunType,
|
||||||
sudo: Option<Sudo>,
|
sudo: Option<Sudo>,
|
||||||
git: &'a Git,
|
|
||||||
config: &'a Config,
|
config: &'a Config,
|
||||||
base_dirs: &'a BaseDirs,
|
#[cfg(target_os = "linux")] distribution: &'a Result<Distribution>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let under_ssh = var("SSH_CLIENT").is_ok() || var("SSH_TTY").is_ok();
|
||||||
Self {
|
Self {
|
||||||
run_type,
|
run_type,
|
||||||
sudo,
|
sudo,
|
||||||
git,
|
|
||||||
config,
|
config,
|
||||||
base_dirs,
|
|
||||||
tmux_session: Mutex::new(None),
|
tmux_session: Mutex::new(None),
|
||||||
|
under_ssh,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
distribution,
|
||||||
|
powershell: LazyLock::new(Powershell::new),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_elevated(&self, command: &Path, interactive: bool) -> Result<Executor> {
|
/// Create an instance of `Executor` that should run `program`.
|
||||||
let sudo = require_option(self.sudo.clone(), "Sudo is required for this operation".into())?;
|
pub fn execute<S: AsRef<OsStr>>(&self, program: S) -> Executor {
|
||||||
Ok(sudo.execute_elevated(self, command, interactive))
|
match self.run_type {
|
||||||
|
RunType::Dry => Executor::Dry(DryCommand::new(program)),
|
||||||
|
RunType::Wet => Executor::Wet(Command::new(program)),
|
||||||
|
RunType::Damp => Executor::Damp(Command::new(program)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_type(&self) -> RunType {
|
pub fn run_type(&self) -> RunType {
|
||||||
self.run_type
|
self.run_type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn git(&self) -> &Git {
|
|
||||||
self.git
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sudo(&self) -> &Option<Sudo> {
|
pub fn sudo(&self) -> &Option<Sudo> {
|
||||||
&self.sudo
|
&self.sudo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn require_sudo(&self) -> Result<&Sudo> {
|
||||||
|
if let Some(value) = self.sudo() {
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Err(MissingSudo().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn config(&self) -> &Config {
|
pub fn config(&self) -> &Config {
|
||||||
self.config
|
self.config
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base_dirs(&self) -> &BaseDirs {
|
pub fn under_ssh(&self) -> bool {
|
||||||
self.base_dirs
|
self.under_ssh
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_tmux_session(&self, session_name: String) {
|
pub fn set_tmux_session(&self, session_name: String) {
|
||||||
@@ -71,4 +119,17 @@ impl<'a> ExecutionContext<'a> {
|
|||||||
pub fn get_tmux_session(&self) -> Option<String> {
|
pub fn get_tmux_session(&self) -> Option<String> {
|
||||||
self.tmux_session.lock().unwrap().clone()
|
self.tmux_session.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn distribution(&self) -> &Result<Distribution> {
|
||||||
|
self.distribution
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn powershell(&self) -> &Option<Powershell> {
|
||||||
|
&self.powershell
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn require_powershell(&self) -> Result<&Powershell> {
|
||||||
|
require_option(self.powershell.as_ref(), t!("Powershell is not installed").to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
200
src/executor.rs
200
src/executor.rs
@@ -1,60 +1,23 @@
|
|||||||
//! Utilities for command execution
|
//! Utilities for command execution
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::iter;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::{Child, Command, ExitStatus, Output};
|
use std::process::{Child, Command, ExitStatus, Output};
|
||||||
|
|
||||||
use color_eyre::eyre;
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use tracing::debug;
|
use rust_i18n::t;
|
||||||
|
use tracing::{debug, enabled, Level};
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::error::DryRun;
|
use crate::error::DryRun;
|
||||||
|
|
||||||
/// An enum telling whether Topgrade should perform dry runs or actually perform the steps.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum RunType {
|
|
||||||
/// Executing commands will just print the command with its argument.
|
|
||||||
Dry,
|
|
||||||
|
|
||||||
/// Executing commands will perform actual execution.
|
|
||||||
Wet,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RunType {
|
|
||||||
/// Create a new instance from a boolean telling whether to dry run.
|
|
||||||
pub fn new(dry_run: bool) -> Self {
|
|
||||||
if dry_run {
|
|
||||||
RunType::Dry
|
|
||||||
} else {
|
|
||||||
RunType::Wet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an instance of `Executor` that should run `program`.
|
|
||||||
pub fn execute<S: AsRef<OsStr>>(self, program: S) -> Executor {
|
|
||||||
match self {
|
|
||||||
RunType::Dry => Executor::Dry(DryCommand {
|
|
||||||
program: program.as_ref().into(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
RunType::Wet => Executor::Wet(Command::new(program)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tells whether we're performing a dry run.
|
|
||||||
pub fn dry(self) -> bool {
|
|
||||||
match self {
|
|
||||||
RunType::Dry => true,
|
|
||||||
RunType::Wet => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An enum providing a similar interface to `std::process::Command`.
|
/// An enum providing a similar interface to `std::process::Command`.
|
||||||
/// If the enum is set to `Wet`, execution will be performed with `std::process::Command`.
|
/// If the enum is set to `Wet`, execution will be performed with `std::process::Command`.
|
||||||
/// If the enum is set to `Dry`, execution will just print the command with its arguments.
|
/// If the enum is set to `Dry`, execution will just print the command with its arguments.
|
||||||
pub enum Executor {
|
pub enum Executor {
|
||||||
Wet(Command),
|
Wet(Command),
|
||||||
|
Damp(Command),
|
||||||
Dry(DryCommand),
|
Dry(DryCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +27,7 @@ impl Executor {
|
|||||||
/// Will give weird results for non-UTF-8 programs; see `to_string_lossy()`.
|
/// Will give weird results for non-UTF-8 programs; see `to_string_lossy()`.
|
||||||
pub fn get_program(&self) -> String {
|
pub fn get_program(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => c.get_program().to_string_lossy().into_owned(),
|
Executor::Wet(c) | Executor::Damp(c) => c.get_program().to_string_lossy().into_owned(),
|
||||||
Executor::Dry(c) => c.program.to_string_lossy().into_owned(),
|
Executor::Dry(c) => c.program.to_string_lossy().into_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +35,7 @@ impl Executor {
|
|||||||
/// See `std::process::Command::arg`
|
/// See `std::process::Command::arg`
|
||||||
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Executor {
|
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Executor {
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => {
|
Executor::Wet(c) | Executor::Damp(c) => {
|
||||||
c.arg(arg);
|
c.arg(arg);
|
||||||
}
|
}
|
||||||
Executor::Dry(c) => {
|
Executor::Dry(c) => {
|
||||||
@@ -90,7 +53,7 @@ impl Executor {
|
|||||||
S: AsRef<OsStr>,
|
S: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => {
|
Executor::Wet(c) | Executor::Damp(c) => {
|
||||||
c.args(args);
|
c.args(args);
|
||||||
}
|
}
|
||||||
Executor::Dry(c) => {
|
Executor::Dry(c) => {
|
||||||
@@ -105,7 +68,7 @@ impl Executor {
|
|||||||
/// See `std::process::Command::current_dir`
|
/// See `std::process::Command::current_dir`
|
||||||
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Executor {
|
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Executor {
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => {
|
Executor::Wet(c) | Executor::Damp(c) => {
|
||||||
c.current_dir(dir);
|
c.current_dir(dir);
|
||||||
}
|
}
|
||||||
Executor::Dry(c) => c.directory = Some(dir.as_ref().into()),
|
Executor::Dry(c) => c.directory = Some(dir.as_ref().into()),
|
||||||
@@ -121,7 +84,7 @@ impl Executor {
|
|||||||
K: AsRef<OsStr>,
|
K: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => {
|
Executor::Wet(c) | Executor::Damp(c) => {
|
||||||
c.env_remove(key);
|
c.env_remove(key);
|
||||||
}
|
}
|
||||||
Executor::Dry(_) => (),
|
Executor::Dry(_) => (),
|
||||||
@@ -138,7 +101,7 @@ impl Executor {
|
|||||||
V: AsRef<OsStr>,
|
V: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => {
|
Executor::Wet(c) | Executor::Damp(c) => {
|
||||||
c.env(key, val);
|
c.env(key, val);
|
||||||
}
|
}
|
||||||
Executor::Dry(_) => (),
|
Executor::Dry(_) => (),
|
||||||
@@ -149,15 +112,16 @@ impl Executor {
|
|||||||
|
|
||||||
/// See `std::process::Command::spawn`
|
/// See `std::process::Command::spawn`
|
||||||
pub fn spawn(&mut self) -> Result<ExecutorChild> {
|
pub fn spawn(&mut self) -> Result<ExecutorChild> {
|
||||||
|
self.log_command();
|
||||||
let result = match self {
|
let result = match self {
|
||||||
Executor::Wet(c) => {
|
Executor::Wet(c) | Executor::Damp(c) => {
|
||||||
debug!("Running {:?}", c);
|
debug!("Running {:?}", c);
|
||||||
c.spawn_checked().map(ExecutorChild::Wet)?
|
// We should use `spawn()` here rather than `spawn_checked()` since
|
||||||
}
|
// their semantics and behaviors are different.
|
||||||
Executor::Dry(c) => {
|
#[allow(clippy::disallowed_methods)]
|
||||||
c.dry_run();
|
c.spawn().map(ExecutorChild::Wet)?
|
||||||
ExecutorChild::Dry
|
|
||||||
}
|
}
|
||||||
|
Executor::Dry(_) => ExecutorChild::Dry,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@@ -165,12 +129,15 @@ impl Executor {
|
|||||||
|
|
||||||
/// See `std::process::Command::output`
|
/// See `std::process::Command::output`
|
||||||
pub fn output(&mut self) -> Result<ExecutorOutput> {
|
pub fn output(&mut self) -> Result<ExecutorOutput> {
|
||||||
|
self.log_command();
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => Ok(ExecutorOutput::Wet(c.output_checked()?)),
|
Executor::Wet(c) | Executor::Damp(c) => {
|
||||||
Executor::Dry(c) => {
|
// We should use `output()` here rather than `output_checked()` since
|
||||||
c.dry_run();
|
// their semantics and behaviors are different.
|
||||||
Ok(ExecutorOutput::Dry)
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
Ok(ExecutorOutput::Wet(c.output()?))
|
||||||
}
|
}
|
||||||
|
Executor::Dry(_) => Ok(ExecutorOutput::Dry),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,19 +145,39 @@ impl Executor {
|
|||||||
/// that can indicate success of a script
|
/// that can indicate success of a script
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn status_checked_with_codes(&mut self, codes: &[i32]) -> Result<()> {
|
pub fn status_checked_with_codes(&mut self, codes: &[i32]) -> Result<()> {
|
||||||
|
self.log_command();
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => c.status_checked_with(|status| {
|
Executor::Wet(c) | Executor::Damp(c) => c.status_checked_with(|status| {
|
||||||
if status.success() || status.code().as_ref().map(|c| codes.contains(c)).unwrap_or(false) {
|
if status.success() || status.code().as_ref().is_some_and(|c| codes.contains(c)) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Executor::Dry(c) => {
|
Executor::Dry(_) => Ok(()),
|
||||||
c.dry_run();
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn log_command(&self) {
|
||||||
|
match self {
|
||||||
|
Executor::Wet(_) => (),
|
||||||
|
Executor::Damp(c) => {
|
||||||
|
log_command(
|
||||||
|
"Executing: {program_name} {arguments}",
|
||||||
|
c.get_program(),
|
||||||
|
c.get_args(),
|
||||||
|
c.get_envs(),
|
||||||
|
c.get_current_dir(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Executor::Dry(c) => log_command(
|
||||||
|
"Dry running: {program_name} {arguments}",
|
||||||
|
&c.program,
|
||||||
|
&c.args,
|
||||||
|
iter::empty(),
|
||||||
|
c.directory.as_ref(),
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,8 +186,7 @@ pub enum ExecutorOutput {
|
|||||||
Dry,
|
Dry,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct represending a command. Trying to execute it will just print its arguments.
|
/// A struct representing a command. Trying to execute it will just print its arguments.
|
||||||
#[derive(Default)]
|
|
||||||
pub struct DryCommand {
|
pub struct DryCommand {
|
||||||
program: OsString,
|
program: OsString,
|
||||||
args: Vec<OsString>,
|
args: Vec<OsString>,
|
||||||
@@ -208,26 +194,19 @@ pub struct DryCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DryCommand {
|
impl DryCommand {
|
||||||
fn dry_run(&self) {
|
pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
|
||||||
print!(
|
Self {
|
||||||
"Dry running: {} {}",
|
program: program.as_ref().to_os_string(),
|
||||||
self.program.to_string_lossy(),
|
args: Vec::new(),
|
||||||
shell_words::join(
|
directory: None,
|
||||||
self.args
|
}
|
||||||
.iter()
|
|
||||||
.map(|a| String::from(a.to_string_lossy()))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
match &self.directory {
|
|
||||||
Some(dir) => println!(" in {}", dir.to_string_lossy()),
|
|
||||||
None => println!(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Result of spawn. Contains an actual `std::process::Child` if executed by a wet command.
|
/// The Result of spawn. Contains an actual `std::process::Child` if executed by a wet command.
|
||||||
pub enum ExecutorChild {
|
pub enum ExecutorChild {
|
||||||
|
// Both RunType::Wet and RunType::Damp use this variant
|
||||||
|
#[allow(unused)] // this type has not been used
|
||||||
Wet(Child),
|
Wet(Child),
|
||||||
Dry,
|
Dry,
|
||||||
}
|
}
|
||||||
@@ -238,27 +217,62 @@ impl CommandExt for Executor {
|
|||||||
// TODO: It might be nice to make `output_checked_with` return something that has a
|
// TODO: It might be nice to make `output_checked_with` return something that has a
|
||||||
// variant for wet/dry runs.
|
// variant for wet/dry runs.
|
||||||
|
|
||||||
fn output_checked_with(&mut self, succeeded: impl Fn(&Output) -> Result<(), ()>) -> eyre::Result<Output> {
|
fn output_checked_with(&mut self, succeeded: impl Fn(&Output) -> Result<(), ()>) -> Result<Output> {
|
||||||
|
self.log_command();
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => c.output_checked_with(succeeded),
|
Executor::Wet(c) | Executor::Damp(c) => c.output_checked_with(succeeded),
|
||||||
Executor::Dry(c) => {
|
Executor::Dry(_) => Err(DryRun().into()),
|
||||||
c.dry_run();
|
|
||||||
Err(DryRun().into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn status_checked_with(&mut self, succeeded: impl Fn(ExitStatus) -> Result<(), ()>) -> eyre::Result<()> {
|
fn status_checked_with(&mut self, succeeded: impl Fn(ExitStatus) -> Result<(), ()>) -> Result<()> {
|
||||||
|
self.log_command();
|
||||||
match self {
|
match self {
|
||||||
Executor::Wet(c) => c.status_checked_with(succeeded),
|
Executor::Wet(c) | Executor::Damp(c) => c.status_checked_with(succeeded),
|
||||||
Executor::Dry(c) => {
|
Executor::Dry(_) => Ok(()),
|
||||||
c.dry_run();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_checked(&mut self) -> eyre::Result<Self::Child> {
|
fn spawn_checked(&mut self) -> Result<Self::Child> {
|
||||||
self.spawn()
|
self.spawn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn log_command<
|
||||||
|
'a,
|
||||||
|
I: ExactSizeIterator<Item = (&'a (impl Debug + 'a + ?Sized), Option<&'a (impl Debug + 'a + ?Sized)>)>,
|
||||||
|
>(
|
||||||
|
prefix: &str,
|
||||||
|
exec: &OsStr,
|
||||||
|
args: impl IntoIterator<Item = &'a (impl AsRef<OsStr> + ?Sized + 'a)>,
|
||||||
|
env: impl IntoIterator<Item = (&'a OsStr, Option<&'a OsStr>), IntoIter = I>,
|
||||||
|
dir: Option<&'a (impl AsRef<Path> + ?Sized)>,
|
||||||
|
) {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
t!(
|
||||||
|
prefix,
|
||||||
|
program_name = exec.to_string_lossy(),
|
||||||
|
arguments = shell_words::join(args.into_iter().map(|s| s.as_ref().to_string_lossy()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let env_iter = env.into_iter();
|
||||||
|
if env_iter.len() != 0 && enabled!(Level::DEBUG) {
|
||||||
|
println!(
|
||||||
|
" {}",
|
||||||
|
t!(
|
||||||
|
"with env: {env}",
|
||||||
|
env = env_iter
|
||||||
|
.filter(|(_, val)| val.is_some())
|
||||||
|
.map(|(key, val)| format!("{:?}={:?}", key, val.unwrap()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(d) = dir {
|
||||||
|
println!(" {}", t!("in {directory}", directory = d.as_ref().display()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
//pub mod steps;
|
|
||||||
//pub mod utils;
|
|
||||||
624
src/main.rs
624
src/main.rs
@@ -2,135 +2,192 @@
|
|||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::breaking_changes::{first_run_of_major_release, print_breaking_changes, should_skip, write_keep_file};
|
||||||
use clap::CommandFactory;
|
use clap::CommandFactory;
|
||||||
use clap::{crate_version, Parser};
|
use clap::{crate_version, Parser};
|
||||||
use color_eyre::eyre::Context;
|
use color_eyre::eyre::Context;
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::Result;
|
||||||
use console::Key;
|
use console::Key;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use etcetera::base_strategy::Windows;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use etcetera::base_strategy::Xdg;
|
||||||
|
use rust_i18n::{i18n, t};
|
||||||
|
use std::sync::LazyLock;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use self::config::{CommandLineArgs, Config, Step};
|
use self::config::{CommandLineArgs, Config};
|
||||||
use self::error::StepFailed;
|
use self::error::StepFailed;
|
||||||
#[cfg(all(windows, feature = "self-update"))]
|
use self::runner::StepResult;
|
||||||
use self::error::Upgraded;
|
#[allow(clippy::wildcard_imports)]
|
||||||
use self::steps::{remote::*, *};
|
use self::steps::{remote::*, *};
|
||||||
|
use self::sudo::{Sudo, SudoCreateError, SudoKind};
|
||||||
|
#[allow(clippy::wildcard_imports)]
|
||||||
use self::terminal::*;
|
use self::terminal::*;
|
||||||
|
use self::utils::{install_color_eyre, install_tracing, is_elevated, update_tracing};
|
||||||
|
|
||||||
|
mod breaking_changes;
|
||||||
mod command;
|
mod command;
|
||||||
mod config;
|
mod config;
|
||||||
mod ctrlc;
|
mod ctrlc;
|
||||||
mod error;
|
mod error;
|
||||||
mod execution_context;
|
mod execution_context;
|
||||||
mod executor;
|
mod executor;
|
||||||
mod report;
|
|
||||||
mod runner;
|
mod runner;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
mod self_renamer;
|
mod self_renamer;
|
||||||
#[cfg(feature = "self-update")]
|
#[cfg(feature = "self-update")]
|
||||||
mod self_update;
|
mod self_update;
|
||||||
|
mod step;
|
||||||
mod steps;
|
mod steps;
|
||||||
mod sudo;
|
mod sudo;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
pub(crate) static HOME_DIR: LazyLock<PathBuf> = LazyLock::new(|| home::home_dir().expect("No home directory"));
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub(crate) static XDG_DIRS: LazyLock<Xdg> = LazyLock::new(|| Xdg::new().expect("No home directory"));
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub(crate) static WINDOWS_DIRS: LazyLock<Windows> = LazyLock::new(|| Windows::new().expect("No home directory"));
|
||||||
|
|
||||||
|
// Init and load the i18n files
|
||||||
|
i18n!("locales", fallback = "en");
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn run() -> Result<()> {
|
fn run() -> Result<()> {
|
||||||
color_eyre::install()?;
|
install_color_eyre()?;
|
||||||
ctrlc::set_handler();
|
ctrlc::set_handler();
|
||||||
|
|
||||||
let base_dirs = directories::BaseDirs::new().ok_or_else(|| eyre!("No base directories"))?;
|
|
||||||
|
|
||||||
let opt = CommandLineArgs::parse();
|
let opt = CommandLineArgs::parse();
|
||||||
|
// Set up the logger with the filter directives from:
|
||||||
|
// 1. CLI option `--log-filter`
|
||||||
|
// 2. `debug` if the `--verbose` option is present
|
||||||
|
// We do this because we need our logger to work while loading the
|
||||||
|
// configuration file.
|
||||||
|
//
|
||||||
|
// When the configuration file is loaded, update the logger with the full
|
||||||
|
// filter directives.
|
||||||
|
//
|
||||||
|
// For more info, see the comments in `CommandLineArgs::tracing_filter_directives()`
|
||||||
|
// and `Config::tracing_filter_directives()`.
|
||||||
|
let reload_handle = install_tracing(&opt.tracing_filter_directives())?;
|
||||||
|
|
||||||
|
// Get current system locale and set it as the default locale
|
||||||
|
let system_locale = sys_locale::get_locale().unwrap_or("en".to_string());
|
||||||
|
rust_i18n::set_locale(&system_locale);
|
||||||
|
debug!("Current system locale is {system_locale}");
|
||||||
|
|
||||||
if let Some(shell) = opt.gen_completion {
|
if let Some(shell) = opt.gen_completion {
|
||||||
let cmd = &mut CommandLineArgs::command();
|
let cmd = &mut CommandLineArgs::command();
|
||||||
clap_complete::generate(shell, cmd, clap::crate_name!(), &mut std::io::stdout());
|
clap_complete::generate(shell, cmd, clap::crate_name!(), &mut io::stdout());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt.gen_manpage {
|
if opt.gen_manpage {
|
||||||
let man = clap_mangen::Man::new(CommandLineArgs::command());
|
let man = clap_mangen::Man::new(CommandLineArgs::command());
|
||||||
man.render(&mut std::io::stdout())?;
|
man.render(&mut io::stdout())?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
install_tracing(&opt.tracing_filter_directives())?;
|
|
||||||
|
|
||||||
for env in opt.env_variables() {
|
for env in opt.env_variables() {
|
||||||
let mut splitted = env.split('=');
|
let mut parts = env.split('=');
|
||||||
let var = splitted.next().unwrap();
|
let var = parts.next().unwrap();
|
||||||
let value = splitted.next().unwrap();
|
let value = parts.next().unwrap();
|
||||||
env::set_var(var, value);
|
env::set_var(var, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt.edit_config() {
|
if opt.edit_config() {
|
||||||
Config::edit(&base_dirs)?;
|
Config::edit()?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
if opt.show_config_reference() {
|
if opt.show_config_reference() {
|
||||||
print!("{}", crate::config::EXAMPLE_CONFIG);
|
print!("{}", config::EXAMPLE_CONFIG);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = Config::load(&base_dirs, opt)?;
|
let config = Config::load(opt)?;
|
||||||
terminal::set_title(config.set_title());
|
// Update the logger with the full filter directives.
|
||||||
terminal::display_time(config.display_time());
|
update_tracing(&reload_handle, &config.tracing_filter_directives())?;
|
||||||
terminal::set_desktop_notifications(config.notify_each_step());
|
set_title(config.set_title());
|
||||||
|
display_time(config.display_time());
|
||||||
|
set_desktop_notifications(config.notify_each_step());
|
||||||
|
|
||||||
debug!("Version: {}", crate_version!());
|
debug!("Version: {}", crate_version!());
|
||||||
debug!("OS: {}", env!("TARGET"));
|
debug!("OS: {}", env!("TARGET"));
|
||||||
debug!("{:?}", std::env::args());
|
debug!("{:?}", env::args());
|
||||||
debug!("Binary path: {:?}", std::env::current_exe());
|
debug!("Binary path: {:?}", env::current_exe());
|
||||||
debug!("Self Update: {:?}", cfg!(feature = "self-update"));
|
debug!("self-update Feature Enabled: {:?}", cfg!(feature = "self-update"));
|
||||||
|
debug!("Configuration: {:?}", config);
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
if config.display_preamble() && !config.skip_notify() {
|
|
||||||
print_warning("Due to a design issue with notify-send it could be that topgrade hangs when it's finished.
|
|
||||||
If this is the case on your system add the --skip-notify flag to the topgrade command or set skip_notify = true in the config file.
|
|
||||||
If you don't want this message to appear any longer set display_preamble = false in the config file.
|
|
||||||
For more information about this issue see https://askubuntu.com/questions/110969/notify-send-ignores-timeout and https://github.com/topgrade-rs/topgrade/issues/288.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
|
if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
tmux::run_in_tmux(config.tmux_arguments()?)?;
|
tmux::run_in_tmux(config.tmux_config()?)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let git = git::Git::new();
|
let elevated = is_elevated();
|
||||||
let mut git_repos = git::Repositories::new(&git);
|
|
||||||
|
|
||||||
let sudo = config.sudo_command().map_or_else(sudo::Sudo::detect, sudo::Sudo::new);
|
#[cfg(unix)]
|
||||||
let run_type = executor::RunType::new(config.dry_run());
|
if !config.allow_root() && elevated {
|
||||||
|
print_warning(t!(
|
||||||
|
"Topgrade should not be run as root, it will run commands with sudo or equivalent where needed."
|
||||||
|
));
|
||||||
|
if !prompt_yesno(&t!("Continue?"))? {
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let ctx = execution_context::ExecutionContext::new(run_type, sudo, &git, &config, &base_dirs);
|
let sudo = match config.sudo_command() {
|
||||||
|
Some(kind) => Sudo::new(kind),
|
||||||
|
None if elevated => Sudo::new(SudoKind::Null),
|
||||||
|
None => Sudo::detect(),
|
||||||
|
};
|
||||||
|
debug!("Sudo: {:?}", sudo);
|
||||||
|
|
||||||
|
let (sudo, sudo_err) = match sudo {
|
||||||
|
Ok(sudo) => (Some(sudo), None),
|
||||||
|
Err(e) => (None, Some(e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
let distribution = linux::Distribution::detect();
|
||||||
|
|
||||||
|
let run_type = config.run_type();
|
||||||
|
let ctx = execution_context::ExecutionContext::new(
|
||||||
|
run_type,
|
||||||
|
sudo,
|
||||||
|
&config,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
&distribution,
|
||||||
|
);
|
||||||
let mut runner = runner::Runner::new(&ctx);
|
let mut runner = runner::Runner::new(&ctx);
|
||||||
|
|
||||||
#[cfg(feature = "self-update")]
|
// If
|
||||||
{
|
//
|
||||||
if !run_type.dry() && env::var("TOPGRADE_NO_SELF_UPGRADE").is_err() {
|
// 1. the breaking changes notification shouldn't be skipped
|
||||||
let result = self_update::self_update();
|
// 2. this is the first execution of a major release
|
||||||
|
//
|
||||||
|
// inform user of breaking changes
|
||||||
|
if !should_skip() && first_run_of_major_release()? {
|
||||||
|
print_breaking_changes();
|
||||||
|
|
||||||
if let Err(e) = &result {
|
if prompt_yesno(&t!("Continue?"))? {
|
||||||
#[cfg(windows)]
|
write_keep_file()?;
|
||||||
{
|
} else {
|
||||||
if e.downcast_ref::<Upgraded>().is_some() {
|
exit(1);
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print_warning(format!("Self update error: {e}"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
step::Step::SelfUpdate.run(&mut runner, &ctx)?;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _self_rename = if config.self_rename() {
|
let _self_rename = if config.self_rename() {
|
||||||
Some(crate::self_renamer::SelfRenamer::create()?)
|
Some(crate::self_renamer::SelfRenamer::create()?)
|
||||||
@@ -138,385 +195,113 @@ For more information about this issue see https://askubuntu.com/questions/110969
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(commands) = config.pre_commands() {
|
|
||||||
for (name, command) in commands {
|
|
||||||
generic::run_custom_command(name, command, &ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.pre_sudo() {
|
if config.pre_sudo() {
|
||||||
if let Some(sudo) = ctx.sudo() {
|
if let Some(sudo) = ctx.sudo() {
|
||||||
sudo.elevate(&ctx)?;
|
sudo.elevate(&ctx)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let powershell = powershell::Powershell::new();
|
if let Some(commands) = config.pre_commands() {
|
||||||
let should_run_powershell = powershell.profile().is_some() && config.should_run(Step::Powershell);
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
runner.execute(Step::Wsl, "WSL", || windows::run_wsl_topgrade(&ctx))?;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
runner.execute(Step::WslUpdate, "WSL", || windows::update_wsl(&ctx))?;
|
|
||||||
|
|
||||||
if let Some(topgrades) = config.remote_topgrades() {
|
|
||||||
for remote_topgrade in topgrades.iter().filter(|t| config.should_execute_remote(t)) {
|
|
||||||
runner.execute(Step::Remotes, format!("Remote ({remote_topgrade})"), || {
|
|
||||||
remote::ssh::ssh_step(&ctx, remote_topgrade)
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
let distribution = linux::Distribution::detect();
|
|
||||||
|
|
||||||
#[cfg(target_os = r#"linux"#)]
|
|
||||||
{
|
|
||||||
match &distribution {
|
|
||||||
Ok(distribution) => {
|
|
||||||
runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error detecting current distribution: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runner.execute(Step::ConfigUpdate, "config-update", || linux::run_config_update(&ctx))?;
|
|
||||||
|
|
||||||
runner.execute(Step::BrewFormula, "Brew", || {
|
|
||||||
unix::run_brew_formula(&ctx, unix::BrewVariant::Path)
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
runner.execute(Step::Chocolatey, "Chocolatey", || windows::run_chocolatey(&ctx))?;
|
|
||||||
runner.execute(Step::Scoop, "Scoop", || windows::run_scoop(config.cleanup(), run_type))?;
|
|
||||||
runner.execute(Step::Winget, "Winget", || windows::run_winget(&ctx))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
runner.execute(Step::BrewFormula, "Brew (ARM)", || {
|
|
||||||
unix::run_brew_formula(&ctx, unix::BrewVariant::MacArm)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::BrewFormula, "Brew (Intel)", || {
|
|
||||||
unix::run_brew_formula(&ctx, unix::BrewVariant::MacIntel)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::BrewFormula, "Brew", || {
|
|
||||||
unix::run_brew_formula(&ctx, unix::BrewVariant::Path)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::BrewCask, "Brew Cask (ARM)", || {
|
|
||||||
unix::run_brew_cask(&ctx, unix::BrewVariant::MacArm)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::BrewCask, "Brew Cask (Intel)", || {
|
|
||||||
unix::run_brew_cask(&ctx, unix::BrewVariant::MacIntel)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::BrewCask, "Brew Cask", || {
|
|
||||||
unix::run_brew_cask(&ctx, unix::BrewVariant::Path)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::Macports, "MacPorts", || macos::run_macports(&ctx))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
runner.execute(Step::Yadm, "yadm", || unix::run_yadm(&ctx))?;
|
|
||||||
runner.execute(Step::Nix, "nix", || unix::run_nix(&ctx))?;
|
|
||||||
runner.execute(Step::Guix, "guix", || unix::run_guix(&ctx))?;
|
|
||||||
|
|
||||||
runner.execute(Step::HomeManager, "home-manager", || unix::run_home_manager(run_type))?;
|
|
||||||
runner.execute(Step::Asdf, "asdf", || unix::run_asdf(run_type))?;
|
|
||||||
runner.execute(Step::Pkgin, "pkgin", || unix::run_pkgin(&ctx))?;
|
|
||||||
runner.execute(Step::Bun, "bun", || unix::run_bun(&ctx))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "dragonfly")]
|
|
||||||
runner.execute(Step::Pkg, "DragonFly BSD Packages", || {
|
|
||||||
dragonfly::upgrade_packages(ctx.sudo().as_ref(), run_type)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
#[cfg(target_os = "freebsd")]
|
|
||||||
runner.execute(Step::Pkg, "FreeBSD Packages", || {
|
|
||||||
freebsd::upgrade_packages(&ctx, ctx.sudo().as_ref(), run_type)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
#[cfg(target_os = "openbsd")]
|
|
||||||
runner.execute(Step::Pkg, "OpenBSD Packages", || {
|
|
||||||
openbsd::upgrade_packages(ctx.sudo().as_ref(), run_type)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
runner.execute(Step::Pkg, "Termux Packages", || android::upgrade_packages(&ctx))?;
|
|
||||||
|
|
||||||
let emacs = emacs::Emacs::new(&base_dirs);
|
|
||||||
if config.use_predefined_git_repos() {
|
|
||||||
if config.should_run(Step::Emacs) {
|
|
||||||
if !emacs.is_doom() {
|
|
||||||
if let Some(directory) = emacs.directory() {
|
|
||||||
git_repos.insert_if_repo(directory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".doom.d"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.should_run(Step::Vim) {
|
|
||||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".vim"));
|
|
||||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".config/nvim"));
|
|
||||||
}
|
|
||||||
|
|
||||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".ideavimrc"));
|
|
||||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".intellimacs"));
|
|
||||||
|
|
||||||
if config.should_run(Step::Rcm) {
|
|
||||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".dotfiles"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
git_repos.insert_if_repo(zsh::zshrc(&base_dirs));
|
|
||||||
if config.should_run(Step::Tmux) {
|
|
||||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".tmux"));
|
|
||||||
}
|
|
||||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".config/fish"));
|
|
||||||
git_repos.insert_if_repo(base_dirs.config_dir().join("openbox"));
|
|
||||||
git_repos.insert_if_repo(base_dirs.config_dir().join("bspwm"));
|
|
||||||
git_repos.insert_if_repo(base_dirs.config_dir().join("i3"));
|
|
||||||
git_repos.insert_if_repo(base_dirs.config_dir().join("sway"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
git_repos.insert_if_repo(
|
|
||||||
base_dirs
|
|
||||||
.data_local_dir()
|
|
||||||
.join("Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState"),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
windows::insert_startup_scripts(&ctx, &mut git_repos).ok();
|
|
||||||
|
|
||||||
if let Some(profile) = powershell.profile() {
|
|
||||||
git_repos.insert_if_repo(profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.should_run(Step::GitRepos) {
|
|
||||||
if let Some(custom_git_repos) = config.git_repos() {
|
|
||||||
for git_repo in custom_git_repos {
|
|
||||||
git_repos.glob_insert(git_repo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runner.execute(Step::GitRepos, "Git repositories", || {
|
|
||||||
git.multi_pull_step(&git_repos, &ctx)
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if should_run_powershell {
|
|
||||||
runner.execute(Step::Powershell, "Powershell Modules Update", || {
|
|
||||||
powershell.update_modules(&ctx)
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
runner.execute(Step::Shell, "zr", || zsh::run_zr(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Shell, "antibody", || zsh::run_antibody(run_type))?;
|
|
||||||
runner.execute(Step::Shell, "antidote", || zsh::run_antidote(&ctx))?;
|
|
||||||
runner.execute(Step::Shell, "antigen", || zsh::run_antigen(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Shell, "zgenom", || zsh::run_zgenom(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Shell, "zplug", || zsh::run_zplug(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Shell, "zinit", || zsh::run_zinit(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Shell, "zi", || zsh::run_zi(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Shell, "zim", || zsh::run_zim(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Shell, "oh-my-zsh", || zsh::run_oh_my_zsh(&ctx))?;
|
|
||||||
runner.execute(Step::Shell, "fisher", || unix::run_fisher(run_type))?;
|
|
||||||
runner.execute(Step::Shell, "bash-it", || unix::run_bashit(&ctx))?;
|
|
||||||
runner.execute(Step::Shell, "oh-my-fish", || unix::run_oh_my_fish(&ctx))?;
|
|
||||||
runner.execute(Step::Shell, "fish-plug", || unix::run_fish_plug(&ctx))?;
|
|
||||||
runner.execute(Step::Shell, "fundle", || unix::run_fundle(&ctx))?;
|
|
||||||
runner.execute(Step::Tmux, "tmux", || tmux::run_tpm(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Tldr, "TLDR", || unix::run_tldr(run_type))?;
|
|
||||||
runner.execute(Step::Pearl, "pearl", || unix::run_pearl(run_type))?;
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "android")))]
|
|
||||||
runner.execute(Step::GnomeShellExtensions, "Gnome Shell Extensions", || {
|
|
||||||
unix::upgrade_gnome_extensions(&ctx)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::Sdkman, "SDKMAN!", || {
|
|
||||||
unix::run_sdkman(&base_dirs, config.cleanup(), run_type)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::Rcm, "rcm", || unix::run_rcm(&ctx))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(
|
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "openbsd",
|
|
||||||
target_os = "netbsd",
|
|
||||||
target_os = "dragonfly"
|
|
||||||
)))]
|
|
||||||
runner.execute(Step::Atom, "apm", || generic::run_apm(run_type))?;
|
|
||||||
runner.execute(Step::Fossil, "fossil", || generic::run_fossil(run_type))?;
|
|
||||||
runner.execute(Step::Rustup, "rustup", || generic::run_rustup(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Juliaup, "juliaup", || generic::run_juliaup(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Dotnet, ".NET", || generic::run_dotnet_upgrade(&ctx))?;
|
|
||||||
runner.execute(Step::Choosenim, "choosenim", || generic::run_choosenim(&ctx))?;
|
|
||||||
runner.execute(Step::Cargo, "cargo", || generic::run_cargo_update(&ctx))?;
|
|
||||||
runner.execute(Step::Flutter, "Flutter", || generic::run_flutter_upgrade(run_type))?;
|
|
||||||
runner.execute(Step::Go, "go-global-update", || go::run_go_global_update(run_type))?;
|
|
||||||
runner.execute(Step::Go, "gup", || go::run_go_gup(run_type))?;
|
|
||||||
runner.execute(Step::Emacs, "Emacs", || emacs.upgrade(&ctx))?;
|
|
||||||
runner.execute(Step::Opam, "opam", || generic::run_opam_update(&ctx))?;
|
|
||||||
runner.execute(Step::Vcpkg, "vcpkg", || generic::run_vcpkg_update(&ctx))?;
|
|
||||||
runner.execute(Step::Pipx, "pipx", || generic::run_pipx_update(run_type))?;
|
|
||||||
runner.execute(Step::Conda, "conda", || generic::run_conda_update(&ctx))?;
|
|
||||||
runner.execute(Step::Pip3, "pip3", || generic::run_pip3_update(run_type))?;
|
|
||||||
runner.execute(Step::PipReview, "pip-review", || generic::run_pip_review_update(&ctx))?;
|
|
||||||
runner.execute(Step::Pipupgrade, "pipupgrade", || generic::run_pipupgrade_update(&ctx))?;
|
|
||||||
runner.execute(Step::Ghcup, "ghcup", || generic::run_ghcup_update(run_type))?;
|
|
||||||
runner.execute(Step::Stack, "stack", || generic::run_stack_update(run_type))?;
|
|
||||||
runner.execute(Step::Tlmgr, "tlmgr", || generic::run_tlmgr_update(&ctx))?;
|
|
||||||
runner.execute(Step::Myrepos, "myrepos", || {
|
|
||||||
generic::run_myrepos_update(&base_dirs, run_type)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::Chezmoi, "chezmoi", || {
|
|
||||||
generic::run_chezmoi_update(&base_dirs, run_type)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::Jetpack, "jetpack", || generic::run_jetpack(run_type))?;
|
|
||||||
runner.execute(Step::Vim, "vim", || vim::upgrade_vim(&base_dirs, &ctx))?;
|
|
||||||
runner.execute(Step::Vim, "Neovim", || vim::upgrade_neovim(&base_dirs, &ctx))?;
|
|
||||||
runner.execute(Step::Vim, "The Ultimate vimrc", || vim::upgrade_ultimate_vimrc(&ctx))?;
|
|
||||||
runner.execute(Step::Vim, "voom", || vim::run_voom(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::Kakoune, "Kakoune", || kakoune::upgrade_kak_plug(&ctx))?;
|
|
||||||
runner.execute(Step::Helix, "helix", || generic::run_helix_grammars(&ctx))?;
|
|
||||||
runner.execute(Step::Node, "npm", || node::run_npm_upgrade(&ctx))?;
|
|
||||||
runner.execute(Step::Yarn, "yarn", || node::run_yarn_upgrade(&ctx))?;
|
|
||||||
runner.execute(Step::Pnpm, "pnpm", || node::run_pnpm_upgrade(&ctx))?;
|
|
||||||
runner.execute(Step::Containers, "Containers", || containers::run_containers(&ctx))?;
|
|
||||||
runner.execute(Step::Deno, "deno", || node::deno_upgrade(&ctx))?;
|
|
||||||
runner.execute(Step::Composer, "composer", || generic::run_composer_update(&ctx))?;
|
|
||||||
runner.execute(Step::Krew, "krew", || generic::run_krew_upgrade(run_type))?;
|
|
||||||
runner.execute(Step::Helm, "helm", || generic::run_helm_repo_update(run_type))?;
|
|
||||||
runner.execute(Step::Gem, "gem", || generic::run_gem(&base_dirs, run_type))?;
|
|
||||||
runner.execute(Step::RubyGems, "rubygems", || generic::run_rubygems(&ctx))?;
|
|
||||||
runner.execute(Step::Julia, "julia", || generic::update_julia_packages(&ctx))?;
|
|
||||||
runner.execute(Step::Haxelib, "haxelib", || generic::run_haxelib_update(&ctx))?;
|
|
||||||
runner.execute(Step::Sheldon, "sheldon", || generic::run_sheldon(&ctx))?;
|
|
||||||
runner.execute(Step::Rtcl, "rtcl", || generic::run_rtcl(&ctx))?;
|
|
||||||
runner.execute(Step::Bin, "bin", || generic::bin_update(&ctx))?;
|
|
||||||
runner.execute(Step::Gcloud, "gcloud", || {
|
|
||||||
generic::run_gcloud_components_update(run_type)
|
|
||||||
})?;
|
|
||||||
runner.execute(Step::Micro, "micro", || generic::run_micro(run_type))?;
|
|
||||||
runner.execute(Step::Raco, "raco", || generic::run_raco_update(run_type))?;
|
|
||||||
runner.execute(Step::Spicetify, "spicetify", || generic::spicetify_upgrade(&ctx))?;
|
|
||||||
runner.execute(Step::GithubCliExtensions, "GitHub CLI Extensions", || {
|
|
||||||
generic::run_ghcli_extensions_upgrade(&ctx)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
runner.execute(Step::AM, "am", || linux::update_am(&ctx))?;
|
|
||||||
runner.execute(Step::DebGet, "deb-get", || linux::run_deb_get(&ctx))?;
|
|
||||||
runner.execute(Step::Toolbx, "toolbx", || toolbx::run_toolbx(&ctx))?;
|
|
||||||
runner.execute(Step::Flatpak, "Flatpak", || linux::flatpak_update(&ctx))?;
|
|
||||||
runner.execute(Step::Snap, "snap", || linux::run_snap(ctx.sudo().as_ref(), run_type))?;
|
|
||||||
runner.execute(Step::Pacstall, "pacstall", || linux::run_pacstall(&ctx))?;
|
|
||||||
runner.execute(Step::Pacdef, "pacdef", || linux::run_pacdef(&ctx))?;
|
|
||||||
runner.execute(Step::Protonup, "protonup", || linux::run_protonup_update(&ctx))?;
|
|
||||||
runner.execute(Step::Distrobox, "distrobox", || linux::run_distrobox_update(&ctx))?;
|
|
||||||
runner.execute(Step::DkpPacman, "dkp-pacman", || linux::run_dkp_pacman_update(&ctx))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(commands) = config.commands() {
|
|
||||||
for (name, command) in commands {
|
for (name, command) in commands {
|
||||||
if config.should_run_custom_command(name) {
|
generic::run_custom_command(name, command, &ctx)?;
|
||||||
runner.execute(Step::CustomCommands, name, || {
|
|
||||||
generic::run_custom_command(name, command, &ctx)
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
for step in step::default_steps() {
|
||||||
|
match step.run(&mut runner, &ctx) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(error)
|
||||||
|
if error
|
||||||
|
.downcast_ref::<io::Error>()
|
||||||
|
.is_some_and(|e| e.kind() == io::ErrorKind::Interrupted) =>
|
||||||
{
|
{
|
||||||
runner.execute(Step::System, "pihole", || {
|
println!();
|
||||||
linux::run_pihole_update(ctx.sudo().as_ref(), run_type)
|
debug!("Interrupted (possibly with 'q' during retry prompt). Printing summary.");
|
||||||
})?;
|
break;
|
||||||
runner.execute(Step::Firmware, "Firmware upgrades", || linux::run_fwupdmgr(&ctx))?;
|
|
||||||
runner.execute(Step::Restarts, "Restarts", || {
|
|
||||||
linux::run_needrestart(ctx.sudo().as_ref(), run_type)
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
Err(error) => return Err(error),
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
runner.execute(Step::Sparkle, "Sparkle", || macos::run_sparkle(&ctx))?;
|
|
||||||
runner.execute(Step::Mas, "App Store", || macos::run_mas(run_type))?;
|
|
||||||
runner.execute(Step::System, "System upgrade", || macos::upgrade_macos(&ctx))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "freebsd")]
|
|
||||||
runner.execute(Step::System, "FreeBSD Upgrade", || {
|
|
||||||
freebsd::upgrade_freebsd(ctx.sudo().as_ref(), run_type)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
#[cfg(target_os = "openbsd")]
|
|
||||||
runner.execute(Step::System, "OpenBSD Upgrade", || {
|
|
||||||
openbsd::upgrade_openbsd(ctx.sudo().as_ref(), run_type)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
runner.execute(Step::System, "Windows update", || windows::windows_update(&ctx))?;
|
|
||||||
|
|
||||||
if config.should_run(Step::Vagrant) {
|
|
||||||
if let Ok(boxes) = vagrant::collect_boxes(&ctx) {
|
|
||||||
for vagrant_box in boxes {
|
|
||||||
runner.execute(Step::Vagrant, format!("Vagrant ({})", vagrant_box.smart_name()), || {
|
|
||||||
vagrant::topgrade_vagrant_box(&ctx, &vagrant_box)
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut failed = false;
|
||||||
|
|
||||||
|
let report = runner.report();
|
||||||
|
if !report.is_empty() {
|
||||||
|
print_separator(t!("Summary"));
|
||||||
|
|
||||||
|
let mut skipped_missing_sudo = false;
|
||||||
|
|
||||||
|
for (key, result) in report {
|
||||||
|
if !failed && result.failed() {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
if let StepResult::SkippedMissingSudo = result {
|
||||||
|
skipped_missing_sudo = true;
|
||||||
}
|
}
|
||||||
runner.execute(Step::Vagrant, "Vagrant boxes", || vagrant::upgrade_vagrant_boxes(&ctx))?;
|
|
||||||
|
|
||||||
if !runner.report().data().is_empty() {
|
|
||||||
print_separator("Summary");
|
|
||||||
|
|
||||||
for (key, result) in runner.report().data() {
|
|
||||||
print_result(key, result);
|
print_result(key, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if skipped_missing_sudo {
|
||||||
|
print_warning(t!(
|
||||||
|
"\nSome steps were skipped as sudo or equivalent could not be found."
|
||||||
|
));
|
||||||
|
// Steps can only fail with SkippedMissingSudo if sudo is None,
|
||||||
|
// therefore we must have a sudo_err
|
||||||
|
match sudo_err.unwrap() {
|
||||||
|
SudoCreateError::CannotFindBinary => {
|
||||||
|
#[cfg(unix)]
|
||||||
|
print_warning(t!(
|
||||||
|
"Install one of `sudo`, `doas`, `pkexec`, `run0` or `please` to run these steps."
|
||||||
|
));
|
||||||
|
|
||||||
|
// if this windows version supported Windows Sudo, the error would have been WinSudoDisabled
|
||||||
|
#[cfg(windows)]
|
||||||
|
print_warning(t!("Install gsudo to run these steps."));
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
SudoCreateError::WinSudoDisabled => {
|
||||||
|
print_warning(t!(
|
||||||
|
"Install gsudo or enable Windows Sudo to run these steps.\nFor Windows Sudo, the default 'In a new window' mode is not supported as it prevents Topgrade from waiting for commands to finish. Please configure it to use 'Inline' mode instead.\nGo to https://go.microsoft.com/fwlink/?linkid=2257346 to learn more."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
SudoCreateError::WinSudoNewWindowMode => {
|
||||||
|
print_warning(t!(
|
||||||
|
"Windows Sudo was found, but it is set to 'In a new window' mode, which prevents Topgrade from waiting for commands to finish. Please configure it to use 'Inline' mode instead.\nGo to https://go.microsoft.com/fwlink/?linkid=2257346 to learn more."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
if config.show_distribution_summary() {
|
||||||
if let Ok(distribution) = &distribution {
|
if let Ok(distribution) = &distribution {
|
||||||
distribution.show_summary();
|
distribution.show_summary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "freebsd")]
|
|
||||||
freebsd::audit_packages(ctx.sudo().as_ref()).ok();
|
|
||||||
|
|
||||||
#[cfg(target_os = "dragonfly")]
|
|
||||||
dragonfly::audit_packages(ctx.sudo().as_ref()).ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut post_command_failed = false;
|
|
||||||
if let Some(commands) = config.post_commands() {
|
if let Some(commands) = config.post_commands() {
|
||||||
for (name, command) in commands {
|
for (name, command) in commands {
|
||||||
if generic::run_custom_command(name, command, &ctx).is_err() {
|
let result = generic::run_custom_command(name, command, &ctx);
|
||||||
post_command_failed = true;
|
if !failed && result.is_err() {
|
||||||
|
failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.keep_at_end() {
|
if config.keep_at_end() {
|
||||||
print_info("\n(R)eboot\n(S)hell\n(Q)uit");
|
print_info(t!("\n(R)eboot\n(S)hell\n(Q)uit"));
|
||||||
loop {
|
loop {
|
||||||
match get_key() {
|
match get_key() {
|
||||||
Ok(Key::Char('s')) | Ok(Key::Char('S')) => {
|
Ok(Key::Char('s' | 'S')) => {
|
||||||
run_shell().context("Failed to execute shell")?;
|
run_shell().context("Failed to execute shell")?;
|
||||||
}
|
}
|
||||||
Ok(Key::Char('r')) | Ok(Key::Char('R')) => {
|
Ok(Key::Char('r' | 'R')) => {
|
||||||
reboot().context("Failed to reboot")?;
|
println!("{}", t!("Rebooting..."));
|
||||||
|
reboot(&ctx).context("Failed to reboot")?;
|
||||||
}
|
}
|
||||||
Ok(Key::Char('q')) | Ok(Key::Char('Q')) => (),
|
Ok(Key::Char('q' | 'Q')) => (),
|
||||||
_ => {
|
_ => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -525,16 +310,15 @@ For more information about this issue see https://askubuntu.com/questions/110969
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let failed = post_command_failed || runner.report().data().iter().any(|(_, result)| result.failed());
|
|
||||||
|
|
||||||
if !config.skip_notify() {
|
if !config.skip_notify() {
|
||||||
terminal::notify_desktop(
|
notify_desktop(
|
||||||
format!(
|
if failed {
|
||||||
"Topgrade finished {}",
|
t!("Topgrade finished with errors")
|
||||||
if failed { "with errors" } else { "successfully" }
|
} else {
|
||||||
),
|
t!("Topgrade finished successfully")
|
||||||
|
},
|
||||||
Some(Duration::from_secs(10)),
|
Some(Duration::from_secs(10)),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed {
|
if failed {
|
||||||
@@ -550,13 +334,6 @@ fn main() {
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
#[cfg(all(windows, feature = "self-update"))]
|
|
||||||
{
|
|
||||||
if let Some(Upgraded(status)) = error.downcast_ref::<Upgraded>() {
|
|
||||||
exit(status.code().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let skip_print = (error.downcast_ref::<StepFailed>().is_some())
|
let skip_print = (error.downcast_ref::<StepFailed>().is_some())
|
||||||
|| (error
|
|| (error
|
||||||
.downcast_ref::<io::Error>()
|
.downcast_ref::<io::Error>()
|
||||||
@@ -567,32 +344,9 @@ fn main() {
|
|||||||
// The `Debug` implementation of `eyre::Result` prints a multi-line
|
// The `Debug` implementation of `eyre::Result` prints a multi-line
|
||||||
// error message that includes all the 'causes' added with
|
// error message that includes all the 'causes' added with
|
||||||
// `.with_context(...)` calls.
|
// `.with_context(...)` calls.
|
||||||
println!("Error: {error:?}");
|
println!("{}", t!("Error: {error}", error = format!("{:?}", error)));
|
||||||
}
|
}
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install_tracing(filter_directives: &str) -> Result<()> {
|
|
||||||
use tracing_subscriber::fmt;
|
|
||||||
use tracing_subscriber::fmt::format::FmtSpan;
|
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
|
||||||
use tracing_subscriber::EnvFilter;
|
|
||||||
|
|
||||||
let env_filter = EnvFilter::try_new(filter_directives)
|
|
||||||
.or_else(|_| EnvFilter::try_from_default_env())
|
|
||||||
.or_else(|_| EnvFilter::try_new("info"))?;
|
|
||||||
|
|
||||||
let fmt_layer = fmt::layer()
|
|
||||||
.with_target(false)
|
|
||||||
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
|
||||||
.without_time();
|
|
||||||
|
|
||||||
let registry = tracing_subscriber::registry();
|
|
||||||
|
|
||||||
registry.with(env_filter).with(fmt_layer).init();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
pub enum StepResult {
|
|
||||||
Success,
|
|
||||||
Failure,
|
|
||||||
Ignored,
|
|
||||||
Skipped(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StepResult {
|
|
||||||
pub fn failed(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
StepResult::Success | StepResult::Ignored | StepResult::Skipped(_) => false,
|
|
||||||
StepResult::Failure => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CowString<'a> = Cow<'a, str>;
|
|
||||||
type ReportData<'a> = Vec<(CowString<'a>, StepResult)>;
|
|
||||||
pub struct Report<'a> {
|
|
||||||
data: ReportData<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Report<'a> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { data: Vec::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_result<M>(&mut self, result: Option<(M, StepResult)>)
|
|
||||||
where
|
|
||||||
M: Into<CowString<'a>>,
|
|
||||||
{
|
|
||||||
if let Some((key, success)) = result {
|
|
||||||
let key = key.into();
|
|
||||||
|
|
||||||
debug_assert!(!self.data.iter().any(|(k, _)| k == &key), "{key} already reported");
|
|
||||||
self.data.push((key, success));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data(&self) -> &ReportData<'a> {
|
|
||||||
&self.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,37 @@
|
|||||||
use crate::ctrlc;
|
use color_eyre::eyre::{Result, WrapErr};
|
||||||
use crate::error::{DryRun, SkipStep};
|
use rust_i18n::t;
|
||||||
use crate::execution_context::ExecutionContext;
|
|
||||||
use crate::report::{Report, StepResult};
|
|
||||||
use crate::terminal::print_error;
|
|
||||||
use crate::{config::Step, terminal::should_retry};
|
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::io;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
use crate::ctrlc;
|
||||||
|
use crate::error::{DryRun, MissingSudo, SkipStep};
|
||||||
|
use crate::execution_context::ExecutionContext;
|
||||||
|
use crate::step::Step;
|
||||||
|
use crate::terminal::{print_error, print_warning, should_retry, ShouldRetry};
|
||||||
|
|
||||||
|
pub enum StepResult {
|
||||||
|
Success,
|
||||||
|
Failure,
|
||||||
|
Ignored,
|
||||||
|
SkippedMissingSudo,
|
||||||
|
Skipped(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StepResult {
|
||||||
|
pub fn failed(&self) -> bool {
|
||||||
|
use StepResult::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Success | Ignored | Skipped(_) | SkippedMissingSudo => false,
|
||||||
|
Failure => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Report<'a> = Vec<(Cow<'a, str>, StepResult)>;
|
||||||
|
|
||||||
pub struct Runner<'a> {
|
pub struct Runner<'a> {
|
||||||
ctx: &'a ExecutionContext<'a>,
|
ctx: &'a ExecutionContext<'a>,
|
||||||
report: Report<'a>,
|
report: Report<'a>,
|
||||||
@@ -18,32 +41,50 @@ impl<'a> Runner<'a> {
|
|||||||
pub fn new(ctx: &'a ExecutionContext) -> Runner<'a> {
|
pub fn new(ctx: &'a ExecutionContext) -> Runner<'a> {
|
||||||
Runner {
|
Runner {
|
||||||
ctx,
|
ctx,
|
||||||
report: Report::new(),
|
report: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute<F, M>(&mut self, step: Step, key: M, func: F) -> Result<()>
|
fn push_result(&mut self, key: Cow<'a, str>, result: StepResult) {
|
||||||
|
debug_assert!(!self.report.iter().any(|(k, _)| k == &key), "{key} already reported");
|
||||||
|
self.report.push((key, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute<K, F>(&mut self, step: Step, key: K, func: F) -> Result<()>
|
||||||
where
|
where
|
||||||
|
K: Into<Cow<'a, str>> + Debug,
|
||||||
F: Fn() -> Result<()>,
|
F: Fn() -> Result<()>,
|
||||||
M: Into<Cow<'a, str>> + Debug,
|
|
||||||
{
|
{
|
||||||
if !self.ctx.config().should_run(step) {
|
if !self.ctx.config().should_run(step) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = key.into();
|
let key: Cow<'a, str> = key.into();
|
||||||
debug!("Step {:?}", key);
|
debug!("Step {:?}", key);
|
||||||
|
|
||||||
|
// alter the `func` to put it in a span
|
||||||
|
let func = || {
|
||||||
|
let span =
|
||||||
|
tracing::span!(parent: tracing::Span::none(), tracing::Level::TRACE, "step", step = ?step, key = %key);
|
||||||
|
let _guard = span.enter();
|
||||||
|
func()
|
||||||
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match func() {
|
match func() {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.report.push_result(Some((key, StepResult::Success)));
|
self.push_result(key, StepResult::Success);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e) if e.downcast_ref::<DryRun>().is_some() => break,
|
Err(e) if e.downcast_ref::<DryRun>().is_some() => break,
|
||||||
|
Err(e) if e.downcast_ref::<MissingSudo>().is_some() => {
|
||||||
|
print_warning(t!("Skipping step, sudo is required"));
|
||||||
|
self.push_result(key, StepResult::SkippedMissingSudo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
Err(e) if e.downcast_ref::<SkipStep>().is_some() => {
|
Err(e) if e.downcast_ref::<SkipStep>().is_some() => {
|
||||||
if self.ctx.config().verbose() || self.ctx.config().show_skipped() {
|
if self.ctx.config().verbose() || self.ctx.config().show_skipped() {
|
||||||
self.report.push_result(Some((key, StepResult::Skipped(e.to_string()))));
|
self.push_result(key, StepResult::Skipped(e.to_string()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -58,22 +99,29 @@ impl<'a> Runner<'a> {
|
|||||||
let should_ask = interrupted || !(self.ctx.config().no_retry() || ignore_failure);
|
let should_ask = interrupted || !(self.ctx.config().no_retry() || ignore_failure);
|
||||||
let should_retry = if should_ask {
|
let should_retry = if should_ask {
|
||||||
print_error(&key, format!("{e:?}"));
|
print_error(&key, format!("{e:?}"));
|
||||||
should_retry(interrupted, key.as_ref())?
|
should_retry(key.as_ref())?
|
||||||
} else {
|
} else {
|
||||||
false
|
ShouldRetry::No
|
||||||
};
|
};
|
||||||
|
|
||||||
if !should_retry {
|
match should_retry {
|
||||||
self.report.push_result(Some((
|
ShouldRetry::No | ShouldRetry::Quit => {
|
||||||
|
self.push_result(
|
||||||
key,
|
key,
|
||||||
if ignore_failure {
|
if ignore_failure {
|
||||||
StepResult::Ignored
|
StepResult::Ignored
|
||||||
} else {
|
} else {
|
||||||
StepResult::Failure
|
StepResult::Failure
|
||||||
},
|
},
|
||||||
)));
|
);
|
||||||
|
if let ShouldRetry::Quit = should_retry {
|
||||||
|
return Err(io::Error::from(io::ErrorKind::Interrupted))
|
||||||
|
.context("Quit from user input");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ShouldRetry::Yes => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +129,7 @@ impl<'a> Runner<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report(&self) -> &Report {
|
pub fn report(&self) -> &Report<'_> {
|
||||||
&self.report
|
&self.report
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(windows)]
|
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use std::{env::current_exe, fs, path::PathBuf};
|
use std::{env::current_exe, fs, path::PathBuf};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|||||||
@@ -1,18 +1,29 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::process::CommandExt as _;
|
use std::os::unix::process::CommandExt as _;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::process::exit;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use color_eyre::eyre::{bail, Result};
|
use crate::step::Step;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use color_eyre::eyre::bail;
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
use self_update_crate::backends::github::Update;
|
use self_update_crate::backends::github::Update;
|
||||||
use self_update_crate::update::UpdateStatus;
|
use self_update_crate::update::UpdateStatus;
|
||||||
|
|
||||||
use super::terminal::*;
|
use super::terminal::{print_info, print_separator};
|
||||||
#[cfg(windows)]
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::error::Upgraded;
|
|
||||||
|
|
||||||
pub fn self_update() -> Result<()> {
|
pub fn self_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
print_separator("Self update");
|
print_separator(t!("Self update"));
|
||||||
|
|
||||||
|
if ctx.run_type().dry() {
|
||||||
|
println!("{}", t!("Would self-update"));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let assume_yes = ctx.config().yes(Step::SelfUpdate);
|
||||||
let current_exe = env::current_exe();
|
let current_exe = env::current_exe();
|
||||||
|
|
||||||
let target = self_update_crate::get_target();
|
let target = self_update_crate::get_target();
|
||||||
@@ -21,25 +32,25 @@ pub fn self_update() -> Result<()> {
|
|||||||
.repo_name("topgrade")
|
.repo_name("topgrade")
|
||||||
.target(target)
|
.target(target)
|
||||||
.bin_name(if cfg!(windows) { "topgrade.exe" } else { "topgrade" })
|
.bin_name(if cfg!(windows) { "topgrade.exe" } else { "topgrade" })
|
||||||
.show_output(false)
|
.show_output(true)
|
||||||
.show_download_progress(true)
|
.show_download_progress(true)
|
||||||
.current_version(self_update_crate::cargo_crate_version!())
|
.current_version(self_update_crate::cargo_crate_version!())
|
||||||
.no_confirm(true)
|
.no_confirm(assume_yes)
|
||||||
.build()?
|
.build()?
|
||||||
.update_extended()?;
|
.update_extended()?;
|
||||||
|
|
||||||
if let UpdateStatus::Updated(release) = &result {
|
if let UpdateStatus::Updated(release) = &result {
|
||||||
println!("\nTopgrade upgraded to {}:\n", release.version);
|
println!("{}", t!("Topgrade upgraded to {version}:\n", version = release.version));
|
||||||
if let Some(body) = &release.body {
|
if let Some(body) = &release.body {
|
||||||
println!("{body}");
|
println!("{body}");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Topgrade is up-to-date");
|
println!("{}", t!("Topgrade is up-to-date"));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if result.updated() {
|
if result.updated() {
|
||||||
print_warning("Respawning...");
|
print_info(t!("Respawning..."));
|
||||||
let mut command = Command::new(current_exe?);
|
let mut command = Command::new(current_exe?);
|
||||||
command.args(env::args().skip(1)).env("TOPGRADE_NO_SELF_UPGRADE", "");
|
command.args(env::args().skip(1)).env("TOPGRADE_NO_SELF_UPGRADE", "");
|
||||||
|
|
||||||
@@ -53,10 +64,11 @@ pub fn self_update() -> Result<()> {
|
|||||||
{
|
{
|
||||||
#[allow(clippy::disallowed_methods)]
|
#[allow(clippy::disallowed_methods)]
|
||||||
let status = command.status()?;
|
let status = command.status()?;
|
||||||
bail!(Upgraded(status));
|
exit(status.code().expect("This cannot return None on Windows"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
902
src/step.rs
Normal file
902
src/step.rs
Normal file
@@ -0,0 +1,902 @@
|
|||||||
|
use crate::execution_context::ExecutionContext;
|
||||||
|
use crate::runner::Runner;
|
||||||
|
use clap::ValueEnum;
|
||||||
|
use color_eyre::Result;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use rust_i18n::t;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use strum::{EnumCount, EnumIter, EnumString, VariantNames};
|
||||||
|
|
||||||
|
#[cfg(feature = "self-update")]
|
||||||
|
use crate::self_update;
|
||||||
|
use crate::steps::remote::vagrant;
|
||||||
|
#[allow(clippy::wildcard_imports)]
|
||||||
|
use crate::steps::*;
|
||||||
|
use crate::utils::hostname;
|
||||||
|
|
||||||
|
#[derive(ValueEnum, EnumString, VariantNames, Debug, Clone, PartialEq, Eq, Deserialize, EnumIter, Copy, EnumCount)]
|
||||||
|
#[clap(rename_all = "snake_case")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
pub enum Step {
|
||||||
|
AM,
|
||||||
|
AndroidStudio,
|
||||||
|
AppMan,
|
||||||
|
Aqua,
|
||||||
|
Asdf,
|
||||||
|
Atom,
|
||||||
|
Atuin,
|
||||||
|
Audit,
|
||||||
|
AutoCpufreq,
|
||||||
|
Bin,
|
||||||
|
Bob,
|
||||||
|
BrewCask,
|
||||||
|
BrewFormula,
|
||||||
|
Bun,
|
||||||
|
BunPackages,
|
||||||
|
Cargo,
|
||||||
|
Certbot,
|
||||||
|
Chezmoi,
|
||||||
|
Chocolatey,
|
||||||
|
Choosenim,
|
||||||
|
CinnamonSpices,
|
||||||
|
ClamAvDb,
|
||||||
|
Composer,
|
||||||
|
Conda,
|
||||||
|
ConfigUpdate,
|
||||||
|
Containers,
|
||||||
|
CustomCommands,
|
||||||
|
DebGet,
|
||||||
|
Deno,
|
||||||
|
Distrobox,
|
||||||
|
DkpPacman,
|
||||||
|
Dotnet,
|
||||||
|
Elan,
|
||||||
|
Emacs,
|
||||||
|
Falconf,
|
||||||
|
Firmware,
|
||||||
|
Flatpak,
|
||||||
|
Flutter,
|
||||||
|
Fossil,
|
||||||
|
Gcloud,
|
||||||
|
Gem,
|
||||||
|
Ghcup,
|
||||||
|
GitRepos,
|
||||||
|
GithubCliExtensions,
|
||||||
|
GnomeShellExtensions,
|
||||||
|
Go,
|
||||||
|
Guix,
|
||||||
|
Haxelib,
|
||||||
|
Helix,
|
||||||
|
Helm,
|
||||||
|
HomeManager,
|
||||||
|
Hyprpm,
|
||||||
|
// These names are miscapitalized on purpose, so the CLI name is
|
||||||
|
// `jetbrains_pycharm` instead of `jet_brains_py_charm`.
|
||||||
|
JetbrainsAqua,
|
||||||
|
JetbrainsClion,
|
||||||
|
JetbrainsDatagrip,
|
||||||
|
JetbrainsDataspell,
|
||||||
|
JetbrainsGateway,
|
||||||
|
JetbrainsGoland,
|
||||||
|
JetbrainsIdea,
|
||||||
|
JetbrainsMps,
|
||||||
|
JetbrainsPhpstorm,
|
||||||
|
JetbrainsPycharm,
|
||||||
|
JetbrainsRider,
|
||||||
|
JetbrainsRubymine,
|
||||||
|
JetbrainsRustrover,
|
||||||
|
JetbrainsToolbox,
|
||||||
|
JetbrainsWebstorm,
|
||||||
|
Jetpack,
|
||||||
|
Julia,
|
||||||
|
Juliaup,
|
||||||
|
Kakoune,
|
||||||
|
Krew,
|
||||||
|
Lensfun,
|
||||||
|
Lure,
|
||||||
|
Macports,
|
||||||
|
Mamba,
|
||||||
|
Mandb,
|
||||||
|
Mas,
|
||||||
|
Maza,
|
||||||
|
Micro,
|
||||||
|
MicrosoftStore,
|
||||||
|
Miktex,
|
||||||
|
Mise,
|
||||||
|
Myrepos,
|
||||||
|
Nix,
|
||||||
|
NixHelper,
|
||||||
|
Node,
|
||||||
|
Opam,
|
||||||
|
Pacdef,
|
||||||
|
Pacstall,
|
||||||
|
Pearl,
|
||||||
|
Pip3,
|
||||||
|
PipReview,
|
||||||
|
PipReviewLocal,
|
||||||
|
Pipupgrade,
|
||||||
|
Pipx,
|
||||||
|
Pipxu,
|
||||||
|
Pixi,
|
||||||
|
Pkg,
|
||||||
|
Pkgfile,
|
||||||
|
Pkgin,
|
||||||
|
PlatformioCore,
|
||||||
|
Pnpm,
|
||||||
|
Poetry,
|
||||||
|
Powershell,
|
||||||
|
Protonup,
|
||||||
|
Pyenv,
|
||||||
|
Raco,
|
||||||
|
Rcm,
|
||||||
|
Remotes,
|
||||||
|
Restarts,
|
||||||
|
Rtcl,
|
||||||
|
RubyGems,
|
||||||
|
Rustup,
|
||||||
|
Rye,
|
||||||
|
Scoop,
|
||||||
|
Sdkman,
|
||||||
|
SelfUpdate,
|
||||||
|
Sheldon,
|
||||||
|
Shell,
|
||||||
|
Snap,
|
||||||
|
Sparkle,
|
||||||
|
Spicetify,
|
||||||
|
Stack,
|
||||||
|
Stew,
|
||||||
|
System,
|
||||||
|
Tldr,
|
||||||
|
Tlmgr,
|
||||||
|
Tmux,
|
||||||
|
Toolbx,
|
||||||
|
Typst,
|
||||||
|
Uv,
|
||||||
|
Vagrant,
|
||||||
|
Vcpkg,
|
||||||
|
Vim,
|
||||||
|
VoltaPackages,
|
||||||
|
Vscode,
|
||||||
|
VscodeInsiders,
|
||||||
|
Vscodium,
|
||||||
|
VscodiumInsiders,
|
||||||
|
Waydroid,
|
||||||
|
Winget,
|
||||||
|
Wsl,
|
||||||
|
WslUpdate,
|
||||||
|
Xcodes,
|
||||||
|
Yadm,
|
||||||
|
Yarn,
|
||||||
|
Yazi,
|
||||||
|
Zigup,
|
||||||
|
Zvm,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Step {
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
pub fn run(&self, runner: &mut Runner, ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
use Step::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
AM =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "am", || linux::run_am(ctx))?
|
||||||
|
}
|
||||||
|
AndroidStudio => runner.execute(*self, "Android Studio Plugins", || generic::run_android_studio(ctx))?,
|
||||||
|
AppMan =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "appman", || linux::run_appman(ctx))?
|
||||||
|
}
|
||||||
|
Aqua => runner.execute(*self, "aqua", || generic::run_aqua(ctx))?,
|
||||||
|
Asdf =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "asdf", || unix::run_asdf(ctx))?
|
||||||
|
}
|
||||||
|
Atom =>
|
||||||
|
{
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "dragonfly"
|
||||||
|
)))]
|
||||||
|
runner.execute(*self, "apm", || generic::run_apm(ctx))?
|
||||||
|
}
|
||||||
|
Atuin =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "atuin", || unix::run_atuin(ctx))?
|
||||||
|
}
|
||||||
|
Audit => {
|
||||||
|
#[cfg(target_os = "dragonfly")]
|
||||||
|
runner.execute(*self, "DragonFly Audit", || dragonfly::audit_packages(ctx))?;
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
runner.execute(*self, "FreeBSD Audit", || freebsd::audit_packages(ctx))?
|
||||||
|
}
|
||||||
|
AutoCpufreq =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "auto-cpufreq", || linux::run_auto_cpufreq(ctx))?
|
||||||
|
}
|
||||||
|
Bin => runner.execute(*self, "bin", || generic::bin_update(ctx))?,
|
||||||
|
Bob => runner.execute(*self, "Bob", || generic::run_bob(ctx))?,
|
||||||
|
BrewCask => {
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
|
runner.execute(*self, "Brew Cask", || unix::run_brew_cask(ctx, unix::BrewVariant::Path))?;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "Brew Cask (Intel)", || {
|
||||||
|
unix::run_brew_cask(ctx, unix::BrewVariant::MacIntel)
|
||||||
|
})?;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "Brew Cask (ARM)", || {
|
||||||
|
unix::run_brew_cask(ctx, unix::BrewVariant::MacArm)
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
BrewFormula => {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "Brew", || unix::run_brew_formula(ctx, unix::BrewVariant::Path))?;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "Brew (ARM)", || {
|
||||||
|
unix::run_brew_formula(ctx, unix::BrewVariant::MacArm)
|
||||||
|
})?;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "Brew (Intel)", || {
|
||||||
|
unix::run_brew_formula(ctx, unix::BrewVariant::MacIntel)
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
Bun => runner.execute(*self, "bun", || generic::run_bun(ctx))?,
|
||||||
|
BunPackages =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "bun-packages", || unix::run_bun_packages(ctx))?
|
||||||
|
}
|
||||||
|
Cargo => runner.execute(*self, "cargo", || generic::run_cargo_update(ctx))?,
|
||||||
|
Certbot => runner.execute(*self, "Certbot", || generic::run_certbot(ctx))?,
|
||||||
|
Chezmoi => runner.execute(*self, "chezmoi", || generic::run_chezmoi_update(ctx))?,
|
||||||
|
Chocolatey =>
|
||||||
|
{
|
||||||
|
#[cfg(windows)]
|
||||||
|
runner.execute(*self, "Chocolatey", || windows::run_chocolatey(ctx))?
|
||||||
|
}
|
||||||
|
Choosenim => runner.execute(*self, "choosenim", || generic::run_choosenim(ctx))?,
|
||||||
|
CinnamonSpices =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "Cinnamon spices", || linux::run_cinnamon_spices_updater(ctx))?
|
||||||
|
}
|
||||||
|
ClamAvDb => runner.execute(*self, "ClamAV Databases", || generic::run_freshclam(ctx))?,
|
||||||
|
Composer => runner.execute(*self, "composer", || generic::run_composer_update(ctx))?,
|
||||||
|
Conda => runner.execute(*self, "conda", || generic::run_conda_update(ctx))?,
|
||||||
|
ConfigUpdate =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "config-update", || linux::run_config_update(ctx))?
|
||||||
|
}
|
||||||
|
Containers => runner.execute(*self, "Containers", || containers::run_containers(ctx))?,
|
||||||
|
CustomCommands => {
|
||||||
|
if let Some(commands) = ctx.config().commands() {
|
||||||
|
for (name, command) in commands
|
||||||
|
.iter()
|
||||||
|
.filter(|(n, _)| ctx.config().should_run_custom_command(n))
|
||||||
|
{
|
||||||
|
runner.execute(*self, name.clone(), || generic::run_custom_command(name, command, ctx))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DebGet =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "deb-get", || linux::run_deb_get(ctx))?
|
||||||
|
}
|
||||||
|
Deno => runner.execute(*self, "deno", || node::deno_upgrade(ctx))?,
|
||||||
|
Distrobox =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "distrobox", || linux::run_distrobox_update(ctx))?
|
||||||
|
}
|
||||||
|
DkpPacman =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "dkp-pacman", || linux::run_dkp_pacman_update(ctx))?
|
||||||
|
}
|
||||||
|
Dotnet => runner.execute(*self, ".NET", || generic::run_dotnet_upgrade(ctx))?,
|
||||||
|
Elan => runner.execute(*self, "elan", || generic::run_elan(ctx))?,
|
||||||
|
Emacs => runner.execute(*self, "Emacs", || emacs::Emacs::new().upgrade(ctx))?,
|
||||||
|
Falconf => runner.execute(*self, "falconf sync", || generic::run_falconf(ctx))?,
|
||||||
|
Firmware =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "Firmware", || linux::run_fwupdmgr(ctx))?
|
||||||
|
}
|
||||||
|
Flatpak =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "Flatpak", || linux::run_flatpak(ctx))?
|
||||||
|
}
|
||||||
|
Flutter => runner.execute(*self, "Flutter", || generic::run_flutter_upgrade(ctx))?,
|
||||||
|
Fossil => runner.execute(*self, "fossil", || generic::run_fossil(ctx))?,
|
||||||
|
Gcloud => runner.execute(*self, "gcloud", || generic::run_gcloud_components_update(ctx))?,
|
||||||
|
Gem => runner.execute(*self, "gem", || generic::run_gem(ctx))?,
|
||||||
|
Ghcup => runner.execute(*self, "ghcup", || generic::run_ghcup_update(ctx))?,
|
||||||
|
GitRepos => runner.execute(*self, "Git Repositories", || git::run_git_pull(ctx))?,
|
||||||
|
GithubCliExtensions => runner.execute(*self, "GitHub CLI Extensions", || {
|
||||||
|
generic::run_ghcli_extensions_upgrade(ctx)
|
||||||
|
})?,
|
||||||
|
GnomeShellExtensions =>
|
||||||
|
{
|
||||||
|
#[cfg(all(unix, not(any(target_os = "macos", target_os = "android"))))]
|
||||||
|
runner.execute(*self, "Gnome Shell Extensions", || unix::upgrade_gnome_extensions(ctx))?
|
||||||
|
}
|
||||||
|
Go => {
|
||||||
|
runner.execute(*self, "go-global-update", || go::run_go_global_update(ctx))?;
|
||||||
|
runner.execute(*self, "gup", || go::run_go_gup(ctx))?
|
||||||
|
}
|
||||||
|
Guix =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "guix", || unix::run_guix(ctx))?
|
||||||
|
}
|
||||||
|
Haxelib => runner.execute(*self, "haxelib", || generic::run_haxelib_update(ctx))?,
|
||||||
|
Helix => runner.execute(*self, "helix", || generic::run_helix_grammars(ctx))?,
|
||||||
|
Helm => runner.execute(*self, "helm", || generic::run_helm_repo_update(ctx))?,
|
||||||
|
HomeManager =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "home-manager", || unix::run_home_manager(ctx))?
|
||||||
|
}
|
||||||
|
Hyprpm =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "hyprpm", || unix::run_hyprpm(ctx))?
|
||||||
|
}
|
||||||
|
JetbrainsAqua => runner.execute(*self, "JetBrains Aqua Plugins", || generic::run_jetbrains_aqua(ctx))?,
|
||||||
|
JetbrainsClion => runner.execute(*self, "JetBrains CL", || generic::run_jetbrains_clion(ctx))?,
|
||||||
|
JetbrainsDatagrip => {
|
||||||
|
runner.execute(*self, "JetBrains DataGrip", || generic::run_jetbrains_datagrip(ctx))?
|
||||||
|
}
|
||||||
|
JetbrainsDataspell => runner.execute(*self, "JetBrains DataSpell Plugins", || {
|
||||||
|
generic::run_jetbrains_dataspell(ctx)
|
||||||
|
})?,
|
||||||
|
JetbrainsGateway => runner.execute(*self, "JetBrains Gateway Plugins", || {
|
||||||
|
generic::run_jetbrains_gateway(ctx)
|
||||||
|
})?,
|
||||||
|
JetbrainsGoland => {
|
||||||
|
runner.execute(*self, "JetBrains GoLand Plugins", || generic::run_jetbrains_goland(ctx))?
|
||||||
|
}
|
||||||
|
JetbrainsIdea => runner.execute(*self, "JetBrains IntelliJ IDEA Plugins", || {
|
||||||
|
generic::run_jetbrains_idea(ctx)
|
||||||
|
})?,
|
||||||
|
JetbrainsMps => runner.execute(*self, "JetBrains MPS Plugins", || generic::run_jetbrains_mps(ctx))?,
|
||||||
|
JetbrainsPhpstorm => runner.execute(*self, "JetBrains PhpStorm Plugins", || {
|
||||||
|
generic::run_jetbrains_phpstorm(ctx)
|
||||||
|
})?,
|
||||||
|
JetbrainsPycharm => runner.execute(*self, "JetBrains PyCharm Plugins", || {
|
||||||
|
generic::run_jetbrains_pycharm(ctx)
|
||||||
|
})?,
|
||||||
|
JetbrainsRider => runner.execute(*self, "JetBrains Rider Plugins", || generic::run_jetbrains_rider(ctx))?,
|
||||||
|
JetbrainsRubymine => runner.execute(*self, "JetBrains RubyMine Plugins", || {
|
||||||
|
generic::run_jetbrains_rubymine(ctx)
|
||||||
|
})?,
|
||||||
|
JetbrainsRustrover => runner.execute(*self, "JetBrains RustRover Plugins", || {
|
||||||
|
generic::run_jetbrains_rustrover(ctx)
|
||||||
|
})?,
|
||||||
|
JetbrainsToolbox => runner.execute(*self, "JetBrains Toolbox", || generic::run_jetbrains_toolbox(ctx))?,
|
||||||
|
JetbrainsWebstorm => runner.execute(*self, "JetBrains WebStorm Plugins", || {
|
||||||
|
generic::run_jetbrains_webstorm(ctx)
|
||||||
|
})?,
|
||||||
|
Jetpack => runner.execute(*self, "jetpack", || generic::run_jetpack(ctx))?,
|
||||||
|
Julia => runner.execute(*self, "julia", || generic::update_julia_packages(ctx))?,
|
||||||
|
Juliaup => runner.execute(*self, "juliaup", || generic::run_juliaup(ctx))?,
|
||||||
|
Kakoune => runner.execute(*self, "Kakoune", || kakoune::upgrade_kak_plug(ctx))?,
|
||||||
|
Krew => runner.execute(*self, "krew", || generic::run_krew_upgrade(ctx))?,
|
||||||
|
Lensfun => runner.execute(*self, "Lensfun's database update", || {
|
||||||
|
generic::run_lensfun_update_data(ctx)
|
||||||
|
})?,
|
||||||
|
Lure =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "LURE", || linux::run_lure_update(ctx))?
|
||||||
|
}
|
||||||
|
Macports =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "MacPorts", || macos::run_macports(ctx))?
|
||||||
|
}
|
||||||
|
Mamba => runner.execute(*self, "mamba", || generic::run_mamba_update(ctx))?,
|
||||||
|
Mandb =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "Manual Entries", || linux::run_mandb(ctx))?
|
||||||
|
}
|
||||||
|
Mas =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "App Store", || macos::run_mas(ctx))?
|
||||||
|
}
|
||||||
|
Maza =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "maza", || unix::run_maza(ctx))?
|
||||||
|
}
|
||||||
|
Micro => runner.execute(*self, "micro", || generic::run_micro(ctx))?,
|
||||||
|
MicrosoftStore =>
|
||||||
|
{
|
||||||
|
#[cfg(windows)]
|
||||||
|
runner.execute(*self, "Microsoft Store", || windows::microsoft_store(ctx))?
|
||||||
|
}
|
||||||
|
Miktex => runner.execute(*self, "miktex", || generic::run_miktex_packages_update(ctx))?,
|
||||||
|
Mise =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "mise", || unix::run_mise(ctx))?
|
||||||
|
}
|
||||||
|
Myrepos => runner.execute(*self, "myrepos", || generic::run_myrepos_update(ctx))?,
|
||||||
|
Nix => {
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "nix", || unix::run_nix(ctx))?;
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "nix upgrade-nix", || unix::run_nix_self_upgrade(ctx))?
|
||||||
|
}
|
||||||
|
NixHelper =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "nh", || unix::run_nix_helper(ctx))?
|
||||||
|
}
|
||||||
|
Node => runner.execute(*self, "npm", || node::run_npm_upgrade(ctx))?,
|
||||||
|
Opam => runner.execute(*self, "opam", || generic::run_opam_update(ctx))?,
|
||||||
|
Pacdef =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "pacdef", || linux::run_pacdef(ctx))?
|
||||||
|
}
|
||||||
|
Pacstall =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "pacstall", || linux::run_pacstall(ctx))?
|
||||||
|
}
|
||||||
|
Pearl =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "pearl", || unix::run_pearl(ctx))?
|
||||||
|
}
|
||||||
|
Pip3 => runner.execute(*self, "pip3", || generic::run_pip3_update(ctx))?,
|
||||||
|
PipReview => runner.execute(*self, "pip-review", || generic::run_pip_review_update(ctx))?,
|
||||||
|
PipReviewLocal => runner.execute(*self, "pip-review (local)", || {
|
||||||
|
generic::run_pip_review_local_update(ctx)
|
||||||
|
})?,
|
||||||
|
Pipupgrade => runner.execute(*self, "pipupgrade", || generic::run_pipupgrade_update(ctx))?,
|
||||||
|
Pipx => runner.execute(*self, "pipx", || generic::run_pipx_update(ctx))?,
|
||||||
|
Pipxu => runner.execute(*self, "pipxu", || generic::run_pipxu_update(ctx))?,
|
||||||
|
Pixi => runner.execute(*self, "pixi", || generic::run_pixi_update(ctx))?,
|
||||||
|
Pkg => {
|
||||||
|
#[cfg(target_os = "dragonfly")]
|
||||||
|
runner.execute(*self, "Dragonfly BSD Packages", || dragonfly::upgrade_packages(ctx))?;
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
runner.execute(*self, "FreeBSD Packages", || freebsd::upgrade_packages(ctx))?;
|
||||||
|
#[cfg(target_os = "openbsd")]
|
||||||
|
runner.execute(*self, "OpenBSD Packages", || openbsd::upgrade_packages(ctx))?;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
runner.execute(*self, "Termux Packages", || android::upgrade_packages(ctx))?
|
||||||
|
}
|
||||||
|
Pkgfile =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "pkgfile", || linux::run_pkgfile(ctx))?
|
||||||
|
}
|
||||||
|
Pkgin =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "pkgin", || unix::run_pkgin(ctx))?
|
||||||
|
}
|
||||||
|
PlatformioCore => runner.execute(*self, "PlatformIO Core", || generic::run_platform_io(ctx))?,
|
||||||
|
Pnpm => runner.execute(*self, "pnpm", || node::run_pnpm_upgrade(ctx))?,
|
||||||
|
Poetry => runner.execute(*self, "Poetry", || generic::run_poetry(ctx))?,
|
||||||
|
Powershell => runner.execute(*self, "Powershell Modules Update", || generic::run_powershell(ctx))?,
|
||||||
|
Protonup =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "protonup", || linux::run_protonup_update(ctx))?
|
||||||
|
}
|
||||||
|
Pyenv =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "pyenv", || unix::run_pyenv(ctx))?
|
||||||
|
}
|
||||||
|
Raco => runner.execute(*self, "raco", || generic::run_raco_update(ctx))?,
|
||||||
|
Rcm =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "rcm", || unix::run_rcm(ctx))?
|
||||||
|
}
|
||||||
|
Remotes => {
|
||||||
|
if let Some(topgrades) = ctx.config().remote_topgrades() {
|
||||||
|
for remote_topgrade in topgrades
|
||||||
|
.iter()
|
||||||
|
.filter(|t| ctx.config().should_execute_remote(hostname(), t))
|
||||||
|
{
|
||||||
|
runner.execute(*self, format!("Remote ({remote_topgrade})"), || {
|
||||||
|
crate::ssh::ssh_step(ctx, remote_topgrade)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Restarts =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "Restarts", || linux::run_needrestart(ctx))?
|
||||||
|
}
|
||||||
|
Rtcl => runner.execute(*self, "rtcl", || generic::run_rtcl(ctx))?,
|
||||||
|
RubyGems => runner.execute(*self, "rubygems", || generic::run_rubygems(ctx))?,
|
||||||
|
Rustup => runner.execute(*self, "rustup", || generic::run_rustup(ctx))?,
|
||||||
|
Rye => runner.execute(*self, "rye", || generic::run_rye(ctx))?,
|
||||||
|
Scoop =>
|
||||||
|
{
|
||||||
|
#[cfg(windows)]
|
||||||
|
runner.execute(*self, "Scoop", || windows::run_scoop(ctx))?
|
||||||
|
}
|
||||||
|
Sdkman =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "SDKMAN!", || unix::run_sdkman(ctx))?
|
||||||
|
}
|
||||||
|
SelfUpdate => {
|
||||||
|
// Self-Update step, this will execute only if:
|
||||||
|
// 1. the `self-update` feature is enabled
|
||||||
|
// 2. it is not disabled from configuration (env var/CLI opt/file)
|
||||||
|
#[cfg(feature = "self-update")]
|
||||||
|
{
|
||||||
|
if std::env::var("TOPGRADE_NO_SELF_UPGRADE").is_err() && !ctx.config().no_self_update() {
|
||||||
|
runner.execute(*self, "Self Update", || self_update::self_update(ctx))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Sheldon => runner.execute(*self, "sheldon", || generic::run_sheldon(ctx))?,
|
||||||
|
Shell => {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
runner.execute(*self, "zr", || zsh::run_zr(ctx))?;
|
||||||
|
runner.execute(*self, "antibody", || zsh::run_antibody(ctx))?;
|
||||||
|
runner.execute(*self, "antidote", || zsh::run_antidote(ctx))?;
|
||||||
|
runner.execute(*self, "antigen", || zsh::run_antigen(ctx))?;
|
||||||
|
runner.execute(*self, "zgenom", || zsh::run_zgenom(ctx))?;
|
||||||
|
runner.execute(*self, "zplug", || zsh::run_zplug(ctx))?;
|
||||||
|
runner.execute(*self, "zinit", || zsh::run_zinit(ctx))?;
|
||||||
|
runner.execute(*self, "zi", || zsh::run_zi(ctx))?;
|
||||||
|
runner.execute(*self, "zim", || zsh::run_zim(ctx))?;
|
||||||
|
runner.execute(*self, "oh-my-zsh", || zsh::run_oh_my_zsh(ctx))?;
|
||||||
|
runner.execute(*self, "oh-my-bash", || unix::run_oh_my_bash(ctx))?;
|
||||||
|
runner.execute(*self, "fisher", || unix::run_fisher(ctx))?;
|
||||||
|
runner.execute(*self, "bash-it", || unix::run_bashit(ctx))?;
|
||||||
|
runner.execute(*self, "oh-my-fish", || unix::run_oh_my_fish(ctx))?;
|
||||||
|
runner.execute(*self, "fish-plug", || unix::run_fish_plug(ctx))?;
|
||||||
|
runner.execute(*self, "fundle", || unix::run_fundle(ctx))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Snap =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "snap", || linux::run_snap(ctx))?
|
||||||
|
}
|
||||||
|
Sparkle =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "Sparkle", || macos::run_sparkle(ctx))?
|
||||||
|
}
|
||||||
|
Spicetify => runner.execute(*self, "spicetify", || generic::spicetify_upgrade(ctx))?,
|
||||||
|
Stack => runner.execute(*self, "stack", || generic::run_stack_update(ctx))?,
|
||||||
|
Stew => runner.execute(*self, "stew", || generic::run_stew(ctx))?,
|
||||||
|
System => {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
// NOTE: Due to breaking `nu` updates, `packer.nu` needs to be updated before `nu` get updated
|
||||||
|
// by other package managers.
|
||||||
|
runner.execute(Shell, "packer.nu", || linux::run_packer_nu(ctx))?;
|
||||||
|
|
||||||
|
match ctx.distribution() {
|
||||||
|
Ok(distribution) => {
|
||||||
|
runner.execute(*self, "System update", || distribution.upgrade(ctx))?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}", t!("Error detecting current distribution: {error}", error = e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runner.execute(*self, "pihole", || linux::run_pihole_update(ctx))?;
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
runner.execute(*self, "Windows update", || windows::windows_update(ctx))?;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "System update", || macos::upgrade_macos(ctx))?;
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
runner.execute(*self, "FreeBSD Upgrade", || freebsd::upgrade_freebsd(ctx))?;
|
||||||
|
#[cfg(target_os = "openbsd")]
|
||||||
|
runner.execute(*self, "OpenBSD Upgrade", || openbsd::upgrade_openbsd(ctx))?
|
||||||
|
}
|
||||||
|
Tldr => runner.execute(*self, "TLDR", || generic::run_tldr(ctx))?,
|
||||||
|
Tlmgr => runner.execute(*self, "tlmgr", || generic::run_tlmgr_update(ctx))?,
|
||||||
|
Tmux =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "tmux", || tmux::run_tpm(ctx))?
|
||||||
|
}
|
||||||
|
Toolbx =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "toolbx", || toolbx::run_toolbx(ctx))?
|
||||||
|
}
|
||||||
|
Typst => runner.execute(*self, "Typst", || generic::run_typst(ctx))?,
|
||||||
|
Uv => runner.execute(*self, "uv", || generic::run_uv(ctx))?,
|
||||||
|
Vagrant => {
|
||||||
|
if ctx.config().should_run(Vagrant) {
|
||||||
|
if let Ok(boxes) = vagrant::collect_boxes(ctx) {
|
||||||
|
for vagrant_box in boxes {
|
||||||
|
runner.execute(*self, format!("Vagrant ({})", vagrant_box.smart_name()), || {
|
||||||
|
vagrant::topgrade_vagrant_box(ctx, &vagrant_box)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runner.execute(*self, "Vagrant boxes", || vagrant::upgrade_vagrant_boxes(ctx))?;
|
||||||
|
}
|
||||||
|
Vcpkg => runner.execute(*self, "vcpkg", || generic::run_vcpkg_update(ctx))?,
|
||||||
|
Vim => {
|
||||||
|
runner.execute(*self, "vim", || vim::upgrade_vim(ctx))?;
|
||||||
|
runner.execute(*self, "Neovim", || vim::upgrade_neovim(ctx))?;
|
||||||
|
runner.execute(*self, "The Ultimate vimrc", || vim::upgrade_ultimate_vimrc(ctx))?;
|
||||||
|
runner.execute(*self, "voom", || vim::run_voom(ctx))?
|
||||||
|
}
|
||||||
|
VoltaPackages => runner.execute(*self, "volta packages", || node::run_volta_packages_upgrade(ctx))?,
|
||||||
|
Vscode => runner.execute(*self, "Visual Studio Code extensions", || {
|
||||||
|
generic::run_vscode_extensions_update(ctx)
|
||||||
|
})?,
|
||||||
|
VscodeInsiders => runner.execute(*self, "Visual Studio Code Insiders extensions", || {
|
||||||
|
generic::run_vscode_insiders_extensions_update(ctx)
|
||||||
|
})?,
|
||||||
|
Vscodium => runner.execute(*self, "VSCodium extensions", || {
|
||||||
|
generic::run_vscodium_extensions_update(ctx)
|
||||||
|
})?,
|
||||||
|
VscodiumInsiders => runner.execute(*self, "VSCodium Insiders extensions", || {
|
||||||
|
generic::run_vscodium_insiders_extensions_update(ctx)
|
||||||
|
})?,
|
||||||
|
Waydroid =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
runner.execute(*self, "Waydroid", || linux::run_waydroid(ctx))?
|
||||||
|
}
|
||||||
|
Winget =>
|
||||||
|
{
|
||||||
|
#[cfg(windows)]
|
||||||
|
runner.execute(*self, "Winget", || windows::run_winget(ctx))?
|
||||||
|
}
|
||||||
|
Wsl =>
|
||||||
|
{
|
||||||
|
#[cfg(windows)]
|
||||||
|
runner.execute(*self, "WSL", || windows::run_wsl_topgrade(ctx))?
|
||||||
|
}
|
||||||
|
WslUpdate =>
|
||||||
|
{
|
||||||
|
#[cfg(windows)]
|
||||||
|
runner.execute(*self, "Update WSL", || windows::update_wsl(ctx))?
|
||||||
|
}
|
||||||
|
Xcodes =>
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
runner.execute(*self, "Xcodes", || macos::update_xcodes(ctx))?
|
||||||
|
}
|
||||||
|
Yadm =>
|
||||||
|
{
|
||||||
|
#[cfg(unix)]
|
||||||
|
runner.execute(*self, "yadm", || unix::run_yadm(ctx))?
|
||||||
|
}
|
||||||
|
Yarn => runner.execute(*self, "yarn", || node::run_yarn_upgrade(ctx))?,
|
||||||
|
Yazi => runner.execute(*self, "Yazi packages", || generic::run_yazi(ctx))?,
|
||||||
|
Zigup => runner.execute(*self, "zigup", || generic::run_zigup(ctx))?,
|
||||||
|
Zvm => runner.execute(*self, "ZVM", || generic::run_zvm(ctx))?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
pub(crate) fn default_steps() -> Vec<Step> {
|
||||||
|
// For now, SelfRenamer and SelfUpdate isn't included as they're ran before the other non-steps (pre-commands, sudo, etc)
|
||||||
|
|
||||||
|
use Step::*;
|
||||||
|
// Could probably have a smaller starting capacity, but this at least ensures only 2 allocations:
|
||||||
|
// initial and shrink
|
||||||
|
let mut steps = Vec::with_capacity(Step::COUNT);
|
||||||
|
|
||||||
|
// Not combined with other generic steps to preserve the order as it was in main.rs originally,
|
||||||
|
// but this can be changed in the future.
|
||||||
|
steps.push(Remotes);
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
steps.extend_from_slice(&[Wsl, WslUpdate, Chocolatey, Scoop, Winget, System, MicrosoftStore]);
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
steps.extend_from_slice(&[BrewFormula, BrewCask, Macports, Xcodes, Sparkle, Mas, System]);
|
||||||
|
|
||||||
|
#[cfg(target_os = "dragonfly")]
|
||||||
|
steps.extend_from_slice(&[Pkg, Audit]);
|
||||||
|
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
steps.extend_from_slice(&[Pkg, System, Audit]);
|
||||||
|
|
||||||
|
#[cfg(target_os = "openbsd")]
|
||||||
|
steps.extend_from_slice(&[Pkg, System]);
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
steps.push(Pkg);
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
steps.extend_from_slice(&[
|
||||||
|
System,
|
||||||
|
ConfigUpdate,
|
||||||
|
AM,
|
||||||
|
AppMan,
|
||||||
|
DebGet,
|
||||||
|
Toolbx,
|
||||||
|
Snap,
|
||||||
|
Pacstall,
|
||||||
|
Pacdef,
|
||||||
|
Protonup,
|
||||||
|
Distrobox,
|
||||||
|
DkpPacman,
|
||||||
|
Firmware,
|
||||||
|
Restarts,
|
||||||
|
Flatpak,
|
||||||
|
BrewFormula,
|
||||||
|
BrewCask,
|
||||||
|
Lure,
|
||||||
|
Waydroid,
|
||||||
|
AutoCpufreq,
|
||||||
|
CinnamonSpices,
|
||||||
|
Mandb,
|
||||||
|
Pkgfile,
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
steps.extend_from_slice(&[
|
||||||
|
Yadm,
|
||||||
|
Nix,
|
||||||
|
NixHelper,
|
||||||
|
Guix,
|
||||||
|
HomeManager,
|
||||||
|
Asdf,
|
||||||
|
Mise,
|
||||||
|
Pkgin,
|
||||||
|
BunPackages,
|
||||||
|
Shell,
|
||||||
|
Tmux,
|
||||||
|
Pearl,
|
||||||
|
#[cfg(not(any(target_os = "macos", target_os = "android")))]
|
||||||
|
GnomeShellExtensions,
|
||||||
|
Pyenv,
|
||||||
|
Sdkman,
|
||||||
|
Rcm,
|
||||||
|
Maza,
|
||||||
|
Hyprpm,
|
||||||
|
Atuin,
|
||||||
|
]);
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "dragonfly"
|
||||||
|
)))]
|
||||||
|
steps.push(Atom);
|
||||||
|
|
||||||
|
// The following update function should be executed on all OSes.
|
||||||
|
steps.extend_from_slice(&[
|
||||||
|
Fossil,
|
||||||
|
Elan,
|
||||||
|
Rye,
|
||||||
|
Rustup,
|
||||||
|
Juliaup,
|
||||||
|
Dotnet,
|
||||||
|
Choosenim,
|
||||||
|
Cargo,
|
||||||
|
Flutter,
|
||||||
|
Go,
|
||||||
|
Emacs,
|
||||||
|
Opam,
|
||||||
|
Vcpkg,
|
||||||
|
Pipx,
|
||||||
|
Pipxu,
|
||||||
|
Vscode,
|
||||||
|
VscodeInsiders,
|
||||||
|
Vscodium,
|
||||||
|
VscodiumInsiders,
|
||||||
|
Conda,
|
||||||
|
Mamba,
|
||||||
|
Pixi,
|
||||||
|
Miktex,
|
||||||
|
Pip3,
|
||||||
|
PipReview,
|
||||||
|
PipReviewLocal,
|
||||||
|
Pipupgrade,
|
||||||
|
Ghcup,
|
||||||
|
Stack,
|
||||||
|
Tldr,
|
||||||
|
Tlmgr,
|
||||||
|
Myrepos,
|
||||||
|
Chezmoi,
|
||||||
|
Jetpack,
|
||||||
|
Vim,
|
||||||
|
Kakoune,
|
||||||
|
Helix,
|
||||||
|
Node,
|
||||||
|
Yarn,
|
||||||
|
Pnpm,
|
||||||
|
VoltaPackages,
|
||||||
|
Containers,
|
||||||
|
Deno,
|
||||||
|
Composer,
|
||||||
|
Krew,
|
||||||
|
Helm,
|
||||||
|
Gem,
|
||||||
|
RubyGems,
|
||||||
|
Julia,
|
||||||
|
Haxelib,
|
||||||
|
Sheldon,
|
||||||
|
Stew,
|
||||||
|
Rtcl,
|
||||||
|
Bin,
|
||||||
|
Gcloud,
|
||||||
|
Micro,
|
||||||
|
Raco,
|
||||||
|
Spicetify,
|
||||||
|
GithubCliExtensions,
|
||||||
|
Bob,
|
||||||
|
Certbot,
|
||||||
|
GitRepos,
|
||||||
|
ClamAvDb,
|
||||||
|
PlatformioCore,
|
||||||
|
Lensfun,
|
||||||
|
Poetry,
|
||||||
|
Uv,
|
||||||
|
Zvm,
|
||||||
|
Aqua,
|
||||||
|
Bun,
|
||||||
|
Zigup,
|
||||||
|
JetbrainsToolbox,
|
||||||
|
AndroidStudio,
|
||||||
|
JetbrainsAqua,
|
||||||
|
JetbrainsClion,
|
||||||
|
JetbrainsDatagrip,
|
||||||
|
JetbrainsDataspell,
|
||||||
|
// JetBrains dotCover has no CLI
|
||||||
|
// JetBrains dotMemory has no CLI
|
||||||
|
// JetBrains dotPeek has no CLI
|
||||||
|
// JetBrains dotTrace has no CLI
|
||||||
|
// JetBrains Fleet has a different CLI without a `fleet update` command.
|
||||||
|
JetbrainsGateway,
|
||||||
|
JetbrainsGoland,
|
||||||
|
JetbrainsIdea,
|
||||||
|
JetbrainsMps,
|
||||||
|
JetbrainsPhpstorm,
|
||||||
|
JetbrainsPycharm,
|
||||||
|
// JetBrains ReSharper has no CLI (it's a VSCode extension)
|
||||||
|
// JetBrains ReSharper C++ has no CLI (it's a VSCode extension)
|
||||||
|
JetbrainsRider,
|
||||||
|
JetbrainsRubymine,
|
||||||
|
JetbrainsRustrover,
|
||||||
|
// JetBrains Space Desktop does not have a CLI
|
||||||
|
JetbrainsWebstorm,
|
||||||
|
Yazi,
|
||||||
|
Falconf,
|
||||||
|
Powershell,
|
||||||
|
CustomCommands,
|
||||||
|
Vagrant,
|
||||||
|
Typst,
|
||||||
|
]);
|
||||||
|
|
||||||
|
steps.shrink_to_fit();
|
||||||
|
|
||||||
|
steps
|
||||||
|
}
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use color_eyre::eyre::eyre;
|
|
||||||
use color_eyre::eyre::Context;
|
use color_eyre::eyre::Context;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use color_eyre::eyre::{eyre, OptionExt};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
use wildmatch::WildMatch;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::error::{self, TopgradeError};
|
use crate::error::{SkipStep, TopgradeError};
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::{execution_context::ExecutionContext, utils::require};
|
use crate::{execution_context::ExecutionContext, utils::require};
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
// A string found in the output of docker for containers that weren't found in
|
// A string found in the output of docker for containers that weren't found in
|
||||||
// the docker registry. We use this to gracefully handle and skip containers
|
// the docker registry. We use this to gracefully handle and skip containers
|
||||||
@@ -18,16 +23,63 @@ use crate::{execution_context::ExecutionContext, utils::require};
|
|||||||
// themselves or when using docker-compose.
|
// themselves or when using docker-compose.
|
||||||
const NONEXISTENT_REPO: &str = "repository does not exist";
|
const NONEXISTENT_REPO: &str = "repository does not exist";
|
||||||
|
|
||||||
|
// A string found in the output of docker when Docker Desktop is not running.
|
||||||
|
const DOCKER_NOT_RUNNING: &str = "We recommend to activate the WSL integration in Docker Desktop settings.";
|
||||||
|
|
||||||
|
/// Uniquely identifies a `Container`.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Container {
|
||||||
|
/// `Repository` and `Tag`
|
||||||
|
///
|
||||||
|
/// format: `Repository:Tag`, e.g., `nixos/nix:latest`.
|
||||||
|
repo_tag: String,
|
||||||
|
/// Platform
|
||||||
|
///
|
||||||
|
/// format: `OS/Architecture`, e.g., `linux/amd64`.
|
||||||
|
platform: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Container {
|
||||||
|
/// Construct a new `Container`.
|
||||||
|
fn new(repo_tag: String, platform: String) -> Self {
|
||||||
|
Self { repo_tag, platform }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Container {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// e.g., "`fedora:latest` for `linux/amd64`"
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
t!(
|
||||||
|
"`{repo_tag}` for `{platform}`",
|
||||||
|
repo_tag = self.repo_tag,
|
||||||
|
platform = self.platform
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a Vector of all containers, with Strings in the format
|
/// Returns a Vector of all containers, with Strings in the format
|
||||||
/// "REGISTRY/[PATH/]CONTAINER_NAME:TAG"
|
/// "REGISTRY/[PATH/]CONTAINER_NAME:TAG"
|
||||||
fn list_containers(crt: &Path) -> Result<Vec<String>> {
|
///
|
||||||
|
/// Containers specified in `ignored_containers` will be filtered out.
|
||||||
|
fn list_containers(crt: &Path, ignored_containers: Option<&Vec<String>>) -> Result<Vec<Container>> {
|
||||||
|
let ignored_containers = ignored_containers.map(|patterns| {
|
||||||
|
patterns
|
||||||
|
.iter()
|
||||||
|
.map(|pattern| WildMatch::new(pattern))
|
||||||
|
.collect::<Vec<WildMatch>>()
|
||||||
|
});
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Querying '{} image ls --format \"{{{{.Repository}}}}:{{{{.Tag}}}}\"' for containers",
|
"Querying '{} image ls --format \"{{{{.Repository}}}}:{{{{.Tag}}}}/{{{{.ID}}}}\"' for containers",
|
||||||
crt.display()
|
crt.display()
|
||||||
);
|
);
|
||||||
let output = Command::new(crt)
|
let output = Command::new(crt)
|
||||||
.args(["image", "ls", "--format", "{{.Repository}}:{{.Tag}}"])
|
.args(["image", "ls", "--format", "{{.Repository}}:{{.Tag}} {{.ID}}"])
|
||||||
.output_checked_with_utf8(|_| Ok(()))?;
|
.output_checked_utf8()?;
|
||||||
|
|
||||||
let mut retval = vec![];
|
let mut retval = vec![];
|
||||||
for line in output.stdout.lines() {
|
for line in output.stdout.lines() {
|
||||||
@@ -49,26 +101,103 @@ fn list_containers(crt: &Path) -> Result<Vec<String>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug!("Using container '{}'", line);
|
debug!("Using container '{}'", line);
|
||||||
retval.push(String::from(line));
|
|
||||||
|
// line is of format: `Repository:Tag ImageID`, e.g., `nixos/nix:latest d80fea9c32b4`
|
||||||
|
let split_res = line.split(' ').collect::<Vec<&str>>();
|
||||||
|
if split_res.len() != 2 {
|
||||||
|
return Err(eyre!(format!(
|
||||||
|
"Got erroneous output from `{} image ls --format \"{{.Repository}}:{{.Tag}} {{.ID}}\"; Expected line to split into 2 parts",
|
||||||
|
crt.display()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let (repo_tag, image_id) = (split_res[0], split_res[1]);
|
||||||
|
|
||||||
|
if let Some(ref ignored_containers) = ignored_containers {
|
||||||
|
if ignored_containers.iter().any(|pattern| pattern.matches(repo_tag)) {
|
||||||
|
debug!("Skipping ignored container '{}'", line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Querying '{} image inspect --format \"{{{{.Os}}}}/{{{{.Architecture}}}}\"' for container {}",
|
||||||
|
crt.display(),
|
||||||
|
image_id
|
||||||
|
);
|
||||||
|
let inspect_output = Command::new(crt)
|
||||||
|
.args(["image", "inspect", image_id, "--format", "{{.Os}}/{{.Architecture}}"])
|
||||||
|
.output_checked_utf8()?;
|
||||||
|
let mut platform = inspect_output.stdout;
|
||||||
|
// truncate the tailing new line character
|
||||||
|
platform.truncate(platform.len() - 1);
|
||||||
|
if !platform.contains('/') {
|
||||||
|
return Err(eyre!(format!(
|
||||||
|
"Got erroneous output from `{} image ls --format \"{{.Repository}}:{{.Tag}} {{.ID}}\"; Expected platform to contain '/'",
|
||||||
|
crt.display()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
retval.push(Container::new(repo_tag.to_string(), platform));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(retval)
|
Ok(retval)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_containers(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_containers(ctx: &ExecutionContext) -> Result<()> {
|
||||||
// Prefer podman, fall back to docker if not present
|
// Check what runtime is specified in the config
|
||||||
let crt = require("podman").or_else(|_| require("docker"))?;
|
let container_runtime = ctx.config().containers_runtime().to_string();
|
||||||
|
let crt = require(container_runtime)?;
|
||||||
debug!("Using container runtime '{}'", crt.display());
|
debug!("Using container runtime '{}'", crt.display());
|
||||||
|
|
||||||
print_separator("Containers");
|
print_separator(t!("Containers"));
|
||||||
let mut success = true;
|
|
||||||
let containers = list_containers(&crt).context("Failed to list Docker containers")?;
|
let output = Command::new(&crt).arg("--help").output_checked_with(|_| Ok(()))?;
|
||||||
|
let status_code = output
|
||||||
|
.status
|
||||||
|
.code()
|
||||||
|
.ok_or_eyre("Couldn't get status code (terminated by signal)")?;
|
||||||
|
let stdout = std::str::from_utf8(&output.stdout).wrap_err("Expected output to be valid UTF-8")?;
|
||||||
|
if stdout.contains(DOCKER_NOT_RUNNING) && status_code == 1 {
|
||||||
|
// Write the output
|
||||||
|
io::stdout().write_all(&output.stdout)?;
|
||||||
|
io::stderr().write_all(&output.stderr)?;
|
||||||
|
// Don't crash, but don't be silent either.
|
||||||
|
// This can happen in other ways than Docker Desktop not running, but even in those cases
|
||||||
|
// we don't want to crash, since the containers step is enabled by default.
|
||||||
|
warn!(
|
||||||
|
"{} seems to be non-functional right now (see above). Is WSL integration enabled for Docker Desktop? Is Docker Desktop running?",
|
||||||
|
crt.display()
|
||||||
|
);
|
||||||
|
return Err(SkipStep(format!(
|
||||||
|
"{} seems to be non-functional right now. Possibly WSL integration is not enabled for Docker Desktop, or Docker Desktop is not running.",
|
||||||
|
crt.display()
|
||||||
|
)).into());
|
||||||
|
} else if !output.status.success() {
|
||||||
|
// Write the output
|
||||||
|
io::stdout().write_all(&output.stdout)?;
|
||||||
|
io::stderr().write_all(&output.stderr)?;
|
||||||
|
// If we saw the message, but the code is not 1 (e.g. 0, or a non-1 failure), crash, as we expect a 1.
|
||||||
|
// If we did not see the message, it's broken in some way we do not understand.
|
||||||
|
return Err(eyre!(
|
||||||
|
"{0} seems to be non-functional (`{0} --help` returned non-zero exit code {1})",
|
||||||
|
crt.display(),
|
||||||
|
status_code,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let containers =
|
||||||
|
list_containers(&crt, ctx.config().containers_ignored_tags()).context("Failed to list Docker containers")?;
|
||||||
debug!("Containers to inspect: {:?}", containers);
|
debug!("Containers to inspect: {:?}", containers);
|
||||||
|
|
||||||
for container in containers.iter() {
|
for container in &containers {
|
||||||
debug!("Pulling container '{}'", container);
|
debug!("Pulling container '{}'", container);
|
||||||
let args = vec!["pull", &container[..]];
|
let mut args = vec!["pull", container.repo_tag.as_str()];
|
||||||
let mut exec = ctx.run_type().execute(&crt);
|
if container.platform.as_str() != "/" {
|
||||||
|
args.push("--platform");
|
||||||
|
args.push(container.platform.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut exec = ctx.execute(&crt);
|
||||||
|
|
||||||
if let Err(e) = exec.args(&args).status_checked() {
|
if let Err(e) = exec.args(&args).status_checked() {
|
||||||
error!("Pulling container '{}' failed: {}", container, e);
|
error!("Pulling container '{}' failed: {}", container, e);
|
||||||
@@ -90,27 +219,21 @@ pub fn run_containers(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
success = false;
|
return Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().containers_system_prune() {
|
||||||
|
// Run system prune to clean up unused containers, networks, and build cache
|
||||||
|
ctx.execute(&crt)
|
||||||
|
.args(["system", "prune", "--force"])
|
||||||
|
.status_checked()?
|
||||||
|
// Only run `image prune` if we don't run `system prune`
|
||||||
|
} else if ctx.config().cleanup() {
|
||||||
// Remove dangling images
|
// Remove dangling images
|
||||||
debug!("Removing dangling images");
|
debug!("Removing dangling images");
|
||||||
if let Err(e) = ctx
|
ctx.execute(&crt).args(["image", "prune", "-f"]).status_checked()?
|
||||||
.run_type()
|
|
||||||
.execute(&crt)
|
|
||||||
.args(["image", "prune", "-f"])
|
|
||||||
.status_checked()
|
|
||||||
{
|
|
||||||
error!("Removing dangling images failed: {}", e);
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if success {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
|
||||||
Err(eyre!(error::StepFailed))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
(when (fboundp 'paradox-upgrade-packages)
|
(when (featurep 'package)
|
||||||
(progn
|
(if (fboundp 'package-upgrade-all)
|
||||||
(unless (boundp 'paradox-github-token)
|
(package-upgrade-all nil)
|
||||||
(setq paradox-github-token t))
|
(message "Your Emacs version doesn't support unattended packages upgrade")))
|
||||||
(paradox-upgrade-packages)
|
|
||||||
(princ
|
|
||||||
(if (get-buffer "*Paradox Report*")
|
|
||||||
(with-current-buffer "*Paradox Report*" (buffer-string))
|
|
||||||
"\nNothing to upgrade\n"))))
|
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
#[cfg(any(windows, target_os = "macos"))]
|
#[cfg(windows)]
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use directories::BaseDirs;
|
use etcetera::base_strategy::BaseStrategy;
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
|
use crate::step::Step;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::{require, require_option, PathExt};
|
use crate::utils::{require, require_option, PathExt};
|
||||||
use crate::Step;
|
|
||||||
|
|
||||||
const EMACS_UPGRADE: &str = include_str!("emacs.el");
|
const EMACS_UPGRADE: &str = include_str!("emacs.el");
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@@ -23,20 +24,12 @@ pub struct Emacs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Emacs {
|
impl Emacs {
|
||||||
fn directory_path(base_dirs: &BaseDirs) -> Option<PathBuf> {
|
fn directory_path() -> Option<PathBuf> {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
cfg_if::cfg_if! {
|
return {
|
||||||
if #[cfg(target_os = "macos")] {
|
let emacs_xdg_dir = crate::XDG_DIRS.config_dir().join("emacs").if_exists();
|
||||||
let emacs_xdg_dir = env::var("XDG_CONFIG_HOME")
|
crate::HOME_DIR.join(".emacs.d").if_exists().or(emacs_xdg_dir)
|
||||||
.ok()
|
};
|
||||||
.and_then(|config| PathBuf::from(config).join("emacs").if_exists())
|
|
||||||
.or_else(|| base_dirs.home_dir().join(".config/emacs").if_exists());
|
|
||||||
} else {
|
|
||||||
let emacs_xdg_dir = base_dirs.config_dir().join("emacs").if_exists();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(unix)]
|
|
||||||
return base_dirs.home_dir().join(".emacs.d").if_exists().or(emacs_xdg_dir);
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
return env::var("HOME")
|
return env::var("HOME")
|
||||||
@@ -47,11 +40,11 @@ impl Emacs {
|
|||||||
.if_exists()
|
.if_exists()
|
||||||
.or_else(|| PathBuf::from(&home).join(".config\\emacs").if_exists())
|
.or_else(|| PathBuf::from(&home).join(".config\\emacs").if_exists())
|
||||||
})
|
})
|
||||||
.or_else(|| base_dirs.data_dir().join(".emacs.d").if_exists());
|
.or_else(|| crate::WINDOWS_DIRS.data_dir().join(".emacs.d").if_exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(base_dirs: &BaseDirs) -> Self {
|
pub fn new() -> Self {
|
||||||
let directory = Emacs::directory_path(base_dirs);
|
let directory = Emacs::directory_path();
|
||||||
let doom = directory.as_ref().and_then(|d| d.join(DOOM_PATH).if_exists());
|
let doom = directory.as_ref().and_then(|d| d.join(DOOM_PATH).if_exists());
|
||||||
Self { directory, doom }
|
Self { directory, doom }
|
||||||
}
|
}
|
||||||
@@ -67,12 +60,16 @@ impl Emacs {
|
|||||||
fn update_doom(doom: &Path, ctx: &ExecutionContext) -> Result<()> {
|
fn update_doom(doom: &Path, ctx: &ExecutionContext) -> Result<()> {
|
||||||
print_separator("Doom Emacs");
|
print_separator("Doom Emacs");
|
||||||
|
|
||||||
let mut command = ctx.run_type().execute(doom);
|
let mut command = ctx.execute(doom);
|
||||||
if ctx.config().yes(Step::Emacs) {
|
if ctx.config().yes(Step::Emacs) {
|
||||||
command.arg("--force");
|
command.arg("--force");
|
||||||
}
|
}
|
||||||
|
|
||||||
command.args(["upgrade"]);
|
command.arg("upgrade");
|
||||||
|
|
||||||
|
if ctx.config().doom_aot() {
|
||||||
|
command.arg("--aot");
|
||||||
|
}
|
||||||
|
|
||||||
command.status_checked()
|
command.status_checked()
|
||||||
}
|
}
|
||||||
@@ -82,13 +79,16 @@ impl Emacs {
|
|||||||
if let Some(doom) = &self.doom {
|
if let Some(doom) = &self.doom {
|
||||||
Emacs::update_doom(doom, ctx)?;
|
Emacs::update_doom(doom, ctx)?;
|
||||||
}
|
}
|
||||||
let init_file = require_option(self.directory.as_ref(), String::from("Emacs directory does not exist"))?
|
let init_file = require_option(
|
||||||
|
self.directory.as_ref(),
|
||||||
|
t!("Emacs directory does not exist").to_string(),
|
||||||
|
)?
|
||||||
.join("init.el")
|
.join("init.el")
|
||||||
.require()?;
|
.require()?;
|
||||||
|
|
||||||
print_separator("Emacs");
|
print_separator("Emacs");
|
||||||
|
|
||||||
let mut command = ctx.run_type().execute(emacs);
|
let mut command = ctx.execute(emacs);
|
||||||
|
|
||||||
command
|
command
|
||||||
.args(["--batch", "--debug-init", "-l"])
|
.args(["--batch", "--debug-init", "-l"])
|
||||||
|
|||||||
1680
src/steps/generic.rs
1680
src/steps/generic.rs
File diff suppressed because it is too large
Load Diff
475
src/steps/git.rs
475
src/steps/git.rs
@@ -3,10 +3,10 @@ use std::io;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Output, Stdio};
|
use std::process::{Command, Output, Stdio};
|
||||||
|
|
||||||
|
use color_eyre::eyre::Context;
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use console::style;
|
use console::style;
|
||||||
use futures::stream::{iter, FuturesUnordered};
|
use futures::stream::{iter, FuturesUnordered, StreamExt};
|
||||||
use futures::StreamExt;
|
|
||||||
use glob::{glob_with, MatchOptions};
|
use glob::{glob_with, MatchOptions};
|
||||||
use tokio::process::Command as AsyncCommand;
|
use tokio::process::Command as AsyncCommand;
|
||||||
use tokio::runtime;
|
use tokio::runtime;
|
||||||
@@ -14,128 +14,166 @@ use tracing::{debug, error};
|
|||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::executor::RunType;
|
use crate::step::Step;
|
||||||
|
use crate::steps::emacs::Emacs;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::{which, PathExt};
|
use crate::utils::{require, PathExt};
|
||||||
use crate::{error::SkipStep, terminal::print_warning};
|
use crate::{error::SkipStep, terminal::print_warning, HOME_DIR};
|
||||||
|
use etcetera::base_strategy::BaseStrategy;
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use crate::XDG_DIRS;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
use crate::WINDOWS_DIRS;
|
||||||
|
|
||||||
|
pub fn run_git_pull(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let mut repos = RepoStep::try_new()?;
|
||||||
|
let config = ctx.config();
|
||||||
|
|
||||||
|
// handle built-in repos
|
||||||
|
if config.use_predefined_git_repos() {
|
||||||
|
// should be executed on all the platforms
|
||||||
|
{
|
||||||
|
if config.should_run(Step::Emacs) {
|
||||||
|
let emacs = Emacs::new();
|
||||||
|
if !emacs.is_doom() {
|
||||||
|
if let Some(directory) = emacs.directory() {
|
||||||
|
repos.insert_if_repo(directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repos.insert_if_repo(HOME_DIR.join(".doom.d"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.should_run(Step::Vim) {
|
||||||
|
repos.insert_if_repo(HOME_DIR.join(".vim"));
|
||||||
|
repos.insert_if_repo(HOME_DIR.join(".config/nvim"));
|
||||||
|
}
|
||||||
|
|
||||||
|
repos.insert_if_repo(HOME_DIR.join(".ideavimrc"));
|
||||||
|
repos.insert_if_repo(HOME_DIR.join(".intellimacs"));
|
||||||
|
|
||||||
|
if config.should_run(Step::Rcm) {
|
||||||
|
repos.insert_if_repo(HOME_DIR.join(".dotfiles"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(powershell) = ctx.powershell() {
|
||||||
|
if let Some(profile) = powershell.profile() {
|
||||||
|
repos.insert_if_repo(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
repos.insert_if_repo(crate::steps::zsh::zshrc());
|
||||||
|
if config.should_run(Step::Tmux) {
|
||||||
|
repos.insert_if_repo(HOME_DIR.join(".tmux"));
|
||||||
|
}
|
||||||
|
repos.insert_if_repo(HOME_DIR.join(".config/fish"));
|
||||||
|
repos.insert_if_repo(XDG_DIRS.config_dir().join("openbox"));
|
||||||
|
repos.insert_if_repo(XDG_DIRS.config_dir().join("bspwm"));
|
||||||
|
repos.insert_if_repo(XDG_DIRS.config_dir().join("i3"));
|
||||||
|
repos.insert_if_repo(XDG_DIRS.config_dir().join("sway"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
repos.insert_if_repo(
|
||||||
|
WINDOWS_DIRS
|
||||||
|
.cache_dir()
|
||||||
|
.join("Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState"),
|
||||||
|
);
|
||||||
|
|
||||||
|
super::os::windows::insert_startup_scripts(&mut repos).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle user-defined repos
|
||||||
|
if let Some(custom_git_repos) = config.git_repos() {
|
||||||
|
for git_repo in custom_git_repos {
|
||||||
|
repos.glob_insert(&shellexpand::tilde(git_repo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn the user about the bad patterns.
|
||||||
|
//
|
||||||
|
// NOTE: this should be executed **before** skipping the Git step or the
|
||||||
|
// user won't receive this warning in the cases where all the paths configured
|
||||||
|
// are bad patterns.
|
||||||
|
repos.bad_patterns.iter().for_each(|pattern| {
|
||||||
|
print_warning(t!(
|
||||||
|
"Path {pattern} did not contain any git repositories",
|
||||||
|
pattern = pattern
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
if repos.is_repos_empty() {
|
||||||
|
return Err(SkipStep(t!("No repositories to pull").to_string()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
print_separator(t!("Git repositories"));
|
||||||
|
|
||||||
|
repos.pull_repos(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
static PATH_PREFIX: &str = "\\\\?\\";
|
static PATH_PREFIX: &str = "\\\\?\\";
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct RepoStep {
|
||||||
pub struct Git {
|
git: PathBuf,
|
||||||
git: Option<PathBuf>,
|
repos: HashSet<PathBuf>,
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Repositories<'a> {
|
|
||||||
git: &'a Git,
|
|
||||||
repositories: HashSet<String>,
|
|
||||||
glob_match_options: MatchOptions,
|
glob_match_options: MatchOptions,
|
||||||
bad_patterns: Vec<String>,
|
bad_patterns: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn output_checked_utf8(output: Output) -> Result<()> {
|
fn output_checked_utf8(output: Output) -> Result<()> {
|
||||||
if !(output.status.success()) {
|
if !(output.status.success()) {
|
||||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
Err(eyre!(stderr))
|
let stderr = stderr.trim();
|
||||||
|
Err(eyre!("{stderr}"))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn pull_repository(repo: String, git: &Path, ctx: &ExecutionContext<'_>) -> Result<()> {
|
fn get_head_revision<P: AsRef<Path>>(git: &Path, repo: P) -> Option<String> {
|
||||||
let path = repo.to_string();
|
|
||||||
let before_revision = get_head_revision(git, &repo);
|
|
||||||
|
|
||||||
println!("{} {}", style("Pulling").cyan().bold(), path);
|
|
||||||
|
|
||||||
let mut command = AsyncCommand::new(git);
|
|
||||||
|
|
||||||
command
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.current_dir(&repo)
|
|
||||||
.args(["pull", "--ff-only"]);
|
|
||||||
|
|
||||||
if let Some(extra_arguments) = ctx.config().git_arguments() {
|
|
||||||
command.args(extra_arguments.split_whitespace());
|
|
||||||
}
|
|
||||||
|
|
||||||
let pull_output = command.output().await?;
|
|
||||||
let submodule_output = AsyncCommand::new(git)
|
|
||||||
.args(["submodule", "update", "--recursive"])
|
|
||||||
.current_dir(&repo)
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.output()
|
|
||||||
.await?;
|
|
||||||
let result = output_checked_utf8(pull_output).and_then(|_| output_checked_utf8(submodule_output));
|
|
||||||
|
|
||||||
if let Err(message) = &result {
|
|
||||||
println!("{} pulling {}", style("Failed").red().bold(), &repo);
|
|
||||||
print!("{message}");
|
|
||||||
} else {
|
|
||||||
let after_revision = get_head_revision(git, &repo);
|
|
||||||
|
|
||||||
match (&before_revision, &after_revision) {
|
|
||||||
(Some(before), Some(after)) if before != after => {
|
|
||||||
println!("{} {}:", style("Changed").yellow().bold(), &repo);
|
|
||||||
|
|
||||||
Command::new(git)
|
Command::new(git)
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.current_dir(&repo)
|
.current_dir(repo.as_ref())
|
||||||
.args([
|
|
||||||
"--no-pager",
|
|
||||||
"log",
|
|
||||||
"--no-decorate",
|
|
||||||
"--oneline",
|
|
||||||
&format!("{before}..{after}"),
|
|
||||||
])
|
|
||||||
.status_checked()?;
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("{} {}", style("Up-to-date").green().bold(), &repo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.map(|_| ())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_head_revision(git: &Path, repo: &str) -> Option<String> {
|
|
||||||
Command::new(git)
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.current_dir(repo)
|
|
||||||
.args(["rev-parse", "HEAD"])
|
.args(["rev-parse", "HEAD"])
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.map(|output| output.stdout.trim().to_string())
|
.map(|output| output.stdout.trim().to_string())
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!("Error getting revision for {}: {}", repo, e);
|
error!("Error getting revision for {}: {e}", repo.as_ref().display(),);
|
||||||
|
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_remotes(git: &Path, repo: &str) -> Option<bool> {
|
impl RepoStep {
|
||||||
Command::new(git)
|
/// Try to create a `RepoStep`, fail if `git` is not found.
|
||||||
.stdin(Stdio::null())
|
pub fn try_new() -> Result<Self> {
|
||||||
.current_dir(repo)
|
let git = require("git")?;
|
||||||
.args(["remote", "show"])
|
let mut glob_match_options = MatchOptions::new();
|
||||||
.output_checked_utf8()
|
|
||||||
.map(|output| output.stdout.lines().count() > 0)
|
|
||||||
.map_err(|e| {
|
|
||||||
error!("Error getting remotes for {}: {}", repo, e);
|
|
||||||
e
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Git {
|
if cfg!(windows) {
|
||||||
pub fn new() -> Self {
|
glob_match_options.case_sensitive = false;
|
||||||
Self { git: which("git") }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_repo_root<P: AsRef<Path>>(&self, path: P) -> Option<String> {
|
Ok(Self {
|
||||||
|
git,
|
||||||
|
repos: HashSet::new(),
|
||||||
|
bad_patterns: Vec::new(),
|
||||||
|
glob_match_options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to get the root of the repo specified in `path`.
|
||||||
|
pub fn get_repo_root<P: AsRef<Path>>(&self, path: P) -> Option<PathBuf> {
|
||||||
match path.as_ref().canonicalize() {
|
match path.as_ref().canonicalize() {
|
||||||
Ok(mut path) => {
|
Ok(mut path) => {
|
||||||
debug_assert!(path.exists());
|
debug_assert!(path.exists());
|
||||||
@@ -159,105 +197,59 @@ impl Git {
|
|||||||
path_string
|
path_string
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(git) = &self.git {
|
let output = Command::new(&self.git)
|
||||||
let output = Command::new(git)
|
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.current_dir(path)
|
.current_dir(path)
|
||||||
.args(["rev-parse", "--show-toplevel"])
|
.args(["rev-parse", "--show-toplevel"])
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|output| output.stdout.trim().to_string());
|
// trim the last newline char
|
||||||
|
.map(|output| PathBuf::from(output.stdout.trim()));
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if e.kind() == io::ErrorKind::NotFound {
|
||||||
|
debug!("{} does not exist", path.as_ref().display());
|
||||||
|
} else {
|
||||||
|
error!("Error looking for {}: {e}", path.as_ref().display());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => match e.kind() {
|
|
||||||
io::ErrorKind::NotFound => debug!("{} does not exists", path.as_ref().display()),
|
|
||||||
_ => error!("Error looking for {}: {}", path.as_ref().display(), e),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
pub fn multi_pull_step(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> {
|
|
||||||
if repositories.repositories.is_empty() {
|
|
||||||
return Err(SkipStep(String::from("No repositories to pull")).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
print_separator("Git repositories");
|
|
||||||
repositories
|
|
||||||
.bad_patterns
|
|
||||||
.iter()
|
|
||||||
.for_each(|pattern| print_warning(format!("Path {pattern} did not contain any git repositories")));
|
|
||||||
self.multi_pull(repositories, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multi_pull(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> {
|
|
||||||
let git = self.git.as_ref().unwrap();
|
|
||||||
|
|
||||||
if let RunType::Dry = ctx.run_type() {
|
|
||||||
repositories
|
|
||||||
.repositories
|
|
||||||
.iter()
|
|
||||||
.for_each(|repo| println!("Would pull {}", &repo));
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let futures_iterator = repositories
|
|
||||||
.repositories
|
|
||||||
.iter()
|
|
||||||
.filter(|repo| match has_remotes(git, repo) {
|
|
||||||
Some(false) => {
|
|
||||||
println!(
|
|
||||||
"{} {} because it has no remotes",
|
|
||||||
style("Skipping").yellow().bold(),
|
|
||||||
repo
|
|
||||||
);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => true, // repo has remotes or command to check for remotes has failed. proceed to pull anyway.
|
|
||||||
})
|
|
||||||
.map(|repo| pull_repository(repo.clone(), git, ctx));
|
|
||||||
|
|
||||||
let stream_of_futures = if let Some(limit) = ctx.config().git_concurrency_limit() {
|
|
||||||
iter(futures_iterator).buffer_unordered(limit).boxed()
|
|
||||||
} else {
|
|
||||||
futures_iterator.collect::<FuturesUnordered<_>>().boxed()
|
|
||||||
};
|
|
||||||
|
|
||||||
let basic_rt = runtime::Runtime::new()?;
|
|
||||||
let results = basic_rt.block_on(async { stream_of_futures.collect::<Vec<Result<()>>>().await });
|
|
||||||
|
|
||||||
let error = results.into_iter().find(|r| r.is_err());
|
|
||||||
error.unwrap_or(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Repositories<'a> {
|
|
||||||
pub fn new(git: &'a Git) -> Self {
|
|
||||||
let mut glob_match_options = MatchOptions::new();
|
|
||||||
|
|
||||||
if cfg!(windows) {
|
|
||||||
glob_match_options.case_sensitive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
git,
|
|
||||||
repositories: HashSet::new(),
|
|
||||||
bad_patterns: Vec::new(),
|
|
||||||
glob_match_options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Check if `path` is a git repo, if yes, add it to `self.repos`.
|
||||||
|
///
|
||||||
|
/// Return the check result.
|
||||||
pub fn insert_if_repo<P: AsRef<Path>>(&mut self, path: P) -> bool {
|
pub fn insert_if_repo<P: AsRef<Path>>(&mut self, path: P) -> bool {
|
||||||
if let Some(repo) = self.git.get_repo_root(path) {
|
if let Some(repo) = self.get_repo_root(path) {
|
||||||
self.repositories.insert(repo);
|
self.repos.insert(repo);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if `repo` has a remote.
|
||||||
|
fn has_remotes<P: AsRef<Path>>(&self, repo: P) -> Option<bool> {
|
||||||
|
let mut cmd = Command::new(&self.git);
|
||||||
|
cmd.stdin(Stdio::null())
|
||||||
|
.current_dir(repo.as_ref())
|
||||||
|
.args(["remote", "show"]);
|
||||||
|
|
||||||
|
let res = cmd.output_checked_utf8();
|
||||||
|
|
||||||
|
res.map(|output| output.stdout.lines().count() > 0)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Error getting remotes for {}: {e}", repo.as_ref().display());
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Similar to `insert_if_repo`, with glob support.
|
||||||
pub fn glob_insert(&mut self, pattern: &str) {
|
pub fn glob_insert(&mut self, pattern: &str) {
|
||||||
if let Ok(glob) = glob_with(pattern, self.glob_match_options) {
|
if let Ok(glob) = glob_with(pattern, self.glob_match_options) {
|
||||||
let mut last_git_repo: Option<PathBuf> = None;
|
let mut last_git_repo: Option<PathBuf> = None;
|
||||||
@@ -267,7 +259,7 @@ impl<'a> Repositories<'a> {
|
|||||||
if let Some(last_git_repo) = &last_git_repo {
|
if let Some(last_git_repo) = &last_git_repo {
|
||||||
if path.is_descendant_of(last_git_repo) {
|
if path.is_descendant_of(last_git_repo) {
|
||||||
debug!(
|
debug!(
|
||||||
"Skipping {} because it's a decendant of last known repo {}",
|
"Skipping {} because it's a descendant of last known repo {}",
|
||||||
path.display(),
|
path.display(),
|
||||||
last_git_repo.display()
|
last_git_repo.display()
|
||||||
);
|
);
|
||||||
@@ -279,7 +271,7 @@ impl<'a> Repositories<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error in path {}", e);
|
error!("Error in path {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,18 +280,141 @@ impl<'a> Repositories<'a> {
|
|||||||
self.bad_patterns.push(String::from(pattern));
|
self.bad_patterns.push(String::from(pattern));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("Bad glob pattern: {}", pattern);
|
error!("Bad glob pattern: {pattern}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
/// True if `self.repos` is empty.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_repos_empty(&self) -> bool {
|
||||||
self.repositories.is_empty()
|
self.repos.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove `path` from `self.repos`.
|
||||||
|
///
|
||||||
|
// `cfg(unix)` because it is only used in the oh-my-zsh step.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub fn remove(&mut self, path: &str) {
|
pub fn remove<P: AsRef<Path>>(&mut self, path: P) {
|
||||||
let _removed = self.repositories.remove(path);
|
let _removed = self.repos.remove(path.as_ref());
|
||||||
debug_assert!(_removed);
|
debug_assert!(_removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to pull a repo.
|
||||||
|
async fn pull_repo<P: AsRef<Path>>(&self, ctx: &ExecutionContext<'_>, repo: P) -> Result<()> {
|
||||||
|
let before_revision = get_head_revision(&self.git, &repo);
|
||||||
|
|
||||||
|
if ctx.config().verbose() {
|
||||||
|
println!("{} {}", style(t!("Pulling")).cyan().bold(), repo.as_ref().display());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut command = AsyncCommand::new(&self.git);
|
||||||
|
|
||||||
|
command
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.current_dir(&repo)
|
||||||
|
.args(["pull", "--ff-only"]);
|
||||||
|
|
||||||
|
if let Some(extra_arguments) = ctx.config().git_arguments() {
|
||||||
|
command.args(extra_arguments.split_whitespace());
|
||||||
|
}
|
||||||
|
|
||||||
|
let pull_output = command.output().await?;
|
||||||
|
let submodule_output = AsyncCommand::new(&self.git)
|
||||||
|
.args(["submodule", "update", "--recursive"])
|
||||||
|
.current_dir(&repo)
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.output()
|
||||||
|
.await?;
|
||||||
|
let result = output_checked_utf8(pull_output)
|
||||||
|
.and_then(|()| output_checked_utf8(submodule_output))
|
||||||
|
.wrap_err_with(|| format!("Failed to pull {}", repo.as_ref().display()));
|
||||||
|
|
||||||
|
if result.is_err() {
|
||||||
|
println!(
|
||||||
|
"{} {} {}",
|
||||||
|
style(t!("Failed")).red().bold(),
|
||||||
|
t!("pulling"),
|
||||||
|
repo.as_ref().display()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let after_revision = get_head_revision(&self.git, repo.as_ref());
|
||||||
|
|
||||||
|
match (&before_revision, &after_revision) {
|
||||||
|
(Some(before), Some(after)) if before != after => {
|
||||||
|
println!("{} {}", style(t!("Changed")).yellow().bold(), repo.as_ref().display());
|
||||||
|
|
||||||
|
Command::new(&self.git)
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.current_dir(&repo)
|
||||||
|
.args([
|
||||||
|
"--no-pager",
|
||||||
|
"log",
|
||||||
|
"--no-decorate",
|
||||||
|
"--oneline",
|
||||||
|
&format!("{before}..{after}"),
|
||||||
|
])
|
||||||
|
.status_checked()?;
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if ctx.config().verbose() {
|
||||||
|
println!("{} {}", style(t!("Up-to-date")).green().bold(), repo.as_ref().display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pull the repositories specified in `self.repos`.
|
||||||
|
///
|
||||||
|
/// # NOTE
|
||||||
|
/// This function will create an async runtime and do the real job so the
|
||||||
|
/// function itself is not async.
|
||||||
|
fn pull_repos(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
if ctx.run_type().dry() {
|
||||||
|
self.repos
|
||||||
|
.iter()
|
||||||
|
.for_each(|repo| println!("{}", t!("Would pull {repo}", repo = repo.display())));
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ctx.config().verbose() {
|
||||||
|
println!(
|
||||||
|
"\n{} {}\n",
|
||||||
|
style(t!("Only")).green().bold(),
|
||||||
|
t!("updated repositories will be shown...")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let futures_iterator = self
|
||||||
|
.repos
|
||||||
|
.iter()
|
||||||
|
.filter(|repo| match self.has_remotes(repo) {
|
||||||
|
Some(false) => {
|
||||||
|
println!(
|
||||||
|
"{} {} {}",
|
||||||
|
style(t!("Skipping")).yellow().bold(),
|
||||||
|
repo.display(),
|
||||||
|
t!("because it has no remotes")
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
_ => true, // repo has remotes or command to check for remotes has failed. proceed to pull anyway.
|
||||||
|
})
|
||||||
|
.map(|repo| self.pull_repo(ctx, repo));
|
||||||
|
|
||||||
|
let stream_of_futures = if let Some(limit) = ctx.config().git_concurrency_limit() {
|
||||||
|
iter(futures_iterator).buffer_unordered(limit).boxed()
|
||||||
|
} else {
|
||||||
|
futures_iterator.collect::<FuturesUnordered<_>>().boxed()
|
||||||
|
};
|
||||||
|
|
||||||
|
let basic_rt = runtime::Runtime::new()?;
|
||||||
|
let results = basic_rt.block_on(async { stream_of_futures.collect::<Vec<Result<()>>>().await });
|
||||||
|
|
||||||
|
let error = results.into_iter().find(std::result::Result::is_err);
|
||||||
|
error.unwrap_or(Ok(()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,27 +4,27 @@ use std::process::Command;
|
|||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::executor::RunType;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use crate::utils::PathExt;
|
use crate::utils::PathExt;
|
||||||
|
|
||||||
/// <https://github.com/Gelio/go-global-update>
|
/// <https://github.com/Gelio/go-global-update>
|
||||||
pub fn run_go_global_update(run_type: RunType) -> Result<()> {
|
pub fn run_go_global_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let go_global_update = require_go_bin("go-global-update")?;
|
let go_global_update = require_go_bin("go-global-update")?;
|
||||||
|
|
||||||
print_separator("go-global-update");
|
print_separator("go-global-update");
|
||||||
|
|
||||||
run_type.execute(go_global_update).status_checked()
|
ctx.execute(go_global_update).status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://github.com/nao1215/gup>
|
/// <https://github.com/nao1215/gup>
|
||||||
pub fn run_go_gup(run_type: RunType) -> Result<()> {
|
pub fn run_go_gup(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let gup = require_go_bin("gup")?;
|
let gup = require_go_bin("gup")?;
|
||||||
|
|
||||||
print_separator("gup");
|
print_separator("gup");
|
||||||
|
|
||||||
run_type.execute(gup).arg("update").status_checked()
|
ctx.execute(gup).arg("update").status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the path of a Go binary.
|
/// Get the path of a Go binary.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::require;
|
use crate::utils::require;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
|
|
||||||
@@ -11,13 +12,10 @@ pub fn upgrade_kak_plug(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
print_separator("Kakoune");
|
print_separator("Kakoune");
|
||||||
|
|
||||||
// TODO: Why supress output for this command?
|
// TODO: Why suppress output for this command?
|
||||||
ctx.run_type()
|
ctx.execute(kak).args(["-ui", "dummy", "-e", UPGRADE_KAK]).output()?;
|
||||||
.execute(kak)
|
|
||||||
.args(["-ui", "dummy", "-e", UPGRADE_KAK])
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
println!("Plugins upgraded");
|
println!("{}", t!("Plugins upgraded"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,16 @@ use std::os::unix::fs::MetadataExt;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use crate::utils::require_option;
|
use crate::HOME_DIR;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use nix::unistd::Uid;
|
use nix::unistd::Uid;
|
||||||
|
use rust_i18n::t;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::{print_info, print_separator};
|
||||||
use crate::utils::{require, PathExt};
|
use crate::utils::{require, PathExt};
|
||||||
use crate::{error::SkipStep, execution_context::ExecutionContext};
|
use crate::{error::SkipStep, execution_context::ExecutionContext};
|
||||||
|
|
||||||
@@ -85,20 +86,16 @@ impl NPM {
|
|||||||
.args(["--version"])
|
.args(["--version"])
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.map(|s| s.stdout.trim().to_owned());
|
.map(|s| s.stdout.trim().to_owned());
|
||||||
Version::parse(&version_str?).map_err(|err| err.into())
|
Version::parse(&version_str?).map_err(std::convert::Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade(&self, ctx: &ExecutionContext, use_sudo: bool) -> Result<()> {
|
fn upgrade(&self, ctx: &ExecutionContext, use_sudo: bool) -> Result<()> {
|
||||||
let args = ["update", self.global_location_arg()];
|
let args = ["update", self.global_location_arg()];
|
||||||
if use_sudo {
|
if use_sudo {
|
||||||
let sudo = require_option(ctx.sudo().clone(), String::from("sudo is not installed"))?;
|
let sudo = ctx.require_sudo()?;
|
||||||
ctx.run_type()
|
sudo.execute(ctx, &self.command)?.args(args).status_checked()?;
|
||||||
.execute(sudo)
|
|
||||||
.arg(&self.command)
|
|
||||||
.args(args)
|
|
||||||
.status_checked()?;
|
|
||||||
} else {
|
} else {
|
||||||
ctx.run_type().execute(&self.command).args(args).status_checked()?;
|
ctx.execute(&self.command).args(args).status_checked()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -120,15 +117,11 @@ impl NPM {
|
|||||||
|
|
||||||
struct Yarn {
|
struct Yarn {
|
||||||
command: PathBuf,
|
command: PathBuf,
|
||||||
yarn: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Yarn {
|
impl Yarn {
|
||||||
fn new(command: PathBuf) -> Self {
|
fn new(command: PathBuf) -> Self {
|
||||||
Self {
|
Self { command }
|
||||||
command,
|
|
||||||
yarn: require("yarn").ok(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_global_subcmd(&self) -> bool {
|
fn has_global_subcmd(&self) -> bool {
|
||||||
@@ -155,14 +148,10 @@ impl Yarn {
|
|||||||
let args = ["global", "upgrade"];
|
let args = ["global", "upgrade"];
|
||||||
|
|
||||||
if use_sudo {
|
if use_sudo {
|
||||||
let sudo = require_option(ctx.sudo().clone(), String::from("sudo is not installed"))?;
|
let sudo = ctx.require_sudo()?;
|
||||||
ctx.run_type()
|
sudo.execute(ctx, &self.command)?.args(args).status_checked()?;
|
||||||
.execute(sudo)
|
|
||||||
.arg(self.yarn.as_ref().unwrap_or(&self.command))
|
|
||||||
.args(args)
|
|
||||||
.status_checked()?;
|
|
||||||
} else {
|
} else {
|
||||||
ctx.run_type().execute(&self.command).args(args).status_checked()?;
|
ctx.execute(&self.command).args(args).status_checked()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -182,6 +171,88 @@ impl Yarn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Deno {
|
||||||
|
command: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deno {
|
||||||
|
fn new(command: PathBuf) -> Self {
|
||||||
|
Self { command }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let mut args = vec![];
|
||||||
|
|
||||||
|
let version = ctx.config().deno_version();
|
||||||
|
if let Some(version) = version {
|
||||||
|
let bin_version = self.version()?;
|
||||||
|
|
||||||
|
if bin_version >= Version::new(2, 0, 0) {
|
||||||
|
args.push(version);
|
||||||
|
} else if bin_version >= Version::new(1, 6, 0) {
|
||||||
|
match version {
|
||||||
|
"stable" => { /* do nothing, as stable is the default channel to upgrade */ }
|
||||||
|
"rc" => {
|
||||||
|
return Err(SkipStep(
|
||||||
|
"Deno (1.6.0-2.0.0) cannot be upgraded to a release candidate".to_string(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
"canary" => args.push("--canary"),
|
||||||
|
_ => {
|
||||||
|
if Version::parse(version).is_err() {
|
||||||
|
return Err(SkipStep("Invalid Deno version".to_string()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push("--version");
|
||||||
|
args.push(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if bin_version >= Version::new(1, 0, 0) {
|
||||||
|
match version {
|
||||||
|
"stable" | "rc" | "canary" => {
|
||||||
|
// Prior to v1.6.0, `deno upgrade` is not able fetch the latest tag version.
|
||||||
|
return Err(
|
||||||
|
SkipStep("Deno (1.0.0-1.6.0) cannot be upgraded to a named channel".to_string()).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if Version::parse(version).is_err() {
|
||||||
|
return Err(SkipStep("Invalid Deno version".to_string()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push("--version");
|
||||||
|
args.push(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// v0.x cannot be upgraded with `deno upgrade` to v1.x or v2.x
|
||||||
|
// nor can be upgraded to a specific version.
|
||||||
|
return Err(SkipStep("Unsupported Deno version".to_string()).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.execute(&self.command).arg("upgrade").args(args).status_checked()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the version of Deno.
|
||||||
|
///
|
||||||
|
/// This function will return the version of Deno installed on the system.
|
||||||
|
/// The version is parsed from the output of `deno -V`.
|
||||||
|
///
|
||||||
|
/// ```sh
|
||||||
|
/// deno -V # deno 1.6.0
|
||||||
|
/// ```
|
||||||
|
fn version(&self) -> Result<Version> {
|
||||||
|
let version_str = Command::new(&self.command)
|
||||||
|
.args(["-V"])
|
||||||
|
.output_checked_utf8()
|
||||||
|
.map(|s| s.stdout.trim().to_owned().split_off(5)); // remove "deno " prefix
|
||||||
|
Version::parse(&version_str?).map_err(std::convert::Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn should_use_sudo(npm: &NPM, ctx: &ExecutionContext) -> Result<bool> {
|
fn should_use_sudo(npm: &NPM, ctx: &ExecutionContext) -> Result<bool> {
|
||||||
if npm.should_use_sudo()? {
|
if npm.should_use_sudo()? {
|
||||||
@@ -213,7 +284,7 @@ fn should_use_sudo_yarn(yarn: &Yarn, ctx: &ExecutionContext) -> Result<bool> {
|
|||||||
pub fn run_npm_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_npm_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let npm = require("npm").map(|b| NPM::new(b, NPMVariant::Npm))?;
|
let npm = require("npm").map(|b| NPM::new(b, NPMVariant::Npm))?;
|
||||||
|
|
||||||
print_separator("Node Package Manager");
|
print_separator(t!("Node Package Manager"));
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
@@ -229,7 +300,7 @@ pub fn run_npm_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
pub fn run_pnpm_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_pnpm_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let pnpm = require("pnpm").map(|b| NPM::new(b, NPMVariant::Pnpm))?;
|
let pnpm = require("pnpm").map(|b| NPM::new(b, NPMVariant::Pnpm))?;
|
||||||
|
|
||||||
print_separator("Node Package Manager");
|
print_separator(t!("Performant Node Package Manager"));
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
@@ -250,7 +321,7 @@ pub fn run_yarn_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Yarn Package Manager");
|
print_separator(t!("Yarn Package Manager"));
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
@@ -264,14 +335,55 @@ pub fn run_yarn_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deno_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
pub fn deno_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let deno = require("deno")?;
|
let deno = require("deno").map(Deno::new)?;
|
||||||
let deno_dir = ctx.base_dirs().home_dir().join(".deno");
|
let deno_dir = HOME_DIR.join(".deno");
|
||||||
|
|
||||||
if !deno.canonicalize()?.is_descendant_of(&deno_dir) {
|
if !deno.command.canonicalize()?.is_descendant_of(&deno_dir) {
|
||||||
let skip_reason = SkipStep("Deno installed outside of .deno directory".to_string());
|
let skip_reason = SkipStep(t!("Deno installed outside of .deno directory").to_string());
|
||||||
return Err(skip_reason.into());
|
return Err(skip_reason.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Deno");
|
print_separator("Deno");
|
||||||
ctx.run_type().execute(&deno).arg("upgrade").status_checked()
|
deno.upgrade(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// There is no `volta upgrade` command, so we need to upgrade each package
|
||||||
|
pub fn run_volta_packages_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let volta = require("volta")?;
|
||||||
|
|
||||||
|
print_separator("Volta");
|
||||||
|
|
||||||
|
if ctx.run_type().dry() {
|
||||||
|
print_info(t!("Updating Volta packages..."));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let list_output = ctx
|
||||||
|
.execute(&volta)
|
||||||
|
.args(["list", "--format=plain"])
|
||||||
|
.output_checked_utf8()?
|
||||||
|
.stdout;
|
||||||
|
|
||||||
|
let installed_packages: Vec<&str> = list_output
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
|
// format is 'kind package@version ...'
|
||||||
|
let mut parts = line.split_whitespace();
|
||||||
|
parts.next();
|
||||||
|
let package_part = parts.next()?;
|
||||||
|
let version_index = package_part.rfind('@').unwrap_or(package_part.len());
|
||||||
|
Some(package_part[..version_index].trim())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if installed_packages.is_empty() {
|
||||||
|
print_info(t!("No packages installed with Volta"));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for package in &installed_packages {
|
||||||
|
ctx.execute(&volta).args(["install", package]).status_checked()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
|
use crate::step::Step;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::require;
|
use crate::utils::require;
|
||||||
use crate::utils::which;
|
use crate::utils::which;
|
||||||
use crate::Step;
|
use color_eyre::Result;
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
|
|
||||||
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
//let pkg = require("pkg")?;
|
//let pkg = require("pkg")?;
|
||||||
@@ -14,7 +14,7 @@ pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
let is_nala = pkg.ends_with("nala");
|
let is_nala = pkg.ends_with("nala");
|
||||||
|
|
||||||
let mut command = ctx.run_type().execute(&pkg);
|
let mut command = ctx.execute(&pkg);
|
||||||
command.arg("upgrade");
|
command.arg("upgrade");
|
||||||
|
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
@@ -23,10 +23,10 @@ pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
command.status_checked()?;
|
command.status_checked()?;
|
||||||
|
|
||||||
if !is_nala && ctx.config().cleanup() {
|
if !is_nala && ctx.config().cleanup() {
|
||||||
ctx.run_type().execute(&pkg).arg("clean").status_checked()?;
|
ctx.execute(&pkg).arg("clean").status_checked()?;
|
||||||
|
|
||||||
let apt = require("apt")?;
|
let apt = require("apt")?;
|
||||||
let mut command = ctx.run_type().execute(apt);
|
let mut command = ctx.execute(apt);
|
||||||
command.arg("autoremove");
|
command.arg("autoremove");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("-y");
|
command.arg("-y");
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ use std::ffi::OsString;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use color_eyre::eyre;
|
use color_eyre::eyre;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::{Context, Result};
|
||||||
|
use rust_i18n::t;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::error::TopgradeError;
|
use crate::error::TopgradeError;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::sudo::Sudo;
|
use crate::step::Step;
|
||||||
use crate::utils::which;
|
use crate::utils::which;
|
||||||
use crate::{config, Step};
|
use crate::{config, output_changed_message};
|
||||||
|
|
||||||
fn get_execution_path() -> OsString {
|
fn get_execution_path() -> OsString {
|
||||||
let mut path = OsString::from("/usr/bin:");
|
let mut path = OsString::from("/usr/bin:");
|
||||||
@@ -31,13 +32,12 @@ pub struct YayParu {
|
|||||||
impl ArchPackageManager for YayParu {
|
impl ArchPackageManager for YayParu {
|
||||||
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
if ctx.config().show_arch_news() {
|
if ctx.config().show_arch_news() {
|
||||||
ctx.run_type()
|
ctx.execute(&self.executable)
|
||||||
.execute(&self.executable)
|
|
||||||
.arg("-Pw")
|
.arg("-Pw")
|
||||||
.status_checked_with_codes(&[1, 0])?;
|
.status_checked_with_codes(&[1, 0])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
|
|
||||||
command
|
command
|
||||||
.arg("--pacman")
|
.arg("--pacman")
|
||||||
@@ -52,7 +52,7 @@ impl ArchPackageManager for YayParu {
|
|||||||
command.status_checked()?;
|
command.status_checked()?;
|
||||||
|
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().cleanup() {
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
command.arg("--pacman").arg(&self.pacman).arg("-Scc");
|
command.arg("--pacman").arg(&self.pacman).arg("-Scc");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("--noconfirm");
|
command.arg("--noconfirm");
|
||||||
@@ -79,7 +79,7 @@ pub struct GarudaUpdate {
|
|||||||
|
|
||||||
impl ArchPackageManager for GarudaUpdate {
|
impl ArchPackageManager for GarudaUpdate {
|
||||||
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
|
|
||||||
command
|
command
|
||||||
.env("PATH", get_execution_path())
|
.env("PATH", get_execution_path())
|
||||||
@@ -110,7 +110,7 @@ pub struct Trizen {
|
|||||||
|
|
||||||
impl ArchPackageManager for Trizen {
|
impl ArchPackageManager for Trizen {
|
||||||
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
|
|
||||||
command
|
command
|
||||||
.arg("-Syu")
|
.arg("-Syu")
|
||||||
@@ -123,7 +123,7 @@ impl ArchPackageManager for Trizen {
|
|||||||
command.status_checked()?;
|
command.status_checked()?;
|
||||||
|
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().cleanup() {
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
command.arg("-Sc");
|
command.arg("-Sc");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("--noconfirm");
|
command.arg("--noconfirm");
|
||||||
@@ -144,25 +144,22 @@ impl Trizen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Pacman {
|
pub struct Pacman {
|
||||||
sudo: Sudo,
|
|
||||||
executable: PathBuf,
|
executable: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArchPackageManager for Pacman {
|
impl ArchPackageManager for Pacman {
|
||||||
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
let mut command = ctx.run_type().execute(&self.sudo);
|
let sudo = ctx.require_sudo()?;
|
||||||
command
|
let mut command = sudo.execute(ctx, &self.executable)?;
|
||||||
.arg(&self.executable)
|
command.arg("-Syu").env("PATH", get_execution_path());
|
||||||
.arg("-Syu")
|
|
||||||
.env("PATH", get_execution_path());
|
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("--noconfirm");
|
command.arg("--noconfirm");
|
||||||
}
|
}
|
||||||
command.status_checked()?;
|
command.status_checked()?;
|
||||||
|
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().cleanup() {
|
||||||
let mut command = ctx.run_type().execute(&self.sudo);
|
let mut command = sudo.execute(ctx, &self.executable)?;
|
||||||
command.arg(&self.executable).arg("-Scc");
|
command.arg("-Scc");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("--noconfirm");
|
command.arg("--noconfirm");
|
||||||
}
|
}
|
||||||
@@ -174,10 +171,9 @@ impl ArchPackageManager for Pacman {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Pacman {
|
impl Pacman {
|
||||||
pub fn get(ctx: &ExecutionContext) -> Option<Self> {
|
pub fn get() -> Option<Self> {
|
||||||
Some(Self {
|
Some(Self {
|
||||||
executable: which("powerpill").unwrap_or_else(|| PathBuf::from("pacman")),
|
executable: which("powerpill").unwrap_or_else(|| PathBuf::from("pacman")),
|
||||||
sudo: ctx.sudo().to_owned()?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +192,7 @@ impl Pikaur {
|
|||||||
|
|
||||||
impl ArchPackageManager for Pikaur {
|
impl ArchPackageManager for Pikaur {
|
||||||
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
|
|
||||||
command
|
command
|
||||||
.arg("-Syu")
|
.arg("-Syu")
|
||||||
@@ -210,7 +206,7 @@ impl ArchPackageManager for Pikaur {
|
|||||||
command.status_checked()?;
|
command.status_checked()?;
|
||||||
|
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().cleanup() {
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
command.arg("-Sc");
|
command.arg("-Sc");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("--noconfirm");
|
command.arg("--noconfirm");
|
||||||
@@ -235,7 +231,7 @@ impl Pamac {
|
|||||||
}
|
}
|
||||||
impl ArchPackageManager for Pamac {
|
impl ArchPackageManager for Pamac {
|
||||||
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
|
|
||||||
command
|
command
|
||||||
.arg("upgrade")
|
.arg("upgrade")
|
||||||
@@ -249,7 +245,7 @@ impl ArchPackageManager for Pamac {
|
|||||||
command.status_checked()?;
|
command.status_checked()?;
|
||||||
|
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().cleanup() {
|
||||||
let mut command = ctx.run_type().execute(&self.executable);
|
let mut command = ctx.execute(&self.executable);
|
||||||
command.arg("clean");
|
command.arg("clean");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("--no-confirm");
|
command.arg("--no-confirm");
|
||||||
@@ -263,46 +259,67 @@ impl ArchPackageManager for Pamac {
|
|||||||
|
|
||||||
pub struct Aura {
|
pub struct Aura {
|
||||||
executable: PathBuf,
|
executable: PathBuf,
|
||||||
sudo: Sudo,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aura {
|
impl Aura {
|
||||||
fn get(ctx: &ExecutionContext) -> Option<Self> {
|
fn get() -> Option<Self> {
|
||||||
Some(Self {
|
Some(Self {
|
||||||
executable: which("aura")?,
|
executable: which("aura")?,
|
||||||
sudo: ctx.sudo().to_owned()?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArchPackageManager for Aura {
|
impl ArchPackageManager for Aura {
|
||||||
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = which("sudo").unwrap_or_else(PathBuf::new);
|
use semver::Version;
|
||||||
let mut aur_update = ctx.run_type().execute(&sudo);
|
|
||||||
|
|
||||||
if sudo.ends_with("sudo") {
|
let version_cmd_output = ctx.execute(&self.executable).arg("--version").output_checked_utf8()?;
|
||||||
aur_update
|
// Output will be something like: "aura x.x.x\n"
|
||||||
.arg(&self.executable)
|
let version_cmd_stdout = version_cmd_output.stdout;
|
||||||
.arg("-Au")
|
let version_str = version_cmd_stdout.trim_start_matches("aura ").trim_end();
|
||||||
|
let version = Version::parse(version_str)
|
||||||
|
.wrap_err_with(|| output_changed_message!("aura --version", "invalid version"))?;
|
||||||
|
|
||||||
|
// Aura, since version 4.0.6, no longer needs sudo.
|
||||||
|
//
|
||||||
|
// https://github.com/fosskers/aura/releases/tag/v4.0.6
|
||||||
|
let version_no_sudo = Version::new(4, 0, 6);
|
||||||
|
|
||||||
|
if version >= version_no_sudo {
|
||||||
|
let mut cmd = ctx.execute(&self.executable);
|
||||||
|
cmd.arg("-Au")
|
||||||
.args(ctx.config().aura_aur_arguments().split_whitespace());
|
.args(ctx.config().aura_aur_arguments().split_whitespace());
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
aur_update.arg("--noconfirm");
|
cmd.arg("--noconfirm");
|
||||||
}
|
}
|
||||||
|
cmd.status_checked()?;
|
||||||
|
|
||||||
aur_update.status_checked()?;
|
let mut cmd = ctx.execute(&self.executable);
|
||||||
} else {
|
cmd.arg("-Syu")
|
||||||
println!("Aura requires sudo installed to work with AUR packages")
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pacman_update = ctx.run_type().execute(&self.sudo);
|
|
||||||
pacman_update
|
|
||||||
.arg(&self.executable)
|
|
||||||
.arg("-Syu")
|
|
||||||
.args(ctx.config().aura_pacman_arguments().split_whitespace());
|
.args(ctx.config().aura_pacman_arguments().split_whitespace());
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
pacman_update.arg("--noconfirm");
|
cmd.arg("--noconfirm");
|
||||||
|
}
|
||||||
|
cmd.status_checked()?;
|
||||||
|
} else {
|
||||||
|
let sudo = ctx.require_sudo()?;
|
||||||
|
|
||||||
|
let mut cmd = sudo.execute(ctx, &self.executable)?;
|
||||||
|
cmd.arg("-Au")
|
||||||
|
.args(ctx.config().aura_aur_arguments().split_whitespace());
|
||||||
|
if ctx.config().yes(Step::System) {
|
||||||
|
cmd.arg("--noconfirm");
|
||||||
|
}
|
||||||
|
cmd.status_checked()?;
|
||||||
|
|
||||||
|
let mut cmd = sudo.execute(ctx, &self.executable)?;
|
||||||
|
cmd.arg("-Syu")
|
||||||
|
.args(ctx.config().aura_pacman_arguments().split_whitespace());
|
||||||
|
if ctx.config().yes(Step::System) {
|
||||||
|
cmd.arg("--noconfirm");
|
||||||
|
}
|
||||||
|
cmd.status_checked()?;
|
||||||
}
|
}
|
||||||
pacman_update.status_checked()?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -323,16 +340,16 @@ pub fn get_arch_package_manager(ctx: &ExecutionContext) -> Option<Box<dyn ArchPa
|
|||||||
.or_else(|| Trizen::get().map(box_package_manager))
|
.or_else(|| Trizen::get().map(box_package_manager))
|
||||||
.or_else(|| Pikaur::get().map(box_package_manager))
|
.or_else(|| Pikaur::get().map(box_package_manager))
|
||||||
.or_else(|| Pamac::get().map(box_package_manager))
|
.or_else(|| Pamac::get().map(box_package_manager))
|
||||||
.or_else(|| Pacman::get(ctx).map(box_package_manager))
|
.or_else(|| Pacman::get().map(box_package_manager))
|
||||||
.or_else(|| Aura::get(ctx).map(box_package_manager)),
|
.or_else(|| Aura::get().map(box_package_manager)),
|
||||||
config::ArchPackageManager::GarudaUpdate => GarudaUpdate::get().map(box_package_manager),
|
config::ArchPackageManager::GarudaUpdate => GarudaUpdate::get().map(box_package_manager),
|
||||||
config::ArchPackageManager::Trizen => Trizen::get().map(box_package_manager),
|
config::ArchPackageManager::Trizen => Trizen::get().map(box_package_manager),
|
||||||
config::ArchPackageManager::Paru => YayParu::get("paru", &pacman).map(box_package_manager),
|
config::ArchPackageManager::Paru => YayParu::get("paru", &pacman).map(box_package_manager),
|
||||||
config::ArchPackageManager::Yay => YayParu::get("yay", &pacman).map(box_package_manager),
|
config::ArchPackageManager::Yay => YayParu::get("yay", &pacman).map(box_package_manager),
|
||||||
config::ArchPackageManager::Pacman => Pacman::get(ctx).map(box_package_manager),
|
config::ArchPackageManager::Pacman => Pacman::get().map(box_package_manager),
|
||||||
config::ArchPackageManager::Pikaur => Pikaur::get().map(box_package_manager),
|
config::ArchPackageManager::Pikaur => Pikaur::get().map(box_package_manager),
|
||||||
config::ArchPackageManager::Pamac => Pamac::get().map(box_package_manager),
|
config::ArchPackageManager::Pamac => Pamac::get().map(box_package_manager),
|
||||||
config::ArchPackageManager::Aura => Aura::get(ctx).map(box_package_manager),
|
config::ArchPackageManager::Aura => Aura::get().map(box_package_manager),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +372,7 @@ pub fn show_pacnew() {
|
|||||||
.peekable();
|
.peekable();
|
||||||
|
|
||||||
if iter.peek().is_some() {
|
if iter.peek().is_some() {
|
||||||
println!("\nPacman backup configuration files found:");
|
println!("\n{}", t!("Pacman backup configuration files found:"));
|
||||||
|
|
||||||
for entry in iter {
|
for entry in iter {
|
||||||
println!("{}", entry.path().display());
|
println!("{}", entry.path().display());
|
||||||
|
|||||||
@@ -1,26 +1,35 @@
|
|||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::executor::RunType;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::sudo::Sudo;
|
use crate::step::Step;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::require_option;
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use std::process::Command;
|
use rust_i18n::t;
|
||||||
|
|
||||||
pub fn upgrade_packages(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
|
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(sudo, String::from("No sudo detected"))?;
|
print_separator(t!("DragonFly BSD Packages"));
|
||||||
print_separator("DragonFly BSD Packages");
|
|
||||||
run_type
|
let sudo = ctx.require_sudo()?;
|
||||||
.execute(sudo)
|
let mut cmd = sudo.execute(ctx, "/usr/local/sbin/pkg")?;
|
||||||
.args(["/usr/local/sbin/pkg", "upgrade"])
|
cmd.arg("upgrade");
|
||||||
.status_checked()
|
if ctx.config().yes(Step::System) {
|
||||||
|
cmd.arg("-y");
|
||||||
|
}
|
||||||
|
cmd.status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn audit_packages(sudo: Option<&Sudo>) -> Result<()> {
|
pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
if let Some(sudo) = sudo {
|
print_separator(t!("DragonFly BSD Audit"));
|
||||||
println!();
|
|
||||||
Command::new(sudo)
|
let sudo = ctx.require_sudo()?;
|
||||||
.args(["/usr/local/sbin/pkg", "audit", "-Fr"])
|
sudo.execute(ctx, "/usr/local/sbin/pkg")?
|
||||||
.status_checked()?;
|
.args(["audit", "-Fr"])
|
||||||
|
.status_checked_with(|status| {
|
||||||
|
if !status.success() {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
t!("The package audit was successful, but vulnerable packages still remain on the system")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,36 @@
|
|||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::executor::RunType;
|
use crate::step::Step;
|
||||||
use crate::sudo::Sudo;
|
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::require_option;
|
use color_eyre::Result;
|
||||||
use crate::Step;
|
use rust_i18n::t;
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
pub fn upgrade_freebsd(sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
|
pub fn upgrade_freebsd(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(sudo, String::from("No sudo detected"))?;
|
print_separator(t!("FreeBSD Update"));
|
||||||
print_separator("FreeBSD Update");
|
|
||||||
run_type
|
let sudo = ctx.require_sudo()?;
|
||||||
.execute(sudo)
|
sudo.execute(ctx, "/usr/sbin/freebsd-update")?
|
||||||
.args(["/usr/sbin/freebsd-update", "fetch", "install"])
|
.args(["fetch", "install"])
|
||||||
.status_checked()
|
.status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upgrade_packages(ctx: &ExecutionContext, sudo: Option<&Sudo>, run_type: RunType) -> Result<()> {
|
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(sudo, String::from("No sudo detected"))?;
|
print_separator(t!("FreeBSD Packages"));
|
||||||
print_separator("FreeBSD Packages");
|
|
||||||
|
|
||||||
let mut command = run_type.execute(sudo);
|
let sudo = ctx.require_sudo()?;
|
||||||
|
let mut command = sudo.execute(ctx, "/usr/sbin/pkg")?;
|
||||||
command.args(["/usr/sbin/pkg", "upgrade"]);
|
command.arg("upgrade");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("-y");
|
command.arg("-y");
|
||||||
}
|
}
|
||||||
command.status_checked()
|
command.status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn audit_packages(sudo: Option<&Sudo>) -> Result<()> {
|
pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
if let Some(sudo) = sudo {
|
print_separator(t!("FreeBSD Audit"));
|
||||||
println!();
|
|
||||||
Command::new(sudo)
|
let sudo = ctx.require_sudo()?;
|
||||||
.args(["/usr/sbin/pkg", "audit", "-Fr"])
|
sudo.execute(ctx, "/usr/sbin/pkg")?
|
||||||
.status_checked()?;
|
.args(["audit", "-Fr"])
|
||||||
}
|
.status_checked()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,61 +1,59 @@
|
|||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::executor::RunType;
|
use crate::step::Step;
|
||||||
use crate::terminal::{print_separator, prompt_yesno};
|
use crate::terminal::{print_separator, prompt_yesno};
|
||||||
use crate::{utils::require, Step};
|
use crate::utils::require;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
pub fn run_macports(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_macports(ctx: &ExecutionContext) -> Result<()> {
|
||||||
require("port")?;
|
let port = require("port")?;
|
||||||
let sudo = ctx.sudo().as_ref().unwrap();
|
|
||||||
print_separator("MacPorts");
|
print_separator("MacPorts");
|
||||||
ctx.run_type()
|
|
||||||
.execute(sudo)
|
let sudo = ctx.require_sudo()?;
|
||||||
.args(["port", "selfupdate"])
|
|
||||||
.status_checked()?;
|
sudo.execute(ctx, &port)?.arg("selfupdate").status_checked()?;
|
||||||
ctx.run_type()
|
sudo.execute(ctx, &port)?
|
||||||
.execute(sudo)
|
.args(["-u", "upgrade", "outdated"])
|
||||||
.args(["port", "-u", "upgrade", "outdated"])
|
|
||||||
.status_checked()?;
|
.status_checked()?;
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().cleanup() {
|
||||||
ctx.run_type()
|
sudo.execute(ctx, &port)?.args(["-N", "reclaim"]).status_checked()?;
|
||||||
.execute(sudo)
|
|
||||||
.args(["port", "-N", "reclaim"])
|
|
||||||
.status_checked()?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_mas(run_type: RunType) -> Result<()> {
|
pub fn run_mas(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let mas = require("mas")?;
|
let mas = require("mas")?;
|
||||||
print_separator("macOS App Store");
|
print_separator(t!("macOS App Store"));
|
||||||
|
|
||||||
run_type.execute(mas).arg("upgrade").status_checked()
|
ctx.execute(mas).arg("upgrade").status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> {
|
||||||
print_separator("macOS system update");
|
print_separator(t!("macOS system update"));
|
||||||
|
|
||||||
let should_ask = !(ctx.config().yes(Step::System)) || (ctx.config().dry_run());
|
let should_ask = !(ctx.config().yes(Step::System) || ctx.run_type().dry());
|
||||||
if should_ask {
|
if should_ask {
|
||||||
println!("Finding available software");
|
println!("{}", t!("Finding available software"));
|
||||||
if system_update_available()? {
|
if system_update_available()? {
|
||||||
let answer = prompt_yesno("A system update is available. Do you wish to install it?")?;
|
let answer = prompt_yesno(t!("A system update is available. Do you wish to install it?").as_ref())?;
|
||||||
if !answer {
|
if !answer {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
} else {
|
} else {
|
||||||
println!("No new software available.");
|
println!("{}", t!("No new software available."));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut command = ctx.run_type().execute("softwareupdate");
|
let mut command = ctx.execute("softwareupdate");
|
||||||
command.args(["--install", "--all"]);
|
command.args(["--install", "--all"]);
|
||||||
|
|
||||||
if should_ask {
|
if should_ask {
|
||||||
@@ -84,7 +82,7 @@ pub fn run_sparkle(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.arg(application.path())
|
.arg(application.path())
|
||||||
.output_checked_utf8();
|
.output_checked_utf8();
|
||||||
if probe.is_ok() {
|
if probe.is_ok() {
|
||||||
let mut command = ctx.run_type().execute(&sparkle);
|
let mut command = ctx.execute(&sparkle);
|
||||||
command.args(["bundle", "--check-immediately", "--application"]);
|
command.args(["bundle", "--check-immediately", "--application"]);
|
||||||
command.arg(application.path());
|
command.arg(application.path());
|
||||||
command.status_checked()?;
|
command.status_checked()?;
|
||||||
@@ -92,3 +90,133 @@ pub fn run_sparkle(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_xcodes(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let xcodes = require("xcodes")?;
|
||||||
|
print_separator("Xcodes");
|
||||||
|
|
||||||
|
let should_ask = !(ctx.config().yes(Step::Xcodes) || ctx.run_type().dry());
|
||||||
|
|
||||||
|
let releases = ctx.execute(&xcodes).args(["update"]).output_checked_utf8()?.stdout;
|
||||||
|
|
||||||
|
let releases_installed: Vec<String> = releases
|
||||||
|
.lines()
|
||||||
|
.filter(|r| r.contains("(Installed)"))
|
||||||
|
.map(String::from)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if releases_installed.is_empty() {
|
||||||
|
println!("{}", t!("No Xcode releases installed."));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (installed_gm, installed_beta, installed_regular) =
|
||||||
|
releases_installed
|
||||||
|
.iter()
|
||||||
|
.fold((false, false, false), |(gm, beta, regular), release| {
|
||||||
|
(
|
||||||
|
gm || release.contains("GM") || release.contains("Release Candidate"),
|
||||||
|
beta || release.contains("Beta"),
|
||||||
|
regular
|
||||||
|
|| !(release.contains("GM")
|
||||||
|
|| release.contains("Release Candidate")
|
||||||
|
|| release.contains("Beta")),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let releases_gm = releases
|
||||||
|
.lines()
|
||||||
|
.filter(|&r| r.matches("GM").count() > 0 || r.matches("Release Candidate").count() > 0)
|
||||||
|
.map(String::from)
|
||||||
|
.collect();
|
||||||
|
let releases_beta = releases
|
||||||
|
.lines()
|
||||||
|
.filter(|&r| r.matches("Beta").count() > 0)
|
||||||
|
.map(String::from)
|
||||||
|
.collect();
|
||||||
|
let releases_regular = releases
|
||||||
|
.lines()
|
||||||
|
.filter(|&r| {
|
||||||
|
r.matches("GM").count() == 0
|
||||||
|
&& r.matches("Release Candidate").count() == 0
|
||||||
|
&& r.matches("Beta").count() == 0
|
||||||
|
})
|
||||||
|
.map(String::from)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if installed_gm {
|
||||||
|
process_xcodes_releases(releases_gm, should_ask, ctx)?;
|
||||||
|
}
|
||||||
|
if installed_beta {
|
||||||
|
process_xcodes_releases(releases_beta, should_ask, ctx)?;
|
||||||
|
}
|
||||||
|
if installed_regular {
|
||||||
|
process_xcodes_releases(releases_regular, should_ask, ctx)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let releases_new = ctx.execute(&xcodes).args(["list"]).output_checked_utf8()?.stdout;
|
||||||
|
|
||||||
|
let releases_gm_new_installed: HashSet<_> = releases_new
|
||||||
|
.lines()
|
||||||
|
.filter(|release| {
|
||||||
|
release.contains("(Installed)") && (release.contains("GM") || release.contains("Release Candidate"))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let releases_beta_new_installed: HashSet<_> = releases_new
|
||||||
|
.lines()
|
||||||
|
.filter(|release| release.contains("(Installed)") && release.contains("Beta"))
|
||||||
|
.collect();
|
||||||
|
let releases_regular_new_installed: HashSet<_> = releases_new
|
||||||
|
.lines()
|
||||||
|
.filter(|release| {
|
||||||
|
release.contains("(Installed)")
|
||||||
|
&& !(release.contains("GM") || release.contains("Release Candidate") || release.contains("Beta"))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for releases_new_installed in [
|
||||||
|
releases_gm_new_installed,
|
||||||
|
releases_beta_new_installed,
|
||||||
|
releases_regular_new_installed,
|
||||||
|
] {
|
||||||
|
if should_ask && releases_new_installed.len() == 2 {
|
||||||
|
let answer_uninstall =
|
||||||
|
prompt_yesno(t!("Would you like to move the former Xcode release to the trash?").as_ref())?;
|
||||||
|
if answer_uninstall {
|
||||||
|
let _ = ctx
|
||||||
|
.execute(&xcodes)
|
||||||
|
.args([
|
||||||
|
"uninstall",
|
||||||
|
releases_new_installed.iter().next().copied().unwrap_or_default(),
|
||||||
|
])
|
||||||
|
.status_checked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_xcodes_releases(releases_filtered: Vec<String>, should_ask: bool, ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let xcodes = require("xcodes")?;
|
||||||
|
|
||||||
|
if releases_filtered.last().map_or(true, |s| !s.contains("(Installed)")) && !releases_filtered.is_empty() {
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
t!("New Xcode release detected:"),
|
||||||
|
releases_filtered.last().cloned().unwrap_or_default()
|
||||||
|
);
|
||||||
|
if should_ask {
|
||||||
|
let answer_install = prompt_yesno(t!("Would you like to install it?").as_ref())?;
|
||||||
|
if answer_install {
|
||||||
|
let _ = ctx
|
||||||
|
.execute(xcodes)
|
||||||
|
.args(["install", &releases_filtered.last().cloned().unwrap_or_default()])
|
||||||
|
.status_checked();
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub mod macos;
|
|||||||
pub mod openbsd;
|
pub mod openbsd;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub mod unix;
|
pub mod unix;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(windows)]
|
||||||
pub mod windows;
|
pub mod windows;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|||||||
@@ -1,23 +1,49 @@
|
|||||||
use crate::executor::RunType;
|
use crate::command::CommandExt;
|
||||||
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::require_option;
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use std::path::PathBuf;
|
use rust_i18n::t;
|
||||||
|
use std::fs;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
pub fn upgrade_openbsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
|
fn is_openbsd_current() -> Result<bool> {
|
||||||
let sudo = require_option(sudo, String::from("No sudo detected"))?;
|
let motd_content = fs::read_to_string("/etc/motd")?;
|
||||||
print_separator("OpenBSD Update");
|
let is_current = ["-current", "-beta"].iter().any(|&s| motd_content.contains(s));
|
||||||
run_type
|
|
||||||
.execute(sudo)
|
debug!("OpenBSD is -current/-beta: {is_current}");
|
||||||
.args(&["/usr/sbin/sysupgrade", "-n"])
|
|
||||||
.status_checked()
|
Ok(is_current)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> {
|
pub fn upgrade_openbsd(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(sudo, String::from("No sudo detected"))?;
|
print_separator(t!("OpenBSD Update"));
|
||||||
print_separator("OpenBSD Packages");
|
|
||||||
run_type
|
let sudo = ctx.require_sudo()?;
|
||||||
.execute(sudo)
|
|
||||||
.args(&["/usr/sbin/pkg_add", "-u"])
|
let is_current = is_openbsd_current()?;
|
||||||
.status_checked()
|
|
||||||
|
if is_current {
|
||||||
|
sudo.execute(ctx, "/usr/sbin/sysupgrade")?.arg("-sn").status_checked()
|
||||||
|
} else {
|
||||||
|
sudo.execute(ctx, "/usr/sbin/syspatch")?.status_checked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
print_separator(t!("OpenBSD Packages"));
|
||||||
|
|
||||||
|
let sudo = ctx.require_sudo()?;
|
||||||
|
|
||||||
|
let is_current = is_openbsd_current()?;
|
||||||
|
|
||||||
|
if ctx.config().cleanup() {
|
||||||
|
sudo.execute(ctx, "/usr/sbin/pkg_delete")?.arg("-ac").status_checked()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut command = sudo.execute(ctx, "/usr/sbin/pkg_add")?;
|
||||||
|
command.arg("-u");
|
||||||
|
if is_current {
|
||||||
|
command.arg("-Dsnap");
|
||||||
|
}
|
||||||
|
command.status_checked()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
NAME="Anarchy Linux"
|
|
||||||
PRETTY_NAME="Anarchy Linux"
|
|
||||||
ID=anarchy
|
|
||||||
ID_LIKE=anarchylinux
|
|
||||||
ANSI_COLOR="0;36"
|
|
||||||
HOME_URL="https://anarchylinux.org/"
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
NAME="Antergos Linux"
|
|
||||||
VERSION="18.7-ISO-Rolling"
|
|
||||||
ID="antergos"
|
|
||||||
ID_LIKE="arch"
|
|
||||||
PRETTY_NAME="Antergos Linux"
|
|
||||||
CPE_NAME="cpe:/o:antergosproject:antergos:18.7"
|
|
||||||
ANSI_COLOR="1;34;40"
|
|
||||||
HOME_URL="antergos.com"
|
|
||||||
SUPPORT_URL="forum.antergos.com"
|
|
||||||
BUG_REPORT_URL="@antergos"
|
|
||||||
10
src/steps/os/os_release/aosc
Normal file
10
src/steps/os/os_release/aosc
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
PRETTY_NAME="AOSC OS (12.2.2)"
|
||||||
|
NAME="AOSC OS"
|
||||||
|
VERSION_ID="12.2.2"
|
||||||
|
VERSION="12.2.2 (localhost)"
|
||||||
|
BUILD_ID="20250916"
|
||||||
|
ID=aosc
|
||||||
|
ANSI_COLOR="1;36"
|
||||||
|
HOME_URL="https://aosc.io/"
|
||||||
|
SUPPORT_URL="https://github.com/AOSC-Dev/aosc-os-abbs"
|
||||||
|
BUG_REPORT_URL="https://github.com/AOSC-Dev/aosc-os-abbs/issues"
|
||||||
23
src/steps/os/os_release/aurora
Normal file
23
src/steps/os/os_release/aurora
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
NAME="Aurora"
|
||||||
|
VERSION="latest-41.20250210.4 (Kinoite)"
|
||||||
|
RELEASE_TYPE=stable
|
||||||
|
ID=aurora
|
||||||
|
ID_LIKE="fedora"
|
||||||
|
VERSION_ID=41
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f41"
|
||||||
|
PRETTY_NAME="Aurora (Version: latest-41.20250210.4 / FROM Fedora Kinoite 41)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:universal-blue:aurora:41"
|
||||||
|
DEFAULT_HOSTNAME="aurora"
|
||||||
|
HOME_URL="https://getaurora.dev/"
|
||||||
|
DOCUMENTATION_URL="https://docs.getaurora.dev"
|
||||||
|
SUPPORT_URL="https://github.com/ublue-os/aurora/issues/"
|
||||||
|
BUG_REPORT_URL="https://github.com/ublue-os/aurora/issues/"
|
||||||
|
SUPPORT_END=2025-12-15
|
||||||
|
VARIANT="Kinoite"
|
||||||
|
VARIANT_ID=aurora
|
||||||
|
OSTREE_VERSION='latest-41.20250210.4'
|
||||||
|
BUILD_ID="fc1570c"
|
||||||
|
IMAGE_ID="aurora"
|
||||||
25
src/steps/os/os_release/bazzite
Normal file
25
src/steps/os/os_release/bazzite
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
NAME="Bazzite"
|
||||||
|
VERSION="41.20250208.0 (Kinoite)"
|
||||||
|
RELEASE_TYPE=stable
|
||||||
|
ID=bazzite
|
||||||
|
ID_LIKE="fedora"
|
||||||
|
VERSION_ID=41
|
||||||
|
VERSION_CODENAME="Holographic"
|
||||||
|
PLATFORM_ID="platform:f41"
|
||||||
|
PRETTY_NAME="Bazzite 41 (FROM Fedora Kinoite)"
|
||||||
|
ANSI_COLOR="0;38;2;138;43;226"
|
||||||
|
LOGO=bazzite-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:universal-blue:bazzite:41"
|
||||||
|
DEFAULT_HOSTNAME="bazzite"
|
||||||
|
HOME_URL="https://bazzite.gg"
|
||||||
|
DOCUMENTATION_URL="https://docs.bazzite.gg"
|
||||||
|
SUPPORT_URL="https://discord.bazzite.gg"
|
||||||
|
BUG_REPORT_URL="https://github.com/ublue-os/bazzite/issues/"
|
||||||
|
SUPPORT_END=2025-12-15
|
||||||
|
VARIANT="Kinoite"
|
||||||
|
VARIANT_ID=bazzite-nvidia-open
|
||||||
|
OSTREE_VERSION='41.20250208.0'
|
||||||
|
BUILD_ID="Stable (F41.20250208)"
|
||||||
|
BOOTLOADER_NAME="Bazzite Stable (F41.20250208)"
|
||||||
|
BUILD_ID="Stable (F41.20250208)"
|
||||||
|
BOOTLOADER_NAME="Bazzite Stable (F41.20250208)"
|
||||||
24
src/steps/os/os_release/bluefin
Normal file
24
src/steps/os/os_release/bluefin
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
NAME="Bluefin"
|
||||||
|
VERSION="41.20250216.1 (Silverblue)"
|
||||||
|
RELEASE_TYPE=stable
|
||||||
|
ID=bluefin
|
||||||
|
ID_LIKE="fedora"
|
||||||
|
VERSION_ID=41
|
||||||
|
VERSION_CODENAME="Archaeopteryx"
|
||||||
|
PLATFORM_ID="platform:f41"
|
||||||
|
PRETTY_NAME="Bluefin (Version: 41.20250216.1 / FROM Fedora Silverblue 41)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:universal-blue:bluefin:41"
|
||||||
|
DEFAULT_HOSTNAME="bluefin"
|
||||||
|
HOME_URL="https://projectbluefin.io"
|
||||||
|
DOCUMENTATION_URL="https://docs.projectbluefin.io"
|
||||||
|
SUPPORT_URL="https://github.com/ublue-os/bluefin/issues/"
|
||||||
|
BUG_REPORT_URL="https://github.com/ublue-os/bluefin/issues/"
|
||||||
|
SUPPORT_END=2025-12-15
|
||||||
|
VARIANT="Silverblue"
|
||||||
|
VARIANT_ID=bluefin
|
||||||
|
OSTREE_VERSION='41.20250216.1'
|
||||||
|
BUILD_ID="185146a"
|
||||||
|
IMAGE_ID="bluefin"
|
||||||
|
IMAGE_VERSION="41.20250216.1"
|
||||||
11
src/steps/os/os_release/cachyos
Normal file
11
src/steps/os/os_release/cachyos
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
NAME="CachyOS Linux"
|
||||||
|
PRETTY_NAME="CachyOS"
|
||||||
|
ID=cachyos
|
||||||
|
BUILD_ID=rolling
|
||||||
|
ANSI_COLOR="38;2;23;147;209"
|
||||||
|
HOME_URL="https://cachyos.org/"
|
||||||
|
DOCUMENTATION_URL="https://wiki.cachyos.org/"
|
||||||
|
SUPPORT_URL="https://discuss.cachyos.org/"
|
||||||
|
BUG_REPORT_URL="https://github.com/cachyos"
|
||||||
|
PRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/"
|
||||||
|
LOGO=cachyos
|
||||||
@@ -13,4 +13,3 @@ CENTOS_MANTISBT_PROJECT="CentOS-7"
|
|||||||
CENTOS_MANTISBT_PROJECT_VERSION="7"
|
CENTOS_MANTISBT_PROJECT_VERSION="7"
|
||||||
REDHAT_SUPPORT_PRODUCT="centos"
|
REDHAT_SUPPORT_PRODUCT="centos"
|
||||||
REDHAT_SUPPORT_PRODUCT_VERSION="7"
|
REDHAT_SUPPORT_PRODUCT_VERSION="7"
|
||||||
|
|
||||||
|
|||||||
23
src/steps/os/os_release/coreos
Normal file
23
src/steps/os/os_release/coreos
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
NAME="Fedora Linux"
|
||||||
|
VERSION="41.20250117.3.0 (CoreOS)"
|
||||||
|
RELEASE_TYPE=stable
|
||||||
|
ID=fedora
|
||||||
|
VERSION_ID=41
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f41"
|
||||||
|
PRETTY_NAME="Fedora CoreOS 41.20250117.3.0 (uCore)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:fedoraproject:fedora:41"
|
||||||
|
HOME_URL="https://getfedora.org/coreos/"
|
||||||
|
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-coreos/"
|
||||||
|
SUPPORT_URL="https://github.com/coreos/fedora-coreos-tracker/"
|
||||||
|
BUG_REPORT_URL="https://github.com/coreos/fedora-coreos-tracker/"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT_VERSION=41
|
||||||
|
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||||
|
REDHAT_SUPPORT_PRODUCT_VERSION=41
|
||||||
|
SUPPORT_END=2025-12-15
|
||||||
|
VARIANT="CoreOS"
|
||||||
|
VARIANT_ID=coreos
|
||||||
|
OSTREE_VERSION='41.20250117.3.0'
|
||||||
8
src/steps/os/os_release/deepin
Normal file
8
src/steps/os/os_release/deepin
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
PRETTY_NAME="Deepin 20.9"
|
||||||
|
NAME="Deepin"
|
||||||
|
VERSION_ID="20.9"
|
||||||
|
VERSION="20.9"
|
||||||
|
VERSION_CODENAME="apricot"
|
||||||
|
ID=Deepin
|
||||||
|
HOME_URL="https://www.deepin.org/"
|
||||||
|
BUG_REPORT_URL="https://bbs.deepin.org/"
|
||||||
22
src/steps/os/os_release/fedoraiot
Normal file
22
src/steps/os/os_release/fedoraiot
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
NAME="Fedora Linux"
|
||||||
|
VERSION="39.20240415.0 (IoT Edition)"
|
||||||
|
ID=fedora
|
||||||
|
VERSION_ID=39
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f39"
|
||||||
|
PRETTY_NAME="Fedora Linux 39.20240415.0 (IoT Edition)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:fedoraproject:fedora:39"
|
||||||
|
HOME_URL="https://fedoraproject.org/"
|
||||||
|
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f39/system-administrators-guide/"
|
||||||
|
SUPPORT_URL="https://ask.fedoraproject.org/"
|
||||||
|
BUG_REPORT_URL="https://bugzilla.redhat.com/"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT_VERSION=39
|
||||||
|
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||||
|
REDHAT_SUPPORT_PRODUCT_VERSION=39
|
||||||
|
SUPPORT_END=2024-11-12
|
||||||
|
VARIANT="IoT Edition"
|
||||||
|
VARIANT_ID=iot
|
||||||
|
OSTREE_VERSION='39.20240415.0'
|
||||||
23
src/steps/os/os_release/fedorakinoite
Normal file
23
src/steps/os/os_release/fedorakinoite
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
NAME="Fedora Linux"
|
||||||
|
VERSION="39.20240105.0 (Kinoite)"
|
||||||
|
ID=fedora
|
||||||
|
VERSION_ID=39
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f39"
|
||||||
|
PRETTY_NAME="Fedora Linux 39.20240105.0 (Kinoite)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:fedoraproject:fedora:39"
|
||||||
|
DEFAULT_HOSTNAME="fedora"
|
||||||
|
HOME_URL="https://kinoite.fedoraproject.org"
|
||||||
|
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-kinoite/"
|
||||||
|
SUPPORT_URL="https://ask.fedoraproject.org/"
|
||||||
|
BUG_REPORT_URL="https://pagure.io/fedora-kde/SIG/issues"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT_VERSION=39
|
||||||
|
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||||
|
REDHAT_SUPPORT_PRODUCT_VERSION=39
|
||||||
|
SUPPORT_END=2024-11-12
|
||||||
|
VARIANT="Kinoite"
|
||||||
|
VARIANT_ID=kinoite
|
||||||
|
OSTREE_VERSION='39.20240105.0'
|
||||||
22
src/steps/os/os_release/fedoraonyx
Normal file
22
src/steps/os/os_release/fedoraonyx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
NAME="Fedora Linux"
|
||||||
|
VERSION="39 (Onyx)"
|
||||||
|
ID=fedora
|
||||||
|
VERSION_ID=39
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f39"
|
||||||
|
PRETTY_NAME="Fedora Linux 39 (Onyx)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:fedoraproject:fedora:39"
|
||||||
|
DEFAULT_HOSTNAME="fedora"
|
||||||
|
HOME_URL="https://fedoraproject.org/onyx/"
|
||||||
|
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-onyx/"
|
||||||
|
SUPPORT_URL="https://ask.fedoraproject.org/"
|
||||||
|
BUG_REPORT_URL="https://bugzilla.redhat.com/"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT_VERSION=39
|
||||||
|
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||||
|
REDHAT_SUPPORT_PRODUCT_VERSION=39
|
||||||
|
SUPPORT_END=2024-05-14
|
||||||
|
VARIANT="Onyx"
|
||||||
|
VARIANT_ID=onyx
|
||||||
22
src/steps/os/os_release/fedorasericea
Normal file
22
src/steps/os/os_release/fedorasericea
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
NAME="Fedora Linux"
|
||||||
|
VERSION="39 (Sericea)"
|
||||||
|
ID=fedora
|
||||||
|
VERSION_ID=39
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f39"
|
||||||
|
PRETTY_NAME="Fedora Linux 39 (Sericea)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:fedoraproject:fedora:39"
|
||||||
|
DEFAULT_HOSTNAME="fedora"
|
||||||
|
HOME_URL="https://fedoraproject.org/sericea/"
|
||||||
|
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-sericea/"
|
||||||
|
SUPPORT_URL="https://ask.fedoraproject.org/"
|
||||||
|
BUG_REPORT_URL="https://gitlab.com/fedora/sigs/sway/SIG/-/issues"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT_VERSION=39
|
||||||
|
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||||
|
REDHAT_SUPPORT_PRODUCT_VERSION=39
|
||||||
|
SUPPORT_END=2024-05-14
|
||||||
|
VARIANT="Sericea"
|
||||||
|
VARIANT_ID=sericea
|
||||||
22
src/steps/os/os_release/fedorasilverblue
Normal file
22
src/steps/os/os_release/fedorasilverblue
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
NAME="Fedora Linux"
|
||||||
|
VERSION="39 (Silverblue)"
|
||||||
|
ID=fedora
|
||||||
|
VERSION_ID=39
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f39"
|
||||||
|
PRETTY_NAME="Fedora Linux 39 (Silverblue)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:fedoraproject:fedora:39"
|
||||||
|
DEFAULT_HOSTNAME="fedora"
|
||||||
|
HOME_URL="https://silverblue.fedoraproject.org"
|
||||||
|
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-silverblue/"
|
||||||
|
SUPPORT_URL="https://ask.fedoraproject.org/"
|
||||||
|
BUG_REPORT_URL="https://github.com/fedora-silverblue/issue-tracker/issues"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT_VERSION=39
|
||||||
|
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||||
|
REDHAT_SUPPORT_PRODUCT_VERSION=39
|
||||||
|
SUPPORT_END=2024-05-14
|
||||||
|
VARIANT="Silverblue"
|
||||||
|
VARIANT_ID=silverblue
|
||||||
23
src/steps/os/os_release/fedoraswayatomic
Normal file
23
src/steps/os/os_release/fedoraswayatomic
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
NAME="Fedora Linux"
|
||||||
|
VERSION="40.20240426.0 (Sway Atomic)"
|
||||||
|
ID=fedora
|
||||||
|
VERSION_ID=40
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f40"
|
||||||
|
PRETTY_NAME="Fedora Linux 40.20240426.0 (Sway Atomic)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=fedora-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:fedoraproject:fedora:40"
|
||||||
|
DEFAULT_HOSTNAME="fedora"
|
||||||
|
HOME_URL="https://fedoraproject.org/atomic-desktops/sway/"
|
||||||
|
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-sericea/"
|
||||||
|
SUPPORT_URL="https://ask.fedoraproject.org/"
|
||||||
|
BUG_REPORT_URL="https://gitlab.com/fedora/sigs/sway/SIG/-/issues"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT_VERSION=40
|
||||||
|
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||||
|
REDHAT_SUPPORT_PRODUCT_VERSION=40
|
||||||
|
SUPPORT_END=2025-05-13
|
||||||
|
VARIANT="Sway Atomic"
|
||||||
|
VARIANT_ID=sway-atomic
|
||||||
|
OSTREE_VERSION='40.20240426.0'
|
||||||
6
src/steps/os/os_release/funtoo
Normal file
6
src/steps/os/os_release/funtoo
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
ID="funtoo"
|
||||||
|
NAME="Funtoo"
|
||||||
|
PRETTY_NAME="Funtoo Linux"
|
||||||
|
ANSI_COLOR="0;34"
|
||||||
|
HOME_URL="https://www.funtoo.org"
|
||||||
|
BUG_REPORT_URL="https://bugs.funtoo.org"
|
||||||
@@ -4,4 +4,3 @@ PRETTY_NAME="Manjaro ARM"
|
|||||||
ANSI_COLOR="1;32"
|
ANSI_COLOR="1;32"
|
||||||
HOME_URL="https://www.manjaro.org/"
|
HOME_URL="https://www.manjaro.org/"
|
||||||
SUPPORT_URL="https://forum.manjaro.org/c/manjaro-arm/"
|
SUPPORT_URL="https://forum.manjaro.org/c/manjaro-arm/"
|
||||||
|
|
||||||
|
|||||||
8
src/steps/os/os_release/nilrt
Normal file
8
src/steps/os/os_release/nilrt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
ID=nilrt
|
||||||
|
NAME="NI Linux Real-Time"
|
||||||
|
VERSION="10.0 (kirkstone)"
|
||||||
|
VERSION_ID=10.0
|
||||||
|
PRETTY_NAME="NI Linux Real-Time 10.0 (kirkstone)"
|
||||||
|
DISTRO_CODENAME="kirkstone"
|
||||||
|
BUILD_ID="23.8.0f153-x64"
|
||||||
|
VERSION_CODENAME="kirkstone"
|
||||||
23
src/steps/os/os_release/nobara
Normal file
23
src/steps/os/os_release/nobara
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
NAME="Nobara Linux"
|
||||||
|
VERSION="39 (GNOME Edition)"
|
||||||
|
ID=nobara
|
||||||
|
ID_LIKE="rhel centos fedora"
|
||||||
|
VERSION_ID=39
|
||||||
|
VERSION_CODENAME=""
|
||||||
|
PLATFORM_ID="platform:f39"
|
||||||
|
PRETTY_NAME="Nobara Linux 39 (GNOME Edition)"
|
||||||
|
ANSI_COLOR="0;38;2;60;110;180"
|
||||||
|
LOGO=nobara-logo-icon
|
||||||
|
CPE_NAME="cpe:/o:nobaraproject:nobara:39"
|
||||||
|
DEFAULT_HOSTNAME="nobara"
|
||||||
|
HOME_URL="https://nobaraproject.org/"
|
||||||
|
DOCUMENTATION_URL="https://www.nobaraproject.org/"
|
||||||
|
SUPPORT_URL="https://www.nobaraproject.org/"
|
||||||
|
BUG_REPORT_URL="https://gitlab.com/gloriouseggroll/nobara-images"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT="Nobara"
|
||||||
|
REDHAT_BUGZILLA_PRODUCT_VERSION=39
|
||||||
|
REDHAT_SUPPORT_PRODUCT="Nobara"
|
||||||
|
REDHAT_SUPPORT_PRODUCT_VERSION=39
|
||||||
|
SUPPORT_END=2024-05-14
|
||||||
|
VARIANT="GNOME Edition"
|
||||||
|
VARIANT_ID=gnome
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user