Compare commits

...

35 Commits

Author SHA1 Message Date
SteveLauC
4488f3d5d3 chore: bump version to 16.0.3 (#1094) 2025-04-03 17:56:01 +08:00
Gideon
5a7958d20e Fix aqua CLI and JetBrains Aqua conflict (#1092) 2025-04-03 17:40:52 +08:00
Gideon
481a942b76 Fix pixi self-update running when pixi is not installed with the … (#1087)
* Fix `pixi self-update` running when `pixi` is not installed with the `self-update` feature

* Format
2025-04-03 17:38:38 +08:00
Red Wizard
a601d8429d added silent install option for winget (#1089)
* added silent install option for winget

* corrected formatting issues.

* Update src/steps/os/windows.rs

Remove code duplication.

Co-authored-by: SteveLauC <stevelauc@outlook.com>

---------

Co-authored-by: SteveLauC <stevelauc@outlook.com>
2025-03-30 21:11:04 +08:00
tdslot
a4a2d52a6d i18n(app.yml): new language lt (#1069)
* i18n(app.yml): new language lt

Lithuanian language.

* 🌐 i18n(app.yml): update translations for WSL error message

- resolve merge conflict in translation strings
- update spanish and french translations for clarity
- add lithuanian translation for WSL error message
- standardize zh_TW translation format

* 🌐 i18n(locales): add missing translations

- add zh_CN translation for "Topgrade not found in any WSL distribution"
- add lt translations for JetBrains Toolbox related messages
- add lt translations for operating system and updater error messages

* 🌐 i18n(locales): update spanish translation for WSL error message

- improve accuracy of spanish translation for "Could not find Topgrade in any WSL distribution"
- change from "Topgrade no se ha instalado dentro de WSL" to "No se pudo encontrar Topgrade en ninguna distribución WSL"
2025-03-30 14:50:37 +08:00
Nils
47fa3ba7de Add German translations to localization file (#1065)
* Add German translations to localization file

* refactor(localization): remove unused/duplicate translations from app.yml

* i18n(app.yml): add German translations for JetBrains Toolbox messages

* chore: Convert locales/app.yml from CRLF to LF line endings

* Update locales/app.yml

* fix: correct German translation for "Breaking changes"

---------

Co-authored-by: nistee <lo9s4b7qp@mozmail.com>
Co-authored-by: SteveLauC <stevelauc@outlook.com>
2025-03-26 08:59:37 +08:00
Gideon
e6bb6709b3 Update jetbrains-toolbox-updater (#1077) 2025-03-25 10:40:04 +08:00
Alexandre Veyrenc
c421742c4f fix:(emacs): fix issue #1075 (#1076) 2025-03-21 17:01:58 +08:00
ZeroDegress
1312cc8f6e i18n(app.yml): new language zh_CN (#1072) 2025-03-19 09:24:10 +08:00
Gideon
ed37763d30 Add JetBrains Toolbox via jetbrains-toolbox-updater (#1064)
* Add jetbrains-toolbox-updater

* Update jetbrains-toolbox-updater

* Update jetbrains-toolbox-updater

* Update jetbrains-toolbox-updater

* Localize prints

* Update jetbrains-toolbox-updater

* Format

* Add localization

* Fix translation
2025-03-18 11:19:37 +08:00
Justin
583bbf65e2 docs: fix --log-filter link in --help (#1073)
docs: fix EnvFilter link
2025-03-17 14:07:40 +08:00
Izzy Meyer
5770a5caa7 FIX: Allow for -beta OR -current detection and use the correct system upgrade command for the OpenBSD step (#1066)
* FIX: Allow for -beta OR -current detection and use the correct system upgrade command for the OpenBSD step

* FIX: Run fmt, clippy, and test
2025-03-13 09:02:45 +08:00
Gijs Key
722903fec3 Create Armv7l debian package (#1068)
* Create Armv7l debian package

* returned inadvertently removed comment.

---------

Co-authored-by: Gijs Keij <gijs.keij@bit-key.nl>
2025-03-12 11:20:19 +08:00
Xarblu
30f1c3c1b4 feat(sudo): add run0 as a sudo variant (#1067) 2025-03-12 09:07:37 +08:00
dependabot[bot]
ef7d146282 chore(deps): bump ring from 0.17.8 to 0.17.13 (#1062)
Bumps [ring](https://github.com/briansmith/ring) from 0.17.8 to 0.17.13.
- [Changelog](https://github.com/briansmith/ring/blob/main/RELEASES.md)
- [Commits](https://github.com/briansmith/ring/commits)

---
updated-dependencies:
- dependency-name: ring
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-09 10:47:33 +08:00
Andreas02-dev
20667a23d3 fix(sudo): fix sudo detection & pre-sudo for GSudo (#1058) 2025-03-06 08:51:40 +08:00
JaRoSchm
26f05827ae feat(pixi): add support for pixi global (#1037)
feat(pixi): add support for pixi global
2025-03-05 08:58:21 +08:00
Max Kapur
b1ffe7d553 Run conda clean after conda update if cleanup = true (#1047)
* Run `conda clean` after `conda upgrade` if `cleanup = true`

* Also run `mamba clean`
2025-03-04 09:15:02 +08:00
yggdr
368a060529 Add pipxu step (#1052) 2025-03-04 09:11:57 +08:00
Gideon
b40bffb1f2 Add "Cinnamon spices" step (#1055)
* Add "Cinnamon spices" step

* Format

* Move step to Linux
2025-03-04 08:57:12 +08:00
Andre Toerien
488ae149f7 fix(poetry): parse arg in script shebang line (#1028)
* fix(poetry): parse arg in script shebang line

* fix(poetry): improved shebang line parsing on windows
2025-02-25 20:00:53 +08:00
Tom van Dijk
fa3e4726b7 fix: uBlue OS should be detected as FedoraImmutable (#1043)
* refactor(parse_os_release): Don't rely on specific `ID`s for Fedora Immutable

Instead match `ID=fedora` or `ID_LIKE=fedora` and decide wether or not
the distro is immutable by `VARIANT`.

* feat: add aurora,bluefin,coreos support

The `os_release`s came from the following images:

- ghcr.io/ublue-os/aurora:93f0fd9f20b3
- ghcr.io/ublue-os/bluefin:5d37394a5d4b
- ghcr.io/ublue-os/ucore:63cd1200c344

* fix: bazzite should be detected as FedoraImmutable

* squash me: cargo fmt

---------

Co-authored-by: Steve Lau <stevelauc@outlook.com>
2025-02-17 09:41:34 +08:00
Ivan Andre Scheel
66a12cc8bf feat(vscode): updated extensions for a given profile (#1022)
* [feat] select user profile for vscode

* [feat] Update example config file

* [fix] Remove unneeded imports

* [feat] PR comments

* [fix] formatting
2025-02-11 12:51:52 +08:00
Alex Böhm
3e0c21e981 docs: fix typo in description (#1032) 2025-02-08 20:03:32 +08:00
Laura Demkowicz-Duffy
da270ae7d9 Add zigup step (#1030)
* feat: add zigup step

* feat(zigup): add various configuration options

* feat(zigup): add cleanup option

* feat(zigup): multiple version support and cleanup

* refactor(zigup): remove set_default and simplify execution

* fix(zigup): always pass path args to zigup for consistent behaviour

* refactor(zigup): use shellexpand to expand tildes
2025-02-08 14:25:10 +08:00
Laura Demkowicz-Duffy
4624f11ba5 Run juliaup gc if cleanup is enabled (#1031)
refactor(juliaup): run juliaup gc if cleanup is enabled
2025-02-08 14:15:25 +08:00
Dan Sully
224bb96a98 chore: update toolchain to 1.84.1. apply clippy fixes & rustfmt (#1026)
* chore: update to stable toolchain. apply clippy fixes & rustfmt

* Bump MSRV

* Try MSRV without the patch version

* fix: pin toolchain to MSRV

* trying again

* fix dead code warning

---------

Co-authored-by: Dan Sully <dsully@users.noreply.github.com>
2025-02-03 11:24:57 +08:00
SteveLauC
9a6fe8eea9 feat: support VSCodium (#788) 2025-01-09 10:35:45 +08:00
SteveLauC
aebc035ec0 fix: do not run asdf update if version >= 0.15.0 (#1008) 2024-12-20 13:31:29 +08:00
LILAY
bd348c328e Add Fedora Copr to Readme.md (#1005)
Update README.md
2024-12-12 10:01:10 +08:00
Samuel Grahn
c5f2d7b473 Detect Elan self update disabled (e.g. installed from distro repos) (#998)
* Add config option for Elan self-update

* Format & Config

* Revert "Format & Config"

This reverts commit 9eedecce8b312f8ad60563488c98cccfd50c0173.

* Revert "Add config option for Elan self-update"

This reverts commit 8c80c7a7d63ecd0936e0bd5cb07c2cbb1452c1fd.

* Allow self-update to fail when disabled

* Formatting

* Don't print in case of failed self-update

* Formatting

* Use the code suggested :)

* Follow the recommendations by Clippy
2024-12-12 09:19:03 +08:00
SteveLauC
dc9d8d55f2 fix: Executor::spawn()/output() should not use their _checked() variants (#1002) 2024-12-11 09:12:57 +08:00
Steve Lau
b172ba7f03 fix: Executor::spawn()/output() should not use their _checked() variants 2024-12-11 08:59:19 +08:00
SteveLauC
8227890808 refactor(uv step): check self update result if self-update feat is available (#1000)
refactor: check self update result if self-update feat is available
2024-12-10 20:55:32 +08:00
befanyt
a0963fe3fc fix: dont ignore rpm-ostree when bootc is found (#999) 2024-12-10 13:00:37 +08:00
37 changed files with 1468 additions and 316 deletions

View File

@@ -6,20 +6,21 @@ on:
# types:
# - completed
release:
types: [ created ]
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",
]
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@v4
@@ -29,9 +30,14 @@ jobs:
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' }}
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'armv7-unknown-linux-gnueabihf' }}
shell: bash
- name: install targets
@@ -75,14 +81,14 @@ jobs:
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' }}
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' }}
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'armv7-unknown-linux-gnueabihf' }}
shell: bash
- name: Release

0
BREAKINGCHANGES_dev.md Normal file
View File

300
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
@@ -202,7 +202,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -237,7 +237,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -355,9 +355,12 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.99"
version = "1.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
@@ -388,7 +391,7 @@ dependencies = [
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@@ -431,7 +434,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -533,9 +536,9 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
@@ -620,7 +623,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -658,7 +661,16 @@ version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
"dirs-sys 0.4.1",
]
[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys 0.5.0",
]
[[package]]
@@ -679,10 +691,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"redox_users 0.4.5",
"windows-sys 0.48.0",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users 0.5.0",
"windows-sys 0.59.0",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
@@ -690,7 +714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"redox_users 0.4.5",
"winapi",
]
@@ -702,7 +726,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -775,7 +799,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -963,7 +987,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -1326,7 +1350,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -1423,6 +1447,17 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jetbrains-toolbox-updater"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d86b38fee698b3f63c9772fe2832d03a84e0724e409d499517c8a102949717"
dependencies = [
"dirs 6.0.0",
"json",
"sysinfo",
]
[[package]]
name = "js-sys"
version = "0.3.69"
@@ -1432,6 +1467,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -1440,9 +1481,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.155"
version = "0.2.170"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
[[package]]
name = "libredox"
@@ -1619,6 +1660,15 @@ dependencies = [
"zbus",
]
[[package]]
name = "ntapi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
dependencies = [
"winapi",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@@ -1757,7 +1807,7 @@ dependencies = [
"bitflags 1.3.2",
"byteorder",
"chrono",
"thiserror",
"thiserror 1.0.61",
"widestring",
]
@@ -1784,7 +1834,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -1888,9 +1938,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.85"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
@@ -1952,6 +2002,26 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
@@ -1969,7 +2039,18 @@ checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
dependencies = [
"getrandom",
"libredox",
"thiserror",
"thiserror 1.0.61",
]
[[package]]
name = "redox_users"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
"getrandom",
"libredox",
"thiserror 2.0.12",
]
[[package]]
@@ -2069,15 +2150,14 @@ dependencies = [
[[package]]
name = "ring"
version = "0.17.8"
version = "0.17.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.52.0",
]
@@ -2116,7 +2196,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -2298,7 +2378,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -2320,7 +2400,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -2399,9 +2479,15 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b"
dependencies = [
"dirs",
"dirs 5.0.1",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
@@ -2446,12 +2532,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spki"
version = "0.7.3"
@@ -2499,7 +2579,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -2521,9 +2601,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.66"
version = "2.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
dependencies = [
"proc-macro2",
"quote",
@@ -2544,7 +2624,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -2556,6 +2636,20 @@ dependencies = [
"libc",
]
[[package]]
name = "sysinfo"
version = "0.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01"
dependencies = [
"core-foundation-sys",
"libc",
"memchr",
"ntapi",
"rayon",
"windows",
]
[[package]]
name = "tar"
version = "0.4.41"
@@ -2596,7 +2690,16 @@ version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
"thiserror-impl 1.0.61",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl 2.0.12",
]
[[package]]
@@ -2607,7 +2710,18 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.99",
]
[[package]]
@@ -2770,7 +2884,7 @@ dependencies = [
[[package]]
name = "topgrade"
version = "16.0.2"
version = "16.0.3"
dependencies = [
"cfg-if",
"chrono",
@@ -2783,6 +2897,7 @@ dependencies = [
"futures",
"glob",
"home",
"jetbrains-toolbox-updater",
"lazy_static",
"merge",
"nix 0.29.0",
@@ -2801,7 +2916,7 @@ dependencies = [
"strum",
"sys-locale",
"tempfile",
"thiserror",
"thiserror 1.0.61",
"tokio",
"toml 0.8.14",
"tracing",
@@ -2859,7 +2974,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -3063,7 +3178,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
"wasm-bindgen-shared",
]
@@ -3097,7 +3212,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3189,7 +3304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
dependencies = [
"windows-core 0.56.0",
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@@ -3198,7 +3313,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@@ -3210,7 +3325,7 @@ dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@@ -3221,7 +3336,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -3232,7 +3347,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -3241,7 +3356,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
dependencies = [
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@@ -3259,7 +3374,16 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
@@ -3279,18 +3403,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
@@ -3299,7 +3423,7 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515"
dependencies = [
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@@ -3310,9 +3434,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
@@ -3322,9 +3446,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
@@ -3334,15 +3458,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
@@ -3352,9 +3476,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
@@ -3364,9 +3488,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -3376,9 +3500,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
@@ -3388,9 +3512,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
@@ -3488,7 +3612,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
"synstructure",
]
@@ -3539,7 +3663,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
"zvariant_utils",
]
@@ -3571,7 +3695,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
"synstructure",
]
@@ -3600,7 +3724,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]
[[package]]
@@ -3624,7 +3748,7 @@ checksum = "2ba5aa1827d6b1a35a29b3413ec69ce5f796e4d897e3e5b38f461bef41d225ea"
dependencies = [
"base64 0.21.7",
"ed25519-dalek",
"thiserror",
"thiserror 1.0.61",
]
[[package]]
@@ -3649,7 +3773,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
"zvariant_utils",
]
@@ -3661,5 +3785,5 @@ checksum = "fc242db087efc22bd9ade7aa7809e4ba828132edc312871584a6b4391bdf8786"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"syn 2.0.99",
]

View File

@@ -5,8 +5,8 @@ categories = ["os"]
keywords = ["upgrade", "update"]
license = "GPL-3.0"
repository = "https://github.com/topgrade-rs/topgrade"
rust-version = "1.76.0"
version = "16.0.2"
rust-version = "1.84.1"
version = "16.0.3"
authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"]
exclude = ["doc/screenshot.gif", "BREAKINGCHANGES_dev.md"]
edition = "2021"
@@ -54,6 +54,7 @@ notify-rust = "~4.11"
wildmatch = "2.3.0"
rust-i18n = "3.0.1"
sys-locale = "0.3.1"
jetbrains-toolbox-updater = "1.1.0"
[package.metadata.generate-rpm]
assets = [{ source = "target/release/topgrade", dest = "/usr/bin/topgrade" }]

View File

@@ -31,6 +31,7 @@ To remedy this, **Topgrade** detects which tools you use and runs the appropriat
- macOS: [Homebrew](https://formulae.brew.sh/formula/topgrade) or [MacPorts](https://ports.macports.org/port/topgrade/)
- Windows: [Chocolatey][choco], [Scoop][scoop] or [Winget][winget]
- PyPi: [pip](https://pypi.org/project/topgrade/)
- Fedora: [Copr](https://copr.fedorainfracloud.org/coprs/lilay/topgrade/)
[choco]: https://community.chocolatey.org/packages/topgrade
[scoop]: https://scoop.sh/#/apps?q=topgrade

View File

@@ -103,8 +103,8 @@
# 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
# 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)
@@ -219,6 +219,10 @@
# 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
# to upgrade it. Use this only if you installed Topgrade by using a package
# manager such as Scoop or Cargo
@@ -290,3 +294,34 @@
# 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 = ""

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "1.76.0"
channel = "1.84.1"

View File

@@ -45,13 +45,13 @@ impl TryFrom<&Output> for Utf8Output {
type Error = eyre::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!(
"Stdout contained invalid UTF-8: {}",
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!(
"Stderr contained invalid UTF-8: {}",
String::from_utf8_lossy(err.as_bytes())
@@ -149,6 +149,7 @@ pub trait CommandExt {
/// Like [`Command::spawn`], but gives a nice error message if the command fails to
/// execute.
#[track_caller]
#[allow(dead_code)]
fn spawn_checked(&mut self) -> eyre::Result<Self::Child>;
}

View File

@@ -69,6 +69,7 @@ pub enum Step {
Chezmoi,
Chocolatey,
Choosenim,
CinnamonSpices,
ClamAvDb,
Composer,
Conda,
@@ -97,6 +98,7 @@ pub enum Step {
Haxelib,
Helm,
HomeManager,
JetBrainsToolbox,
Jetpack,
Julia,
Juliaup,
@@ -125,6 +127,7 @@ pub enum Step {
PipReviewLocal,
Pipupgrade,
Pipx,
Pipxu,
Pixi,
Pkg,
Pkgin,
@@ -163,6 +166,7 @@ pub enum Step {
Vim,
VoltaPackages,
Vscode,
Vscodium,
Waydroid,
Winget,
Wsl,
@@ -170,6 +174,7 @@ pub enum Step {
Xcodes,
Yadm,
Yarn,
Zigup,
Zvm,
}
@@ -220,6 +225,7 @@ pub struct Windows {
open_remotes_in_new_terminal: Option<bool>,
wsl_update_pre_release: Option<bool>,
wsl_update_use_web_download: Option<bool>,
winget_silent_install: Option<bool>,
}
#[derive(Deserialize, Default, Debug, Merge)]
@@ -460,6 +466,21 @@ pub struct JuliaConfig {
startup_file: Option<bool>,
}
#[derive(Deserialize, Default, Debug, Merge)]
#[serde(deny_unknown_fields)]
pub struct Zigup {
target_versions: Option<Vec<String>>,
install_dir: Option<String>,
path_link: Option<String>,
cleanup: Option<bool>,
}
#[derive(Deserialize, Default, Debug, Merge)]
#[serde(deny_unknown_fields)]
pub struct VscodeConfig {
profile: Option<String>,
}
#[derive(Deserialize, Default, Debug, Merge)]
#[serde(deny_unknown_fields)]
/// Configuration file
@@ -529,6 +550,12 @@ pub struct ConfigFile {
#[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)]
julia: Option<JuliaConfig>,
#[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)]
zigup: Option<Zigup>,
#[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)]
vscode: Option<VscodeConfig>,
}
fn config_directory() -> PathBuf {
@@ -560,7 +587,7 @@ impl ConfigFile {
];
// Search for the main config file
for path in possible_config_paths.iter() {
for path in &possible_config_paths {
if path.exists() {
debug!("Configuration at {}", path.display());
res.0.clone_from(path);
@@ -824,7 +851,7 @@ pub struct CommandLineArgs {
/// Tracing filter directives.
///
/// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html
/// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
#[arg(long, default_value = DEFAULT_LOG_LEVEL)]
pub log_filter: String,
@@ -1474,8 +1501,7 @@ impl Config {
.misc
.as_ref()
.and_then(|misc| misc.ignore_failures.as_ref())
.map(|v| v.contains(&step))
.unwrap_or(false)
.is_some_and(|v| v.contains(&step))
}
pub fn use_predefined_git_repos(&self) -> bool {
@@ -1525,6 +1551,14 @@ impl Config {
.unwrap_or(false)
}
pub fn winget_silent_install(&self) -> bool {
self.config_file
.windows
.as_ref()
.and_then(|windows| windows.winget_silent_install)
.unwrap_or(true)
}
pub fn sudo_command(&self) -> Option<SudoKind> {
self.config_file.misc.as_ref().and_then(|misc| misc.sudo_command)
}
@@ -1667,6 +1701,47 @@ impl Config {
.and_then(|julia| julia.startup_file)
.unwrap_or(true)
}
pub fn zigup_target_versions(&self) -> Vec<String> {
self.config_file
.zigup
.as_ref()
.and_then(|zigup| zigup.target_versions.clone())
.unwrap_or(vec!["master".to_owned()])
}
pub fn zigup_install_dir(&self) -> Option<&str> {
self.config_file
.zigup
.as_ref()
.and_then(|zigup| zigup.install_dir.as_deref())
}
pub fn zigup_path_link(&self) -> Option<&str> {
self.config_file
.zigup
.as_ref()
.and_then(|zigup| zigup.path_link.as_deref())
}
pub fn zigup_cleanup(&self) -> bool {
self.config_file
.zigup
.as_ref()
.and_then(|zigup| zigup.cleanup)
.unwrap_or(false)
}
pub fn vscode_profile(&self) -> Option<&str> {
let vscode_cfg = self.config_file.vscode.as_ref()?;
let profile = vscode_cfg.profile.as_ref()?;
if profile.is_empty() {
None
} else {
Some(profile.as_str())
}
}
}
#[cfg(test)]
@@ -1693,40 +1768,40 @@ mod test {
#[test]
fn test_should_execute_remote_different_hostname() {
assert!(config().should_execute_remote(Ok("hostname".to_string()), "remote_hostname"))
assert!(config().should_execute_remote(Ok("hostname".to_string()), "remote_hostname"));
}
#[test]
fn test_should_execute_remote_different_hostname_with_user() {
assert!(config().should_execute_remote(Ok("hostname".to_string()), "user@remote_hostname"))
assert!(config().should_execute_remote(Ok("hostname".to_string()), "user@remote_hostname"));
}
#[test]
fn test_should_execute_remote_unknown_hostname() {
assert!(config().should_execute_remote(Err(eyre!("failed to get hostname")), "remote_hostname"))
assert!(config().should_execute_remote(Err(eyre!("failed to get hostname")), "remote_hostname"));
}
#[test]
fn test_should_not_execute_remote_same_hostname() {
assert!(!config().should_execute_remote(Ok("hostname".to_string()), "hostname"))
assert!(!config().should_execute_remote(Ok("hostname".to_string()), "hostname"));
}
#[test]
fn test_should_not_execute_remote_same_hostname_with_user() {
assert!(!config().should_execute_remote(Ok("hostname".to_string()), "user@hostname"))
assert!(!config().should_execute_remote(Ok("hostname".to_string()), "user@hostname"));
}
#[test]
fn test_should_execute_remote_matching_limit() {
let mut config = config();
config.opt = CommandLineArgs::parse_from(["topgrade", "--remote-host-limit", "remote_hostname"]);
assert!(config.should_execute_remote(Ok("hostname".to_string()), "user@remote_hostname"))
assert!(config.should_execute_remote(Ok("hostname".to_string()), "user@remote_hostname"));
}
#[test]
fn test_should_not_execute_remote_not_matching_limit() {
let mut config = config();
config.opt = CommandLineArgs::parse_from(["topgrade", "--remote-host-limit", "other_hostname"]);
assert!(!config.should_execute_remote(Ok("hostname".to_string()), "user@remote_hostname"))
assert!(!config.should_execute_remote(Ok("hostname".to_string()), "user@remote_hostname"));
}
}

View File

@@ -11,9 +11,9 @@ pub fn interrupted() -> bool {
/// Clears the interrupted flag
pub fn unset_interrupted() {
debug_assert!(INTERRUPTED.load(Ordering::SeqCst));
INTERRUPTED.store(false, Ordering::SeqCst)
INTERRUPTED.store(false, Ordering::SeqCst);
}
pub fn set_interrupted() {
INTERRUPTED.store(true, Ordering::SeqCst)
INTERRUPTED.store(true, Ordering::SeqCst);
}

View File

@@ -4,7 +4,7 @@ use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal
/// Handle SIGINT. Set the interruption flag.
extern "C" fn handle_sigint(_: i32) {
set_interrupted()
set_interrupted();
}
/// Set the necessary signal handlers.

View File

@@ -152,7 +152,10 @@ impl Executor {
let result = match self {
Executor::Wet(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.
#[allow(clippy::disallowed_methods)]
c.spawn().map(ExecutorChild::Wet)?
}
Executor::Dry(c) => {
c.dry_run();
@@ -166,7 +169,12 @@ impl Executor {
/// See `std::process::Command::output`
pub fn output(&mut self) -> Result<ExecutorOutput> {
match self {
Executor::Wet(c) => Ok(ExecutorOutput::Wet(c.output_checked()?)),
Executor::Wet(c) => {
// We should use `output()` here rather than `output_checked()` since
// their semantics and behaviors are different.
#[allow(clippy::disallowed_methods)]
Ok(ExecutorOutput::Wet(c.output()?))
}
Executor::Dry(c) => {
c.dry_run();
Ok(ExecutorOutput::Dry)
@@ -180,7 +188,7 @@ impl Executor {
pub fn status_checked_with_codes(&mut self, codes: &[i32]) -> Result<()> {
match self {
Executor::Wet(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(())
} else {
Err(())

View File

@@ -25,7 +25,9 @@ use self::config::{CommandLineArgs, Config, Step};
use self::error::StepFailed;
#[cfg(all(windows, feature = "self-update"))]
use self::error::Upgraded;
#[allow(clippy::wildcard_imports)]
use self::steps::{remote::*, *};
#[allow(clippy::wildcard_imports)]
use self::terminal::*;
use self::utils::{hostname, install_color_eyre, install_tracing, update_tracing};
@@ -58,6 +60,7 @@ pub(crate) static WINDOWS_DIRS: Lazy<Windows> = Lazy::new(|| Windows::new().expe
// Init and load the i18n files
i18n!("locales", fallback = "en");
#[allow(clippy::too_many_lines)]
fn run() -> Result<()> {
install_color_eyre()?;
ctrlc::set_handler();
@@ -248,6 +251,9 @@ fn run() -> Result<()> {
runner.execute(Step::Lure, "LURE", || linux::run_lure_update(&ctx))?;
runner.execute(Step::Waydroid, "Waydroid", || linux::run_waydroid(&ctx))?;
runner.execute(Step::AutoCpufreq, "auto-cpufreq", || linux::run_auto_cpufreq(&ctx))?;
runner.execute(Step::CinnamonSpices, "Cinnamon spices", || {
linux::run_cinnamon_spices_updater(&ctx)
})?;
}
#[cfg(target_os = "macos")]
@@ -369,9 +375,13 @@ fn run() -> Result<()> {
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(&ctx))?;
runner.execute(Step::Pipxu, "pipxu", || generic::run_pipxu_update(&ctx))?;
runner.execute(Step::Vscode, "Visual Studio Code extensions", || {
generic::run_vscode_extensions_update(&ctx)
})?;
runner.execute(Step::Vscodium, "VSCodium extensions", || {
generic::run_vscodium_extensions_update(&ctx)
})?;
runner.execute(Step::Conda, "conda", || generic::run_conda_update(&ctx))?;
runner.execute(Step::Mamba, "mamba", || generic::run_mamba_update(&ctx))?;
runner.execute(Step::Pixi, "pixi", || generic::run_pixi_update(&ctx))?;
@@ -435,6 +445,10 @@ fn run() -> Result<()> {
runner.execute(Step::Zvm, "ZVM", || generic::run_zvm(&ctx))?;
runner.execute(Step::Aqua, "aqua", || generic::run_aqua(&ctx))?;
runner.execute(Step::Bun, "bun", || generic::run_bun(&ctx))?;
runner.execute(Step::Zigup, "zigup", || generic::run_zigup(&ctx))?;
runner.execute(Step::JetBrainsToolbox, "JetBrains Toolbox", || {
generic::run_jetbrains_toolbox(&ctx)
})?;
if should_run_powershell {
runner.execute(Step::Powershell, "Powershell Modules Update", || {
@@ -491,13 +505,13 @@ fn run() -> Result<()> {
print_info(t!("\n(R)eboot\n(S)hell\n(Q)uit"));
loop {
match get_key() {
Ok(Key::Char('s')) | Ok(Key::Char('S')) => {
Ok(Key::Char('s' | 'S')) => {
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")?;
}
Ok(Key::Char('q')) | Ok(Key::Char('Q')) => (),
Ok(Key::Char('q' | 'Q')) => (),
_ => {
continue;
}
@@ -516,7 +530,7 @@ fn run() -> Result<()> {
t!("Topgrade finished successfully")
},
Some(Duration::from_secs(10)),
)
);
}
if failed {

View File

@@ -9,7 +9,7 @@ use rust_i18n::t;
use self_update_crate::backends::github::Update;
use self_update_crate::update::UpdateStatus;
use super::terminal::*;
use super::terminal::{print_info, print_separator};
#[cfg(windows)]
use crate::error::Upgraded;

View File

@@ -140,7 +140,7 @@ pub fn run_containers(ctx: &ExecutionContext) -> Result<()> {
list_containers(&crt, ctx.config().containers_ignored_tags()).context("Failed to list Docker containers")?;
debug!("Containers to inspect: {:?}", containers);
for container in containers.iter() {
for container in &containers {
debug!("Pulling container '{}'", container);
let args = vec![
"pull",

View File

@@ -1,9 +1,4 @@
(when (fboundp 'paradox-upgrade-packages)
(progn
(unless (boundp 'paradox-github-token)
(setq paradox-github-token t))
(paradox-upgrade-packages)
(princ
(if (get-buffer "*Paradox Report*")
(with-current-buffer "*Paradox Report*" (buffer-string))
"\nNothing to upgrade\n"))))
(when (featurep 'package)
(if (fboundp 'package-upgrade-all)
(package-upgrade-all nil)
(message "Your Emacs version doesn't support unattended packages upgrade")))

View File

@@ -1,6 +1,6 @@
#![allow(unused_imports)]
use std::ffi::OsStr;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
use std::process::Command;
use std::{env, path::Path};
@@ -9,6 +9,9 @@ use std::{fs, io::Write};
use color_eyre::eyre::eyre;
use color_eyre::eyre::Context;
use color_eyre::eyre::Result;
use jetbrains_toolbox_updater::{find_jetbrains_toolbox, update_jetbrains_toolbox, FindError};
use lazy_static::lazy_static;
use regex::bytes::Regex;
use rust_i18n::t;
use semver::Version;
use tempfile::tempfile_in;
@@ -40,8 +43,7 @@ pub fn is_wsl() -> Result<bool> {
pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> {
let cargo_dir = env::var_os("CARGO_HOME")
.map(PathBuf::from)
.unwrap_or_else(|| HOME_DIR.join(".cargo"))
.map_or_else(|| HOME_DIR.join(".cargo"), PathBuf::from)
.require()?;
require("cargo").or_else(|_| {
require_option(
@@ -60,13 +62,11 @@ pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> {
let cargo_update = require("cargo-install-update")
.ok()
.or_else(|| cargo_dir.join("bin/cargo-install-update").if_exists());
let cargo_update = match cargo_update {
Some(e) => e,
None => {
let message = String::from("cargo-update isn't installed so Topgrade can't upgrade cargo packages.\nInstall cargo-update by running `cargo install cargo-update`");
print_warning(&message);
return Err(SkipStep(message).into());
}
let Some(cargo_update) = cargo_update else {
let message = String::from("cargo-update isn't installed so Topgrade can't upgrade cargo packages.\nInstall cargo-update by running `cargo install cargo-update`");
print_warning(&message);
return Err(SkipStep(message).into());
};
ctx.run_type()
@@ -78,14 +78,11 @@ pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> {
let cargo_cache = require("cargo-cache")
.ok()
.or_else(|| cargo_dir.join("bin/cargo-cache").if_exists());
match cargo_cache {
Some(e) => {
ctx.run_type().execute(e).args(["-a"]).status_checked()?;
}
None => {
let message = String::from("cargo-cache isn't installed so Topgrade can't cleanup cargo packages.\nInstall cargo-cache by running `cargo install cargo-cache`");
print_warning(message);
}
if let Some(e) = cargo_cache {
ctx.run_type().execute(e).args(["-a"]).status_checked()?;
} else {
let message = String::from("cargo-cache isn't installed so Topgrade can't cleanup cargo packages.\nInstall cargo-cache by running `cargo install cargo-cache`");
print_warning(message);
}
}
@@ -229,6 +226,13 @@ pub fn run_apm(ctx: &ExecutionContext) -> Result<()> {
pub fn run_aqua(ctx: &ExecutionContext) -> Result<()> {
let aqua = require("aqua")?;
// Check if `aqua --help` mentions "aqua". JetBrains aqua does not, aqua CLI does.
let output = ctx.run_type().execute(&aqua).arg("--help").output_checked()?;
if !String::from_utf8(output.stdout)?.contains("aqua") {
return Err(SkipStep("Command aqua probably points to JetBrains Aqua".to_string()).into());
}
print_separator("Aqua");
if ctx.run_type().dry() {
println!("{}", t!("Updating aqua ..."));
@@ -258,10 +262,35 @@ pub fn run_elan(ctx: &ExecutionContext) -> Result<()> {
let elan = require("elan")?;
print_separator("elan");
ctx.run_type()
.execute(&elan)
.args(["self", "update"])
.status_checked()?;
let disabled_error_msg = "self-update is disabled";
let executor_output = ctx.run_type().execute(&elan).args(["self", "update"]).output()?;
match executor_output {
ExecutorOutput::Wet(command_output) => {
if command_output.status.success() {
// Flush the captured output
std::io::stdout().lock().write_all(&command_output.stdout).unwrap();
std::io::stderr().lock().write_all(&command_output.stderr).unwrap();
} else {
let stderr_as_str = std::str::from_utf8(&command_output.stderr).unwrap();
if stderr_as_str.contains(disabled_error_msg) {
// `elan` is externally managed, we cannot do the update. Users
// won't see any error message because Topgrade captures them
// all.
} else {
// `elan` is NOT externally managed, `elan self update` can
// be performed, but the invocation failed, so we report the
// error to the user and error out.
std::io::stdout().lock().write_all(&command_output.stdout).unwrap();
std::io::stderr().lock().write_all(&command_output.stderr).unwrap();
return Err(StepFailed.into());
}
}
}
ExecutorOutput::Dry => { /* nothing needed because in a dry run */ }
}
ctx.run_type().execute(&elan).arg("update").status_checked()
}
@@ -277,7 +306,13 @@ pub fn run_juliaup(ctx: &ExecutionContext) -> Result<()> {
.status_checked()?;
}
ctx.run_type().execute(&juliaup).arg("update").status_checked()
ctx.run_type().execute(&juliaup).arg("update").status_checked()?;
if ctx.config().cleanup() {
ctx.run_type().execute(&juliaup).arg("gc").status_checked()?;
}
Ok(())
}
pub fn run_choosenim(ctx: &ExecutionContext) -> Result<()> {
@@ -375,6 +410,48 @@ pub fn run_vcpkg_update(ctx: &ExecutionContext) -> Result<()> {
command.args(["upgrade", "--no-dry-run"]).status_checked()
}
/// Make VSCodium a separate step because:
///
/// 1. Users could use both VSCode and VSCodium
/// 2. Just in case, VSCodium could have incompatible changes with VSCode
pub fn run_vscodium_extensions_update(ctx: &ExecutionContext) -> Result<()> {
// Calling vscodoe in WSL may install a server instead of updating extensions (https://github.com/topgrade-rs/topgrade/issues/594#issuecomment-1782157367)
if is_wsl()? {
return Err(SkipStep(String::from("Should not run in WSL")).into());
}
let vscodium = require("codium")?;
// VSCode has update command only since 1.86 version ("january 2024" update), disable the update for prior versions
// Use command `code --version` which returns 3 lines: version, git commit, instruction set. We parse only the first one
//
// This should apply to VSCodium as well.
let version: Result<Version> = match Command::new(&vscodium)
.arg("--version")
.output_checked_utf8()?
.stdout
.lines()
.next()
{
Some(item) => Version::parse(item).map_err(std::convert::Into::into),
_ => return Err(SkipStep(String::from("Cannot find vscodium version")).into()),
};
if !matches!(version, Ok(version) if version >= Version::new(1, 86, 0)) {
return Err(SkipStep(String::from(
"Too old vscodium version to have update extensions command",
))
.into());
}
print_separator("VSCodium extensions");
ctx.run_type()
.execute(vscodium)
.arg("--update-extensions")
.status_checked()
}
pub fn run_vscode_extensions_update(ctx: &ExecutionContext) -> Result<()> {
// Calling vscode in WSL may install a server instead of updating extensions (https://github.com/topgrade-rs/topgrade/issues/594#issuecomment-1782157367)
if is_wsl()? {
@@ -392,7 +469,7 @@ pub fn run_vscode_extensions_update(ctx: &ExecutionContext) -> Result<()> {
.lines()
.next()
{
Some(item) => Version::parse(item).map_err(|err| err.into()),
Some(item) => Version::parse(item).map_err(std::convert::Into::into),
_ => return Err(SkipStep(String::from("Cannot find vscode version")).into()),
};
@@ -402,10 +479,19 @@ pub fn run_vscode_extensions_update(ctx: &ExecutionContext) -> Result<()> {
print_separator("Visual Studio Code extensions");
ctx.run_type()
.execute(vscode)
.arg("--update-extensions")
.status_checked()
if let Some(profile) = ctx.config().vscode_profile() {
ctx.run_type()
.execute(vscode)
.arg("--profile")
.arg(profile)
.arg("--update-extensions")
.status_checked()
} else {
ctx.run_type()
.execute(vscode)
.arg("--update-extensions")
.status_checked()
}
}
pub fn run_pipx_update(ctx: &ExecutionContext) -> Result<()> {
@@ -422,12 +508,22 @@ pub fn run_pipx_update(ctx: &ExecutionContext) -> Result<()> {
.map(|s| s.stdout.trim().to_owned());
let version = Version::parse(&version_str?);
if matches!(version, Ok(version) if version >= Version::new(1, 4, 0)) {
command_args.push("--quiet")
command_args.push("--quiet");
}
ctx.run_type().execute(pipx).args(command_args).status_checked()
}
pub fn run_pipxu_update(ctx: &ExecutionContext) -> Result<()> {
let pipxu = require("pipxu")?;
print_separator("pipxu");
ctx.run_type()
.execute(pipxu)
.args(["upgrade", "--all"])
.status_checked()
}
pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> {
let conda = require("conda")?;
@@ -441,19 +537,45 @@ pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> {
print_separator("Conda");
let mut command = ctx.run_type().execute(conda);
let mut command = ctx.run_type().execute(&conda);
command.args(["update", "--all", "-n", "base"]);
if ctx.config().yes(Step::Conda) {
command.arg("--yes");
}
command.status_checked()
command.status_checked()?;
if ctx.config().cleanup() {
let mut command = ctx.run_type().execute(conda);
command.args(["clean", "--all"]);
if ctx.config().yes(Step::Conda) {
command.arg("--yes");
}
command.status_checked()?;
}
Ok(())
}
pub fn run_pixi_update(ctx: &ExecutionContext) -> Result<()> {
let pixi = require("pixi")?;
print_separator("Pixi");
ctx.run_type().execute(pixi).args(["self-update"]).status_checked()
// Check if `pixi --help` mentions self-update, if yes, self-update must be enabled.
// pixi self-update --help works regardless of whether the feature is enabled.
let output = ctx.run_type().execute(&pixi).arg("--help").output_checked()?;
if String::from_utf8(output.stdout)?.contains("self-update") {
ctx.run_type()
.execute(&pixi)
.args(["self-update"])
.status_checked()
.ok();
}
ctx.run_type()
.execute(&pixi)
.args(["global", "update"])
.status_checked()
}
pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> {
@@ -461,12 +583,23 @@ pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> {
print_separator("Mamba");
let mut command = ctx.run_type().execute(mamba);
let mut command = ctx.run_type().execute(&mamba);
command.args(["update", "--all", "-n", "base"]);
if ctx.config().yes(Step::Mamba) {
command.arg("--yes");
}
command.status_checked()
command.status_checked()?;
if ctx.config().cleanup() {
let mut command = ctx.run_type().execute(&mamba);
command.args(["clean", "--all"]);
if ctx.config().yes(Step::Mamba) {
command.arg("--yes");
}
command.status_checked()?;
}
Ok(())
}
pub fn run_miktex_packages_update(ctx: &ExecutionContext) -> Result<()> {
@@ -488,7 +621,7 @@ pub fn run_pip3_update(ctx: &ExecutionContext) -> Result<()> {
(Ok(py), _) => py,
(Err(_), Ok(py3)) => py3,
(Err(py_err), Err(py3_err)) => {
return Err(SkipStep(format!("Skip due to following reasons: {} {}", py_err, py3_err)).into());
return Err(SkipStep(format!("Skip due to following reasons: {py_err} {py3_err}")).into());
}
};
@@ -837,7 +970,7 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
.execute(&dotnet)
.args(["tool", "update", package_name, "--global"])
.status_checked()
.with_context(|| format!("Failed to update .NET package {:?}", package_name))?;
.with_context(|| format!("Failed to update .NET package {package_name:?}"))?;
}
Ok(())
@@ -1032,24 +1165,39 @@ pub fn run_poetry(ctx: &ExecutionContext) -> Result<()> {
let poetry = require("poetry")?;
#[cfg(unix)]
fn get_interpreter(poetry: &PathBuf) -> Result<PathBuf> {
fn get_interpreter(poetry: &PathBuf) -> Result<(PathBuf, Option<OsString>)> {
// Parse the standard Unix shebang line: #!interpreter [optional-arg]
// Spaces and tabs on either side of interpreter are ignored.
use std::os::unix::ffi::OsStrExt;
lazy_static! {
static ref SHEBANG_REGEX: Regex = Regex::new(r"^#![ \t]*([^ \t\n]+)(?:[ \t]+([^\n]+)?)?").unwrap();
}
let script = fs::read(poetry)?;
if let Some(r) = script.iter().position(|&b| b == b'\n') {
let first_line = &script[..r];
if first_line.starts_with(b"#!") {
return Ok(OsStr::from_bytes(&first_line[2..]).into());
}
if let Some(c) = SHEBANG_REGEX.captures(&script) {
let interpreter = OsStr::from_bytes(&c[1]).into();
let args = c.get(2).map(|args| OsStr::from_bytes(args.as_bytes()).into());
return Ok((interpreter, args));
}
Err(eyre!("Could not find shebang"))
}
#[cfg(windows)]
fn get_interpreter(poetry: &PathBuf) -> Result<PathBuf> {
let data = fs::read(poetry)?;
fn get_interpreter(poetry: &PathBuf) -> Result<(PathBuf, Option<OsString>)> {
// Parse the shebang line from scripts using https://bitbucket.org/vinay.sajip/simple_launcher,
// such as those created by pip. In contrast to Unix shebang lines, interpreter paths can
// contain spaces, if they are double-quoted.
// https://bitbucket.org/vinay.sajip/simple_launcher/src/master/compare_launchers.py
use std::str;
lazy_static! {
static ref SHEBANG_REGEX: Regex =
Regex::new(r#"^#![ \t]*(?:"([^"\n]+)"|([^" \t\n]+))(?:[ \t]+([^\n]+)?)?"#).unwrap();
}
let data = fs::read(poetry)?;
let pos = match data.windows(4).rposition(|b| b == b"PK\x05\x06") {
Some(i) => i,
@@ -1068,29 +1216,40 @@ pub fn run_poetry(ctx: &ExecutionContext) -> Result<()> {
return Err(eyre!("Invalid ZIP archive"));
}
let arc_pos = pos - cdr_size - cdr_offset;
let shebang = match data[..arc_pos].windows(2).rposition(|b| b == b"#!") {
Some(l) => &data[l + 2..arc_pos - 1],
None => return Err(eyre!("Could not find shebang")),
};
// shebang line is utf8
Ok(std::str::from_utf8(shebang)?.into())
match data[..arc_pos].windows(2).rposition(|b| b == b"#!") {
Some(l) => {
let line = &data[l..arc_pos - 1];
if let Some(c) = SHEBANG_REGEX.captures(line) {
let interpreter = c.get(1).or_else(|| c.get(2)).unwrap();
// shebang line should be valid utf8
let interpreter = str::from_utf8(interpreter.as_bytes())?.into();
let args = match c.get(3) {
Some(args) => Some(str::from_utf8(args.as_bytes())?.into()),
None => None,
};
Ok((interpreter, args))
} else {
Err(eyre!("Invalid shebang line"))
}
}
None => Err(eyre!("Could not find shebang")),
}
}
if ctx.config().poetry_force_self_update() {
debug!("forcing poetry self update");
} else {
let interpreter = match get_interpreter(&poetry) {
Ok(p) => p,
Err(e) => {
return Err(SkipStep(format!("Could not find interpreter for {}: {}", poetry.display(), e)).into())
}
};
debug!("poetry interpreter: {}", interpreter.display());
let (interp, interp_args) = get_interpreter(&poetry)
.map_err(|e| SkipStep(format!("Could not find interpreter for {}: {}", poetry.display(), e)))?;
debug!("poetry interpreter: {:?}, args: {:?}", interp, interp_args);
let check_official_install_script =
"import sys; from os import path; print('Y') if path.isfile(path.join(sys.prefix, 'poetry_env')) else print('N')";
let output = Command::new(&interpreter)
let mut command = Command::new(&interp);
if let Some(args) = interp_args {
command.arg(args);
}
let output = command
.args(["-c", check_official_install_script])
.output_checked_utf8()?;
let stdout = output.stdout.trim();
@@ -1129,8 +1288,7 @@ pub fn run_uv(ctx: &ExecutionContext) -> Result<()> {
ctx.run_type()
.execute(&uv_exec)
.args(["self", "update"])
.status_checked()
.ok();
.status_checked()?;
}
ctx.run_type()
@@ -1155,3 +1313,87 @@ pub fn run_bun(ctx: &ExecutionContext) -> Result<()> {
ctx.run_type().execute(bun).arg("upgrade").status_checked()
}
pub fn run_zigup(ctx: &ExecutionContext) -> Result<()> {
let zigup = require("zigup")?;
let config = ctx.config();
print_separator("zigup");
let mut path_args = Vec::new();
if let Some(path) = config.zigup_path_link() {
path_args.push("--path-link".to_owned());
path_args.push(shellexpand::tilde(path).into_owned());
}
if let Some(path) = config.zigup_install_dir() {
path_args.push("--install-dir".to_owned());
path_args.push(shellexpand::tilde(path).into_owned());
}
for zig_version in config.zigup_target_versions() {
ctx.run_type()
.execute(&zigup)
.args(&path_args)
.arg("fetch")
.arg(&zig_version)
.status_checked()?;
if config.zigup_cleanup() {
ctx.run_type()
.execute(&zigup)
.args(&path_args)
.arg("keep")
.arg(&zig_version)
.status_checked()?;
}
}
if config.zigup_cleanup() {
ctx.run_type()
.execute(zigup)
.args(&path_args)
.arg("clean")
.status_checked()?;
}
Ok(())
}
pub fn run_jetbrains_toolbox(_ctx: &ExecutionContext) -> Result<()> {
let installation = find_jetbrains_toolbox();
match installation {
Err(FindError::NotFound) => {
// Skip
Err(SkipStep(format!("{}", t!("No JetBrains Toolbox installation found"))).into())
}
Err(FindError::UnsupportedOS(os)) => {
// Skip
Err(SkipStep(format!("{}", t!("Unsupported operating system {os}", os = os))).into())
}
Err(e) => {
// Unexpected error
println!(
"{}",
t!("jetbrains-toolbox-updater encountered an unexpected error during finding:")
);
println!("{e:?}");
Err(StepFailed.into())
}
Ok(installation) => {
print_separator("JetBrains Toolbox");
match update_jetbrains_toolbox(installation) {
Err(e) => {
// Unexpected error
println!(
"{}",
t!("jetbrains-toolbox-updater encountered an unexpected error during updating:")
);
println!("{e:?}");
Err(StepFailed.into())
}
Ok(()) => Ok(()),
}
}
}
}

View File

@@ -105,7 +105,7 @@ pub fn run_git_pull(ctx: &ExecutionContext) -> Result<()> {
print_warning(t!(
"Path {pattern} did not contain any git repositories",
pattern = pattern
))
));
});
if repos.is_repos_empty() {
@@ -207,10 +207,13 @@ impl RepoStep {
return output;
}
Err(e) => match e.kind() {
io::ErrorKind::NotFound => debug!("{} does not exist", path.as_ref().display()),
_ => error!("Error looking for {}: {e}", path.as_ref().display(),),
},
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());
}
}
}
None
@@ -321,7 +324,7 @@ impl RepoStep {
.output()
.await?;
let result = output_checked_utf8(pull_output)
.and_then(|_| output_checked_utf8(submodule_output))
.and_then(|()| output_checked_utf8(submodule_output))
.wrap_err_with(|| format!("Failed to pull {}", repo.as_ref().display()));
if result.is_err() {
@@ -359,7 +362,7 @@ impl RepoStep {
}
}
result.map(|_| ())
result
}
/// Pull the repositories specified in `self.repos`.
@@ -410,7 +413,7 @@ impl RepoStep {
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());
let error = results.into_iter().find(std::result::Result::is_err);
error.unwrap_or(Ok(()))
}
}

View File

@@ -87,7 +87,7 @@ impl NPM {
.args(["--version"])
.output_checked_utf8()
.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<()> {
@@ -266,7 +266,7 @@ impl Deno {
.args(["-V"])
.output_checked_utf8()
.map(|s| s.stdout.trim().to_owned().split_off(5)); // remove "deno " prefix
Version::parse(&version_str?).map_err(|err| err.into())
Version::parse(&version_str?).map_err(std::convert::Into::into)
}
}
@@ -399,7 +399,7 @@ pub fn run_volta_packages_upgrade(ctx: &ExecutionContext) -> Result<()> {
return Ok(());
}
for package in installed_packages.iter() {
for package in &installed_packages {
ctx.run_type()
.execute(&volta)
.args(["install", package])

View File

@@ -60,19 +60,7 @@ impl Distribution {
Some("wolfi") => Distribution::Wolfi,
Some("centos") | Some("rhel") | Some("ol") => Distribution::CentOS,
Some("clear-linux-os") => Distribution::ClearLinux,
Some("fedora") => {
return if let Some(variant) = variant {
match variant {
"Silverblue" | "Kinoite" | "Sericea" | "Onyx" | "IoT Edition" | "Sway Atomic" => {
Ok(Distribution::FedoraImmutable)
}
_ => Ok(Distribution::Fedora),
}
} else {
Ok(Distribution::Fedora)
};
}
Some("fedora") => Distribution::match_fedora_variant(&variant),
Some("nilrt") => Distribution::NILRT,
Some("nobara") => Distribution::Nobara,
Some("void") => Distribution::Void,
@@ -109,7 +97,7 @@ impl Distribution {
} else if id_like.contains(&"alpine") {
return Ok(Distribution::Alpine);
} else if id_like.contains(&"fedora") {
return Ok(Distribution::Fedora);
return Ok(Distribution::match_fedora_variant(&variant));
}
}
return Err(TopgradeError::UnknownLinuxDistribution.into());
@@ -117,6 +105,15 @@ impl Distribution {
})
}
fn match_fedora_variant(variant: &Option<&str>) -> Self {
if let Some("Silverblue" | "Kinoite" | "Sericea" | "Onyx" | "IoT Edition" | "Sway Atomic" | "CoreOS") = variant
{
Distribution::FedoraImmutable
} else {
Distribution::Fedora
}
}
pub fn detect() -> Result<Self> {
if PathBuf::from("/bedrock").exists() {
return Ok(Distribution::Bedrock);
@@ -230,7 +227,9 @@ fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
return ctx.run_type().execute(sudo).arg(&bootc).arg("upgrade").status_checked();
}
} else if let Some(ostree) = which("rpm-ostree") {
}
if let Some(ostree) = which("rpm-ostree") {
if ctx.config().rpm_ostree() {
let mut command = ctx.run_type().execute(ostree);
command.arg("upgrade");
@@ -1118,6 +1117,17 @@ pub fn run_auto_cpufreq(ctx: &ExecutionContext) -> Result<()> {
.status_checked()
}
pub fn run_cinnamon_spices_updater(ctx: &ExecutionContext) -> Result<()> {
let cinnamon_spice_updater = require("cinnamon-spice-updater")?;
print_separator("Cinnamon spices");
ctx.run_type()
.execute(cinnamon_spice_updater)
.arg("--update-all")
.status_checked()
}
#[cfg(test)]
mod tests {
use super::*;
@@ -1286,4 +1296,24 @@ mod tests {
fn test_nilrt() {
test_template(include_str!("os_release/nilrt"), Distribution::NILRT);
}
#[test]
fn test_coreos() {
test_template(include_str!("os_release/coreos"), Distribution::FedoraImmutable);
}
#[test]
fn test_aurora() {
test_template(include_str!("os_release/aurora"), Distribution::FedoraImmutable);
}
#[test]
fn test_bluefin() {
test_template(include_str!("os_release/bluefin"), Distribution::FedoraImmutable);
}
#[test]
fn test_bazzite() {
test_template(include_str!("os_release/bazzite"), Distribution::FedoraImmutable);
}
}

View File

@@ -203,7 +203,7 @@ pub fn update_xcodes(ctx: &ExecutionContext) -> Result<()> {
.execute(&xcodes)
.args([
"uninstall",
releases_new_installed.iter().next().cloned().unwrap_or_default(),
releases_new_installed.iter().next().copied().unwrap_or_default(),
])
.status_checked();
}
@@ -216,12 +216,7 @@ pub fn update_xcodes(ctx: &ExecutionContext) -> Result<()> {
pub fn process_xcodes_releases(releases_filtered: Vec<String>, should_ask: bool, ctx: &ExecutionContext) -> Result<()> {
let xcodes = require("xcodes")?;
if releases_filtered
.last()
.map(|s| !s.contains("(Installed)"))
.unwrap_or(true)
&& !releases_filtered.is_empty()
{
if releases_filtered.last().map_or(true, |s| !s.contains("(Installed)")) && !releases_filtered.is_empty() {
println!(
"{} {}",
t!("New Xcode release detected:"),

View File

@@ -8,7 +8,7 @@ use std::fs;
fn is_openbsd_current(ctx: &ExecutionContext) -> Result<bool> {
let motd_content = fs::read_to_string("/etc/motd")?;
let is_current = motd_content.contains("-current");
let is_current = ["-current", "-beta"].iter().any(|&s| motd_content.contains(s));
if ctx.config().dry_run() {
println!("{}", t!("Would check if OpenBSD is -current"));
Ok(is_current)
@@ -28,10 +28,11 @@ pub fn upgrade_openbsd(ctx: &ExecutionContext) -> Result<()> {
return Ok(());
}
let mut args = vec!["/usr/sbin/sysupgrade", "-n"];
if is_current {
args.push("-s");
}
let args = if is_current {
vec!["/usr/sbin/sysupgrade", "-sn"]
} else {
vec!["/usr/sbin/syspatch"]
};
ctx.run_type().execute(sudo).args(&args).status_checked()
}

View 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"

View 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)"

View 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"

View 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'

View File

@@ -463,7 +463,7 @@ pub fn run_nix(ctx: &ExecutionContext) -> Result<()> {
lazy_static! {
static ref NIX_VERSION_REGEX: Regex =
Regex::new(r#"^nix \([^)]*\) ([0-9.]+)"#).expect("Nix version regex always compiles");
Regex::new(r"^nix \([^)]*\) ([0-9.]+)").expect("Nix version regex always compiles");
}
if get_version_cmd_first_line_stdout.is_empty() {
@@ -611,8 +611,7 @@ fn nix_profile_dir(nix: &Path) -> Result<Option<PathBuf>> {
if user_env
.file_name()
.and_then(|name| name.to_str())
.map(|name| name.ends_with("user-environment"))
.unwrap_or(false)
.is_some_and(|name| name.ends_with("user-environment"))
{
Some(profile_dir)
} else {
@@ -637,10 +636,33 @@ pub fn run_asdf(ctx: &ExecutionContext) -> Result<()> {
let asdf = require("asdf")?;
print_separator("asdf");
ctx.run_type()
.execute(&asdf)
.arg("update")
.status_checked_with_codes(&[42])?;
// asdf (>= 0.15.0) won't support the self-update command
//
// https://github.com/topgrade-rs/topgrade/issues/1007
let version_output = Command::new(&asdf).arg("version").output_checked_utf8()?;
// Example output
//
// ```
// $ asdf version
// v0.15.0-31e8c93
//
// ```
let version_stdout = version_output.stdout.trim();
// trim the starting 'v'
let mut remaining = version_stdout.trim_start_matches('v');
let idx = remaining
.find('-')
.expect("the output of `asdf version` changed, please file an issue to Topgrade");
// remove the hash part
remaining = &remaining[..idx];
let version = Version::parse(remaining).expect("should be a valid version");
if version < Version::new(0, 15, 0) {
ctx.run_type()
.execute(&asdf)
.arg("update")
.status_checked_with_codes(&[42])?;
}
ctx.run_type()
.execute(&asdf)
@@ -694,9 +716,7 @@ pub fn run_pyenv(ctx: &ExecutionContext) -> Result<()> {
let pyenv = require("pyenv")?;
print_separator("pyenv");
let pyenv_dir = var("PYENV_ROOT")
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".pyenv"));
let pyenv_dir = var("PYENV_ROOT").map_or_else(|_| HOME_DIR.join(".pyenv"), PathBuf::from);
if !pyenv_dir.exists() {
return Err(SkipStep(t!("Pyenv is installed, but $PYENV_ROOT is not set correctly").to_string()).into());
@@ -717,8 +737,7 @@ pub fn run_sdkman(ctx: &ExecutionContext) -> Result<()> {
let bash = require("bash")?;
let sdkman_init_path = var("SDKMAN_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".sdkman"))
.map_or_else(|_| HOME_DIR.join(".sdkman"), PathBuf::from)
.join("bin")
.join("sdkman-init.sh")
.require()
@@ -727,8 +746,7 @@ pub fn run_sdkman(ctx: &ExecutionContext) -> Result<()> {
print_separator("SDKMAN!");
let sdkman_config_path = var("SDKMAN_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".sdkman"))
.map_or_else(|_| HOME_DIR.join(".sdkman"), PathBuf::from)
.join("etc")
.join("config")
.require()?;
@@ -781,9 +799,7 @@ pub fn run_bun_packages(ctx: &ExecutionContext) -> Result<()> {
print_separator(t!("Bun Packages"));
let mut package_json: PathBuf = var("BUN_INSTALL")
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".bun"));
let mut package_json: PathBuf = var("BUN_INSTALL").map_or_else(|_| HOME_DIR.join(".bun"), PathBuf::from);
package_json.push("install/global/package.json");
if !package_json.exists() {

View File

@@ -42,10 +42,12 @@ pub fn run_winget(ctx: &ExecutionContext) -> Result<()> {
print_separator("winget");
ctx.run_type()
.execute(winget)
.args(["upgrade", "--all"])
.status_checked()
let mut args = vec!["upgrade", "--all"];
if ctx.config().winget_silent_install() {
args.push("--silent");
}
ctx.run_type().execute(winget).args(args).status_checked()
}
pub fn run_scoop(ctx: &ExecutionContext) -> Result<()> {

View File

@@ -9,7 +9,7 @@ use rust_i18n::t;
use crate::command::CommandExt;
use crate::execution_context::ExecutionContext;
use crate::terminal::{is_dumb, print_separator};
use crate::utils::{require_option, which, PathExt};
use crate::utils::{require_option, which};
use crate::Step;
pub struct Powershell {
@@ -30,7 +30,7 @@ impl Powershell {
.args(["-NoProfile", "-Command", "Split-Path $profile"])
.output_checked_utf8()
.map(|output| PathBuf::from(output.stdout.trim()))
.and_then(|p| p.require())
.and_then(super::super::utils::PathExt::require)
.ok()
});
@@ -70,11 +70,11 @@ impl Powershell {
let mut cmd = vec!["Update-Module"];
if ctx.config().verbose() {
cmd.push("-Verbose")
cmd.push("-Verbose");
}
if ctx.config().yes(Step::Powershell) {
cmd.push("-Force")
cmd.push("-Force");
}
println!("{}", t!("Updating modules..."));

View File

@@ -232,7 +232,7 @@ pub fn upgrade_vagrant_boxes(ctx: &ExecutionContext) -> Result<()> {
}
if !found {
println!("{}", t!("No outdated boxes"))
println!("{}", t!("No outdated boxes"));
} else {
ctx.run_type()
.execute(&vagrant)

View File

@@ -128,7 +128,7 @@ impl Tmux {
.output_checked_utf8()?
.stdout
.lines()
.map(|l| l.parse())
.map(str::parse)
.collect::<Result<Vec<usize>, _>>()
.context("Failed to compute tmux windows")
}
@@ -181,19 +181,16 @@ pub fn run_in_tmux(config: TmuxConfig) -> Result<()> {
pub fn run_command(ctx: &ExecutionContext, window_name: &str, command: &str) -> Result<()> {
let tmux = Tmux::new(ctx.config().tmux_config()?.args);
match ctx.get_tmux_session() {
Some(session_name) => {
let indices = tmux.window_indices(&session_name)?;
let last_window = indices
.iter()
.last()
.ok_or_else(|| eyre!("tmux session {session_name} has no windows"))?;
tmux.new_window(&session_name, &format!("{last_window}"), command)?;
}
None => {
let name = tmux.new_unique_session("topgrade", window_name, command)?;
ctx.set_tmux_session(name);
}
if let Some(session_name) = ctx.get_tmux_session() {
let indices = tmux.window_indices(&session_name)?;
let last_window = indices
.iter()
.last()
.ok_or_else(|| eyre!("tmux session {session_name} has no windows"))?;
tmux.new_window(&session_name, &format!("{last_window}"), command)?;
} else {
let name = tmux.new_unique_session("topgrade", window_name, command)?;
ctx.set_tmux_session(name);
}
Ok(())
}

View File

@@ -65,7 +65,7 @@ fn upgrade(command: &mut Executor, ctx: &ExecutionContext) -> Result<()> {
if !status.success() {
return Err(TopgradeError::ProcessFailed(command.get_program(), status).into());
} else {
println!("{}", t!("Plugins upgraded"))
println!("{}", t!("Plugins upgraded"));
}
}

View File

@@ -30,9 +30,7 @@ pub fn run_zr(ctx: &ExecutionContext) -> Result<()> {
}
fn zdotdir() -> PathBuf {
env::var("ZDOTDIR")
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.clone())
env::var("ZDOTDIR").map_or_else(|_| HOME_DIR.clone(), PathBuf::from)
}
pub fn zshrc() -> PathBuf {
@@ -66,8 +64,7 @@ pub fn run_antigen(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?;
let zshrc = zshrc().require()?;
env::var("ADOTDIR")
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join("antigen.zsh"))
.map_or_else(|_| HOME_DIR.join("antigen.zsh"), PathBuf::from)
.require()?;
print_separator("antigen");
@@ -83,8 +80,7 @@ pub fn run_zgenom(ctx: &ExecutionContext) -> Result<()> {
let zsh = require("zsh")?;
let zshrc = zshrc().require()?;
env::var("ZGEN_SOURCE")
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".zgenom"))
.map_or_else(|_| HOME_DIR.join(".zgenom"), PathBuf::from)
.require()?;
print_separator("zgenom");
@@ -101,8 +97,7 @@ pub fn run_zplug(ctx: &ExecutionContext) -> Result<()> {
zshrc().require()?;
env::var("ZPLUG_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".zplug"))
.map_or_else(|_| HOME_DIR.join(".zplug"), PathBuf::from)
.require()?;
print_separator("zplug");
@@ -118,8 +113,7 @@ pub fn run_zinit(ctx: &ExecutionContext) -> Result<()> {
let zshrc = zshrc().require()?;
env::var("ZINIT_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| XDG_DIRS.data_dir().join("zinit"))
.map_or_else(|_| XDG_DIRS.data_dir().join("zinit"), PathBuf::from)
.require()?;
print_separator("zinit");
@@ -153,8 +147,7 @@ pub fn run_zim(ctx: &ExecutionContext) -> Result<()> {
.output_checked_utf8()
.map(|o| o.stdout)
})
.map(PathBuf::from)
.unwrap_or_else(|_| HOME_DIR.join(".zim"))
.map_or_else(|_| HOME_DIR.join(".zim"), PathBuf::from)
.require()?;
print_separator("zim");

View File

@@ -22,13 +22,28 @@ pub struct Sudo {
}
impl Sudo {
/// Get the `sudo` binary or the `gsudo` binary in the case of `gsudo`
/// masquerading as the `sudo` binary.
fn determine_sudo_variant(sudo_p: PathBuf) -> (PathBuf, SudoKind) {
match which("gsudo") {
Some(gsudo_p) => {
match std::fs::canonicalize(&gsudo_p).unwrap() == std::fs::canonicalize(&sudo_p).unwrap() {
true => (gsudo_p, SudoKind::Gsudo),
false => (sudo_p, SudoKind::Sudo),
}
}
None => (sudo_p, SudoKind::Sudo),
}
}
/// Get the `sudo` binary for this platform.
pub fn detect() -> Option<Self> {
which("doas")
.map(|p| (p, SudoKind::Doas))
.or_else(|| which("sudo").map(|p| (p, SudoKind::Sudo)))
.or_else(|| which("sudo").map(Self::determine_sudo_variant))
.or_else(|| which("gsudo").map(|p| (p, SudoKind::Gsudo)))
.or_else(|| which("pkexec").map(|p| (p, SudoKind::Pkexec)))
.or_else(|| which("run0").map(|p| (p, SudoKind::Run0)))
.or_else(|| which("please").map(|p| (p, SudoKind::Please)))
.map(|(path, kind)| Self { path, kind })
}
@@ -65,9 +80,11 @@ impl Sudo {
cmd.arg("-v");
}
SudoKind::Gsudo => {
// Shows current user, cache and console status.
// `gsudo` doesn't have anything like `sudo -v` to cache credentials,
// so we just execute a dummy `echo` command so we have something
// unobtrusive to run.
// See: https://gerardog.github.io/gsudo/docs/usage
cmd.arg("status");
cmd.arg("echo");
}
SudoKind::Pkexec => {
// I don't think this does anything; `pkexec` usually asks for
@@ -79,6 +96,13 @@ impl Sudo {
// See: https://linux.die.net/man/1/pkexec
cmd.arg("echo");
}
SudoKind::Run0 => {
// `run0` uses polkit for authentication
// and thus has the same issues as `pkexec`.
//
// See: https://www.freedesktop.org/software/systemd/man/devel/run0.html
cmd.arg("echo");
}
SudoKind::Please => {
// From `man please`
// -w, --warm
@@ -115,6 +139,7 @@ pub enum SudoKind {
Sudo,
Gsudo,
Pkexec,
Run0,
Please,
}

View File

@@ -52,9 +52,7 @@ impl Terminal {
Self {
width: term.size_checked().map(|(_, w)| w),
term,
prefix: env::var("TOPGRADE_PREFIX")
.map(|prefix| format!("({prefix}) "))
.unwrap_or_else(|_| String::new()),
prefix: env::var("TOPGRADE_PREFIX").map_or_else(|_| String::new(), |prefix| format!("({prefix}) ")),
set_title: true,
display_time: true,
desktop_notification: false,
@@ -62,15 +60,15 @@ impl Terminal {
}
fn set_desktop_notifications(&mut self, desktop_notifications: bool) {
self.desktop_notification = desktop_notifications
self.desktop_notification = desktop_notifications;
}
fn set_title(&mut self, set_title: bool) {
self.set_title = set_title
self.set_title = set_title;
}
fn display_time(&mut self, display_time: bool) {
self.display_time = display_time
self.display_time = display_time;
}
fn notify_desktop<P: AsRef<str>>(&self, message: P, timeout: Option<Duration>) {
@@ -223,8 +221,8 @@ impl Terminal {
let answer = loop {
match self.term.read_key() {
Ok(Key::Char('y')) | Ok(Key::Char('Y')) => break Ok(true),
Ok(Key::Char('s')) | Ok(Key::Char('S')) => {
Ok(Key::Char('y' | 'Y')) => break Ok(true),
Ok(Key::Char('s' | 'S')) => {
println!(
"\n\n{}\n",
t!("Dropping you to shell. Fix what you need and then exit the shell.")
@@ -235,12 +233,12 @@ impl Terminal {
break Ok(true);
}
}
Ok(Key::Char('n')) | Ok(Key::Char('N')) | Ok(Key::Enter) => break Ok(false),
Ok(Key::Char('n' | 'N') | Key::Enter) => break Ok(false),
Err(e) => {
error!("Error reading from terminal: {}", e);
break Ok(false);
}
Ok(Key::Char('q')) | Ok(Key::Char('Q')) => {
Ok(Key::Char('q' | 'Q')) => {
return Err(io::Error::from(io::ErrorKind::Interrupted)).context("Quit from user input")
}
_ => (),
@@ -268,26 +266,26 @@ pub fn should_retry(interrupted: bool, step_name: &str) -> eyre::Result<bool> {
}
pub fn print_separator<P: AsRef<str>>(message: P) {
TERMINAL.lock().unwrap().print_separator(message)
TERMINAL.lock().unwrap().print_separator(message);
}
#[allow(dead_code)]
pub fn print_error<P: AsRef<str>, Q: AsRef<str>>(key: Q, message: P) {
TERMINAL.lock().unwrap().print_error(key, message)
TERMINAL.lock().unwrap().print_error(key, message);
}
#[allow(dead_code)]
pub fn print_warning<P: AsRef<str>>(message: P) {
TERMINAL.lock().unwrap().print_warning(message)
TERMINAL.lock().unwrap().print_warning(message);
}
#[allow(dead_code)]
pub fn print_info<P: AsRef<str>>(message: P) {
TERMINAL.lock().unwrap().print_info(message)
TERMINAL.lock().unwrap().print_info(message);
}
pub fn print_result<P: AsRef<str>>(key: P, result: &StepResult) {
TERMINAL.lock().unwrap().print_result(key, result)
TERMINAL.lock().unwrap().print_result(key, result);
}
/// Tells whether the terminal is dumb.
@@ -316,7 +314,7 @@ pub fn prompt_yesno(question: &str) -> Result<bool, io::Error> {
}
pub fn notify_desktop<P: AsRef<str>>(message: P, timeout: Option<Duration>) {
TERMINAL.lock().unwrap().notify_desktop(message, timeout)
TERMINAL.lock().unwrap().notify_desktop(message, timeout);
}
pub fn display_time(display_time: bool) {

View File

@@ -86,7 +86,7 @@ pub fn editor() -> Vec<String> {
env::var("EDITOR")
.unwrap_or_else(|_| String::from(if cfg!(windows) { "notepad" } else { "vi" }))
.split_whitespace()
.map(|s| s.to_owned())
.map(std::borrow::ToOwned::to_owned)
.collect()
}