Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
22
.github/workflows/check_i18n.yml
vendored
Normal file
22
.github/workflows/check_i18n.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
name: Check i18n
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_locale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- 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
|
||||||
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@@ -7,7 +7,6 @@ on:
|
|||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
env:
|
env:
|
||||||
RUST_VER: 'stable'
|
|
||||||
CROSS_VER: '0.2.5'
|
CROSS_VER: '0.2.5'
|
||||||
CARGO_NET_RETRY: 3
|
CARGO_NET_RETRY: 3
|
||||||
|
|
||||||
@@ -19,12 +18,6 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Rust
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: '${{ env.RUST_VER }}'
|
|
||||||
components: rustfmt
|
|
||||||
|
|
||||||
- name: Run cargo fmt
|
- name: Run cargo fmt
|
||||||
env:
|
env:
|
||||||
TERM: xterm-256color
|
TERM: xterm-256color
|
||||||
@@ -73,12 +66,6 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Rust
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: '${{ env.RUST_VER }}'
|
|
||||||
components: clippy
|
|
||||||
|
|
||||||
- name: Setup Rust Cache
|
- name: Setup Rust Cache
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
28
.github/workflows/create_release_assets.yml
vendored
28
.github/workflows/create_release_assets.yml
vendored
@@ -18,10 +18,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup Rust
|
- name: Install cargo-deb
|
||||||
uses: dtolnay/rust-toolchain@stable
|
run: cargo install cargo-deb
|
||||||
with:
|
if: ${{ matrix.platform == 'ubuntu-latest' }}
|
||||||
components: rustfmt, clippy
|
shell: bash
|
||||||
|
|
||||||
- name: Check format
|
- name: Check format
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
- name: Rename Release (Unix)
|
- name: Rename Release (Unix)
|
||||||
run: |
|
run: |
|
||||||
cargo install default-target
|
cargo install default-target
|
||||||
mkdir assets
|
mkdir -p assets
|
||||||
FILENAME=topgrade-${{github.event.release.tag_name}}-$(default-target)
|
FILENAME=topgrade-${{github.event.release.tag_name}}-$(default-target)
|
||||||
mv target/release/topgrade assets
|
mv target/release/topgrade assets
|
||||||
cd assets
|
cd assets
|
||||||
@@ -51,6 +51,24 @@ jobs:
|
|||||||
if: ${{ matrix.platform != 'windows-latest' }}
|
if: ${{ matrix.platform != 'windows-latest' }}
|
||||||
shell: bash
|
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: ${{ matrix.platform == 'ubuntu-latest' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Move Debian-based system package
|
||||||
|
run: |
|
||||||
|
mkdir -p assets
|
||||||
|
mv target/debian/*.deb assets
|
||||||
|
if: ${{ matrix.platform == 'ubuntu-latest' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Rename Release (Windows)
|
- name: Rename Release (Windows)
|
||||||
run: |
|
run: |
|
||||||
cargo install default-target
|
cargo install default-target
|
||||||
|
|||||||
@@ -24,10 +24,15 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup Rust
|
- name: Install cargo-deb cross compilation dependencies
|
||||||
uses: dtolnay/rust-toolchain@stable
|
run: sudo apt-get install libc6-arm64-cross libgcc-s1-arm64-cross
|
||||||
with:
|
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
|
||||||
components: rustfmt, clippy
|
shell: bash
|
||||||
|
|
||||||
|
- name: Install cargo-deb
|
||||||
|
run: cargo install cargo-deb
|
||||||
|
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: install targets
|
- name: install targets
|
||||||
run: rustup target add ${{ matrix.target }}
|
run: rustup target add ${{ matrix.target }}
|
||||||
@@ -54,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Rename Release
|
- name: Rename Release
|
||||||
run: |
|
run: |
|
||||||
mkdir assets
|
mkdir -p assets
|
||||||
FILENAME=topgrade-${{github.event.release.tag_name}}-${{matrix.target}}
|
FILENAME=topgrade-${{github.event.release.tag_name}}-${{matrix.target}}
|
||||||
mv target/${{matrix.target}}/release/topgrade assets
|
mv target/${{matrix.target}}/release/topgrade assets
|
||||||
cd assets
|
cd assets
|
||||||
@@ -62,6 +67,24 @@ jobs:
|
|||||||
rm topgrade
|
rm topgrade
|
||||||
ls .
|
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' }}
|
||||||
|
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' }}
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
3
.github/workflows/release_to_aur.yml
vendored
3
.github/workflows/release_to_aur.yml
vendored
@@ -14,8 +14,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Publish AUR package
|
- name: Publish AUR package
|
||||||
uses: ATiltedTree/create-aur-release@v1
|
uses: aksh1618/update-aur-package@v1.0.5
|
||||||
with:
|
with:
|
||||||
|
tag_version_prefix: v
|
||||||
package_name: topgrade
|
package_name: topgrade
|
||||||
commit_username: "Thomas Schönauer"
|
commit_username: "Thomas Schönauer"
|
||||||
commit_email: t.schoenauer@hgs-wt.at
|
commit_email: t.schoenauer@hgs-wt.at
|
||||||
|
|||||||
2
.github/workflows/release_to_winget.yml
vendored
2
.github/workflows/release_to_winget.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
|||||||
publish:
|
publish:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
- uses: vedantmgoyal2009/winget-releaser@main
|
||||||
with:
|
with:
|
||||||
identifier: topgrade-rs.topgrade
|
identifier: topgrade-rs.topgrade
|
||||||
max-versions-to-keep: 5 # keep only latest 5 versions
|
max-versions-to-keep: 5 # keep only latest 5 versions
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
# Git: Pull Repos
|
# Containers step
|
||||||
|
|
||||||
1. The output of "Pulling <repository path>" has been moved behind the
|
* New default behavior: In the previous versions, if you have both Docker and
|
||||||
--verbose flag / [misc] configuration block.
|
Podman installed, Podman will be used by Topgrade. Now the default option
|
||||||
|
has been changed to Docker. This can be overridden by setting the
|
||||||
# Configuration
|
`containers.runtime` option in the configuration TOML to "podman".
|
||||||
|
|
||||||
1. The `enable_winget` configuration entry in the `windows` section has been
|
|
||||||
removed because it will not cause any issues and will be enabled by default.
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ To add a new `step` to `topgrade`:
|
|||||||
|
|
||||||
// Invoke the new step to get things updated!
|
// Invoke the new step to get things updated!
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute("xxx")
|
.execute(xxx)
|
||||||
.arg(/* args required by this step */)
|
.arg(/* args required by this step */)
|
||||||
.status_checked()
|
.status_checked()
|
||||||
}
|
}
|
||||||
|
|||||||
245
Cargo.lock
generated
245
Cargo.lock
generated
@@ -90,6 +90,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arc-swap"
|
||||||
|
version = "1.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -319,6 +325,16 @@ dependencies = [
|
|||||||
"piper",
|
"piper",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.16.0"
|
version = "3.16.0"
|
||||||
@@ -539,6 +555,25 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.20"
|
version = "0.8.20"
|
||||||
@@ -994,6 +1029,36 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globset"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"bstr",
|
||||||
|
"log",
|
||||||
|
"regex-automata 0.4.7",
|
||||||
|
"regex-syntax 0.8.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globwalk"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"ignore",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
@@ -1276,12 +1341,38 @@ dependencies = [
|
|||||||
"utf8_iter",
|
"utf8_iter",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ignore"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"globset",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata 0.4.7",
|
||||||
|
"same-file",
|
||||||
|
"walkdir",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indenter"
|
name = "indenter"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.2.6"
|
version = "2.2.6"
|
||||||
@@ -1289,7 +1380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.14.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1363,6 +1454,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
@@ -1500,6 +1597,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "normpath"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify-rust"
|
name = "notify-rust"
|
||||||
version = "4.11.0"
|
version = "4.11.0"
|
||||||
@@ -1611,7 +1717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
|
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dlv-list",
|
"dlv-list",
|
||||||
"hashbrown",
|
"hashbrown 0.14.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1982,6 +2088,57 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-i18n"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dcd94370631e5658a0a23635f7f47e43d06a00ad948e0bb5de79b00d85b880c"
|
||||||
|
dependencies = [
|
||||||
|
"globwalk",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"rust-i18n-macro",
|
||||||
|
"rust-i18n-support",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-i18n-macro"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "355763801dcf287e777e42def7c578410783477b804b1107852119e0b2518396"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rust-i18n-support",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-i18n-support"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "399801f4d955abf1c3ce3ce2215dc76bd40beb4ae39e3a84936b21a79ce2caa5"
|
||||||
|
dependencies = [
|
||||||
|
"arc-swap",
|
||||||
|
"globwalk",
|
||||||
|
"lazy_static",
|
||||||
|
"normpath",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"toml 0.7.8",
|
||||||
|
"triomphe",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-ini"
|
name = "rust-ini"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
@@ -2187,6 +2344,18 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yaml"
|
||||||
|
version = "0.8.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 1.9.3",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
"yaml-rust",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -2378,6 +2547,15 @@ dependencies = [
|
|||||||
"syn 2.0.66",
|
"syn 2.0.66",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sys-locale"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tar"
|
name = "tar"
|
||||||
version = "0.4.41"
|
version = "0.4.41"
|
||||||
@@ -2520,6 +2698,18 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.7.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit 0.19.15",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.14"
|
version = "0.8.14"
|
||||||
@@ -2541,13 +2731,26 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.19.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 2.2.6",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow 0.5.40",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap 2.2.6",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"winnow 0.5.40",
|
"winnow 0.5.40",
|
||||||
]
|
]
|
||||||
@@ -2558,7 +2761,7 @@ version = "0.22.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap 2.2.6",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@@ -2567,7 +2770,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "topgrade"
|
name = "topgrade"
|
||||||
version = "15.0.0"
|
version = "16.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -2588,6 +2791,7 @@ dependencies = [
|
|||||||
"parselnk",
|
"parselnk",
|
||||||
"regex",
|
"regex",
|
||||||
"regex-split",
|
"regex-split",
|
||||||
|
"rust-i18n",
|
||||||
"rust-ini",
|
"rust-ini",
|
||||||
"self_update",
|
"self_update",
|
||||||
"semver",
|
"semver",
|
||||||
@@ -2595,10 +2799,11 @@ dependencies = [
|
|||||||
"shell-words",
|
"shell-words",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"strum",
|
"strum",
|
||||||
|
"sys-locale",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml 0.8.14",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
@@ -2713,6 +2918,17 @@ version = "0.1.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
|
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "triomphe"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369"
|
||||||
|
dependencies = [
|
||||||
|
"arc-swap",
|
||||||
|
"serde",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -3243,6 +3459,15 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
@@ -3358,9 +3583,9 @@ checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec"
|
name = "zerovec"
|
||||||
version = "0.10.2"
|
version = "0.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c"
|
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"yoke",
|
"yoke",
|
||||||
"zerofrom",
|
"zerofrom",
|
||||||
@@ -3369,9 +3594,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec-derive"
|
name = "zerovec-derive"
|
||||||
version = "0.10.2"
|
version = "0.10.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7"
|
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
15
Cargo.toml
15
Cargo.toml
@@ -5,7 +5,8 @@ categories = ["os"]
|
|||||||
keywords = ["upgrade", "update"]
|
keywords = ["upgrade", "update"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
repository = "https://github.com/topgrade-rs/topgrade"
|
repository = "https://github.com/topgrade-rs/topgrade"
|
||||||
version = "15.0.0"
|
rust-version = "1.76.0"
|
||||||
|
version = "16.0.0"
|
||||||
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", "BREAKINGCHANGES_dev.md"]
|
exclude = ["doc/screenshot.gif", "BREAKINGCHANGES_dev.md"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -51,6 +52,8 @@ merge = "~0.1"
|
|||||||
regex-split = "~0.1"
|
regex-split = "~0.1"
|
||||||
notify-rust = "~4.11"
|
notify-rust = "~4.11"
|
||||||
wildmatch = "2.3.0"
|
wildmatch = "2.3.0"
|
||||||
|
rust-i18n = "3.0.1"
|
||||||
|
sys-locale = "0.3.1"
|
||||||
|
|
||||||
[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" }]
|
||||||
@@ -59,7 +62,15 @@ assets = [{ source = "target/release/topgrade", dest = "/usr/bin/topgrade" }]
|
|||||||
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
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
nix = { version = "~0.29", features = ["hostname", "signal", "user"] }
|
nix = { version = "~0.29", features = ["hostname", "signal", "user"] }
|
||||||
|
|||||||
@@ -29,18 +29,16 @@ To remedy this, **Topgrade** detects which tools you use and runs the appropriat
|
|||||||
- NixOS: [Nixpkgs](https://search.nixos.org/packages?show=topgrade)
|
- NixOS: [Nixpkgs](https://search.nixos.org/packages?show=topgrade)
|
||||||
- Void Linux: [XBPS](https://voidlinux.org/packages/?arch=x86_64&q=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/)
|
- macOS: [Homebrew](https://formulae.brew.sh/formula/topgrade) or [MacPorts](https://ports.macports.org/port/topgrade/)
|
||||||
- Windows: [Scoop][scoop] or [Winget][winget]
|
- Windows: [Chocolatey][choco], [Scoop][scoop] or [Winget][winget]
|
||||||
- PyPi: [pip](https://pypi.org/project/topgrade/)
|
- PyPi: [pip](https://pypi.org/project/topgrade/)
|
||||||
|
|
||||||
|
[choco]: https://community.chocolatey.org/packages/topgrade
|
||||||
[scoop]: https://scoop.sh/#/apps?q=topgrade
|
[scoop]: https://scoop.sh/#/apps?q=topgrade
|
||||||
[winget]: https://winstall.app/apps/topgrade-rs.topgrade
|
[winget]: https://winstall.app/apps/topgrade-rs.topgrade
|
||||||
|
|
||||||
Other systems users can either use `cargo install` or the compiled binaries from the release page.
|
Other systems users can either use `cargo install` or the compiled binaries from the release page.
|
||||||
The compiled binaries contain a self-upgrading feature.
|
The compiled binaries contain a self-upgrading feature.
|
||||||
|
|
||||||
> Currently, Topgrade requires Rust 1.65 or above. In general, Topgrade tracks
|
|
||||||
> the latest stable toolchain.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Just run `topgrade`.
|
Just run `topgrade`.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
```sh'
|
```sh'
|
||||||
$ cd topgrade
|
$ cd topgrade
|
||||||
$ cp BREAKINGCHANGES_dev.md BREAKINGCHANGES.md
|
$ mv BREAKINGCHANGES_dev.md BREAKINGCHANGES.md
|
||||||
$ touch BREAKINGCHANGES_dev.md
|
$ touch BREAKINGCHANGES_dev.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ We only support the latest major version and each subversion.
|
|||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| -------- | ------------------ |
|
| -------- | ------------------ |
|
||||||
| 10.0.x | :white_check_mark: |
|
| 15.0.x | :white_check_mark: |
|
||||||
| < 10.0 | :x: |
|
| < 15.0 | :x: |
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,12 @@
|
|||||||
# Run inside tmux (default: false)
|
# Run inside tmux (default: false)
|
||||||
# run_in_tmux = true
|
# run_in_tmux = true
|
||||||
|
|
||||||
|
# Changes the way topgrade interacts with
|
||||||
|
# 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"
|
||||||
|
|
||||||
# Cleanup temporary or old files (default: false)
|
# Cleanup temporary or old files (default: false)
|
||||||
# cleanup = true
|
# cleanup = true
|
||||||
|
|
||||||
@@ -114,6 +120,12 @@
|
|||||||
# both of them, they won't clash with each other.
|
# both of them, they won't clash with each other.
|
||||||
# greedy_latest = true
|
# 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
|
# For the BrewFormula step
|
||||||
# Execute `brew autoremove` after the step.
|
# Execute `brew autoremove` after the step.
|
||||||
# autoremove = true
|
# autoremove = true
|
||||||
@@ -244,6 +256,8 @@
|
|||||||
[containers]
|
[containers]
|
||||||
# Specify the containers to ignore while updating (Wildcard supported)
|
# Specify the containers to ignore while updating (Wildcard supported)
|
||||||
# ignored_containers = ["ghcr.io/rancher-sandbox/rancher-desktop/rdx-proxy:latest", "docker.io*"]
|
# 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"
|
||||||
|
|
||||||
[lensfun]
|
[lensfun]
|
||||||
# If disabled, Topgrade invokes `lensfun‑update‑data` without root priviledge,
|
# If disabled, Topgrade invokes `lensfun‑update‑data` without root priviledge,
|
||||||
|
|||||||
307
locales/app.yml
Normal file
307
locales/app.yml
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
_version: 2
|
||||||
|
|
||||||
|
"Current system locale is {system_locale}":
|
||||||
|
en: "Current system locale is %{system_locale}"
|
||||||
|
"Dry running: {program_name} {arguments}":
|
||||||
|
en: "Dry running: %{program_name} %{arguments}"
|
||||||
|
"in {directory}":
|
||||||
|
en: "in %{directory}"
|
||||||
|
"Rebooting...":
|
||||||
|
en: "Rebooting..."
|
||||||
|
"Plugins upgraded":
|
||||||
|
en: "Plugins upgraded"
|
||||||
|
"Would self-update":
|
||||||
|
en: "Would self-update"
|
||||||
|
"Pulling":
|
||||||
|
en: "Pulling"
|
||||||
|
"No Breaking changes":
|
||||||
|
en: "No Breaking changes"
|
||||||
|
"Dropping you to shell. Fix what you need and then exit the shell.":
|
||||||
|
en: "Dropping you to shell. Fix what you need and then exit the shell."
|
||||||
|
"Topgrade launched in a new tmux session":
|
||||||
|
en: "Topgrade launched in a new tmux session"
|
||||||
|
'Topgrade upgraded to {version}:\n':
|
||||||
|
en: 'Topgrade upgraded to %{version}:\n'
|
||||||
|
"Topgrade is up-to-date":
|
||||||
|
en: "Topgrade is up-to-date"
|
||||||
|
"Updating modules...":
|
||||||
|
en: "Updating modules..."
|
||||||
|
"Powershell Modules Update":
|
||||||
|
en: "Powershell Modules Update"
|
||||||
|
"Powershell is not installed":
|
||||||
|
en: "Powershell is not installed"
|
||||||
|
"Error detecting current distribution: {error}":
|
||||||
|
en: "Error detecting current distribution: %{error}"
|
||||||
|
"Error: {error}":
|
||||||
|
en: "Error: %{error}"
|
||||||
|
"Failed":
|
||||||
|
en: "Failed"
|
||||||
|
"pulling":
|
||||||
|
en: "pulling"
|
||||||
|
"Changed":
|
||||||
|
en: "Changed"
|
||||||
|
"Up-to-date":
|
||||||
|
en: "Up-to-date"
|
||||||
|
"Self update":
|
||||||
|
en: "Self update"
|
||||||
|
|
||||||
|
# The following 2 strings are used in the same sentence
|
||||||
|
"Only":
|
||||||
|
en: "Only"
|
||||||
|
"updated repositories will be shown...":
|
||||||
|
en: "updated repositories will be shown..."
|
||||||
|
|
||||||
|
"because it has no remotes":
|
||||||
|
en: "because it has no remotes"
|
||||||
|
"Skipping":
|
||||||
|
en: "Skipping"
|
||||||
|
"Aura(<0.4.6) requires sudo installed to work with AUR packages":
|
||||||
|
en: "Aura(<0.4.6) requires sudo installed to work with AUR packages"
|
||||||
|
"Pacman backup configuration files found:":
|
||||||
|
en: "Pacman backup configuration files found:"
|
||||||
|
"The package audit was successful, but vulnerable packages still remain on the system":
|
||||||
|
en: "The package audit was successful, but vulnerable packages still remain on the system"
|
||||||
|
"Syncing portage":
|
||||||
|
en: "Syncing portage"
|
||||||
|
"Finding available software":
|
||||||
|
en: "Finding available software"
|
||||||
|
"A system update is available. Do you wish to install it?":
|
||||||
|
en: "A system update is available. Do you wish to install it?"
|
||||||
|
"No new software available.":
|
||||||
|
en: "No new software available."
|
||||||
|
"No Xcode releases installed.":
|
||||||
|
en: "No Xcode releases installed."
|
||||||
|
"Would you like to move the former Xcode release to the trash?":
|
||||||
|
en: "Would you like to move the former Xcode release to the trash?"
|
||||||
|
"New Xcode release detected:":
|
||||||
|
en: "New Xcode release detected:"
|
||||||
|
"Would you like to install it?":
|
||||||
|
en: "Would you like to install it?"
|
||||||
|
"No global packages installed":
|
||||||
|
en: "No global packages installed"
|
||||||
|
"Remote Topgrade launched in Tmux":
|
||||||
|
en: "Remote Topgrade launched in Tmux"
|
||||||
|
"Remote Topgrade launched in an external terminal":
|
||||||
|
en: "Remote Topgrade launched in an external terminal"
|
||||||
|
"Collecting Vagrant boxes":
|
||||||
|
en: "Collecting Vagrant boxes"
|
||||||
|
"No Vagrant directories were specified in the configuration file":
|
||||||
|
en: "No Vagrant directories were specified in the configuration file"
|
||||||
|
"Vagrant boxes":
|
||||||
|
en: "Vagrant boxes"
|
||||||
|
"No outdated boxes":
|
||||||
|
en: "No outdated boxes"
|
||||||
|
"Summary":
|
||||||
|
en: "Summary"
|
||||||
|
"Topgrade finished with errors":
|
||||||
|
en: "Topgrade finished with errors"
|
||||||
|
"Topgrade finished successfully":
|
||||||
|
en: "Topgrade finished successfully"
|
||||||
|
"Topgrade {version_str} Breaking Changes":
|
||||||
|
en: "Topgrade %{version_str} Breaking Changes"
|
||||||
|
"Path {path} expanded to {expanded}":
|
||||||
|
en: "Path %{path} expanded to %{expanded}"
|
||||||
|
"Path {path} doesn't exist":
|
||||||
|
en: "Path %{path} doesn't exist"
|
||||||
|
"Cannot find {binary_name} in PATH":
|
||||||
|
en: "Cannot find %{binary_name} in PATH"
|
||||||
|
"Failed to get a UTF-8 encoded hostname":
|
||||||
|
en: "Failed to get a UTF-8 encoded hostname"
|
||||||
|
"Failed to get hostname: {err}":
|
||||||
|
en: "Failed to get hostname: %{err}"
|
||||||
|
"{python} is a Python 2, skip.":
|
||||||
|
en: "%{python} is a Python 2, skip."
|
||||||
|
"{python} is a Python shim, skip.":
|
||||||
|
en: "%{python} is a Python shim, skip."
|
||||||
|
"{key} failed:":
|
||||||
|
en: "%{key} failed:"
|
||||||
|
"{step_name} failed":
|
||||||
|
en: "%{step_name} failed"
|
||||||
|
"DragonFly BSD Packages":
|
||||||
|
en: "DragonFly BSD Packages"
|
||||||
|
"DragonFly BSD Audit":
|
||||||
|
en: "DragonFly BSD Audit"
|
||||||
|
"FreeBSD Update":
|
||||||
|
en: "FreeBSD Update"
|
||||||
|
"FreeBSD Packages":
|
||||||
|
en: "FreeBSD Packages"
|
||||||
|
"FreeBSD Audit":
|
||||||
|
en: "FreeBSD Audit"
|
||||||
|
"System update":
|
||||||
|
en: "System update"
|
||||||
|
"needrestart will be ran by the package manager":
|
||||||
|
en: "needrestart will be ran by the package manager"
|
||||||
|
"Check for needed restarts":
|
||||||
|
en: "Check for needed restarts"
|
||||||
|
"Should not run in WSL":
|
||||||
|
en: "Should not run in WSL"
|
||||||
|
"Firmware upgrades":
|
||||||
|
en: "Firmware upgrades"
|
||||||
|
"Flatpak System Packages":
|
||||||
|
en: "Flatpak System Packages"
|
||||||
|
"Snapd socket does not exist":
|
||||||
|
en: "Snapd socket does not exist"
|
||||||
|
"You need to specify at least one container":
|
||||||
|
en: "You need to specify at least one container"
|
||||||
|
"Skipped in --yes":
|
||||||
|
en: "Skipped in --yes"
|
||||||
|
"Configuration update":
|
||||||
|
en: "Configuration update"
|
||||||
|
"Going to execute `waydroid upgrade`, which would STOP the running container, is this ok?":
|
||||||
|
en: "Going to execute `waydroid upgrade`, which would STOP the running container, is this ok?"
|
||||||
|
"Skip the Waydroid step because the user don't want to proceed":
|
||||||
|
en: "Skip the Waydroid step because the user don't want to proceed"
|
||||||
|
"macOS App Store":
|
||||||
|
en: "macOS App Store"
|
||||||
|
"macOS system update":
|
||||||
|
en: "macOS system update"
|
||||||
|
"OpenBSD Update":
|
||||||
|
en: "OpenBSD Update"
|
||||||
|
"OpenBSD Packages":
|
||||||
|
en: "OpenBSD Packages"
|
||||||
|
"`fisher` is not defined in `fish`":
|
||||||
|
en: "`fisher` is not defined in `fish`"
|
||||||
|
"`fish_plugins` path doesn't exist: {err}":
|
||||||
|
en: "`fish_plugins` path doesn't exist: %{err}"
|
||||||
|
"`fish_update_completions` is not available":
|
||||||
|
en: "`fish_update_completions` is not available"
|
||||||
|
"Desktop doest not appear to be gnome":
|
||||||
|
en: "Desktop doest not appear to be gnome"
|
||||||
|
"Gnome shell extensions are unregistered in DBus":
|
||||||
|
en: "Gnome shell extensions are unregistered in DBus"
|
||||||
|
"Gnome Shell extensions":
|
||||||
|
en: "Gnome Shell extensions"
|
||||||
|
"Not a custom brew for macOS":
|
||||||
|
en: "Not a custom brew for macOS"
|
||||||
|
"Guix Pull Failed, Skipping":
|
||||||
|
en: "Guix Pull Failed, Skipping"
|
||||||
|
"Nix-darwin on macOS must be upgraded via darwin-rebuild switch":
|
||||||
|
en: "Nix-darwin on macOS must be upgraded via darwin-rebuild switch"
|
||||||
|
"`nix upgrade-nix` can only be used on macOS or non-NixOS Linux":
|
||||||
|
en: "`nix upgrade-nix` can only be used on macOS or non-NixOS Linux"
|
||||||
|
"`nix upgrade-nix` cannot be run when Nix is installed in a profile":
|
||||||
|
en: "`nix upgrade-nix` cannot be run when Nix is installed in a profile"
|
||||||
|
"Nix (self-upgrade)":
|
||||||
|
en: "Nix (self-upgrade)"
|
||||||
|
"Pyenv is installed, but $PYENV_ROOT is not set correctly":
|
||||||
|
en: "Pyenv is installed, but $PYENV_ROOT is not set correctly"
|
||||||
|
"pyenv is not a git repository":
|
||||||
|
en: "pyenv is not a git repository"
|
||||||
|
"Bun Packages":
|
||||||
|
en: "Bun Packages"
|
||||||
|
"WSL not installed":
|
||||||
|
en: "WSL not installed"
|
||||||
|
"Update WSL":
|
||||||
|
en: "Update WSL"
|
||||||
|
"Could not find Topgrade installed in WSL":
|
||||||
|
en: "Could not find Topgrade installed in WSL"
|
||||||
|
"Consider installing PSWindowsUpdate as the use of Windows Update via USOClient is not supported.":
|
||||||
|
en: "Consider installing PSWindowsUpdate as the use of Windows Update via USOClient is not supported."
|
||||||
|
"USOClient not supported.":
|
||||||
|
en: "USOClient not supported."
|
||||||
|
"Connecting to {hostname}...":
|
||||||
|
en: "Connecting to %{hostname}..."
|
||||||
|
"Skipping powered off box {vagrant_box}":
|
||||||
|
en: "Skipping powered off box %{vagrant_box}"
|
||||||
|
"`{repo_tag}` for `{platform}`":
|
||||||
|
en: "`%{repo_tag}` for `%{platform}`"
|
||||||
|
"Containers":
|
||||||
|
en: "Containers"
|
||||||
|
"Emacs directory does not exist":
|
||||||
|
en: "Emacs directory does not exist"
|
||||||
|
"Error getting the composer directory: {error}":
|
||||||
|
en: "Error getting the composer directory: %{error}"
|
||||||
|
"Composer directory {composer_home} isn't a descendant of the user's home directory":
|
||||||
|
en: "Composer directory %{composer_home} isn't a descendant of the user's home directory"
|
||||||
|
"Composer":
|
||||||
|
en: "Composer"
|
||||||
|
"Error running `dotnet tool list`. This is expected when a dotnet runtime is installed but no SDK.":
|
||||||
|
en: "Error running `dotnet tool list`. This is expected when a dotnet runtime is installed but no SDK."
|
||||||
|
"No dotnet global tools installed":
|
||||||
|
en: "No dotnet global tools installed"
|
||||||
|
"Racket Package Manager":
|
||||||
|
en: "Racket Package Manager"
|
||||||
|
"GH failed":
|
||||||
|
en: "GH failed"
|
||||||
|
"GitHub CLI Extensions":
|
||||||
|
en: "GitHub CLI Extensions"
|
||||||
|
"Julia Packages":
|
||||||
|
en: "Julia Packages"
|
||||||
|
"Update ClamAV Database(FreshClam)":
|
||||||
|
en: "Update ClamAV Database(FreshClam)"
|
||||||
|
"Path {pattern} did not contain any git repositories":
|
||||||
|
en: "Path %{pattern} did not contain any git repositories"
|
||||||
|
"No repositories to pull":
|
||||||
|
en: "No repositories to pull"
|
||||||
|
"Git repositories":
|
||||||
|
en: "Git repositories"
|
||||||
|
"Would pull {repo}":
|
||||||
|
en: "Would pull %{repo}"
|
||||||
|
"Node Package Manager":
|
||||||
|
en: "Node Package Manager"
|
||||||
|
"Performant Node Package Manager":
|
||||||
|
en: "Performant Node Package Manager"
|
||||||
|
"Yarn Package Manager":
|
||||||
|
en: "Yarn Package Manager"
|
||||||
|
"Deno installed outside of .deno directory":
|
||||||
|
en: "Deno installed outside of .deno directory"
|
||||||
|
"The Ultimate vimrc":
|
||||||
|
en: "The Ultimate vimrc"
|
||||||
|
"vim binary might be actually nvim":
|
||||||
|
en: "vim binary might be actually nvim"
|
||||||
|
"`{process}` failed: {exit_satus}":
|
||||||
|
en: "`%{process}` failed: %{exit_satus}"
|
||||||
|
"`{process}` failed: {exit_satus} with {output}":
|
||||||
|
en: "`%{process}` failed: %{exit_satus} with %{output}"
|
||||||
|
"Unknown Linux Distribution":
|
||||||
|
en: "Unknown Linux Distribution"
|
||||||
|
'File "/etc/os-release" does not exist or is empty':
|
||||||
|
en: 'File "/etc/os-release" does not exist or is empty'
|
||||||
|
"Failed getting the system package manager":
|
||||||
|
en: "Failed getting the system package manager"
|
||||||
|
"A step failed":
|
||||||
|
en: "A step failed"
|
||||||
|
"Dry running":
|
||||||
|
en: "Dry running"
|
||||||
|
"Topgrade Upgraded":
|
||||||
|
en: "Topgrade Upgraded"
|
||||||
|
"OK":
|
||||||
|
en: "OK"
|
||||||
|
"FAILED":
|
||||||
|
en: "FAILED"
|
||||||
|
"IGNORED":
|
||||||
|
en: "IGNORED"
|
||||||
|
"SKIPPED":
|
||||||
|
en: "SKIPPED"
|
||||||
|
|
||||||
|
# 'Y' and 'N' have to stay the same characters. Eg for German the translation
|
||||||
|
# would look sth like "(Y) Ja / (N) Nein"
|
||||||
|
"(Y)es/(N)o":
|
||||||
|
en: "(Y)es/(N)o"
|
||||||
|
# 'y', 'N', 's', 'q' have to stay the same throughout all translations.
|
||||||
|
# Eg German would look like "(y) Wiederholen / (N) Nein / (s) Konsole / (q) Beenden"
|
||||||
|
"Retry? (y)es/(N)o/(s)hell/(q)uit":
|
||||||
|
en: "Retry? (y)es/(N)o/(s)hell/(q)uit"
|
||||||
|
# 'R', 'S', 'Q' have to stay the same throughout all translations. Eg German would look like "\n(R) Neustarten\n(S) Konsole\n(Q) Beenden"
|
||||||
|
'\n(R)eboot\n(S)hell\n(Q)uit':
|
||||||
|
en: '\n(R)eboot\n(S)hell\n(Q)uit'
|
||||||
|
"Require sudo or counterpart but not found, skip":
|
||||||
|
en: "Require sudo or counterpart but not found, skip"
|
||||||
|
"sudo as user '{user}'":
|
||||||
|
en: "sudo as user '%{user}'"
|
||||||
|
"Updating aqua ...":
|
||||||
|
en: "Updating aqua ..."
|
||||||
|
"Updating aqua installed cli tools ...":
|
||||||
|
en: "Updating aqua installed cli tools ..."
|
||||||
|
"Updating Volta packages...":
|
||||||
|
en: "Updating Volta packages..."
|
||||||
|
"No packages installed with Volta":
|
||||||
|
en: "No packages installed with Volta"
|
||||||
|
"pyenv-update plugin is not installed":
|
||||||
|
en: "pyenv-update plugin is not installed"
|
||||||
|
"Respawning...":
|
||||||
|
en: "Respawning..."
|
||||||
|
"Could not find Topgrade in any WSL disribution":
|
||||||
|
en: "Could not find Topgrade in any WSL disribution"
|
||||||
|
"Windows Update":
|
||||||
|
en: "Windows Update"
|
||||||
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "1.76.0"
|
||||||
@@ -11,6 +11,7 @@ use crate::WINDOWS_DIRS;
|
|||||||
use crate::XDG_DIRS;
|
use crate::XDG_DIRS;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use etcetera::base_strategy::BaseStrategy;
|
use etcetera::base_strategy::BaseStrategy;
|
||||||
|
use rust_i18n::t;
|
||||||
use std::{
|
use std::{
|
||||||
env::var,
|
env::var,
|
||||||
fs::{read_to_string, OpenOptions},
|
fs::{read_to_string, OpenOptions},
|
||||||
@@ -45,7 +46,7 @@ impl FromStr for Version {
|
|||||||
// They cannot be all 0s
|
// They cannot be all 0s
|
||||||
assert!(
|
assert!(
|
||||||
!(major == 0 && minor == 0 && patch == 0),
|
!(major == 0 && minor == 0 && patch == 0),
|
||||||
"Version numbers can not be all 0s"
|
"Version numbers cannot be all 0s"
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -118,12 +119,15 @@ pub(crate) fn first_run_of_major_release() -> Result<bool> {
|
|||||||
|
|
||||||
/// Print breaking changes to the user.
|
/// Print breaking changes to the user.
|
||||||
pub(crate) fn print_breaking_changes() {
|
pub(crate) fn print_breaking_changes() {
|
||||||
let header = format!("Topgrade {VERSION_STR} Breaking Changes");
|
let header = format!(
|
||||||
|
"{}",
|
||||||
|
t!("Topgrade {version_str} Breaking Changes", version_str = VERSION_STR)
|
||||||
|
);
|
||||||
print_separator(header);
|
print_separator(header);
|
||||||
let contents = if BREAKINGCHANGES.is_empty() {
|
let contents = if BREAKINGCHANGES.is_empty() {
|
||||||
"No Breaking changes"
|
t!("No Breaking changes").to_string()
|
||||||
} else {
|
} else {
|
||||||
BREAKINGCHANGES
|
BREAKINGCHANGES.to_string()
|
||||||
};
|
};
|
||||||
println!("{contents}\n");
|
println!("{contents}\n");
|
||||||
}
|
}
|
||||||
@@ -159,7 +163,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Version numbers can not be all 0s")]
|
#[should_panic(expected = "Version numbers cannot be all 0s")]
|
||||||
fn invalid_version() {
|
fn invalid_version() {
|
||||||
let all_0 = "0.0.0";
|
let all_0 = "0.0.0";
|
||||||
all_0.parse::<Version>().unwrap();
|
all_0.parse::<Version>().unwrap();
|
||||||
|
|||||||
159
src/config.rs
159
src/config.rs
@@ -5,7 +5,7 @@ use std::fs::{write, File};
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::{env, fs};
|
use std::{env, fmt, fs};
|
||||||
|
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use clap_complete::Shell;
|
use clap_complete::Shell;
|
||||||
@@ -15,6 +15,7 @@ use etcetera::base_strategy::BaseStrategy;
|
|||||||
use merge::Merge;
|
use merge::Merge;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use regex_split::RegexSplit;
|
use regex_split::RegexSplit;
|
||||||
|
use rust_i18n::t;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use strum::{EnumIter, EnumString, IntoEnumIterator, VariantNames};
|
use strum::{EnumIter, EnumString, IntoEnumIterator, VariantNames};
|
||||||
use which_crate::which;
|
use which_crate::which;
|
||||||
@@ -25,6 +26,7 @@ use crate::sudo::SudoKind;
|
|||||||
use crate::utils::string_prepend_str;
|
use crate::utils::string_prepend_str;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
|
// TODO: Add i18n to this. Tracking issue: https://github.com/topgrade-rs/topgrade/issues/859
|
||||||
pub static EXAMPLE_CONFIG: &str = include_str!("../config.example.toml");
|
pub static EXAMPLE_CONFIG: &str = include_str!("../config.example.toml");
|
||||||
|
|
||||||
/// Topgrade's default log level.
|
/// Topgrade's default log level.
|
||||||
@@ -53,6 +55,7 @@ pub enum Step {
|
|||||||
AppMan,
|
AppMan,
|
||||||
Asdf,
|
Asdf,
|
||||||
Atom,
|
Atom,
|
||||||
|
Aqua,
|
||||||
Audit,
|
Audit,
|
||||||
AutoCpufreq,
|
AutoCpufreq,
|
||||||
Bin,
|
Bin,
|
||||||
@@ -121,10 +124,12 @@ pub enum Step {
|
|||||||
PipReviewLocal,
|
PipReviewLocal,
|
||||||
Pipupgrade,
|
Pipupgrade,
|
||||||
Pipx,
|
Pipx,
|
||||||
|
Pixi,
|
||||||
Pkg,
|
Pkg,
|
||||||
Pkgin,
|
Pkgin,
|
||||||
PlatformioCore,
|
PlatformioCore,
|
||||||
Pnpm,
|
Pnpm,
|
||||||
|
Poetry,
|
||||||
Powershell,
|
Powershell,
|
||||||
Protonup,
|
Protonup,
|
||||||
Pyenv,
|
Pyenv,
|
||||||
@@ -151,9 +156,11 @@ pub enum Step {
|
|||||||
Tlmgr,
|
Tlmgr,
|
||||||
Tmux,
|
Tmux,
|
||||||
Toolbx,
|
Toolbx,
|
||||||
|
Uv,
|
||||||
Vagrant,
|
Vagrant,
|
||||||
Vcpkg,
|
Vcpkg,
|
||||||
Vim,
|
Vim,
|
||||||
|
VoltaPackages,
|
||||||
Vscode,
|
Vscode,
|
||||||
Waydroid,
|
Waydroid,
|
||||||
Winget,
|
Winget,
|
||||||
@@ -162,6 +169,7 @@ pub enum Step {
|
|||||||
Xcodes,
|
Xcodes,
|
||||||
Yadm,
|
Yadm,
|
||||||
Yarn,
|
Yarn,
|
||||||
|
Zvm,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Merge)]
|
#[derive(Deserialize, Default, Debug, Merge)]
|
||||||
@@ -176,6 +184,7 @@ pub struct Include {
|
|||||||
pub struct Containers {
|
pub struct Containers {
|
||||||
#[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)]
|
#[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)]
|
||||||
ignored_containers: Option<Vec<String>>,
|
ignored_containers: Option<Vec<String>>,
|
||||||
|
runtime: Option<ContainerRuntime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Merge)]
|
#[derive(Deserialize, Default, Debug, Merge)]
|
||||||
@@ -264,6 +273,7 @@ pub struct Flatpak {
|
|||||||
pub struct Brew {
|
pub struct Brew {
|
||||||
greedy_cask: Option<bool>,
|
greedy_cask: Option<bool>,
|
||||||
greedy_latest: Option<bool>,
|
greedy_latest: Option<bool>,
|
||||||
|
greedy_auto_updates: Option<bool>,
|
||||||
autoremove: Option<bool>,
|
autoremove: Option<bool>,
|
||||||
fetch_head: Option<bool>,
|
fetch_head: Option<bool>,
|
||||||
}
|
}
|
||||||
@@ -282,6 +292,22 @@ pub enum ArchPackageManager {
|
|||||||
Yay,
|
Yay,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ContainerRuntime {
|
||||||
|
Docker,
|
||||||
|
Podman,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ContainerRuntime {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ContainerRuntime::Docker => write!(f, "docker"),
|
||||||
|
ContainerRuntime::Podman => write!(f, "podman"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Merge)]
|
#[derive(Deserialize, Default, Debug, Merge)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Linux {
|
pub struct Linux {
|
||||||
@@ -381,6 +407,8 @@ pub struct Misc {
|
|||||||
|
|
||||||
run_in_tmux: Option<bool>,
|
run_in_tmux: Option<bool>,
|
||||||
|
|
||||||
|
tmux_session_mode: Option<TmuxSessionMode>,
|
||||||
|
|
||||||
cleanup: Option<bool>,
|
cleanup: Option<bool>,
|
||||||
|
|
||||||
notify_each_step: Option<bool>,
|
notify_each_step: Option<bool>,
|
||||||
@@ -397,6 +425,19 @@ pub struct Misc {
|
|||||||
log_filters: Option<Vec<String>>,
|
log_filters: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, ValueEnum)]
|
||||||
|
#[clap(rename_all = "snake_case")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum TmuxSessionMode {
|
||||||
|
AttachIfNotInSession,
|
||||||
|
AttachAlways,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TmuxConfig {
|
||||||
|
pub args: Vec<String>,
|
||||||
|
pub session_mode: TmuxSessionMode,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Merge)]
|
#[derive(Deserialize, Default, Debug, Merge)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Lensfun {
|
pub struct Lensfun {
|
||||||
@@ -532,7 +573,9 @@ impl ConfigFile {
|
|||||||
if dir_to_search.exists() {
|
if dir_to_search.exists() {
|
||||||
for entry in fs::read_dir(dir_to_search)? {
|
for entry in fs::read_dir(dir_to_search)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
if entry.file_type()?.is_file() {
|
// Use `Path::is_file()` here to traverse symbolic links.
|
||||||
|
// `DirEntry::file_type()` and `FileType::is_file()` will not traverse symbolic links.
|
||||||
|
if entry.path().is_file() {
|
||||||
debug!(
|
debug!(
|
||||||
"Found additional (directory) configuration file at {}",
|
"Found additional (directory) configuration file at {}",
|
||||||
entry.path().display()
|
entry.path().display()
|
||||||
@@ -565,13 +608,11 @@ impl ConfigFile {
|
|||||||
to read the include directory before returning the main config path
|
to read the include directory before returning the main config path
|
||||||
*/
|
*/
|
||||||
for include in dir_include {
|
for include in dir_include {
|
||||||
let include_contents = fs::read_to_string(&include).map_err(|e| {
|
let include_contents = fs::read_to_string(&include).inspect_err(|_| {
|
||||||
error!("Unable to read {}", include.display());
|
error!("Unable to read {}", include.display());
|
||||||
e
|
|
||||||
})?;
|
})?;
|
||||||
let include_contents_parsed = toml::from_str(include_contents.as_str()).map_err(|e| {
|
let include_contents_parsed = toml::from_str(include_contents.as_str()).inspect_err(|_| {
|
||||||
error!("Failed to deserialize {}", include.display());
|
error!("Failed to deserialize {}", include.display());
|
||||||
e
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
result.merge(include_contents_parsed);
|
result.merge(include_contents_parsed);
|
||||||
@@ -586,9 +627,8 @@ impl ConfigFile {
|
|||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut contents_non_split = fs::read_to_string(&config_path).map_err(|e| {
|
let mut contents_non_split = fs::read_to_string(&config_path).inspect_err(|_| {
|
||||||
error!("Unable to read {}", config_path.display());
|
error!("Unable to read {}", config_path.display());
|
||||||
e
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Self::ensure_misc_is_present(&mut contents_non_split, &config_path);
|
Self::ensure_misc_is_present(&mut contents_non_split, &config_path);
|
||||||
@@ -599,9 +639,8 @@ impl ConfigFile {
|
|||||||
let contents_split = regex_match_include.split_inclusive_left(contents_non_split.as_str());
|
let contents_split = regex_match_include.split_inclusive_left(contents_non_split.as_str());
|
||||||
|
|
||||||
for contents in contents_split {
|
for contents in contents_split {
|
||||||
let config_file_include_only: ConfigFileIncludeOnly = toml::from_str(contents).map_err(|e| {
|
let config_file_include_only: ConfigFileIncludeOnly = toml::from_str(contents).inspect_err(|_| {
|
||||||
error!("Failed to deserialize an include section of {}", config_path.display());
|
error!("Failed to deserialize an include section of {}", config_path.display());
|
||||||
e
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if let Some(includes) = &config_file_include_only.include {
|
if let Some(includes) = &config_file_include_only.include {
|
||||||
@@ -613,14 +652,14 @@ impl ConfigFile {
|
|||||||
let include_contents = match fs::read_to_string(&include_path) {
|
let include_contents = match fs::read_to_string(&include_path) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Unable to read {}: {}", include_path.display(), e);
|
error!("Unable to read {}: {e}", include_path.display(),);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match toml::from_str::<Self>(&include_contents) {
|
match toml::from_str::<Self>(&include_contents) {
|
||||||
Ok(include_parsed) => result.merge(include_parsed),
|
Ok(include_parsed) => result.merge(include_parsed),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to deserialize {}: {}", include_path.display(), e);
|
error!("Failed to deserialize {}: {e}", include_path.display(),);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -630,14 +669,17 @@ impl ConfigFile {
|
|||||||
|
|
||||||
match toml::from_str::<Self>(contents) {
|
match toml::from_str::<Self>(contents) {
|
||||||
Ok(contents) => result.merge(contents),
|
Ok(contents) => result.merge(contents),
|
||||||
Err(e) => error!("Failed to deserialize {}: {}", config_path.display(), e),
|
Err(e) => error!("Failed to deserialize {}: {e}", config_path.display(),),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(paths) = result.git.as_mut().and_then(|git| git.repos.as_mut()) {
|
if let Some(paths) = result.git.as_mut().and_then(|git| git.repos.as_mut()) {
|
||||||
for path in paths.iter_mut() {
|
for path in paths.iter_mut() {
|
||||||
let expanded = shellexpand::tilde::<&str>(&path.as_ref()).into_owned();
|
let expanded = shellexpand::tilde::<&str>(&path.as_ref()).into_owned();
|
||||||
debug!("Path {} expanded to {}", path, expanded);
|
debug!(
|
||||||
|
"{}",
|
||||||
|
t!("Path {path} expanded to {expanded}", path = path, expanded = expanded)
|
||||||
|
);
|
||||||
*path = expanded;
|
*path = expanded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -675,63 +717,65 @@ impl ConfigFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Command line arguments
|
// Command line arguments
|
||||||
|
// TODO: i18n of clap currently not easily possible. Waiting for https://github.com/clap-rs/clap/issues/380
|
||||||
|
// Tracking issue for i18n: https://github.com/topgrade-rs/topgrade/issues/859
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(name = "Topgrade", version)]
|
#[command(name = "topgrade", version)]
|
||||||
pub struct CommandLineArgs {
|
pub struct CommandLineArgs {
|
||||||
/// Edit the configuration file
|
/// Edit the configuration file
|
||||||
#[clap(long = "edit-config")]
|
#[arg(long = "edit-config")]
|
||||||
edit_config: bool,
|
edit_config: bool,
|
||||||
|
|
||||||
/// Show config reference
|
/// Show config reference
|
||||||
#[clap(long = "config-reference")]
|
#[arg(long = "config-reference")]
|
||||||
show_config_reference: bool,
|
show_config_reference: bool,
|
||||||
|
|
||||||
/// Run inside tmux
|
/// Run inside tmux
|
||||||
#[clap(short = 't', long = "tmux")]
|
#[arg(short = 't', long = "tmux")]
|
||||||
run_in_tmux: bool,
|
run_in_tmux: bool,
|
||||||
|
|
||||||
/// Cleanup temporary or old files
|
/// Cleanup temporary or old files
|
||||||
#[clap(short = 'c', long = "cleanup")]
|
#[arg(short = 'c', long = "cleanup")]
|
||||||
cleanup: bool,
|
cleanup: bool,
|
||||||
|
|
||||||
/// Print what would be done
|
/// Print what would be done
|
||||||
#[clap(short = 'n', long = "dry-run")]
|
#[arg(short = 'n', long = "dry-run")]
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
|
|
||||||
/// Do not ask to retry failed steps
|
/// Do not ask to retry failed steps
|
||||||
#[clap(long = "no-retry")]
|
#[arg(long = "no-retry")]
|
||||||
no_retry: bool,
|
no_retry: bool,
|
||||||
|
|
||||||
/// Do not perform upgrades for the given steps
|
/// Do not perform upgrades for the given steps
|
||||||
#[clap(long = "disable", value_name = "STEP", value_enum, num_args = 1..)]
|
#[arg(long = "disable", value_name = "STEP", value_enum, num_args = 1..)]
|
||||||
disable: Vec<Step>,
|
disable: Vec<Step>,
|
||||||
|
|
||||||
/// Perform only the specified steps (experimental)
|
/// Perform only the specified steps
|
||||||
#[clap(long = "only", value_name = "STEP", value_enum, num_args = 1..)]
|
#[arg(long = "only", value_name = "STEP", value_enum, num_args = 1..)]
|
||||||
only: Vec<Step>,
|
only: Vec<Step>,
|
||||||
|
|
||||||
/// Run only specific custom commands
|
/// Run only specific custom commands
|
||||||
#[clap(long = "custom-commands", value_name = "NAME", num_args = 1..)]
|
#[arg(long = "custom-commands", value_name = "NAME", num_args = 1..)]
|
||||||
custom_commands: Vec<String>,
|
custom_commands: Vec<String>,
|
||||||
|
|
||||||
/// Set environment variables
|
/// Set environment variables
|
||||||
#[clap(long = "env", value_name = "NAME=VALUE", num_args = 1..)]
|
#[arg(long = "env", value_name = "NAME=VALUE", num_args = 1..)]
|
||||||
env: Vec<String>,
|
env: Vec<String>,
|
||||||
|
|
||||||
/// Output debug logs. Alias for `--log-filter debug`.
|
/// Output debug logs. Alias for `--log-filter debug`.
|
||||||
#[clap(short = 'v', long = "verbose")]
|
#[arg(short = 'v', long = "verbose")]
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
|
|
||||||
/// Prompt for a key before exiting
|
/// Prompt for a key before exiting
|
||||||
#[clap(short = 'k', long = "keep")]
|
#[arg(short = 'k', long = "keep")]
|
||||||
keep_at_end: bool,
|
keep_at_end: bool,
|
||||||
|
|
||||||
/// Skip sending a notification at the end of a run
|
/// Skip sending a notification at the end of a run
|
||||||
#[clap(long = "skip-notify")]
|
#[arg(long = "skip-notify")]
|
||||||
skip_notify: bool,
|
skip_notify: bool,
|
||||||
|
|
||||||
/// Say yes to package manager's prompt
|
/// Say yes to package manager's prompt
|
||||||
#[clap(
|
#[arg(
|
||||||
short = 'y',
|
short = 'y',
|
||||||
long = "yes",
|
long = "yes",
|
||||||
value_name = "STEP",
|
value_name = "STEP",
|
||||||
@@ -741,37 +785,37 @@ pub struct CommandLineArgs {
|
|||||||
yes: Option<Vec<Step>>,
|
yes: Option<Vec<Step>>,
|
||||||
|
|
||||||
/// Don't pull the predefined git repos
|
/// Don't pull the predefined git repos
|
||||||
#[clap(long = "disable-predefined-git-repos")]
|
#[arg(long = "disable-predefined-git-repos")]
|
||||||
disable_predefined_git_repos: bool,
|
disable_predefined_git_repos: bool,
|
||||||
|
|
||||||
/// Alternative configuration file
|
/// Alternative configuration file
|
||||||
#[clap(long = "config", value_name = "PATH")]
|
#[arg(long = "config", value_name = "PATH")]
|
||||||
config: Option<PathBuf>,
|
config: Option<PathBuf>,
|
||||||
|
|
||||||
/// A regular expression for restricting remote host execution
|
/// A regular expression for restricting remote host execution
|
||||||
#[clap(long = "remote-host-limit", value_name = "REGEX")]
|
#[arg(long = "remote-host-limit", value_name = "REGEX")]
|
||||||
remote_host_limit: Option<Regex>,
|
remote_host_limit: Option<Regex>,
|
||||||
|
|
||||||
/// Show the reason for skipped steps
|
/// Show the reason for skipped steps
|
||||||
#[clap(long = "show-skipped")]
|
#[arg(long = "show-skipped")]
|
||||||
show_skipped: bool,
|
show_skipped: bool,
|
||||||
|
|
||||||
/// Tracing filter directives.
|
/// Tracing filter directives.
|
||||||
///
|
///
|
||||||
/// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html
|
/// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html
|
||||||
#[clap(long, default_value = DEFAULT_LOG_LEVEL)]
|
#[arg(long, default_value = DEFAULT_LOG_LEVEL)]
|
||||||
pub log_filter: String,
|
pub log_filter: String,
|
||||||
|
|
||||||
/// Print completion script for the given shell and exit
|
/// Print completion script for the given shell and exit
|
||||||
#[clap(long, value_enum, hide = true)]
|
#[arg(long, value_enum, hide = true)]
|
||||||
pub gen_completion: Option<Shell>,
|
pub gen_completion: Option<Shell>,
|
||||||
|
|
||||||
/// Print roff manpage and exit
|
/// Print roff manpage and exit
|
||||||
#[clap(long, hide = true)]
|
#[arg(long, hide = true)]
|
||||||
pub gen_manpage: bool,
|
pub gen_manpage: bool,
|
||||||
|
|
||||||
/// Don't update Topgrade
|
/// Don't update Topgrade
|
||||||
#[clap(long = "no-self-update")]
|
#[arg(long = "no-self-update")]
|
||||||
pub no_self_update: bool,
|
pub no_self_update: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,7 +876,7 @@ impl Config {
|
|||||||
ConfigFile::read(opt.config.clone()).unwrap_or_else(|e| {
|
ConfigFile::read(opt.config.clone()).unwrap_or_else(|e| {
|
||||||
// Inform the user about errors when loading the configuration,
|
// Inform the user about errors when loading the configuration,
|
||||||
// but fallback to the default config to at least attempt to do something
|
// but fallback to the default config to at least attempt to do something
|
||||||
error!("failed to load configuration: {}", e);
|
error!("failed to load configuration: {e}");
|
||||||
ConfigFile::default()
|
ConfigFile::default()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -882,6 +926,15 @@ impl Config {
|
|||||||
.and_then(|containers| containers.ignored_containers.as_ref())
|
.and_then(|containers| containers.ignored_containers.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The preferred runtime for container updates (podman / docker).
|
||||||
|
pub fn containers_runtime(&self) -> ContainerRuntime {
|
||||||
|
self.config_file
|
||||||
|
.containers
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|containers| containers.runtime)
|
||||||
|
.unwrap_or(ContainerRuntime::Docker) // defaults to a popular choice
|
||||||
|
}
|
||||||
|
|
||||||
/// Tell whether the specified step should run.
|
/// Tell whether the specified step should run.
|
||||||
///
|
///
|
||||||
/// If the step appears either in the `--disable` command line argument
|
/// If the step appears either in the `--disable` command line argument
|
||||||
@@ -938,6 +991,15 @@ impl Config {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The preferred way to run the new tmux session.
|
||||||
|
fn tmux_session_mode(&self) -> TmuxSessionMode {
|
||||||
|
self.config_file
|
||||||
|
.misc
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|misc| misc.tmux_session_mode)
|
||||||
|
.unwrap_or(TmuxSessionMode::AttachIfNotInSession)
|
||||||
|
}
|
||||||
|
|
||||||
/// Tell whether we should perform cleanup steps.
|
/// Tell whether we should perform cleanup steps.
|
||||||
pub fn cleanup(&self) -> bool {
|
pub fn cleanup(&self) -> bool {
|
||||||
self.opt.cleanup
|
self.opt.cleanup
|
||||||
@@ -995,8 +1057,16 @@ impl Config {
|
|||||||
self.config_file.git.as_ref().and_then(|git| git.arguments.as_ref())
|
self.config_file.git.as_ref().and_then(|git| git.arguments.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tmux_config(&self) -> Result<TmuxConfig> {
|
||||||
|
let args = self.tmux_arguments()?;
|
||||||
|
Ok(TmuxConfig {
|
||||||
|
args,
|
||||||
|
session_mode: self.tmux_session_mode(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Extra Tmux arguments
|
/// Extra Tmux arguments
|
||||||
pub fn tmux_arguments(&self) -> Result<Vec<String>> {
|
fn tmux_arguments(&self) -> Result<Vec<String>> {
|
||||||
let args = &self
|
let args = &self
|
||||||
.config_file
|
.config_file
|
||||||
.misc
|
.misc
|
||||||
@@ -1117,6 +1187,15 @@ impl Config {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether Brew cask should be auto_updates
|
||||||
|
pub fn brew_greedy_auto_updates(&self) -> bool {
|
||||||
|
self.config_file
|
||||||
|
.brew
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| c.greedy_auto_updates)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether Brew should autoremove
|
/// Whether Brew should autoremove
|
||||||
pub fn brew_autoremove(&self) -> bool {
|
pub fn brew_autoremove(&self) -> bool {
|
||||||
self.config_file
|
self.config_file
|
||||||
|
|||||||
81
src/error.rs
81
src/error.rs
@@ -1,41 +1,98 @@
|
|||||||
use std::process::ExitStatus;
|
use std::{fmt::Display, process::ExitStatus};
|
||||||
|
|
||||||
|
use rust_i18n::t;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[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("Unknown Linux Distribution")]
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
UnknownLinuxDistribution,
|
UnknownLinuxDistribution,
|
||||||
|
|
||||||
#[error("File \"/etc/os-release\" does not exist or is empty")]
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
EmptyOSReleaseFile,
|
EmptyOSReleaseFile,
|
||||||
|
|
||||||
#[error("Failed getting the system package manager")]
|
|
||||||
#[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_satus}",
|
||||||
|
process = process,
|
||||||
|
exit_status = exit_status
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TopgradeError::ProcessFailedWithOutput(process, exit_status, output) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
t!(
|
||||||
|
"`{process}` failed: {exit_satus} 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;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
impl Display for StepFailed {
|
||||||
#[error("Dry running")]
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
pub struct DryRun();
|
write!(f, "{}", t!("A step failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
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);
|
||||||
|
|
||||||
|
impl Display for SkipStep {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(windows, feature = "self-update"))]
|
#[cfg(all(windows, feature = "self-update"))]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("Topgrade Upgraded")]
|
|
||||||
pub struct Upgraded(pub ExitStatus);
|
pub struct Upgraded(pub ExitStatus);
|
||||||
|
|
||||||
|
#[cfg(all(windows, feature = "self-update"))]
|
||||||
|
impl Display for Upgraded {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", t!("Topgrade Upgraded"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use crate::executor::RunType;
|
use crate::executor::RunType;
|
||||||
use crate::sudo::Sudo;
|
use crate::sudo::Sudo;
|
||||||
use crate::utils::{require_option, REQUIRE_SUDO};
|
use crate::utils::{get_require_sudo_string, require_option};
|
||||||
use crate::{config::Config, executor::Executor};
|
use crate::{config::Config, executor::Executor};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use std::env::var;
|
use std::env::var;
|
||||||
@@ -33,7 +33,7 @@ impl<'a> ExecutionContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_elevated(&self, command: &Path, interactive: bool) -> Result<Executor> {
|
pub fn execute_elevated(&self, command: &Path, interactive: bool) -> Result<Executor> {
|
||||||
let sudo = require_option(self.sudo.as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(self.sudo.as_ref(), get_require_sudo_string())?;
|
||||||
Ok(sudo.execute_elevated(self, command, interactive))
|
Ok(sudo.execute_elevated(self, command, interactive))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::path::Path;
|
|||||||
use std::process::{Child, Command, ExitStatus, Output};
|
use std::process::{Child, Command, ExitStatus, Output};
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
@@ -209,17 +210,20 @@ pub struct DryCommand {
|
|||||||
impl DryCommand {
|
impl DryCommand {
|
||||||
fn dry_run(&self) {
|
fn dry_run(&self) {
|
||||||
print!(
|
print!(
|
||||||
"Dry running: {} {}",
|
"{}",
|
||||||
self.program.to_string_lossy(),
|
t!(
|
||||||
shell_words::join(
|
"Dry running: {program_name} {arguments}",
|
||||||
|
program_name = self.program.to_string_lossy(),
|
||||||
|
arguments = shell_words::join(
|
||||||
self.args
|
self.args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| String::from(a.to_string_lossy()))
|
.map(|a| String::from(a.to_string_lossy()))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
)
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
match &self.directory {
|
match &self.directory {
|
||||||
Some(dir) => println!(" in {}", dir.to_string_lossy()),
|
Some(dir) => println!(" {}", t!("in {directory}", directory = dir.to_string_lossy())),
|
||||||
None => println!(),
|
None => println!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/main.rs
38
src/main.rs
@@ -18,6 +18,7 @@ use etcetera::base_strategy::Windows;
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use etcetera::base_strategy::Xdg;
|
use etcetera::base_strategy::Xdg;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use rust_i18n::{i18n, t};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use self::config::{CommandLineArgs, Config, Step};
|
use self::config::{CommandLineArgs, Config, Step};
|
||||||
@@ -54,6 +55,9 @@ pub(crate) static XDG_DIRS: Lazy<Xdg> = Lazy::new(|| Xdg::new().expect("No home
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub(crate) static WINDOWS_DIRS: Lazy<Windows> = Lazy::new(|| Windows::new().expect("No home directory"));
|
pub(crate) static WINDOWS_DIRS: Lazy<Windows> = Lazy::new(|| Windows::new().expect("No home directory"));
|
||||||
|
|
||||||
|
// Init and load the i18n files
|
||||||
|
i18n!("locales", fallback = "en");
|
||||||
|
|
||||||
fn run() -> Result<()> {
|
fn run() -> Result<()> {
|
||||||
install_color_eyre()?;
|
install_color_eyre()?;
|
||||||
ctrlc::set_handler();
|
ctrlc::set_handler();
|
||||||
@@ -72,6 +76,11 @@ fn run() -> Result<()> {
|
|||||||
// and `Config::tracing_filter_directives()`.
|
// and `Config::tracing_filter_directives()`.
|
||||||
let reload_handle = install_tracing(&opt.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 io::stdout());
|
clap_complete::generate(shell, cmd, clap::crate_name!(), &mut io::stdout());
|
||||||
@@ -118,7 +127,7 @@ fn run() -> Result<()> {
|
|||||||
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(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +219,7 @@ fn run() -> Result<()> {
|
|||||||
runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?;
|
runner.execute(Step::System, "System update", || distribution.upgrade(&ctx))?;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error detecting current distribution: {e}");
|
println!("{}", t!("Error detecting current distribution: {error}", error = e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runner.execute(Step::ConfigUpdate, "config-update", || linux::run_config_update(&ctx))?;
|
runner.execute(Step::ConfigUpdate, "config-update", || linux::run_config_update(&ctx))?;
|
||||||
@@ -301,7 +310,6 @@ fn run() -> Result<()> {
|
|||||||
runner.execute(Step::Asdf, "asdf", || unix::run_asdf(&ctx))?;
|
runner.execute(Step::Asdf, "asdf", || unix::run_asdf(&ctx))?;
|
||||||
runner.execute(Step::Mise, "mise", || unix::run_mise(&ctx))?;
|
runner.execute(Step::Mise, "mise", || unix::run_mise(&ctx))?;
|
||||||
runner.execute(Step::Pkgin, "pkgin", || unix::run_pkgin(&ctx))?;
|
runner.execute(Step::Pkgin, "pkgin", || unix::run_pkgin(&ctx))?;
|
||||||
runner.execute(Step::Bun, "bun", || unix::run_bun(&ctx))?;
|
|
||||||
runner.execute(Step::BunPackages, "bun-packages", || unix::run_bun_packages(&ctx))?;
|
runner.execute(Step::BunPackages, "bun-packages", || unix::run_bun_packages(&ctx))?;
|
||||||
runner.execute(Step::Shell, "zr", || zsh::run_zr(&ctx))?;
|
runner.execute(Step::Shell, "zr", || zsh::run_zr(&ctx))?;
|
||||||
runner.execute(Step::Shell, "antibody", || zsh::run_antibody(&ctx))?;
|
runner.execute(Step::Shell, "antibody", || zsh::run_antibody(&ctx))?;
|
||||||
@@ -363,6 +371,7 @@ fn run() -> Result<()> {
|
|||||||
})?;
|
})?;
|
||||||
runner.execute(Step::Conda, "conda", || generic::run_conda_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::Mamba, "mamba", || generic::run_mamba_update(&ctx))?;
|
||||||
|
runner.execute(Step::Pixi, "pixi", || generic::run_pixi_update(&ctx))?;
|
||||||
runner.execute(Step::Miktex, "miktex", || generic::run_miktex_packages_update(&ctx))?;
|
runner.execute(Step::Miktex, "miktex", || generic::run_miktex_packages_update(&ctx))?;
|
||||||
runner.execute(Step::Pip3, "pip3", || generic::run_pip3_update(&ctx))?;
|
runner.execute(Step::Pip3, "pip3", || generic::run_pip3_update(&ctx))?;
|
||||||
runner.execute(Step::PipReview, "pip-review", || generic::run_pip_review_update(&ctx))?;
|
runner.execute(Step::PipReview, "pip-review", || generic::run_pip_review_update(&ctx))?;
|
||||||
@@ -385,6 +394,9 @@ fn run() -> Result<()> {
|
|||||||
runner.execute(Step::Node, "npm", || node::run_npm_upgrade(&ctx))?;
|
runner.execute(Step::Node, "npm", || node::run_npm_upgrade(&ctx))?;
|
||||||
runner.execute(Step::Yarn, "yarn", || node::run_yarn_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::Pnpm, "pnpm", || node::run_pnpm_upgrade(&ctx))?;
|
||||||
|
runner.execute(Step::VoltaPackages, "volta packages", || {
|
||||||
|
node::run_volta_packages_upgrade(&ctx)
|
||||||
|
})?;
|
||||||
runner.execute(Step::Containers, "Containers", || containers::run_containers(&ctx))?;
|
runner.execute(Step::Containers, "Containers", || containers::run_containers(&ctx))?;
|
||||||
runner.execute(Step::Deno, "deno", || node::deno_upgrade(&ctx))?;
|
runner.execute(Step::Deno, "deno", || node::deno_upgrade(&ctx))?;
|
||||||
runner.execute(Step::Composer, "composer", || generic::run_composer_update(&ctx))?;
|
runner.execute(Step::Composer, "composer", || generic::run_composer_update(&ctx))?;
|
||||||
@@ -415,6 +427,11 @@ fn run() -> Result<()> {
|
|||||||
runner.execute(Step::Lensfun, "Lensfun's database update", || {
|
runner.execute(Step::Lensfun, "Lensfun's database update", || {
|
||||||
generic::run_lensfun_update_data(&ctx)
|
generic::run_lensfun_update_data(&ctx)
|
||||||
})?;
|
})?;
|
||||||
|
runner.execute(Step::Poetry, "Poetry", || generic::run_poetry(&ctx))?;
|
||||||
|
runner.execute(Step::Uv, "uv", || generic::run_uv(&ctx))?;
|
||||||
|
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))?;
|
||||||
|
|
||||||
if should_run_powershell {
|
if should_run_powershell {
|
||||||
runner.execute(Step::Powershell, "Powershell Modules Update", || {
|
runner.execute(Step::Powershell, "Powershell Modules Update", || {
|
||||||
@@ -444,7 +461,7 @@ fn run() -> Result<()> {
|
|||||||
runner.execute(Step::Vagrant, "Vagrant boxes", || vagrant::upgrade_vagrant_boxes(&ctx))?;
|
runner.execute(Step::Vagrant, "Vagrant boxes", || vagrant::upgrade_vagrant_boxes(&ctx))?;
|
||||||
|
|
||||||
if !runner.report().data().is_empty() {
|
if !runner.report().data().is_empty() {
|
||||||
print_separator("Summary");
|
print_separator(t!("Summary"));
|
||||||
|
|
||||||
for (key, result) in runner.report().data() {
|
for (key, result) in runner.report().data() {
|
||||||
print_result(key, result);
|
print_result(key, result);
|
||||||
@@ -468,7 +485,7 @@ fn run() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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')) | Ok(Key::Char('S')) => {
|
||||||
@@ -490,10 +507,11 @@ fn run() -> Result<()> {
|
|||||||
|
|
||||||
if !config.skip_notify() {
|
if !config.skip_notify() {
|
||||||
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)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -528,7 +546,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::process::Command;
|
|||||||
|
|
||||||
use crate::config::Step;
|
use crate::config::Step;
|
||||||
use color_eyre::eyre::{bail, Result};
|
use color_eyre::eyre::{bail, 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;
|
||||||
|
|
||||||
@@ -15,10 +16,10 @@ use crate::error::Upgraded;
|
|||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
|
|
||||||
pub fn self_update(ctx: &ExecutionContext) -> Result<()> {
|
pub fn self_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
print_separator("Self update");
|
print_separator(t!("Self update"));
|
||||||
|
|
||||||
if ctx.run_type().dry() {
|
if ctx.run_type().dry() {
|
||||||
println!("Would self-update");
|
println!("{}", t!("Would self-update"));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let assume_yes = ctx.config().yes(Step::SelfUpdate);
|
let assume_yes = ctx.config().yes(Step::SelfUpdate);
|
||||||
@@ -38,17 +39,17 @@ pub fn self_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.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_info("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", "");
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::command::CommandExt;
|
|||||||
use crate::error::{self, TopgradeError};
|
use crate::error::{self, 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
|
||||||
@@ -43,7 +44,15 @@ impl Container {
|
|||||||
impl Display for Container {
|
impl Display for Container {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
// e.g., "`fedora:latest` for `linux/amd64`"
|
// e.g., "`fedora:latest` for `linux/amd64`"
|
||||||
write!(f, "`{}` for `{}`", self.repo_tag, self.platform)
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
t!(
|
||||||
|
"`{repo_tag}` for `{platform}`",
|
||||||
|
repo_tag = self.repo_tag,
|
||||||
|
platform = self.platform
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,11 +129,12 @@ fn list_containers(crt: &Path, ignored_containers: Option<&Vec<String>>) -> Resu
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 mut success = true;
|
||||||
let containers =
|
let containers =
|
||||||
list_containers(&crt, ctx.config().containers_ignored_tags()).context("Failed to list Docker containers")?;
|
list_containers(&crt, ctx.config().containers_ignored_tags()).context("Failed to list Docker containers")?;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use etcetera::base_strategy::BaseStrategy;
|
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;
|
||||||
@@ -74,7 +75,10 @@ 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()?;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::{fs, io::Write};
|
|||||||
use color_eyre::eyre::eyre;
|
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 rust_i18n::t;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use tempfile::tempfile_in;
|
use tempfile::tempfile_in;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
@@ -16,7 +17,7 @@ use crate::command::{CommandExt, Utf8Output};
|
|||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::executor::ExecutorOutput;
|
use crate::executor::ExecutorOutput;
|
||||||
use crate::terminal::{print_separator, shell};
|
use crate::terminal::{print_separator, shell};
|
||||||
use crate::utils::{self, check_is_python_2_or_shim, require, require_option, which, PathExt, REQUIRE_SUDO};
|
use crate::utils::{self, check_is_python_2_or_shim, get_require_sudo_string, require, require_option, which, PathExt};
|
||||||
use crate::Step;
|
use crate::Step;
|
||||||
use crate::HOME_DIR;
|
use crate::HOME_DIR;
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -121,6 +122,7 @@ pub fn run_rubygems(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
print_separator("RubyGems");
|
print_separator("RubyGems");
|
||||||
let gem_path_str = gem.as_os_str();
|
let gem_path_str = gem.as_os_str();
|
||||||
if gem_path_str.to_str().unwrap().contains("asdf")
|
if gem_path_str.to_str().unwrap().contains("asdf")
|
||||||
|
|| gem_path_str.to_str().unwrap().contains("mise")
|
||||||
|| gem_path_str.to_str().unwrap().contains(".rbenv")
|
|| gem_path_str.to_str().unwrap().contains(".rbenv")
|
||||||
|| gem_path_str.to_str().unwrap().contains(".rvm")
|
|| gem_path_str.to_str().unwrap().contains(".rvm")
|
||||||
{
|
{
|
||||||
@@ -129,7 +131,7 @@ pub fn run_rubygems(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.args(["update", "--system"])
|
.args(["update", "--system"])
|
||||||
.status_checked()?;
|
.status_checked()?;
|
||||||
} else {
|
} else {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
if !Path::new("/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb").exists() {
|
if !Path::new("/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb").exists() {
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
@@ -158,7 +160,7 @@ pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let mut command = if directory_writable {
|
let mut command = if directory_writable {
|
||||||
ctx.run_type().execute(&haxelib)
|
ctx.run_type().execute(&haxelib)
|
||||||
} else {
|
} else {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut c = ctx.run_type().execute(sudo);
|
let mut c = ctx.run_type().execute(sudo);
|
||||||
c.arg(&haxelib);
|
c.arg(&haxelib);
|
||||||
c
|
c
|
||||||
@@ -223,6 +225,20 @@ pub fn run_apm(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.status_checked()
|
.status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_aqua(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let aqua = require("aqua")?;
|
||||||
|
|
||||||
|
print_separator("Aqua");
|
||||||
|
if ctx.run_type().dry() {
|
||||||
|
println!("{}", t!("Updating aqua ..."));
|
||||||
|
println!("{}", t!("Updating aqua installed cli tools ..."));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
ctx.run_type().execute(&aqua).arg("update-aqua").status_checked()?;
|
||||||
|
ctx.run_type().execute(&aqua).arg("update").status_checked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run_rustup(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_rustup(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let rustup = require("rustup")?;
|
let rustup = require("rustup")?;
|
||||||
|
|
||||||
@@ -349,7 +365,7 @@ pub fn run_vcpkg_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let mut command = if is_root_install {
|
let mut command = if is_root_install {
|
||||||
ctx.run_type().execute(&vcpkg)
|
ctx.run_type().execute(&vcpkg)
|
||||||
} else {
|
} else {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut c = ctx.run_type().execute(sudo);
|
let mut c = ctx.run_type().execute(sudo);
|
||||||
c.arg(&vcpkg);
|
c.arg(&vcpkg);
|
||||||
c
|
c
|
||||||
@@ -432,17 +448,16 @@ pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
command.status_checked()
|
command.status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let mamba = require("mamba")?;
|
let mamba = require("mamba")?;
|
||||||
|
|
||||||
let output = Command::new(&mamba)
|
|
||||||
.args(["config", "--show", "auto_activate_base"])
|
|
||||||
.output_checked_utf8()?;
|
|
||||||
debug!("Mamba output: {}", output.stdout);
|
|
||||||
if output.stdout.contains("False") {
|
|
||||||
return Err(SkipStep("auto_activate_base is set to False".to_string()).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
print_separator("Mamba");
|
print_separator("Mamba");
|
||||||
|
|
||||||
let mut command = ctx.run_type().execute(mamba);
|
let mut command = ctx.run_type().execute(mamba);
|
||||||
@@ -651,7 +666,7 @@ pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let mut command = if directory_writable {
|
let mut command = if directory_writable {
|
||||||
ctx.run_type().execute(&tlmgr)
|
ctx.run_type().execute(&tlmgr)
|
||||||
} else {
|
} else {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut c = ctx.run_type().execute(sudo);
|
let mut c = ctx.run_type().execute(sudo);
|
||||||
c.arg(&tlmgr);
|
c.arg(&tlmgr);
|
||||||
c
|
c
|
||||||
@@ -708,19 +723,22 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let composer_home = Command::new(&composer)
|
let composer_home = Command::new(&composer)
|
||||||
.args(["global", "config", "--absolute", "--quiet", "home"])
|
.args(["global", "config", "--absolute", "--quiet", "home"])
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.map_err(|e| (SkipStep(format!("Error getting the composer directory: {e}"))))
|
.map_err(|e| (SkipStep(t!("Error getting the composer directory: {error}", error = e).to_string())))
|
||||||
.map(|s| PathBuf::from(s.stdout.trim()))?
|
.map(|s| PathBuf::from(s.stdout.trim()))?
|
||||||
.require()?;
|
.require()?;
|
||||||
|
|
||||||
if !composer_home.is_descendant_of(&HOME_DIR) {
|
if !composer_home.is_descendant_of(&HOME_DIR) {
|
||||||
return Err(SkipStep(format!(
|
return Err(SkipStep(
|
||||||
"Composer directory {} isn't a descendant of the user's home directory",
|
t!(
|
||||||
composer_home.display()
|
"Composer directory {composer_home} isn't a descendant of the user's home directory",
|
||||||
))
|
composer_home = composer_home.display()
|
||||||
|
)
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Composer");
|
print_separator(t!("Composer"));
|
||||||
|
|
||||||
if ctx.config().composer_self_update() {
|
if ctx.config().composer_self_update() {
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
@@ -732,7 +750,7 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if has_update {
|
if has_update {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
.arg(&composer)
|
.arg(&composer)
|
||||||
@@ -776,9 +794,10 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
{
|
{
|
||||||
Ok(output) => output,
|
Ok(output) => output,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(SkipStep(String::from(
|
return Err(SkipStep(
|
||||||
"Error running `dotnet tool list`. This is expected when a dotnet runtime is installed but no SDK.",
|
t!("Error running `dotnet tool list`. This is expected when a dotnet runtime is installed but no SDK.")
|
||||||
))
|
.to_string(),
|
||||||
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -806,7 +825,7 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.peekable();
|
.peekable();
|
||||||
|
|
||||||
if packages.peek().is_none() {
|
if packages.peek().is_none() {
|
||||||
return Err(SkipStep(String::from("No dotnet global tools installed")).into());
|
return Err(SkipStep(t!("No dotnet global tools installed").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator(".NET");
|
print_separator(".NET");
|
||||||
@@ -817,7 +836,7 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.execute(&dotnet)
|
.execute(&dotnet)
|
||||||
.args(["tool", "update", package_name, "--global"])
|
.args(["tool", "update", package_name, "--global"])
|
||||||
.status_checked()
|
.status_checked()
|
||||||
.with_context(|| format!("Failed to update .NET package {package_name}"))?;
|
.with_context(|| format!("Failed to update .NET package {:?}", package_name))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -846,7 +865,7 @@ pub fn run_helix_grammars(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
pub fn run_raco_update(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_raco_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let raco = require("raco")?;
|
let raco = require("raco")?;
|
||||||
|
|
||||||
print_separator("Racket Package Manager");
|
print_separator(t!("Racket Package Manager"));
|
||||||
|
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(raco)
|
.execute(raco)
|
||||||
@@ -874,10 +893,10 @@ pub fn run_ghcli_extensions_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let result = Command::new(&gh).args(["extensions", "list"]).output_checked_utf8();
|
let result = Command::new(&gh).args(["extensions", "list"]).output_checked_utf8();
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
debug!("GH result {:?}", result);
|
debug!("GH result {:?}", result);
|
||||||
return Err(SkipStep(String::from("GH failed")).into());
|
return Err(SkipStep(t!("GH failed").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("GitHub CLI Extensions");
|
print_separator(t!("GitHub CLI Extensions"));
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(&gh)
|
.execute(&gh)
|
||||||
.args(["extension", "upgrade", "--all"])
|
.args(["extension", "upgrade", "--all"])
|
||||||
@@ -887,7 +906,7 @@ pub fn run_ghcli_extensions_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> {
|
pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let julia = require("julia")?;
|
let julia = require("julia")?;
|
||||||
|
|
||||||
print_separator("Julia Packages");
|
print_separator(t!("Julia Packages"));
|
||||||
|
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(julia)
|
.execute(julia)
|
||||||
@@ -904,7 +923,7 @@ pub fn run_helm_repo_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let mut success = true;
|
let mut success = true;
|
||||||
let mut exec = ctx.run_type().execute(helm);
|
let mut exec = ctx.run_type().execute(helm);
|
||||||
if let Err(e) = exec.arg("repo").arg("update").status_checked() {
|
if let Err(e) = exec.arg("repo").arg("update").status_checked() {
|
||||||
error!("Updating repositories failed: {}", e);
|
error!("Updating repositories failed: {e}");
|
||||||
success = match exec.output_checked_utf8() {
|
success = match exec.output_checked_utf8() {
|
||||||
Ok(s) => s.stdout.contains(no_repo) || s.stderr.contains(no_repo),
|
Ok(s) => s.stdout.contains(no_repo) || s.stderr.contains(no_repo),
|
||||||
Err(e) => match e.downcast_ref::<TopgradeError>() {
|
Err(e) => match e.downcast_ref::<TopgradeError>() {
|
||||||
@@ -937,7 +956,7 @@ pub fn run_bob(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_certbot(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_certbot(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let certbot = require("certbot")?;
|
let certbot = require("certbot")?;
|
||||||
|
|
||||||
print_separator("Certbot");
|
print_separator("Certbot");
|
||||||
@@ -954,7 +973,7 @@ pub fn run_certbot(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
/// doc: https://docs.clamav.net/manual/Usage/SignatureManagement.html#freshclam
|
/// doc: https://docs.clamav.net/manual/Usage/SignatureManagement.html#freshclam
|
||||||
pub fn run_freshclam(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_freshclam(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let freshclam = require("freshclam")?;
|
let freshclam = require("freshclam")?;
|
||||||
print_separator("Update ClamAV Database(FreshClam)");
|
print_separator(t!("Update ClamAV Database(FreshClam)"));
|
||||||
ctx.run_type().execute(freshclam).status_checked()
|
ctx.run_type().execute(freshclam).status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,7 +1006,7 @@ pub fn run_lensfun_update_data(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
const EXIT_CODE_WHEN_NO_UPDATE: i32 = 1;
|
const EXIT_CODE_WHEN_NO_UPDATE: i32 = 1;
|
||||||
|
|
||||||
if ctx.config().lensfun_use_sudo() {
|
if ctx.config().lensfun_use_sudo() {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
print_separator(SEPARATOR);
|
print_separator(SEPARATOR);
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
@@ -1002,3 +1021,46 @@ pub fn run_lensfun_update_data(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.status_checked_with_codes(&[EXIT_CODE_WHEN_NO_UPDATE])
|
.status_checked_with_codes(&[EXIT_CODE_WHEN_NO_UPDATE])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_poetry(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let poetry = require("poetry")?;
|
||||||
|
print_separator("Poetry");
|
||||||
|
ctx.run_type().execute(poetry).args(["self", "update"]).status_checked()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_uv(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let uv_exec = require("uv")?;
|
||||||
|
print_separator("uv");
|
||||||
|
|
||||||
|
ctx.run_type()
|
||||||
|
.execute(&uv_exec)
|
||||||
|
.args(["self", "update"])
|
||||||
|
.status_checked()
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// ignoring self-update errors, because they are likely due to uv's
|
||||||
|
// installation being managed by another package manager, in which
|
||||||
|
// case another step will handle the update.
|
||||||
|
|
||||||
|
ctx.run_type()
|
||||||
|
.execute(&uv_exec)
|
||||||
|
.args(["tool", "upgrade", "--all"])
|
||||||
|
.status_checked()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Involve `zvm upgrade` to update ZVM
|
||||||
|
pub fn run_zvm(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let zvm = require("zvm")?;
|
||||||
|
|
||||||
|
print_separator("ZVM");
|
||||||
|
|
||||||
|
ctx.run_type().execute(zvm).arg("upgrade").status_checked()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_bun(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let bun = require("bun")?;
|
||||||
|
|
||||||
|
print_separator("Bun");
|
||||||
|
|
||||||
|
ctx.run_type().execute(bun).arg("upgrade").status_checked()
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use crate::terminal::print_separator;
|
|||||||
use crate::utils::{require, PathExt};
|
use crate::utils::{require, PathExt};
|
||||||
use crate::{error::SkipStep, terminal::print_warning, HOME_DIR};
|
use crate::{error::SkipStep, terminal::print_warning, HOME_DIR};
|
||||||
use etcetera::base_strategy::BaseStrategy;
|
use etcetera::base_strategy::BaseStrategy;
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use crate::XDG_DIRS;
|
use crate::XDG_DIRS;
|
||||||
@@ -100,16 +101,18 @@ pub fn run_git_pull(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
// NOTE: this should be executed **before** skipping the Git step or the
|
// 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
|
// user won't receive this warning in the cases where all the paths configured
|
||||||
// are bad patterns.
|
// are bad patterns.
|
||||||
repos
|
repos.bad_patterns.iter().for_each(|pattern| {
|
||||||
.bad_patterns
|
print_warning(t!(
|
||||||
.iter()
|
"Path {pattern} did not contain any git repositories",
|
||||||
.for_each(|pattern| print_warning(format!("Path {pattern} did not contain any git repositories")));
|
pattern = pattern
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
if repos.is_repos_empty() {
|
if repos.is_repos_empty() {
|
||||||
return Err(SkipStep(String::from("No repositories to pull")).into());
|
return Err(SkipStep(t!("No repositories to pull").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Git repositories");
|
print_separator(t!("Git repositories"));
|
||||||
|
|
||||||
repos.pull_repos(ctx)
|
repos.pull_repos(ctx)
|
||||||
}
|
}
|
||||||
@@ -143,7 +146,7 @@ fn get_head_revision<P: AsRef<Path>>(git: &Path, repo: P) -> Option<String> {
|
|||||||
.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.as_ref().display(), e);
|
error!("Error getting revision for {}: {e}", repo.as_ref().display(),);
|
||||||
|
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
@@ -206,7 +209,7 @@ impl RepoStep {
|
|||||||
}
|
}
|
||||||
Err(e) => match e.kind() {
|
Err(e) => match e.kind() {
|
||||||
io::ErrorKind::NotFound => debug!("{} does not exist", path.as_ref().display()),
|
io::ErrorKind::NotFound => debug!("{} does not exist", path.as_ref().display()),
|
||||||
_ => error!("Error looking for {}: {}", path.as_ref().display(), e),
|
_ => error!("Error looking for {}: {e}", path.as_ref().display(),),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +239,7 @@ impl RepoStep {
|
|||||||
|
|
||||||
res.map(|output| output.stdout.lines().count() > 0)
|
res.map(|output| output.stdout.lines().count() > 0)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!("Error getting remotes for {}: {}", repo.as_ref().display(), e);
|
error!("Error getting remotes for {}: {e}", repo.as_ref().display());
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
@@ -264,7 +267,7 @@ impl RepoStep {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error in path {}", e);
|
error!("Error in path {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,7 +276,7 @@ impl RepoStep {
|
|||||||
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}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +299,7 @@ impl RepoStep {
|
|||||||
let before_revision = get_head_revision(&self.git, &repo);
|
let before_revision = get_head_revision(&self.git, &repo);
|
||||||
|
|
||||||
if ctx.config().verbose() {
|
if ctx.config().verbose() {
|
||||||
println!("{} {}", style("Pulling").cyan().bold(), repo.as_ref().display());
|
println!("{} {}", style(t!("Pulling")).cyan().bold(), repo.as_ref().display());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut command = AsyncCommand::new(&self.git);
|
let mut command = AsyncCommand::new(&self.git);
|
||||||
@@ -322,13 +325,18 @@ impl RepoStep {
|
|||||||
.wrap_err_with(|| format!("Failed to pull {}", repo.as_ref().display()));
|
.wrap_err_with(|| format!("Failed to pull {}", repo.as_ref().display()));
|
||||||
|
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
println!("{} pulling {}", style("Failed").red().bold(), repo.as_ref().display());
|
println!(
|
||||||
|
"{} {} {}",
|
||||||
|
style(t!("Failed")).red().bold(),
|
||||||
|
t!("pulling"),
|
||||||
|
repo.as_ref().display()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let after_revision = get_head_revision(&self.git, repo.as_ref());
|
let after_revision = get_head_revision(&self.git, repo.as_ref());
|
||||||
|
|
||||||
match (&before_revision, &after_revision) {
|
match (&before_revision, &after_revision) {
|
||||||
(Some(before), Some(after)) if before != after => {
|
(Some(before), Some(after)) if before != after => {
|
||||||
println!("{} {}", style("Changed").yellow().bold(), repo.as_ref().display());
|
println!("{} {}", style(t!("Changed")).yellow().bold(), repo.as_ref().display());
|
||||||
|
|
||||||
Command::new(&self.git)
|
Command::new(&self.git)
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
@@ -345,7 +353,7 @@ impl RepoStep {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if ctx.config().verbose() {
|
if ctx.config().verbose() {
|
||||||
println!("{} {}", style("Up-to-date").green().bold(), repo.as_ref().display());
|
println!("{} {}", style(t!("Up-to-date")).green().bold(), repo.as_ref().display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,15 +371,16 @@ impl RepoStep {
|
|||||||
if ctx.run_type().dry() {
|
if ctx.run_type().dry() {
|
||||||
self.repos
|
self.repos
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|repo| println!("Would pull {}", repo.display()));
|
.for_each(|repo| println!("{}", t!("Would pull {repo}", repo = repo.display())));
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.config().verbose() {
|
if !ctx.config().verbose() {
|
||||||
println!(
|
println!(
|
||||||
"\n{} updated repositories will be shown...\n",
|
"\n{} {}\n",
|
||||||
style("Only").green().bold()
|
style(t!("Only")).green().bold(),
|
||||||
|
t!("updated repositories will be shown...")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,9 +390,10 @@ impl RepoStep {
|
|||||||
.filter(|repo| match self.has_remotes(repo) {
|
.filter(|repo| match self.has_remotes(repo) {
|
||||||
Some(false) => {
|
Some(false) => {
|
||||||
println!(
|
println!(
|
||||||
"{} {} because it has no remotes",
|
"{} {} {}",
|
||||||
style("Skipping").yellow().bold(),
|
style(t!("Skipping")).yellow().bold(),
|
||||||
repo.display()
|
repo.display(),
|
||||||
|
t!("because it has no remotes")
|
||||||
);
|
);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ pub fn upgrade_kak_plug(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.args(["-ui", "dummy", "-e", UPGRADE_KAK])
|
.args(["-ui", "dummy", "-e", UPGRADE_KAK])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
println!("Plugins upgraded");
|
println!("{}", t!("Plugins upgraded"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,17 @@ 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, REQUIRE_SUDO};
|
use crate::utils::{get_require_sudo_string, require_option};
|
||||||
use crate::HOME_DIR;
|
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};
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ impl NPM {
|
|||||||
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(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().clone(), get_require_sudo_string())?;
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
.arg(&self.command)
|
.arg(&self.command)
|
||||||
@@ -156,7 +157,7 @@ impl Yarn {
|
|||||||
let args = ["global", "upgrade"];
|
let args = ["global", "upgrade"];
|
||||||
|
|
||||||
if use_sudo {
|
if use_sudo {
|
||||||
let sudo = require_option(ctx.sudo().clone(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().clone(), get_require_sudo_string())?;
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
.arg(self.yarn.as_ref().unwrap_or(&self.command))
|
.arg(self.yarn.as_ref().unwrap_or(&self.command))
|
||||||
@@ -214,7 +215,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")]
|
||||||
{
|
{
|
||||||
@@ -230,7 +231,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("Performant Node Package Manager");
|
print_separator(t!("Performant Node Package Manager"));
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
@@ -251,7 +252,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")]
|
||||||
{
|
{
|
||||||
@@ -269,10 +270,55 @@ pub fn deno_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let deno_dir = HOME_DIR.join(".deno");
|
let deno_dir = HOME_DIR.join(".deno");
|
||||||
|
|
||||||
if !deno.canonicalize()?.is_descendant_of(&deno_dir) {
|
if !deno.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()
|
ctx.run_type().execute(&deno).arg("upgrade").status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
.run_type()
|
||||||
|
.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.iter() {
|
||||||
|
ctx.run_type()
|
||||||
|
.execute(&volta)
|
||||||
|
.args(["install", package])
|
||||||
|
.status_checked()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use color_eyre::eyre;
|
use color_eyre::eyre;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::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::utils::require_option;
|
||||||
use crate::utils::which;
|
use crate::utils::which;
|
||||||
use crate::{config, Step};
|
use crate::{config, Step};
|
||||||
|
|
||||||
@@ -144,13 +145,13 @@ 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 = require_option(ctx.sudo().as_ref(), "sudo is required to run pacman".into())?;
|
||||||
|
let mut command = ctx.run_type().execute(sudo);
|
||||||
command
|
command
|
||||||
.arg(&self.executable)
|
.arg(&self.executable)
|
||||||
.arg("-Syu")
|
.arg("-Syu")
|
||||||
@@ -161,7 +162,7 @@ impl ArchPackageManager for Pacman {
|
|||||||
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 = ctx.run_type().execute(sudo);
|
||||||
command.arg(&self.executable).arg("-Scc");
|
command.arg(&self.executable).arg("-Scc");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
command.arg("--noconfirm");
|
command.arg("--noconfirm");
|
||||||
@@ -174,10 +175,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()?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,46 +263,75 @@ 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_default();
|
use semver::Version;
|
||||||
let mut aur_update = ctx.run_type().execute(&sudo);
|
|
||||||
|
|
||||||
if sudo.ends_with("sudo") {
|
let version_cmd_output = ctx
|
||||||
aur_update
|
.run_type()
|
||||||
.arg(&self.executable)
|
.execute(&self.executable)
|
||||||
|
.arg("--version")
|
||||||
|
.output_checked_utf8()?;
|
||||||
|
// Output will be something like: "aura x.x.x\n"
|
||||||
|
let version_cmd_stdout = version_cmd_output.stdout;
|
||||||
|
let version_str = version_cmd_stdout.trim_start_matches("aura ").trim_end();
|
||||||
|
let version = Version::parse(version_str).expect("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.run_type().execute(&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 = ctx.run_type().execute(&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()?;
|
||||||
|
} else {
|
||||||
|
let sudo = crate::utils::require_option(
|
||||||
|
ctx.sudo().as_ref(),
|
||||||
|
t!("Aura(<0.4.6) requires sudo installed to work with AUR packages").to_string(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut cmd = ctx.run_type().execute(sudo);
|
||||||
|
cmd.arg(&self.executable)
|
||||||
.arg("-Au")
|
.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.run_type().execute(sudo);
|
||||||
} else {
|
cmd.arg(&self.executable)
|
||||||
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")
|
.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()?;
|
||||||
}
|
}
|
||||||
pacman_update.status_checked()?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -323,16 +352,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 +384,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,14 +1,14 @@
|
|||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::{require_option, REQUIRE_SUDO};
|
use crate::utils::{get_require_sudo_string, require_option};
|
||||||
use crate::Step;
|
use crate::Step;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
print_separator("DragonFly BSD Packages");
|
print_separator(t!("DragonFly BSD Packages"));
|
||||||
let mut cmd = ctx.run_type().execute(sudo);
|
let mut cmd = ctx.run_type().execute(sudo);
|
||||||
cmd.args(["/usr/local/sbin/pkg", "upgrade"]);
|
cmd.args(["/usr/local/sbin/pkg", "upgrade"]);
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
@@ -18,9 +18,9 @@ pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
|
pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
|
||||||
print_separator("DragonFly BSD Audit");
|
print_separator(t!("DragonFly BSD Audit"));
|
||||||
|
|
||||||
#[allow(clippy::disallowed_methods)]
|
#[allow(clippy::disallowed_methods)]
|
||||||
if !Command::new(sudo)
|
if !Command::new(sudo)
|
||||||
@@ -28,7 +28,9 @@ pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.status()?
|
.status()?
|
||||||
.success()
|
.success()
|
||||||
{
|
{
|
||||||
println!("The package audit was successful, but vulnerable packages still remain on the system");
|
println!(t!(
|
||||||
|
"The package audit was successful, but vulnerable packages still remain on the system"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::{require_option, REQUIRE_SUDO};
|
use crate::utils::{get_require_sudo_string, require_option};
|
||||||
use crate::Step;
|
use crate::Step;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
pub fn upgrade_freebsd(ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade_freebsd(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
print_separator("FreeBSD Update");
|
print_separator(t!("FreeBSD Update"));
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
.args(["/usr/sbin/freebsd-update", "fetch", "install"])
|
.args(["/usr/sbin/freebsd-update", "fetch", "install"])
|
||||||
@@ -16,8 +17,8 @@ pub fn upgrade_freebsd(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
print_separator("FreeBSD Packages");
|
print_separator(t!("FreeBSD Packages"));
|
||||||
|
|
||||||
let mut command = ctx.run_type().execute(sudo);
|
let mut command = ctx.run_type().execute(sudo);
|
||||||
|
|
||||||
@@ -29,9 +30,9 @@ pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
|
pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
|
||||||
print_separator("FreeBSD Audit");
|
print_separator(t!("FreeBSD Audit"));
|
||||||
|
|
||||||
Command::new(sudo)
|
Command::new(sudo)
|
||||||
.args(["/usr/sbin/pkg", "audit", "-Fr"])
|
.args(["/usr/sbin/pkg", "audit", "-Fr"])
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::process::Command;
|
|||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use ini::Ini;
|
use ini::Ini;
|
||||||
|
use rust_i18n::t;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
@@ -11,7 +12,7 @@ use crate::execution_context::ExecutionContext;
|
|||||||
use crate::steps::generic::is_wsl;
|
use crate::steps::generic::is_wsl;
|
||||||
use crate::steps::os::archlinux;
|
use crate::steps::os::archlinux;
|
||||||
use crate::terminal::{print_separator, prompt_yesno};
|
use crate::terminal::{print_separator, prompt_yesno};
|
||||||
use crate::utils::{require, require_option, which, PathExt, REQUIRE_SUDO};
|
use crate::utils::{get_require_sudo_string, require, require_option, which, PathExt};
|
||||||
use crate::{Step, HOME_DIR};
|
use crate::{Step, HOME_DIR};
|
||||||
|
|
||||||
static OS_RELEASE_PATH: &str = "/etc/os-release";
|
static OS_RELEASE_PATH: &str = "/etc/os-release";
|
||||||
@@ -30,6 +31,7 @@ pub enum Distribution {
|
|||||||
FedoraImmutable,
|
FedoraImmutable,
|
||||||
Debian,
|
Debian,
|
||||||
Gentoo,
|
Gentoo,
|
||||||
|
NILRT,
|
||||||
OpenMandriva,
|
OpenMandriva,
|
||||||
OpenSuseTumbleweed,
|
OpenSuseTumbleweed,
|
||||||
PCLinuxOS,
|
PCLinuxOS,
|
||||||
@@ -71,12 +73,13 @@ impl Distribution {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some("nilrt") => Distribution::NILRT,
|
||||||
Some("nobara") => Distribution::Nobara,
|
Some("nobara") => Distribution::Nobara,
|
||||||
Some("void") => Distribution::Void,
|
Some("void") => Distribution::Void,
|
||||||
Some("debian") | Some("pureos") | Some("Deepin") | Some("linuxmint") => Distribution::Debian,
|
Some("debian") | Some("pureos") | Some("Deepin") | Some("linuxmint") => Distribution::Debian,
|
||||||
Some("arch") | Some("manjaro-arm") | Some("garuda") | Some("artix") => Distribution::Arch,
|
Some("arch") | Some("manjaro-arm") | Some("garuda") | Some("artix") => Distribution::Arch,
|
||||||
Some("solus") => Distribution::Solus,
|
Some("solus") => Distribution::Solus,
|
||||||
Some("gentoo") => Distribution::Gentoo,
|
Some("gentoo") | Some("funtoo") => Distribution::Gentoo,
|
||||||
Some("exherbo") => Distribution::Exherbo,
|
Some("exherbo") => Distribution::Exherbo,
|
||||||
Some("nixos") => Distribution::NixOS,
|
Some("nixos") => Distribution::NixOS,
|
||||||
Some("opensuse-microos") => Distribution::SuseMicro,
|
Some("opensuse-microos") => Distribution::SuseMicro,
|
||||||
@@ -133,7 +136,7 @@ impl Distribution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn upgrade(self, ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade(self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
print_separator("System update");
|
print_separator(t!("System update"));
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Distribution::Alpine => upgrade_alpine_linux(ctx),
|
Distribution::Alpine => upgrade_alpine_linux(ctx),
|
||||||
@@ -158,6 +161,7 @@ impl Distribution {
|
|||||||
Distribution::OpenMandriva => upgrade_openmandriva(ctx),
|
Distribution::OpenMandriva => upgrade_openmandriva(ctx),
|
||||||
Distribution::PCLinuxOS => upgrade_pclinuxos(ctx),
|
Distribution::PCLinuxOS => upgrade_pclinuxos(ctx),
|
||||||
Distribution::Nobara => upgrade_nobara(ctx),
|
Distribution::Nobara => upgrade_nobara(ctx),
|
||||||
|
Distribution::NILRT => upgrade_nilrt(ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +177,7 @@ impl Distribution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_bedrock(ctx: &ExecutionContext) -> Result<()> {
|
fn update_bedrock(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
|
||||||
ctx.run_type().execute(sudo).args(["brl", "update"]);
|
ctx.run_type().execute(sudo).args(["brl", "update"]);
|
||||||
|
|
||||||
@@ -198,7 +202,7 @@ fn update_bedrock(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
fn upgrade_alpine_linux(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_alpine_linux(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let apk = require("apk")?;
|
let apk = require("apk")?;
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
|
||||||
ctx.run_type().execute(sudo).arg(&apk).arg("update").status_checked()?;
|
ctx.run_type().execute(sudo).arg(&apk).arg("update").status_checked()?;
|
||||||
ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").status_checked()
|
ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").status_checked()
|
||||||
@@ -206,7 +210,7 @@ fn upgrade_alpine_linux(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
fn upgrade_chimera_linux(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_chimera_linux(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let apk = require("apk")?;
|
let apk = require("apk")?;
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
|
||||||
ctx.run_type().execute(sudo).arg(&apk).arg("update").status_checked()?;
|
ctx.run_type().execute(sudo).arg(&apk).arg("update").status_checked()?;
|
||||||
ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").status_checked()
|
ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").status_checked()
|
||||||
@@ -214,7 +218,7 @@ fn upgrade_chimera_linux(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
fn upgrade_wolfi_linux(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_wolfi_linux(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let apk = require("apk")?;
|
let apk = require("apk")?;
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
|
||||||
ctx.run_type().execute(sudo).arg(&apk).arg("update").status_checked()?;
|
ctx.run_type().execute(sudo).arg(&apk).arg("update").status_checked()?;
|
||||||
ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").status_checked()
|
ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").status_checked()
|
||||||
@@ -229,7 +233,7 @@ fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut command = ctx.run_type().execute(sudo);
|
let mut command = ctx.run_type().execute(sudo);
|
||||||
command
|
command
|
||||||
.arg(which("dnf").unwrap_or_else(|| Path::new("yum").to_path_buf()))
|
.arg(which("dnf").unwrap_or_else(|| Path::new("yum").to_path_buf()))
|
||||||
@@ -252,7 +256,7 @@ fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_nobara(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_nobara(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let pkg_manager = require("dnf")?;
|
let pkg_manager = require("dnf")?;
|
||||||
|
|
||||||
let mut update_command = ctx.run_type().execute(sudo);
|
let mut update_command = ctx.run_type().execute(sudo);
|
||||||
@@ -285,6 +289,14 @@ fn upgrade_nobara(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn upgrade_nilrt(ctx: &ExecutionContext) -> Result<()> {
|
||||||
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
let opkg = require("opkg")?;
|
||||||
|
|
||||||
|
ctx.run_type().execute(sudo).arg(&opkg).arg("update").status_checked()?;
|
||||||
|
ctx.run_type().execute(sudo).arg(&opkg).arg("upgrade").status_checked()
|
||||||
|
}
|
||||||
|
|
||||||
fn upgrade_fedora_immutable(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_fedora_immutable(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let ostree = require("rpm-ostree")?;
|
let ostree = require("rpm-ostree")?;
|
||||||
let mut command = ctx.run_type().execute(ostree);
|
let mut command = ctx.run_type().execute(ostree);
|
||||||
@@ -294,14 +306,14 @@ fn upgrade_fedora_immutable(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_bedrock_strata(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_bedrock_strata(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
ctx.run_type().execute(sudo).args(["brl", "update"]).status_checked()?;
|
ctx.run_type().execute(sudo).args(["brl", "update"]).status_checked()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_suse(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_suse(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
.args(["zypper", "refresh"])
|
.args(["zypper", "refresh"])
|
||||||
@@ -324,7 +336,7 @@ fn upgrade_suse(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_opensuse_tumbleweed(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_opensuse_tumbleweed(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
.args(["zypper", "refresh"])
|
.args(["zypper", "refresh"])
|
||||||
@@ -342,7 +354,7 @@ fn upgrade_opensuse_tumbleweed(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_suse_micro(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_suse_micro(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut cmd = ctx.run_type().execute(sudo);
|
let mut cmd = ctx.run_type().execute(sudo);
|
||||||
cmd.arg("transactional-update");
|
cmd.arg("transactional-update");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
@@ -355,10 +367,10 @@ fn upgrade_suse_micro(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_openmandriva(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_openmandriva(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut command = ctx.run_type().execute(sudo);
|
let mut command = ctx.run_type().execute(sudo);
|
||||||
|
|
||||||
command.arg(&which("dnf").unwrap()).arg("upgrade");
|
command.arg(which("dnf").unwrap()).arg("upgrade");
|
||||||
|
|
||||||
if let Some(args) = ctx.config().dnf_arguments() {
|
if let Some(args) = ctx.config().dnf_arguments() {
|
||||||
command.args(args.split_whitespace());
|
command.args(args.split_whitespace());
|
||||||
@@ -374,10 +386,10 @@ fn upgrade_openmandriva(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_pclinuxos(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_pclinuxos(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut command_update = ctx.run_type().execute(sudo);
|
let mut command_update = ctx.run_type().execute(sudo);
|
||||||
|
|
||||||
command_update.arg(&which("apt-get").unwrap()).arg("update");
|
command_update.arg(which("apt-get").unwrap()).arg("update");
|
||||||
|
|
||||||
if let Some(args) = ctx.config().dnf_arguments() {
|
if let Some(args) = ctx.config().dnf_arguments() {
|
||||||
command_update.args(args.split_whitespace());
|
command_update.args(args.split_whitespace());
|
||||||
@@ -390,7 +402,7 @@ fn upgrade_pclinuxos(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
command_update.status_checked()?;
|
command_update.status_checked()?;
|
||||||
|
|
||||||
let mut cmd = ctx.run_type().execute(sudo);
|
let mut cmd = ctx.run_type().execute(sudo);
|
||||||
cmd.arg(&which("apt-get").unwrap());
|
cmd.arg(which("apt-get").unwrap());
|
||||||
cmd.arg("dist-upgrade");
|
cmd.arg("dist-upgrade");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
cmd.arg("-y");
|
cmd.arg("-y");
|
||||||
@@ -421,7 +433,7 @@ fn upgrade_vanilla(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_void(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_void(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut command = ctx.run_type().execute(sudo);
|
let mut command = ctx.run_type().execute(sudo);
|
||||||
command.args(["xbps-install", "-Su", "xbps"]);
|
command.args(["xbps-install", "-Su", "xbps"]);
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
@@ -442,7 +454,7 @@ fn upgrade_void(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let run_type = ctx.run_type();
|
let run_type = ctx.run_type();
|
||||||
|
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
if let Some(layman) = which("layman") {
|
if let Some(layman) = which("layman") {
|
||||||
run_type
|
run_type
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
@@ -451,7 +463,11 @@ fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.status_checked()?;
|
.status_checked()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Syncing portage");
|
println!("{}", t!("Syncing portage"));
|
||||||
|
if let Some(ego) = which("ego") {
|
||||||
|
// The Funtoo team doesn't reccomend running both ego sync and emerge --sync
|
||||||
|
run_type.execute(sudo).arg(ego).arg("sync").status_checked()?;
|
||||||
|
} else {
|
||||||
run_type
|
run_type
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
.args(["emerge", "--sync"])
|
.args(["emerge", "--sync"])
|
||||||
@@ -462,6 +478,7 @@ fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.unwrap_or_else(|| vec!["-q"]),
|
.unwrap_or_else(|| vec!["-q"]),
|
||||||
)
|
)
|
||||||
.status_checked()?;
|
.status_checked()?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(eix_update) = which("eix-update") {
|
if let Some(eix_update) = which("eix-update") {
|
||||||
run_type.execute(sudo).arg(eix_update).status_checked()?;
|
run_type.execute(sudo).arg(eix_update).status_checked()?;
|
||||||
@@ -512,7 +529,7 @@ fn upgrade_debian(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
if !is_nala {
|
if !is_nala {
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
@@ -566,7 +583,7 @@ pub fn run_deb_get(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_solus(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_solus(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut cmd = ctx.run_type().execute(sudo);
|
let mut cmd = ctx.run_type().execute(sudo);
|
||||||
cmd.arg("eopkg");
|
cmd.arg("eopkg");
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
@@ -675,7 +692,7 @@ pub fn run_packer_nu(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut cmd = ctx.run_type().execute(sudo);
|
let mut cmd = ctx.run_type().execute(sudo);
|
||||||
cmd.args(["swupd", "update"]);
|
cmd.args(["swupd", "update"]);
|
||||||
if ctx.config().yes(Step::System) {
|
if ctx.config().yes(Step::System) {
|
||||||
@@ -687,7 +704,7 @@ fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_exherbo(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_exherbo(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
ctx.run_type().execute(sudo).args(["cave", "sync"]).status_checked()?;
|
ctx.run_type().execute(sudo).args(["cave", "sync"]).status_checked()?;
|
||||||
|
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
@@ -716,7 +733,7 @@ fn upgrade_exherbo(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_nixos(ctx: &ExecutionContext) -> Result<()> {
|
fn upgrade_nixos(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let mut command = ctx.run_type().execute(sudo);
|
let mut command = ctx.run_type().execute(sudo);
|
||||||
command.args(["/run/current-system/sw/bin/nixos-rebuild", "switch", "--upgrade"]);
|
command.args(["/run/current-system/sw/bin/nixos-rebuild", "switch", "--upgrade"]);
|
||||||
|
|
||||||
@@ -742,7 +759,7 @@ fn upgrade_neon(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
// seems rare
|
// seems rare
|
||||||
// if that comes up we need to create a Distribution::PackageKit or some such
|
// if that comes up we need to create a Distribution::PackageKit or some such
|
||||||
|
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let pkcon = which("pkcon").unwrap();
|
let pkcon = which("pkcon").unwrap();
|
||||||
// pkcon ignores update with update and refresh provided together
|
// pkcon ignores update with update and refresh provided together
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
@@ -771,7 +788,7 @@ fn upgrade_neon(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
/// alternative
|
/// alternative
|
||||||
fn should_skip_needrestart() -> Result<()> {
|
fn should_skip_needrestart() -> Result<()> {
|
||||||
let distribution = Distribution::detect()?;
|
let distribution = Distribution::detect()?;
|
||||||
let msg = "needrestart will be ran by the package manager";
|
let msg = t!("needrestart will be ran by the package manager");
|
||||||
|
|
||||||
if distribution.redhat_based() {
|
if distribution.redhat_based() {
|
||||||
return Err(SkipStep(String::from(msg)).into());
|
return Err(SkipStep(String::from(msg)).into());
|
||||||
@@ -806,12 +823,12 @@ fn should_skip_needrestart() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_needrestart(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_needrestart(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let needrestart = require("needrestart")?;
|
let needrestart = require("needrestart")?;
|
||||||
|
|
||||||
should_skip_needrestart()?;
|
should_skip_needrestart()?;
|
||||||
|
|
||||||
print_separator("Check for needed restarts");
|
print_separator(t!("Check for needed restarts"));
|
||||||
|
|
||||||
ctx.run_type().execute(sudo).arg(needrestart).status_checked()?;
|
ctx.run_type().execute(sudo).arg(needrestart).status_checked()?;
|
||||||
|
|
||||||
@@ -822,10 +839,10 @@ pub fn run_fwupdmgr(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let fwupdmgr = require("fwupdmgr")?;
|
let fwupdmgr = require("fwupdmgr")?;
|
||||||
|
|
||||||
if is_wsl()? {
|
if is_wsl()? {
|
||||||
return Err(SkipStep(String::from("Should not run in WSL")).into());
|
return Err(SkipStep(t!("Should not run in WSL").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Firmware upgrades");
|
print_separator(t!("Firmware upgrades"));
|
||||||
|
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(&fwupdmgr)
|
.execute(&fwupdmgr)
|
||||||
@@ -847,7 +864,7 @@ pub fn run_fwupdmgr(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
pub fn run_flatpak(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_flatpak(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let flatpak = require("flatpak")?;
|
let flatpak = require("flatpak")?;
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let cleanup = ctx.config().cleanup();
|
let cleanup = ctx.config().cleanup();
|
||||||
let yes = ctx.config().yes(Step::Flatpak);
|
let yes = ctx.config().yes(Step::Flatpak);
|
||||||
let run_type = ctx.run_type();
|
let run_type = ctx.run_type();
|
||||||
@@ -867,7 +884,7 @@ pub fn run_flatpak(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
run_type.execute(&flatpak).args(&cleanup_args).status_checked()?;
|
run_type.execute(&flatpak).args(&cleanup_args).status_checked()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Flatpak System Packages");
|
print_separator(t!("Flatpak System Packages"));
|
||||||
if ctx.config().flatpak_use_sudo() || std::env::var("SSH_CLIENT").is_ok() {
|
if ctx.config().flatpak_use_sudo() || std::env::var("SSH_CLIENT").is_ok() {
|
||||||
let mut update_args = vec!["update", "--system"];
|
let mut update_args = vec!["update", "--system"];
|
||||||
if yes {
|
if yes {
|
||||||
@@ -908,11 +925,11 @@ pub fn run_flatpak(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_snap(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_snap(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let snap = require("snap")?;
|
let snap = require("snap")?;
|
||||||
|
|
||||||
if !PathBuf::from("/var/snapd.socket").exists() && !PathBuf::from("/run/snapd.socket").exists() {
|
if !PathBuf::from("/var/snapd.socket").exists() && !PathBuf::from("/run/snapd.socket").exists() {
|
||||||
return Err(SkipStep(String::from("Snapd socket does not exist")).into());
|
return Err(SkipStep(t!("Snapd socket does not exist").to_string()).into());
|
||||||
}
|
}
|
||||||
print_separator("snap");
|
print_separator("snap");
|
||||||
|
|
||||||
@@ -920,7 +937,7 @@ pub fn run_snap(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_pihole_update(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_pihole_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let pihole = require("pihole")?;
|
let pihole = require("pihole")?;
|
||||||
Path::new("/opt/pihole/update.sh").require()?;
|
Path::new("/opt/pihole/update.sh").require()?;
|
||||||
|
|
||||||
@@ -954,7 +971,7 @@ pub fn run_distrobox_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
) {
|
) {
|
||||||
(r, Some(c)) => {
|
(r, Some(c)) => {
|
||||||
if c.is_empty() {
|
if c.is_empty() {
|
||||||
return Err(SkipStep("You need to specify at least one container".to_string()).into());
|
return Err(SkipStep(t!("You need to specify at least one container").to_string()).into());
|
||||||
}
|
}
|
||||||
r.args(c)
|
r.args(c)
|
||||||
}
|
}
|
||||||
@@ -969,7 +986,7 @@ pub fn run_distrobox_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_dkp_pacman_update(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_dkp_pacman_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let dkp_pacman = require("dkp-pacman")?;
|
let dkp_pacman = require("dkp-pacman")?;
|
||||||
|
|
||||||
print_separator("Devkitpro pacman");
|
print_separator("Devkitpro pacman");
|
||||||
@@ -992,20 +1009,20 @@ pub fn run_dkp_pacman_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_config_update(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_config_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
if ctx.config().yes(Step::ConfigUpdate) {
|
if ctx.config().yes(Step::ConfigUpdate) {
|
||||||
return Err(SkipStep("Skipped in --yes".to_string()).into());
|
return Err(SkipStep(t!("Skipped in --yes").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(etc_update) = require("etc-update") {
|
if let Ok(etc_update) = require("etc-update") {
|
||||||
print_separator("Configuration update");
|
print_separator(t!("Configuration update"));
|
||||||
ctx.run_type().execute(sudo).arg(etc_update).status_checked()?;
|
ctx.run_type().execute(sudo).arg(etc_update).status_checked()?;
|
||||||
} else if let Ok(pacdiff) = require("pacdiff") {
|
} else if let Ok(pacdiff) = require("pacdiff") {
|
||||||
if std::env::var("DIFFPROG").is_err() {
|
if std::env::var("DIFFPROG").is_err() {
|
||||||
require("vim")?;
|
require("vim")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Configuration update");
|
print_separator(t!("Configuration update"));
|
||||||
ctx.execute_elevated(&pacdiff, false)?.status_checked()?;
|
ctx.execute_elevated(&pacdiff, false)?.status_checked()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1029,7 +1046,7 @@ pub fn run_lure_update(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_waydroid(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_waydroid(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let waydroid = require("waydroid")?;
|
let waydroid = require("waydroid")?;
|
||||||
let status = ctx.run_type().execute(&waydroid).arg("status").output_checked_utf8()?;
|
let status = ctx.run_type().execute(&waydroid).arg("status").output_checked_utf8()?;
|
||||||
// example output of `waydroid status`:
|
// example output of `waydroid status`:
|
||||||
@@ -1053,17 +1070,20 @@ pub fn run_waydroid(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.stdout
|
.stdout
|
||||||
.lines()
|
.lines()
|
||||||
.find(|line| line.contains("Session:"))
|
.find(|line| line.contains("Session:"))
|
||||||
.expect("the output of `waydroid status` should contain `Session:`");
|
.unwrap_or_else(|| panic!("the output of `waydroid status` should contain `Session:`"));
|
||||||
let is_container_running = session.contains("RUNNING");
|
let is_container_running = session.contains("RUNNING");
|
||||||
let assume_yes = ctx.config().yes(Step::Waydroid);
|
let assume_yes = ctx.config().yes(Step::Waydroid);
|
||||||
|
|
||||||
print_separator("Waydroid");
|
print_separator("Waydroid");
|
||||||
|
|
||||||
if is_container_running && !assume_yes {
|
if is_container_running && !assume_yes {
|
||||||
let update_allowed =
|
let update_allowed = prompt_yesno(&t!(
|
||||||
prompt_yesno("Going to execute `waydroid upgrade`, which would STOP the running container, is this ok?")?;
|
"Going to execute `waydroid upgrade`, which would STOP the running container, is this ok?"
|
||||||
|
))?;
|
||||||
if !update_allowed {
|
if !update_allowed {
|
||||||
return Err(SkipStep("Skip the Waydroid step because the user don't want to proceed".to_string()).into());
|
return Err(
|
||||||
|
SkipStep(t!("Skip the Waydroid step because the user don't want to proceed").to_string()).into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
@@ -1074,7 +1094,7 @@ pub fn run_waydroid(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_auto_cpufreq(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_auto_cpufreq(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
let auto_cpu_freq = require("auto-cpufreq")?;
|
let auto_cpu_freq = require("auto-cpufreq")?;
|
||||||
|
|
||||||
print_separator("auto-cpufreq");
|
print_separator("auto-cpufreq");
|
||||||
@@ -1185,6 +1205,11 @@ mod tests {
|
|||||||
test_template(include_str!("os_release/gentoo"), Distribution::Gentoo);
|
test_template(include_str!("os_release/gentoo"), Distribution::Gentoo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_funtoo() {
|
||||||
|
test_template(include_str!("os_release/funtoo"), Distribution::Gentoo);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exherbo() {
|
fn test_exherbo() {
|
||||||
test_template(include_str!("os_release/exherbo"), Distribution::Exherbo);
|
test_template(include_str!("os_release/exherbo"), Distribution::Exherbo);
|
||||||
@@ -1244,4 +1269,9 @@ mod tests {
|
|||||||
fn test_nobara() {
|
fn test_nobara() {
|
||||||
test_template(include_str!("os_release/nobara"), Distribution::Nobara);
|
test_template(include_str!("os_release/nobara"), Distribution::Nobara);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nilrt() {
|
||||||
|
test_template(include_str!("os_release/nilrt"), Distribution::NILRT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::terminal::{print_separator, prompt_yesno};
|
use crate::terminal::{print_separator, prompt_yesno};
|
||||||
use crate::utils::{require_option, REQUIRE_SUDO};
|
use crate::utils::{get_require_sudo_string, require_option};
|
||||||
use crate::{utils::require, Step};
|
use crate::{utils::require, Step};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -11,7 +12,7 @@ use tracing::debug;
|
|||||||
|
|
||||||
pub fn run_macports(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_macports(ctx: &ExecutionContext) -> Result<()> {
|
||||||
require("port")?;
|
require("port")?;
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
|
||||||
print_separator("MacPorts");
|
print_separator("MacPorts");
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
@@ -34,25 +35,25 @@ pub fn run_macports(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
pub fn run_mas(ctx: &ExecutionContext) -> 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"));
|
||||||
|
|
||||||
ctx.run_type().execute(mas).arg("upgrade").status_checked()
|
ctx.run_type().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.config().dry_run());
|
||||||
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(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +116,7 @@ pub fn update_xcodes(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if releases_installed.is_empty() {
|
if releases_installed.is_empty() {
|
||||||
println!("No Xcode releases installed.");
|
println!("{}", t!("No Xcode releases installed."));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +195,8 @@ pub fn update_xcodes(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
releases_regular_new_installed,
|
releases_regular_new_installed,
|
||||||
] {
|
] {
|
||||||
if should_ask && releases_new_installed.len() == 2 {
|
if should_ask && releases_new_installed.len() == 2 {
|
||||||
let answer_uninstall = prompt_yesno("Would you like to move the former Xcode release to the trash?")?;
|
let answer_uninstall =
|
||||||
|
prompt_yesno(t!("Would you like to move the former Xcode release to the trash?").as_ref())?;
|
||||||
if answer_uninstall {
|
if answer_uninstall {
|
||||||
let _ = ctx
|
let _ = ctx
|
||||||
.run_type()
|
.run_type()
|
||||||
@@ -221,11 +223,12 @@ pub fn process_xcodes_releases(releases_filtered: Vec<String>, should_ask: bool,
|
|||||||
&& !releases_filtered.is_empty()
|
&& !releases_filtered.is_empty()
|
||||||
{
|
{
|
||||||
println!(
|
println!(
|
||||||
"New Xcode release detected: {}",
|
"{} {}",
|
||||||
|
t!("New Xcode release detected:"),
|
||||||
releases_filtered.last().cloned().unwrap_or_default()
|
releases_filtered.last().cloned().unwrap_or_default()
|
||||||
);
|
);
|
||||||
if should_ask {
|
if should_ask {
|
||||||
let answer_install = prompt_yesno("Would you like to install it?")?;
|
let answer_install = prompt_yesno(t!("Would you like to install it?").as_ref())?;
|
||||||
if answer_install {
|
if answer_install {
|
||||||
let _ = ctx
|
let _ = ctx
|
||||||
.run_type()
|
.run_type()
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::{require_option, REQUIRE_SUDO};
|
use crate::utils::{get_require_sudo_string, require_option};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
pub fn upgrade_openbsd(ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade_openbsd(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
print_separator("OpenBSD Update");
|
print_separator(t!("OpenBSD Update"));
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(sudo)
|
.execute(sudo)
|
||||||
.args(["/usr/sbin/sysupgrade", "-n"])
|
.args(["/usr/sbin/sysupgrade", "-n"])
|
||||||
@@ -14,8 +14,8 @@ pub fn upgrade_openbsd(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
print_separator("OpenBSD Packages");
|
print_separator(t!("OpenBSD Packages"));
|
||||||
|
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().cleanup() {
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
|
|||||||
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"
|
||||||
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"
|
||||||
@@ -13,6 +13,10 @@ use color_eyre::eyre::Context;
|
|||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use home;
|
use home;
|
||||||
use ini::Ini;
|
use ini::Ini;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use nix::unistd::Uid;
|
||||||
|
use rust_i18n::t;
|
||||||
|
use semver::Version;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@@ -24,7 +28,7 @@ use crate::executor::Executor;
|
|||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
use crate::executor::RunType;
|
use crate::executor::RunType;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::utils::{require, require_option, PathExt, REQUIRE_SUDO};
|
use crate::utils::{get_require_sudo_string, require, require_option, PathExt};
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
const INTEL_BREW: &str = "/usr/local/bin/brew";
|
const INTEL_BREW: &str = "/usr/local/bin/brew";
|
||||||
@@ -98,19 +102,19 @@ pub fn run_fisher(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.args(["-c", "type -t fisher"])
|
.args(["-c", "type -t fisher"])
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|_| SkipStep("`fisher` is not defined in `fish`".to_owned()))?;
|
.map_err(|_| SkipStep(t!("`fisher` is not defined in `fish`").to_string()))?;
|
||||||
|
|
||||||
Command::new(&fish)
|
Command::new(&fish)
|
||||||
.args(["-c", "echo \"$__fish_config_dir/fish_plugins\""])
|
.args(["-c", "echo \"$__fish_config_dir/fish_plugins\""])
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.and_then(|output| Path::new(&output.stdout.trim()).require().map(|_| ()))
|
.and_then(|output| Path::new(&output.stdout.trim()).require().map(|_| ()))
|
||||||
.map_err(|err| SkipStep(format!("`fish_plugins` path doesn't exist: {err}")))?;
|
.map_err(|err| SkipStep(t!("`fish_plugins` path doesn't exist: {err}", err = err).to_string()))?;
|
||||||
|
|
||||||
Command::new(&fish)
|
Command::new(&fish)
|
||||||
.args(["-c", "fish_update_completions"])
|
.args(["-c", "fish_update_completions"])
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|_| SkipStep("`fish_update_completions` is not available".to_owned()))?;
|
.map_err(|_| SkipStep(t!("`fish_update_completions` is not available").to_string()))?;
|
||||||
|
|
||||||
print_separator("Fisher");
|
print_separator("Fisher");
|
||||||
|
|
||||||
@@ -177,7 +181,7 @@ pub fn run_oh_my_fish(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
pub fn run_pkgin(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_pkgin(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let pkgin = require("pkgin")?;
|
let pkgin = require("pkgin")?;
|
||||||
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
||||||
|
|
||||||
print_separator("Pkgin");
|
print_separator("Pkgin");
|
||||||
|
|
||||||
@@ -232,7 +236,7 @@ pub fn upgrade_gnome_extensions(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let gdbus = require("gdbus")?;
|
let gdbus = require("gdbus")?;
|
||||||
require_option(
|
require_option(
|
||||||
var("XDG_CURRENT_DESKTOP").ok().filter(|p| p.contains("GNOME")),
|
var("XDG_CURRENT_DESKTOP").ok().filter(|p| p.contains("GNOME")),
|
||||||
"Desktop doest not appear to be gnome".to_string(),
|
t!("Desktop doest not appear to be gnome").to_string(),
|
||||||
)?;
|
)?;
|
||||||
let output = Command::new("gdbus")
|
let output = Command::new("gdbus")
|
||||||
.args([
|
.args([
|
||||||
@@ -249,10 +253,10 @@ pub fn upgrade_gnome_extensions(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
debug!("Checking for gnome extensions: {}", output);
|
debug!("Checking for gnome extensions: {}", output);
|
||||||
if !output.stdout.contains("org.gnome.Shell.Extensions") {
|
if !output.stdout.contains("org.gnome.Shell.Extensions") {
|
||||||
return Err(SkipStep(String::from("Gnome shell extensions are unregistered in DBus")).into());
|
return Err(SkipStep(t!("Gnome shell extensions are unregistered in DBus").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Gnome Shell extensions");
|
print_separator(t!("Gnome Shell extensions"));
|
||||||
|
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(gdbus)
|
.execute(gdbus)
|
||||||
@@ -269,6 +273,23 @@ pub fn upgrade_gnome_extensions(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.status_checked()
|
.status_checked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn brew_linux_sudo_uid() -> Option<u32> {
|
||||||
|
let linuxbrew_directory = "/home/linuxbrew/.linuxbrew";
|
||||||
|
if let Ok(metadata) = std::fs::metadata(linuxbrew_directory) {
|
||||||
|
let owner_id = metadata.uid();
|
||||||
|
let current_id = Uid::effective();
|
||||||
|
// print debug these two values
|
||||||
|
debug!("linuxbrew_directory owner_id: {}, current_id: {}", owner_id, current_id);
|
||||||
|
return if owner_id == current_id.as_raw() {
|
||||||
|
None // no need for sudo if linuxbrew is owned by the current user
|
||||||
|
} else {
|
||||||
|
Some(owner_id) // otherwise use sudo to run brew as the owner
|
||||||
|
};
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
pub fn run_brew_formula(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()> {
|
pub fn run_brew_formula(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()> {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
@@ -277,10 +298,37 @@ pub fn run_brew_formula(ctx: &ExecutionContext, variant: BrewVariant) -> Result<
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
if variant.is_path() && !BrewVariant::is_macos_custom(binary_name) {
|
if variant.is_path() && !BrewVariant::is_macos_custom(binary_name) {
|
||||||
return Err(SkipStep("Not a custom brew for macOS".to_string()).into());
|
return Err(SkipStep(t!("Not a custom brew for macOS").to_string()).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let sudo_uid = brew_linux_sudo_uid();
|
||||||
|
// if brew is owned by another user, execute "sudo -Hu <uid> brew update"
|
||||||
|
if let Some(user_id) = sudo_uid {
|
||||||
|
let uid = nix::unistd::Uid::from_raw(user_id);
|
||||||
|
let user = nix::unistd::User::from_uid(uid)
|
||||||
|
.expect("failed to call getpwuid()")
|
||||||
|
.expect("this user should exist");
|
||||||
|
|
||||||
|
let sudo_as_user = t!("sudo as user '{user}'", user = user.name);
|
||||||
|
print_separator(format!("{} ({})", variant.step_title(), sudo_as_user));
|
||||||
|
|
||||||
|
let sudo = crate::utils::require_option(ctx.sudo().as_ref(), crate::utils::get_require_sudo_string())?;
|
||||||
|
ctx.run_type()
|
||||||
|
.execute(sudo)
|
||||||
|
.current_dir("/tmp") // brew needs a writable current directory
|
||||||
|
.args([
|
||||||
|
"--set-home",
|
||||||
|
&format!("--user={}", user.name),
|
||||||
|
&format!("{}", binary_name.to_string_lossy()),
|
||||||
|
"update",
|
||||||
|
])
|
||||||
|
.status_checked()?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
print_separator(variant.step_title());
|
print_separator(variant.step_title());
|
||||||
let run_type = ctx.run_type();
|
let run_type = ctx.run_type();
|
||||||
|
|
||||||
@@ -310,7 +358,7 @@ pub fn run_brew_formula(ctx: &ExecutionContext, variant: BrewVariant) -> Result<
|
|||||||
pub fn run_brew_cask(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()> {
|
pub fn run_brew_cask(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()> {
|
||||||
let binary_name = require(variant.binary_name())?;
|
let binary_name = require(variant.binary_name())?;
|
||||||
if variant.is_path() && !BrewVariant::is_macos_custom(binary_name) {
|
if variant.is_path() && !BrewVariant::is_macos_custom(binary_name) {
|
||||||
return Err(SkipStep("Not a custom brew for macOS".to_string()).into());
|
return Err(SkipStep(t!("Not a custom brew for macOS").to_string()).into());
|
||||||
}
|
}
|
||||||
print_separator(format!("{} - Cask", variant.step_title()));
|
print_separator(format!("{} - Cask", variant.step_title()));
|
||||||
let run_type = ctx.run_type();
|
let run_type = ctx.run_type();
|
||||||
@@ -336,6 +384,9 @@ pub fn run_brew_cask(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()>
|
|||||||
if ctx.config().brew_greedy_latest() {
|
if ctx.config().brew_greedy_latest() {
|
||||||
brew_args.push("--greedy-latest");
|
brew_args.push("--greedy-latest");
|
||||||
}
|
}
|
||||||
|
if ctx.config().brew_greedy_auto_updates() {
|
||||||
|
brew_args.push("--greedy-auto-updates");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
variant.execute(run_type).args(&brew_args).status_checked()?;
|
variant.execute(run_type).args(&brew_args).status_checked()?;
|
||||||
@@ -362,7 +413,7 @@ pub fn run_guix(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
if should_upgrade {
|
if should_upgrade {
|
||||||
return run_type.execute(&guix).args(["package", "-u"]).status_checked();
|
return run_type.execute(&guix).args(["package", "-u"]).status_checked();
|
||||||
}
|
}
|
||||||
Err(SkipStep(String::from("Guix Pull Failed, Skipping")).into())
|
Err(SkipStep(t!("Guix Pull Failed, Skipping").to_string()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_nix(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_nix(ctx: &ExecutionContext) -> Result<()> {
|
||||||
@@ -382,23 +433,46 @@ pub fn run_nix(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
if require("darwin-rebuild").is_ok() {
|
if require("darwin-rebuild").is_ok() {
|
||||||
return Err(SkipStep(String::from(
|
return Err(
|
||||||
"Nix-darwin on macOS must be upgraded via darwin-rebuild switch",
|
SkipStep(t!("Nix-darwin on macOS must be upgraded via darwin-rebuild switch").to_string()).into(),
|
||||||
))
|
);
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let run_type = ctx.run_type();
|
let run_type = ctx.run_type();
|
||||||
run_type.execute(nix_channel).arg("--update").status_checked()?;
|
run_type.execute(nix_channel).arg("--update").status_checked()?;
|
||||||
|
|
||||||
|
let mut get_version_cmd = ctx.run_type().execute(&nix);
|
||||||
|
get_version_cmd.arg("--version");
|
||||||
|
let get_version_cmd_output = get_version_cmd.output_checked_utf8()?;
|
||||||
|
let get_version_cmd_first_line_stdout = get_version_cmd_output
|
||||||
|
.stdout
|
||||||
|
.lines()
|
||||||
|
.next()
|
||||||
|
.expect("nix --version gives an empty output");
|
||||||
|
let splitted: Vec<&str> = get_version_cmd_first_line_stdout.split_whitespace().collect();
|
||||||
|
let version = if splitted.len() >= 3 {
|
||||||
|
Version::parse(splitted[2]).expect("invalid version")
|
||||||
|
} else {
|
||||||
|
panic!("nix --version output format changed, file an issue to Topgrade!")
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("Nix version: {:?}", version);
|
||||||
|
|
||||||
|
// Nix since 2.21.0 uses `--all --impure` rather than `.*` to upgrade all packages
|
||||||
|
let packages = if version >= Version::new(2, 21, 0) {
|
||||||
|
vec!["--all", "--impure"]
|
||||||
|
} else {
|
||||||
|
vec![".*"]
|
||||||
|
};
|
||||||
|
|
||||||
if Path::new(&manifest_json_path).exists() {
|
if Path::new(&manifest_json_path).exists() {
|
||||||
run_type
|
run_type
|
||||||
.execute(nix)
|
.execute(nix)
|
||||||
.args(nix_args())
|
.args(nix_args())
|
||||||
.arg("profile")
|
.arg("profile")
|
||||||
.arg("upgrade")
|
.arg("upgrade")
|
||||||
.arg(".*")
|
.args(&packages)
|
||||||
.arg("--verbose")
|
.arg("--verbose")
|
||||||
.status_checked()
|
.status_checked()
|
||||||
} else {
|
} else {
|
||||||
@@ -427,20 +501,16 @@ pub fn run_nix_self_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !should_self_upgrade {
|
if !should_self_upgrade {
|
||||||
return Err(SkipStep(String::from(
|
return Err(SkipStep(t!("`nix upgrade-nix` can only be used on macOS or non-NixOS Linux").to_string()).into());
|
||||||
"`nix upgrade-nix` can only be used on macOS or non-NixOS Linux",
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if nix_profile_dir(&nix)?.is_none() {
|
if nix_profile_dir(&nix)?.is_none() {
|
||||||
return Err(SkipStep(String::from(
|
return Err(
|
||||||
"`nix upgrade-nix` cannot be run when Nix is installed in a profile",
|
SkipStep(t!("`nix upgrade-nix` cannot be run when Nix is installed in a profile").to_string()).into(),
|
||||||
))
|
);
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print_separator("Nix (self-upgrade)");
|
print_separator(t!("Nix (self-upgrade)"));
|
||||||
|
|
||||||
let multi_user = fs::metadata(&nix)?.uid() == 0;
|
let multi_user = fs::metadata(&nix)?.uid() == 0;
|
||||||
debug!("Multi user nix: {}", multi_user);
|
debug!("Multi user nix: {}", multi_user);
|
||||||
@@ -505,7 +575,6 @@ fn nix_profile_dir(nix: &Path) -> Result<Option<PathBuf>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug!("Found Nix profile {profile_dir:?}");
|
debug!("Found Nix profile {profile_dir:?}");
|
||||||
|
|
||||||
let user_env = profile_dir
|
let user_env = profile_dir
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.wrap_err_with(|| format!("Failed to canonicalize {profile_dir:?}"))?;
|
.wrap_err_with(|| format!("Failed to canonicalize {profile_dir:?}"))?;
|
||||||
@@ -602,11 +671,15 @@ pub fn run_pyenv(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.unwrap_or_else(|_| HOME_DIR.join(".pyenv"));
|
.unwrap_or_else(|_| HOME_DIR.join(".pyenv"));
|
||||||
|
|
||||||
if !pyenv_dir.exists() {
|
if !pyenv_dir.exists() {
|
||||||
return Err(SkipStep("Pyenv is installed, but $PYENV_ROOT is not set correctly".to_string()).into());
|
return Err(SkipStep(t!("Pyenv is installed, but $PYENV_ROOT is not set correctly").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pyenv_dir.join(".git").exists() {
|
if !pyenv_dir.join(".git").exists() {
|
||||||
return Err(SkipStep("pyenv is not a git repository".to_string()).into());
|
return Err(SkipStep(t!("pyenv is not a git repository").to_string()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pyenv_dir.join("plugins").join("pyenv-update").exists() {
|
||||||
|
return Err(SkipStep(t!("pyenv-update plugin is not installed").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.run_type().execute(pyenv).arg("update").status_checked()
|
ctx.run_type().execute(pyenv).arg("update").status_checked()
|
||||||
@@ -675,18 +748,10 @@ pub fn run_sdkman(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_bun(ctx: &ExecutionContext) -> Result<()> {
|
|
||||||
let bun = require("bun")?;
|
|
||||||
|
|
||||||
print_separator("Bun");
|
|
||||||
|
|
||||||
ctx.run_type().execute(bun).arg("upgrade").status_checked()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_bun_packages(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_bun_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let bun = require("bun")?;
|
let bun = require("bun")?;
|
||||||
|
|
||||||
print_separator("Bun Packages");
|
print_separator(t!("Bun Packages"));
|
||||||
|
|
||||||
let mut package_json: PathBuf = var("BUN_INSTALL")
|
let mut package_json: PathBuf = var("BUN_INSTALL")
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
@@ -694,7 +759,7 @@ pub fn run_bun_packages(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
package_json.push("install/global/package.json");
|
package_json.push("install/global/package.json");
|
||||||
|
|
||||||
if !package_json.exists() {
|
if !package_json.exists() {
|
||||||
println!("No global packages installed");
|
println!("{}", t!("No global packages installed"));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,6 +784,7 @@ pub fn run_maza(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reboot() -> Result<()> {
|
pub fn reboot() -> Result<()> {
|
||||||
print!("Rebooting...");
|
print!("{}", t!("Rebooting..."));
|
||||||
|
|
||||||
Command::new("sudo").arg("reboot").status_checked()
|
Command::new("sudo").arg("reboot").status_checked()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::terminal::{print_separator, print_warning};
|
|||||||
use crate::utils::{require, which};
|
use crate::utils::{require, which};
|
||||||
use crate::{error::SkipStep, steps::git::RepoStep};
|
use crate::{error::SkipStep, steps::git::RepoStep};
|
||||||
use crate::{powershell, Step};
|
use crate::{powershell, Step};
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
pub fn run_chocolatey(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_chocolatey(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let choco = require("choco")?;
|
let choco = require("choco")?;
|
||||||
@@ -57,6 +58,10 @@ pub fn run_scoop(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
if ctx.config().cleanup() {
|
if ctx.config().cleanup() {
|
||||||
ctx.run_type().execute(&scoop).args(["cleanup", "*"]).status_checked()?;
|
ctx.run_type().execute(&scoop).args(["cleanup", "*"]).status_checked()?;
|
||||||
|
ctx.run_type()
|
||||||
|
.execute(&scoop)
|
||||||
|
.args(["cache", "rm", "-a"])
|
||||||
|
.status_checked()?
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -64,12 +69,12 @@ pub fn run_scoop(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
pub fn update_wsl(ctx: &ExecutionContext) -> Result<()> {
|
pub fn update_wsl(ctx: &ExecutionContext) -> Result<()> {
|
||||||
if !is_wsl_installed()? {
|
if !is_wsl_installed()? {
|
||||||
return Err(SkipStep("WSL not installed".to_string()).into());
|
return Err(SkipStep(t!("WSL not installed").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let wsl = require("wsl")?;
|
let wsl = require("wsl")?;
|
||||||
|
|
||||||
print_separator("Update WSL");
|
print_separator(t!("Update WSL"));
|
||||||
|
|
||||||
let mut wsl_command = ctx.run_type().execute(wsl);
|
let mut wsl_command = ctx.run_type().execute(wsl);
|
||||||
wsl_command.args(["--update"]);
|
wsl_command.args(["--update"]);
|
||||||
@@ -122,7 +127,7 @@ fn upgrade_wsl_distribution(wsl: &Path, dist: &str, ctx: &ExecutionContext) -> R
|
|||||||
let topgrade = Command::new(wsl)
|
let topgrade = Command::new(wsl)
|
||||||
.args(["-d", dist, "bash", "-lc", "which topgrade"])
|
.args(["-d", dist, "bash", "-lc", "which topgrade"])
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.map_err(|_| SkipStep(String::from("Could not find Topgrade installed in WSL")))?
|
.map_err(|_| SkipStep(t!("Could not find Topgrade installed in WSL").to_string()))?
|
||||||
.stdout // The normal output from `which topgrade` appends a newline, so we trim it here.
|
.stdout // The normal output from `which topgrade` appends a newline, so we trim it here.
|
||||||
.trim_end()
|
.trim_end()
|
||||||
.to_owned();
|
.to_owned();
|
||||||
@@ -171,7 +176,7 @@ fn upgrade_wsl_distribution(wsl: &Path, dist: &str, ctx: &ExecutionContext) -> R
|
|||||||
|
|
||||||
pub fn run_wsl_topgrade(ctx: &ExecutionContext) -> Result<()> {
|
pub fn run_wsl_topgrade(ctx: &ExecutionContext) -> Result<()> {
|
||||||
if !is_wsl_installed()? {
|
if !is_wsl_installed()? {
|
||||||
return Err(SkipStep("WSL not installed".to_string()).into());
|
return Err(SkipStep(t!("WSL not installed").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let wsl = require("wsl")?;
|
let wsl = require("wsl")?;
|
||||||
@@ -194,23 +199,25 @@ pub fn run_wsl_topgrade(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
if ran {
|
if ran {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(SkipStep(String::from("Could not find Topgrade in any WSL disribution")).into())
|
Err(SkipStep(t!("Could not find Topgrade in any WSL disribution").to_string()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn windows_update(ctx: &ExecutionContext) -> Result<()> {
|
pub fn windows_update(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let powershell = powershell::Powershell::windows_powershell();
|
let powershell = powershell::Powershell::windows_powershell();
|
||||||
|
|
||||||
print_separator("Windows Update");
|
print_separator(t!("Windows Update"));
|
||||||
|
|
||||||
if powershell.supports_windows_update() {
|
if powershell.supports_windows_update() {
|
||||||
|
println!("The installer will request to run as administrator, expect a prompt.");
|
||||||
|
|
||||||
powershell.windows_update(ctx)
|
powershell.windows_update(ctx)
|
||||||
} else {
|
} else {
|
||||||
print_warning(
|
print_warning(t!(
|
||||||
"Consider installing PSWindowsUpdate as the use of Windows Update via USOClient is not supported.",
|
"Consider installing PSWindowsUpdate as the use of Windows Update via USOClient is not supported."
|
||||||
);
|
));
|
||||||
|
|
||||||
Err(SkipStep("USOClient not supported.".to_string()).into())
|
Err(SkipStep(t!("USOClient not supported.").to_string()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +237,7 @@ pub fn insert_startup_scripts(git_repos: &mut RepoStep) -> Result<()> {
|
|||||||
if let Ok(lnk) = parselnk::Lnk::try_from(Path::new(&path)) {
|
if let Ok(lnk) = parselnk::Lnk::try_from(Path::new(&path)) {
|
||||||
debug!("Startup link: {:?}", lnk);
|
debug!("Startup link: {:?}", lnk);
|
||||||
if let Some(path) = lnk.relative_path() {
|
if let Some(path) = lnk.relative_path() {
|
||||||
git_repos.insert_if_repo(&startup_dir.join(path));
|
git_repos.insert_if_repo(startup_dir.join(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::path::PathBuf;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
use crate::execution_context::ExecutionContext;
|
use crate::execution_context::ExecutionContext;
|
||||||
@@ -62,9 +63,9 @@ impl Powershell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_modules(&self, ctx: &ExecutionContext) -> Result<()> {
|
pub fn update_modules(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
let powershell = require_option(self.path.as_ref(), String::from("Powershell is not installed"))?;
|
let powershell = require_option(self.path.as_ref(), t!("Powershell is not installed").to_string())?;
|
||||||
|
|
||||||
print_separator("Powershell Modules Update");
|
print_separator(t!("Powershell Modules Update"));
|
||||||
|
|
||||||
let mut cmd = vec!["Update-Module"];
|
let mut cmd = vec!["Update-Module"];
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ impl Powershell {
|
|||||||
cmd.push("-Force")
|
cmd.push("-Force")
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Updating modules...");
|
println!("{}", t!("Updating modules..."));
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(powershell)
|
.execute(powershell)
|
||||||
// This probably doesn't need `shell_words::join`.
|
// This probably doesn't need `shell_words::join`.
|
||||||
@@ -94,10 +95,18 @@ impl Powershell {
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn windows_update(&self, ctx: &ExecutionContext) -> Result<()> {
|
pub fn windows_update(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||||
let powershell = require_option(self.path.as_ref(), String::from("Powershell is not installed"))?;
|
let powershell = require_option(self.path.as_ref(), t!("Powershell is not installed").to_string())?;
|
||||||
|
|
||||||
debug_assert!(self.supports_windows_update());
|
debug_assert!(self.supports_windows_update());
|
||||||
|
|
||||||
|
let accept_all = if ctx.config().accept_all_windows_updates() {
|
||||||
|
"-AcceptAll"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
let install_windowsupdate_verbose = "Install-WindowsUpdate -Verbose".to_string();
|
||||||
|
|
||||||
let mut command = if let Some(sudo) = ctx.sudo() {
|
let mut command = if let Some(sudo) = ctx.sudo() {
|
||||||
let mut command = ctx.run_type().execute(sudo);
|
let mut command = ctx.run_type().execute(sudo);
|
||||||
command.arg(powershell);
|
command.arg(powershell);
|
||||||
@@ -107,18 +116,7 @@ impl Powershell {
|
|||||||
};
|
};
|
||||||
|
|
||||||
command
|
command
|
||||||
.args([
|
.args(["-NoProfile", &install_windowsupdate_verbose, accept_all])
|
||||||
"-NoProfile",
|
|
||||||
"-Command",
|
|
||||||
&format!(
|
|
||||||
"Start-Process powershell -Verb runAs -ArgumentList 'Import-Module PSWindowsUpdate; Install-WindowsUpdate -MicrosoftUpdate {} -Verbose'",
|
|
||||||
if ctx.config().accept_all_windows_updates() {
|
|
||||||
"-AcceptAll"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.status_checked()
|
.status_checked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command::CommandExt, error::SkipStep, execution_context::ExecutionContext, terminal::print_separator, utils,
|
command::CommandExt, error::SkipStep, execution_context::ExecutionContext, terminal::print_separator, utils,
|
||||||
@@ -27,7 +28,7 @@ pub fn ssh_step(ctx: &ExecutionContext, hostname: &str) -> Result<()> {
|
|||||||
{
|
{
|
||||||
prepare_async_ssh_command(&mut args);
|
prepare_async_ssh_command(&mut args);
|
||||||
crate::tmux::run_command(ctx, hostname, &shell_words::join(args))?;
|
crate::tmux::run_command(ctx, hostname, &shell_words::join(args))?;
|
||||||
Err(SkipStep(String::from("Remote Topgrade launched in Tmux")).into())
|
Err(SkipStep(String::from(t!("Remote Topgrade launched in Tmux"))).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
@@ -35,7 +36,7 @@ pub fn ssh_step(ctx: &ExecutionContext, hostname: &str) -> Result<()> {
|
|||||||
} else if ctx.config().open_remotes_in_new_terminal() && !ctx.run_type().dry() && cfg!(windows) {
|
} else if ctx.config().open_remotes_in_new_terminal() && !ctx.run_type().dry() && cfg!(windows) {
|
||||||
prepare_async_ssh_command(&mut args);
|
prepare_async_ssh_command(&mut args);
|
||||||
ctx.run_type().execute("wt").args(&args).spawn()?;
|
ctx.run_type().execute("wt").args(&args).spawn()?;
|
||||||
Err(SkipStep(String::from("Remote Topgrade launched in an external terminal")).into())
|
Err(SkipStep(String::from(t!("Remote Topgrade launched in an external terminal"))).into())
|
||||||
} else {
|
} else {
|
||||||
let mut args = vec!["-t", hostname];
|
let mut args = vec!["-t", hostname];
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ pub fn ssh_step(ctx: &ExecutionContext, hostname: &str) -> Result<()> {
|
|||||||
args.extend(["env", &env, "$SHELL", "-lc", topgrade]);
|
args.extend(["env", &env, "$SHELL", "-lc", topgrade]);
|
||||||
|
|
||||||
print_separator(format!("Remote ({hostname})"));
|
print_separator(format!("Remote ({hostname})"));
|
||||||
println!("Connecting to {hostname}...");
|
println!("{}", t!("Connecting to {hostname}...", hostname = hostname));
|
||||||
|
|
||||||
ctx.run_type().execute(ssh).args(&args).status_checked()
|
ctx.run_type().execute(ssh).args(&args).status_checked()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::{fmt::Display, rc::Rc, str::FromStr};
|
|||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use rust_i18n::t;
|
||||||
use strum::EnumString;
|
use strum::EnumString;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
@@ -151,14 +152,14 @@ impl<'a> Drop for TemporaryPowerOn<'a> {
|
|||||||
pub fn collect_boxes(ctx: &ExecutionContext) -> Result<Vec<VagrantBox>> {
|
pub fn collect_boxes(ctx: &ExecutionContext) -> Result<Vec<VagrantBox>> {
|
||||||
let directories = utils::require_option(
|
let directories = utils::require_option(
|
||||||
ctx.config().vagrant_directories(),
|
ctx.config().vagrant_directories(),
|
||||||
String::from("No Vagrant directories were specified in the configuration file"),
|
String::from(t!("No Vagrant directories were specified in the configuration file")),
|
||||||
)?;
|
)?;
|
||||||
let vagrant = Vagrant {
|
let vagrant = Vagrant {
|
||||||
path: utils::require("vagrant")?,
|
path: utils::require("vagrant")?,
|
||||||
};
|
};
|
||||||
|
|
||||||
print_separator("Vagrant");
|
print_separator("Vagrant");
|
||||||
println!("Collecting Vagrant boxes");
|
println!("{}", t!("Collecting Vagrant boxes"));
|
||||||
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
|
||||||
@@ -183,7 +184,11 @@ pub fn topgrade_vagrant_box(ctx: &ExecutionContext, vagrant_box: &VagrantBox) ->
|
|||||||
let mut _poweron = None;
|
let mut _poweron = None;
|
||||||
if !vagrant_box.initial_status.powered_on() {
|
if !vagrant_box.initial_status.powered_on() {
|
||||||
if !(ctx.config().vagrant_power_on().unwrap_or(true)) {
|
if !(ctx.config().vagrant_power_on().unwrap_or(true)) {
|
||||||
return Err(SkipStep(format!("Skipping powered off box {vagrant_box}")).into());
|
return Err(SkipStep(format!(
|
||||||
|
"{}",
|
||||||
|
t!("Skipping powered off box {vagrant_box}", vagrant_box = vagrant_box)
|
||||||
|
))
|
||||||
|
.into());
|
||||||
} else {
|
} else {
|
||||||
print_separator(seperator);
|
print_separator(seperator);
|
||||||
_poweron = Some(vagrant.temporary_power_on(vagrant_box, ctx)?);
|
_poweron = Some(vagrant.temporary_power_on(vagrant_box, ctx)?);
|
||||||
@@ -205,7 +210,7 @@ pub fn topgrade_vagrant_box(ctx: &ExecutionContext, vagrant_box: &VagrantBox) ->
|
|||||||
|
|
||||||
pub fn upgrade_vagrant_boxes(ctx: &ExecutionContext) -> Result<()> {
|
pub fn upgrade_vagrant_boxes(ctx: &ExecutionContext) -> Result<()> {
|
||||||
let vagrant = utils::require("vagrant")?;
|
let vagrant = utils::require("vagrant")?;
|
||||||
print_separator("Vagrant boxes");
|
print_separator(t!("Vagrant boxes"));
|
||||||
|
|
||||||
let outdated = Command::new(&vagrant)
|
let outdated = Command::new(&vagrant)
|
||||||
.args(["box", "outdated", "--global"])
|
.args(["box", "outdated", "--global"])
|
||||||
@@ -227,7 +232,7 @@ pub fn upgrade_vagrant_boxes(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
println!("No outdated boxes")
|
println!("{}", t!("No outdated boxes"))
|
||||||
} else {
|
} else {
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(&vagrant)
|
.execute(&vagrant)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use color_eyre::eyre::Context;
|
|||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
|
use crate::config::TmuxConfig;
|
||||||
|
use crate::config::TmuxSessionMode;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::HOME_DIR;
|
use crate::HOME_DIR;
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -14,6 +16,7 @@ use crate::{
|
|||||||
utils::{which, PathExt},
|
utils::{which, PathExt},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use rust_i18n::t;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::process::CommandExt as _;
|
use std::os::unix::process::CommandExt as _;
|
||||||
|
|
||||||
@@ -131,7 +134,7 @@ impl Tmux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_in_tmux(args: Vec<String>) -> Result<()> {
|
pub fn run_in_tmux(config: TmuxConfig) -> Result<()> {
|
||||||
let command = {
|
let command = {
|
||||||
let mut command = vec![
|
let mut command = vec![
|
||||||
String::from("env"),
|
String::from("env"),
|
||||||
@@ -144,25 +147,39 @@ pub fn run_in_tmux(args: Vec<String>) -> Result<()> {
|
|||||||
shell_words::join(command)
|
shell_words::join(command)
|
||||||
};
|
};
|
||||||
|
|
||||||
let tmux = Tmux::new(args);
|
let tmux = Tmux::new(config.args);
|
||||||
|
|
||||||
// Find an unused session and run `topgrade` in it with the current command's arguments.
|
// Find an unused session and run `topgrade` in it with the current command's arguments.
|
||||||
let session_name = "topgrade";
|
let session_name = "topgrade";
|
||||||
let window_name = "topgrade";
|
let window_name = "topgrade";
|
||||||
let session = tmux.new_unique_session(session_name, window_name, &command)?;
|
let session = tmux.new_unique_session(session_name, window_name, &command)?;
|
||||||
|
|
||||||
|
let is_inside_tmux = env::var("TMUX").is_ok();
|
||||||
|
let err = match config.session_mode {
|
||||||
|
TmuxSessionMode::AttachIfNotInSession => {
|
||||||
|
if is_inside_tmux {
|
||||||
// Only attach to the newly-created session if we're not currently in a tmux session.
|
// Only attach to the newly-created session if we're not currently in a tmux session.
|
||||||
if env::var("TMUX").is_err() {
|
println!("{}", t!("Topgrade launched in a new tmux session"));
|
||||||
let err = tmux.build().args(["attach-session", "-t", &session]).exec();
|
return Ok(());
|
||||||
Err(eyre!("{err}")).context("Failed to `execvp(3)` tmux")
|
|
||||||
} else {
|
} else {
|
||||||
println!("Topgrade launched in a new tmux session");
|
tmux.build().args(["attach-client", "-t", &session]).exec()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TmuxSessionMode::AttachAlways => {
|
||||||
|
if is_inside_tmux {
|
||||||
|
tmux.build().args(["switch-client", "-t", &session]).exec()
|
||||||
|
} else {
|
||||||
|
tmux.build().args(["attach-client", "-t", &session]).exec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Err(eyre!("{err}")).context("Failed to `execvp(3)` tmux")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_command(ctx: &ExecutionContext, window_name: &str, command: &str) -> Result<()> {
|
pub fn run_command(ctx: &ExecutionContext, window_name: &str, command: &str) -> Result<()> {
|
||||||
let tmux = Tmux::new(ctx.config().tmux_arguments()?);
|
let tmux = Tmux::new(ctx.config().tmux_config()?.args);
|
||||||
|
|
||||||
match ctx.get_tmux_session() {
|
match ctx.get_tmux_session() {
|
||||||
Some(session_name) => {
|
Some(session_name) => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use crate::{
|
|||||||
execution_context::ExecutionContext,
|
execution_context::ExecutionContext,
|
||||||
utils::{require, PathExt},
|
utils::{require, PathExt},
|
||||||
};
|
};
|
||||||
|
use rust_i18n::t;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
@@ -64,7 +65,7 @@ fn upgrade(command: &mut Executor, ctx: &ExecutionContext) -> Result<()> {
|
|||||||
if !status.success() {
|
if !status.success() {
|
||||||
return Err(TopgradeError::ProcessFailed(command.get_program(), status).into());
|
return Err(TopgradeError::ProcessFailed(command.get_program(), status).into());
|
||||||
} else {
|
} else {
|
||||||
println!("Plugins upgraded")
|
println!("{}", t!("Plugins upgraded"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ pub fn upgrade_ultimate_vimrc(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
let python = require("python3")?;
|
let python = require("python3")?;
|
||||||
let update_plugins = config_dir.join("update_plugins.py").require()?;
|
let update_plugins = config_dir.join("update_plugins.py").require()?;
|
||||||
|
|
||||||
print_separator("The Ultimate vimrc");
|
print_separator(t!("The Ultimate vimrc"));
|
||||||
|
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute(&git)
|
.execute(&git)
|
||||||
@@ -108,7 +109,7 @@ pub fn upgrade_vim(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
|
|
||||||
let output = Command::new(&vim).arg("--version").output_checked_utf8()?;
|
let output = Command::new(&vim).arg("--version").output_checked_utf8()?;
|
||||||
if !output.stdout.starts_with("VIM") {
|
if !output.stdout.starts_with("VIM") {
|
||||||
return Err(SkipStep(String::from("vim binary might be actually nvim")).into());
|
return Err(SkipStep(t!("vim binary might be actually nvim").to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let vimrc = vimrc()?;
|
let vimrc = vimrc()?;
|
||||||
|
|||||||
@@ -210,8 +210,7 @@ pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
let default_path = oh_my_zsh.join("custom");
|
let default_path = oh_my_zsh.join("custom");
|
||||||
debug!(
|
debug!(
|
||||||
"Running zsh returned {}. Using default path: {}",
|
"Running zsh returned {e}. Using default path: {}",
|
||||||
e,
|
|
||||||
default_path.display()
|
default_path.display()
|
||||||
);
|
);
|
||||||
default_path
|
default_path
|
||||||
@@ -229,7 +228,7 @@ pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> {
|
|||||||
custom_repos.remove(&oh_my_zsh);
|
custom_repos.remove(&oh_my_zsh);
|
||||||
ctx.run_type()
|
ctx.run_type()
|
||||||
.execute("zsh")
|
.execute("zsh")
|
||||||
.arg(&oh_my_zsh.join("tools/upgrade.sh"))
|
.arg(oh_my_zsh.join("tools/upgrade.sh"))
|
||||||
// oh-my-zsh returns 80 when it is already updated and no changes pulled
|
// oh-my-zsh returns 80 when it is already updated and no changes pulled
|
||||||
// in this update.
|
// in this update.
|
||||||
// See this comment: https://github.com/r-darwish/topgrade/issues/569#issuecomment-736756731
|
// See this comment: https://github.com/r-darwish/topgrade/issues/569#issuecomment-736756731
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use color_eyre::eyre::Context;
|
|||||||
use console::{style, Key, Term};
|
use console::{style, Key, Term};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use notify_rust::{Notification, Timeout};
|
use notify_rust::{Notification, Timeout};
|
||||||
|
use rust_i18n::t;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use which_crate::which;
|
use which_crate::which;
|
||||||
@@ -144,7 +145,7 @@ impl Terminal {
|
|||||||
self.term
|
self.term
|
||||||
.write_fmt(format_args!(
|
.write_fmt(format_args!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
style(format!("{key} failed:")).red().bold(),
|
style(format!("{}", t!("{key} failed:", key = key))).red().bold(),
|
||||||
message
|
message
|
||||||
))
|
))
|
||||||
.ok();
|
.ok();
|
||||||
@@ -174,10 +175,10 @@ impl Terminal {
|
|||||||
"{}: {}\n",
|
"{}: {}\n",
|
||||||
key,
|
key,
|
||||||
match result {
|
match result {
|
||||||
StepResult::Success => format!("{}", style("OK").bold().green()),
|
StepResult::Success => format!("{}", style(t!("OK")).bold().green()),
|
||||||
StepResult::Failure => format!("{}", style("FAILED").bold().red()),
|
StepResult::Failure => format!("{}", style(t!("FAILED")).bold().red()),
|
||||||
StepResult::Ignored => format!("{}", style("IGNORED").bold().yellow()),
|
StepResult::Ignored => format!("{}", style(t!("IGNORED")).bold().yellow()),
|
||||||
StepResult::Skipped(reason) => format!("{}: {}", style("SKIPPED").bold().blue(), reason),
|
StepResult::Skipped(reason) => format!("{}: {}", style(t!("SKIPPED")).bold().blue(), reason),
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
.ok();
|
.ok();
|
||||||
@@ -188,7 +189,7 @@ impl Terminal {
|
|||||||
self.term
|
self.term
|
||||||
.write_fmt(format_args!(
|
.write_fmt(format_args!(
|
||||||
"{}",
|
"{}",
|
||||||
style(format!("{question} (y)es/(N)o",)).yellow().bold()
|
style(format!("{question} {}", t!("(Y)es/(N)o"))).yellow().bold()
|
||||||
))
|
))
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
@@ -207,14 +208,14 @@ impl Terminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.set_title {
|
if self.set_title {
|
||||||
self.term.set_title("Topgrade - Awaiting user");
|
self.term.set_title(format!("Topgrade - {}", t!("Awaiting user")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.desktop_notification {
|
if self.desktop_notification {
|
||||||
self.notify_desktop(format!("{step_name} failed"), None);
|
self.notify_desktop(format!("{}", t!("{step_name} failed", step_name = step_name)), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let prompt_inner = style(format!("{}Retry? (y)es/(N)o/(s)hell/(q)uit", self.prefix))
|
let prompt_inner = style(format!("{}{}", self.prefix, t!("Retry? (y)es/(N)o/(s)hell/(q)uit")))
|
||||||
.yellow()
|
.yellow()
|
||||||
.bold();
|
.bold();
|
||||||
|
|
||||||
@@ -224,7 +225,10 @@ impl Terminal {
|
|||||||
match self.term.read_key() {
|
match self.term.read_key() {
|
||||||
Ok(Key::Char('y')) | Ok(Key::Char('Y')) => break Ok(true),
|
Ok(Key::Char('y')) | Ok(Key::Char('Y')) => break Ok(true),
|
||||||
Ok(Key::Char('s')) | Ok(Key::Char('S')) => {
|
Ok(Key::Char('s')) | Ok(Key::Char('S')) => {
|
||||||
println!("\n\nDropping you to shell. Fix what you need and then exit the shell.\n");
|
println!(
|
||||||
|
"\n\n{}\n",
|
||||||
|
t!("Dropping you to shell. Fix what you need and then exit the shell.")
|
||||||
|
);
|
||||||
if let Err(err) = run_shell().context("Failed to run shell") {
|
if let Err(err) = run_shell().context("Failed to run shell") {
|
||||||
self.term.write_fmt(format_args!("{err:?}\n{prompt_inner}")).ok();
|
self.term.write_fmt(format_args!("{err:?}\n{prompt_inner}")).ok();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
30
src/utils.rs
30
src/utils.rs
@@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
@@ -51,7 +52,11 @@ where
|
|||||||
debug!("Path {:?} exists", self.as_ref());
|
debug!("Path {:?} exists", self.as_ref());
|
||||||
Ok(self)
|
Ok(self)
|
||||||
} else {
|
} else {
|
||||||
Err(SkipStep(format!("Path {:?} doesn't exist", self.as_ref())).into())
|
Err(SkipStep(format!(
|
||||||
|
"{}",
|
||||||
|
t!("Path {path} doesn't exist", path = format!("{:?}", self.as_ref()))
|
||||||
|
))
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,9 +97,14 @@ pub fn require<T: AsRef<OsStr> + Debug>(binary_name: T) -> Result<PathBuf> {
|
|||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
which_crate::Error::CannotFindBinaryPath => {
|
which_crate::Error::CannotFindBinaryPath => Err(SkipStep(format!(
|
||||||
Err(SkipStep(format!("Cannot find {:?} in PATH", &binary_name)).into())
|
"{}",
|
||||||
}
|
t!(
|
||||||
|
"Cannot find {binary_name} in PATH",
|
||||||
|
binary_name = format!("{:?}", &binary_name)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
.into()),
|
||||||
_ => {
|
_ => {
|
||||||
panic!("Detecting {:?} failed: {}", &binary_name, e);
|
panic!("Detecting {:?} failed: {}", &binary_name, e);
|
||||||
}
|
}
|
||||||
@@ -123,7 +133,7 @@ pub fn hostname() -> Result<String> {
|
|||||||
match nix::unistd::gethostname() {
|
match nix::unistd::gethostname() {
|
||||||
Ok(os_str) => Ok(os_str
|
Ok(os_str) => Ok(os_str
|
||||||
.into_string()
|
.into_string()
|
||||||
.map_err(|_| SkipStep("Failed to get a UTF-8 encoded hostname".into()))?),
|
.map_err(|_| SkipStep(t!("Failed to get a UTF-8 encoded hostname").into()))?),
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +142,7 @@ pub fn hostname() -> Result<String> {
|
|||||||
pub fn hostname() -> Result<String> {
|
pub fn hostname() -> Result<String> {
|
||||||
Command::new("hostname")
|
Command::new("hostname")
|
||||||
.output_checked_utf8()
|
.output_checked_utf8()
|
||||||
.map_err(|err| SkipStep(format!("Failed to get hostname: {err}")).into())
|
.map_err(|err| SkipStep(t!("Failed to get hostname: {err}", err = err).to_string()).into())
|
||||||
.map(|output| output.stdout.trim().to_owned())
|
.map(|output| output.stdout.trim().to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +201,9 @@ pub mod merge_strategies {
|
|||||||
|
|
||||||
// Skip causes
|
// Skip causes
|
||||||
// TODO: Put them in a better place when we have more of them
|
// TODO: Put them in a better place when we have more of them
|
||||||
pub const REQUIRE_SUDO: &str = "Require sudo or counterpart but not found, skip";
|
pub fn get_require_sudo_string() -> String {
|
||||||
|
t!("Require sudo or counterpart but not found, skip").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
/// Return `Err(SkipStep)` if `python` is a Python 2 or shim.
|
/// Return `Err(SkipStep)` if `python` is a Python 2 or shim.
|
||||||
///
|
///
|
||||||
@@ -218,11 +230,11 @@ pub fn check_is_python_2_or_shim(python: PathBuf) -> Result<PathBuf> {
|
|||||||
.parse::<u32>()
|
.parse::<u32>()
|
||||||
.expect("Major version should be a valid number");
|
.expect("Major version should be a valid number");
|
||||||
if major_version == 2 {
|
if major_version == 2 {
|
||||||
return Err(SkipStep(format!("{} is a Python 2, skip.", python.display())).into());
|
return Err(SkipStep(t!("{python} is a Python 2, skip.", python = python.display()).to_string()).into());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No version number, is a shim
|
// No version number, is a shim
|
||||||
return Err(SkipStep(format!("{} is a Python shim, skip.", python.display())).into());
|
return Err(SkipStep(t!("{python} is a Python shim, skip.", python = python.display()).to_string()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(python)
|
Ok(python)
|
||||||
|
|||||||
Reference in New Issue
Block a user