diff --git a/.github/workflows/check-and-lint.yaml b/.github/workflows/check-and-lint.yaml index de30d19d..6f2155a9 100644 --- a/.github/workflows/check-and-lint.yaml +++ b/.github/workflows/check-and-lint.yaml @@ -4,88 +4,66 @@ on: branches: - main -name: CI -env: - RUST_VER: '1.60.0' - CROSS_VER: '0.2.4' - CARGO_NET_RETRY: 3 +name: Check and Lint jobs: + check: + name: Check + strategy: + matrix: + platform: [ ubuntu-latest, macos-latest, windows-latest ] + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + fmt: name: Rustfmt - runs-on: ubuntu-20.04 - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: '${{ env.RUST_VER }}' - targets: ${{ matrix.target }} - components: clippy, rustfmt - - - name: Run cargo fmt - run: | - cargo fmt --all -- --check - - main: - needs: fmt - name: ${{ matrix.target_name }} (check, clippy) - runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: - include: - - target: x86_64-linux-android - target_name: Android - use_cross: true - os: ubuntu-20.04 - - - target: x86_64-unknown-freebsd - target_name: FreeBSD - use_cross: true - os: ubuntu-20.04 - - - target: x86_64-unknown-linux-gnu - target_name: Linux - os: ubuntu-20.04 - - - target: x86_64-apple-darwin - target_name: macOS - os: macos-11 - - - target: x86_64-unknown-netbsd - target_name: NetBSD - use_cross: true - os: ubuntu-20.04 - - - target: x86_64-pc-windows-msvc - target_name: Windows - os: windows-2019 + platform: [ ubuntu-latest, macos-latest, windows-latest ] + runs-on: ${{ matrix.platform }} steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Rust - uses: dtolnay/rust-toolchain@master + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 with: - toolchain: '${{ env.RUST_VER }}' - targets: ${{ matrix.target }} - components: clippy, rustfmt - - - name: Setup Rust Cache - uses: Swatinem/rust-cache@v2 + profile: minimal + toolchain: stable + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 with: - prefix-key: ${{ matrix.target }} + command: fmt + args: --all -- --check - - name: Setup cross - if: matrix.use_cross == true - run: curl -fL --retry 3 https://github.com/cross-rs/cross/releases/download/v${{ env.CROSS_VER }}/cross-x86_64-unknown-linux-musl.tar.gz | tar vxz -C /usr/local/bin - - - name: Run cargo check - run: ${{ matrix.use_cross == true && 'cross' || 'cargo' }} check --locked --target ${{ matrix.target }} - - - name: Run cargo clippy - run: ${{ matrix.use_cross == true && 'cross' || 'cargo' }} clippy --locked --target ${{ matrix.target }} --all-features -- -D warnings + clippy: + name: Clippy + strategy: + matrix: + platform: [ ubuntu-latest, macos-latest, windows-latest ] + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: clippy + override: true + - uses: actions-rs/cargo@v1.0.1 + with: + command: clippy + args: --all-targets --locked -- -D warnings + name: Clippy Output + - uses: actions-rs/cargo@v1.0.1 + with: + command: clippy + args: --all-targets --locked --all-features -- -D warnings + name: Clippy (All features) Output diff --git a/Cargo.lock b/Cargo.lock index 81de8e5e..0aa46ae7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - [[package]] name = "adler" version = "1.0.2" @@ -30,9 +21,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -46,6 +37,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + [[package]] name = "async-broadcast" version = "0.4.1" @@ -63,22 +60,22 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" dependencies = [ - "concurrent-queue 1.2.4", + "concurrent-queue", "event-listener", "futures-core", ] [[package]] name = "async-executor" -version = "1.5.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" dependencies = [ - "async-lock", "async-task", - "concurrent-queue 2.0.0", + "concurrent-queue", "fastrand", "futures-lite", + "once_cell", "slab", ] @@ -90,7 +87,7 @@ checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" dependencies = [ "async-lock", "autocfg", - "concurrent-queue 1.2.4", + "concurrent-queue", "futures-lite", "libc", "log", @@ -157,21 +154,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backtrace" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" version = "0.13.1" @@ -204,9 +186,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cache-padded" @@ -216,9 +198,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cc" -version = "1.0.77" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" [[package]] name = "cfg-if" @@ -228,9 +210,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ "iana-time-zone", "js-sys", @@ -290,33 +272,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "color-eyre" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" -dependencies = [ - "backtrace", - "color-spantrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", - "tracing-error", -] - -[[package]] -name = "color-spantrace" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" -dependencies = [ - "once_cell", - "owo-colors", - "tracing-core", - "tracing-error", -] - [[package]] name = "concurrent-queue" version = "1.2.4" @@ -326,15 +281,6 @@ dependencies = [ "cache-padded", ] -[[package]] -name = "concurrent-queue" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "console" version = "0.15.2" @@ -366,18 +312,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if", ] [[package]] name = "cxx" -version = "1.0.82" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453" +checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" dependencies = [ "cc", "cxxbridge-flags", @@ -387,9 +333,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.82" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0" +checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" dependencies = [ "cc", "codespan-reporting", @@ -402,15 +348,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.82" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71" +checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" [[package]] name = "cxxbridge-macro" -version = "1.0.82" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" +checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" dependencies = [ "proc-macro2", "quote", @@ -526,22 +472,25 @@ dependencies = [ "syn", ] +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" -[[package]] -name = "eyre" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" -dependencies = [ - "indenter", - "once_cell", -] - [[package]] name = "fastrand" version = "1.8.0" @@ -703,12 +652,6 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" - [[package]] name = "glob" version = "0.3.0" @@ -817,10 +760,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] -name = "hyper" -version = "0.14.23" +name = "humantime" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "hyper" +version = "0.14.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" dependencies = [ "bytes", "futures-channel", @@ -842,9 +794,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" dependencies = [ "http", "hyper", @@ -887,17 +839,11 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", @@ -926,9 +872,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itoa" @@ -995,7 +941,7 @@ dependencies = [ "dirs-next", "objc-foundation", "objc_id", - "time 0.3.17", + "time 0.3.16", ] [[package]] @@ -1007,15 +953,6 @@ dependencies = [ "libc", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - [[package]] name = "memchr" version = "2.5.0" @@ -1116,16 +1053,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num-integer" version = "0.1.45" @@ -1147,14 +1074,23 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -1190,15 +1126,6 @@ dependencies = [ "objc", ] -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.16.0" @@ -1227,21 +1154,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "owo-colors" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" [[package]] name = "parking" @@ -1319,9 +1234,19 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] [[package]] name = "proc-macro-crate" @@ -1367,6 +1292,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-xml" version = "0.22.0" @@ -1455,20 +1386,11 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -1481,9 +1403,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.13" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" dependencies = [ "base64", "bytes", @@ -1543,12 +1465,6 @@ dependencies = [ "ordered-multimap", ] -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - [[package]] name = "rustls" version = "0.20.7" @@ -1662,9 +1578,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" dependencies = [ "itoa", "ryu", @@ -1709,15 +1625,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - [[package]] name = "shell-words" version = "1.1.0" @@ -1839,6 +1746,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sys-info" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "tar" version = "0.4.38" @@ -1896,9 +1813,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.2" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" @@ -1920,15 +1837,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - [[package]] name = "time" version = "0.1.44" @@ -1942,11 +1850,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca" dependencies = [ "itoa", + "libc", + "num_threads", "serde", "time-core", "time-macros", @@ -1960,9 +1870,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b" dependencies = [ "time-core", ] @@ -2036,22 +1946,23 @@ dependencies = [ [[package]] name = "topgrade" -version = "10.2.0" +version = "10.1.2" dependencies = [ + "anyhow", "cfg-if", "chrono", "clap", - "color-eyre", "console", "directories", "futures", "glob", "home", "lazy_static", - "libc", + "log", "nix 0.24.2", "notify-rust", "parselnk", + "pretty_env_logger", "regex", "rust-ini", "self_update", @@ -2060,12 +1971,11 @@ dependencies = [ "shell-words", "shellexpand", "strum 0.24.1", + "sys-info", "tempfile", "thiserror", "tokio", "toml", - "tracing", - "tracing-subscriber", "walkdir", "which", "winapi", @@ -2084,7 +1994,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2108,47 +2017,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-error" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" -dependencies = [ - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "time 0.3.17", - "tracing", - "tracing-core", - "tracing-log", ] [[package]] @@ -2217,12 +2085,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "version_check" version = "0.9.4" @@ -2592,9 +2454,9 @@ dependencies = [ [[package]] name = "zbus_names" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69bb79b44e1901ed8b217e485d0f01991aec574479b68cb03415f142bc7ae67" +checksum = "41a408fd8a352695690f53906dc7fd036be924ec51ea5e05666ff42685ed0af5" dependencies = [ "serde", "static_assertions", @@ -2611,14 +2473,14 @@ dependencies = [ "crc32fast", "crossbeam-utils", "flate2", - "time 0.3.17", + "time 0.3.16", ] [[package]] name = "zvariant" -version = "3.8.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c817f416f05fcbc833902f1e6064b72b1778573978cfeac54731451ccc9e207" +checksum = "b794fb7f59af4105697b0449ba31731ee5dbb3e773a17dbdf3d36206ea1b1644" dependencies = [ "byteorder", "enumflags2", @@ -2630,9 +2492,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "3.8.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd24fffd02794a76eb10109de463444064c88f5adb9e9d1a78488adc332bfef" +checksum = "dd58d4b6c8e26d3dd2149c8c40c6613ef6451b9885ff1296d1ac86c388351a54" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 7ae19233..63c321ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,9 @@ name = "topgrade" description = "Upgrade all the things" categories = ["os"] keywords = ["upgrade", "update"] -license = "GPL-3.0" -# license-file = "LICENSE" +license-file = "LICENSE" repository = "https://github.com/topgrade-rs/topgrade" -version = "10.2.0" +version = "10.1.2" authors = ["Roey Darwish Dror ", "Thomas Schönauer "] exclude = ["doc/screenshot.gif"] edition = "2021" @@ -28,38 +27,29 @@ toml = "0.5" which_crate = { version = "~4.1", package = "which" } shellexpand = "~2.1" clap = { version = "~3.1", features = ["cargo", "derive"] } +log = "~0.4" walkdir = "~2.3" console = "~0.15" lazy_static = "~1.4" chrono = "~0.4" +pretty_env_logger = "~0.4" glob = "~0.3" strum = { version = "~0.24", features = ["derive"] } thiserror = "~1.0" +anyhow = "~1.0" tempfile = "~3.2" cfg-if = "~1.0" tokio = { version = "~1.5", features = ["process", "rt-multi-thread"] } futures = "~0.3" regex = "~1.5" +sys-info = "~0.9" semver = "~1.0" shell-words = "~1.1" -color-eyre = "0.6.2" -tracing = { version = "0.1.37", features = ["attributes", "log"] } -tracing-subscriber = { version = "0.3.16", features = ["env-filter", "time"] } [target.'cfg(target_os = "macos")'.dependencies] notify-rust = "~4.5" -[package.metadata.generate-rpm] -assets = [{source = "target/release/topgrade", dest="/usr/bin/topgrade"}] - -[package.metadata.generate-rpm.requires] -git = "*" - -[package.metadata.deb] -depends = "$auto,git" - [target.'cfg(unix)'.dependencies] -libc = "~0.2" nix = "~0.24" rust-ini = "~0.18" self_update_crate = { version = "~0.30", default-features = false, optional = true, package = "self_update", features = ["archive-tar", "compression-flate2", "rustls"] } diff --git a/Cross.toml b/Cross.toml deleted file mode 100644 index 33be5cdd..00000000 --- a/Cross.toml +++ /dev/null @@ -1,4 +0,0 @@ -# Workaround for: https://github.com/cross-rs/cross/issues/1100 -# TODO: Remove this file altogether once a new version of cross (after v0.2.4) is released. -[target.x86_64-unknown-freebsd.env] -passthrough = ["AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar"] diff --git a/README.md b/README.md index db8f5cfe..aa5bedf1 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ To remedy this, **Topgrade** detects which tools you use and runs the appropriat Other systems users can either use `cargo install` or the compiled binaries from the release page. The compiled binaries contain a self-upgrading feature. -Topgrade requires Rust 1.60 or above. +Topgrade requires Rust 1.51 or above. ## Usage diff --git a/clippy.toml b/clippy.toml deleted file mode 100644 index cc1ff16c..00000000 --- a/clippy.toml +++ /dev/null @@ -1,5 +0,0 @@ -disallowed-methods = [ - { path = "std::process::Command::output", reason = "Use `output_checked[_with][_utf8]`" }, - { path = "std::process::Command::spawn", reason = "Use `spawn_checked`" }, - { path = "std::process::Command::status", reason = "Use `status_checked`" }, -] diff --git a/src/command.rs b/src/command.rs deleted file mode 100644 index d8a87e25..00000000 --- a/src/command.rs +++ /dev/null @@ -1,244 +0,0 @@ -//! Utilities for running commands and providing user-friendly error messages. - -use std::fmt::Display; -use std::process::Child; -use std::process::{Command, ExitStatus, Output}; - -use color_eyre::eyre; -use color_eyre::eyre::eyre; -use color_eyre::eyre::Context; - -use crate::error::TopgradeError; - -/// Like [`Output`], but UTF-8 decoded. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Utf8Output { - pub status: ExitStatus, - pub stdout: String, - pub stderr: String, -} - -impl TryFrom for Utf8Output { - type Error = eyre::Error; - - fn try_from(Output { status, stdout, stderr }: Output) -> Result { - let stdout = String::from_utf8(stdout).map_err(|err| { - eyre!( - "Stdout contained invalid UTF-8: {}", - String::from_utf8_lossy(err.as_bytes()) - ) - })?; - let stderr = String::from_utf8(stderr).map_err(|err| { - eyre!( - "Stderr contained invalid UTF-8: {}", - String::from_utf8_lossy(err.as_bytes()) - ) - })?; - - Ok(Utf8Output { status, stdout, stderr }) - } -} - -impl TryFrom<&Output> for Utf8Output { - type Error = eyre::Error; - - fn try_from(Output { status, stdout, stderr }: &Output) -> Result { - let stdout = String::from_utf8(stdout.to_vec()).map_err(|err| { - eyre!( - "Stdout contained invalid UTF-8: {}", - String::from_utf8_lossy(err.as_bytes()) - ) - })?; - let stderr = String::from_utf8(stderr.to_vec()).map_err(|err| { - eyre!( - "Stderr contained invalid UTF-8: {}", - String::from_utf8_lossy(err.as_bytes()) - ) - })?; - let status = *status; - - Ok(Utf8Output { status, stdout, stderr }) - } -} - -impl Display for Utf8Output { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.stdout) - } -} - -/// Extension trait for [`Command`], adding helpers to gather output while checking the exit -/// status. -/// -/// These also give us significantly better error messages, which include: -/// -/// 1. The command and arguments that were executed, escaped with familiar `sh` syntax. -/// 2. The exit status of the command or the signal that killed it. -/// 3. If we were capturing the output of the command, rather than forwarding it to the user's -/// stdout/stderr, the error message includes the command's stdout and stderr output. -/// -/// Additionally, executing commands with these methods will log the command at debug-level, -/// useful when gathering error reports. -pub trait CommandExt { - type Child; - - /// Like [`Command::output`], but checks the exit status and provides nice error messages. - /// - /// Returns an `Err` if the command failed to execute or returned a non-zero exit code. - #[track_caller] - fn output_checked(&mut self) -> eyre::Result { - self.output_checked_with(|output: &Output| if output.status.success() { Ok(()) } else { Err(()) }) - } - - /// Like [`output_checked`], but also decodes Stdout and Stderr as UTF-8. - /// - /// Returns an `Err` if the command failed to execute, returned a non-zero exit code, or if the - /// output contains invalid UTF-8. - #[track_caller] - fn output_checked_utf8(&mut self) -> eyre::Result { - let output = self.output_checked()?; - output.try_into() - } - - /// Like [`output_checked`] but a closure determines if the command failed instead of - /// [`ExitStatus::success`]. - /// - /// Returns an `Err` if the command failed to execute or if `succeeded` returns an `Err`. - /// (This lets the caller substitute their own notion of "success" instead of assuming - /// non-zero exit codes indicate success.) - #[track_caller] - fn output_checked_with(&mut self, succeeded: impl Fn(&Output) -> Result<(), ()>) -> eyre::Result; - - /// Like [`output_checked_with`], but also decodes Stdout and Stderr as UTF-8. - /// - /// Returns an `Err` if the command failed to execute, if `succeeded` returns an `Err`, or if - /// the output contains invalid UTF-8. - #[track_caller] - fn output_checked_with_utf8( - &mut self, - succeeded: impl Fn(&Utf8Output) -> Result<(), ()>, - ) -> eyre::Result { - // This decodes the Stdout and Stderr as UTF-8 twice... - let output = - self.output_checked_with(|output| output.try_into().map_err(|_| ()).and_then(|o| succeeded(&o)))?; - output.try_into() - } - - /// Like [`Command::status`], but gives a nice error message if the status is unsuccessful - /// rather than returning the [`ExitStatus`]. - /// - /// Returns `Ok` if the command executes successfully, returns `Err` if the command fails to - /// execute or returns a non-zero exit code. - #[track_caller] - fn status_checked(&mut self) -> eyre::Result<()> { - self.status_checked_with(|status| if status.success() { Ok(()) } else { Err(()) }) - } - - /// Like [`status_checked`], but gives a nice error message if the status is unsuccessful - /// rather than returning the [`ExitStatus`]. - /// - /// Returns `Ok` if the command executes successfully, returns `Err` if the command fails to - /// execute or if `succeeded` returns an `Err`. - /// (This lets the caller substitute their own notion of "success" instead of assuming - /// non-zero exit codes indicate success.) - #[track_caller] - fn status_checked_with(&mut self, succeeded: impl Fn(ExitStatus) -> Result<(), ()>) -> eyre::Result<()>; - - /// Like [`Command::spawn`], but gives a nice error message if the command fails to - /// execute. - #[track_caller] - fn spawn_checked(&mut self) -> eyre::Result; -} - -impl CommandExt for Command { - type Child = Child; - - fn output_checked_with(&mut self, succeeded: impl Fn(&Output) -> Result<(), ()>) -> eyre::Result { - let command = log(self); - - // This is where we implement `output_checked`, which is what we prefer to use instead of - // `output`, so we allow `Command::output` here. - #[allow(clippy::disallowed_methods)] - let output = self - .output() - .with_context(|| format!("Failed to execute `{command}`"))?; - - if succeeded(&output).is_ok() { - Ok(output) - } else { - let mut message = format!("Command failed: `{command}`"); - let stderr = String::from_utf8_lossy(&output.stderr); - let stdout = String::from_utf8_lossy(&output.stdout); - - let stdout_trimmed = stdout.trim(); - if !stdout_trimmed.is_empty() { - message.push_str(&format!("\n\nStdout:\n{stdout_trimmed}")); - } - let stderr_trimmed = stderr.trim(); - if !stderr_trimmed.is_empty() { - message.push_str(&format!("\n\nStderr:\n{stderr_trimmed}")); - } - - let (program, _) = get_program_and_args(self); - let err = TopgradeError::ProcessFailedWithOutput(program, output.status, stderr.into_owned()); - - let ret = Err(err).with_context(|| message); - tracing::debug!("Command failed: {ret:?}"); - ret - } - } - - fn status_checked_with(&mut self, succeeded: impl Fn(ExitStatus) -> Result<(), ()>) -> eyre::Result<()> { - let command = log(self); - let message = format!("Failed to execute `{command}`"); - - // This is where we implement `status_checked`, which is what we prefer to use instead of - // `status`, so we allow `Command::status` here. - #[allow(clippy::disallowed_methods)] - let status = self.status().with_context(|| message.clone())?; - - if succeeded(status).is_ok() { - Ok(()) - } else { - let (program, _) = get_program_and_args(self); - let err = TopgradeError::ProcessFailed(program, status); - let ret = Err(err).with_context(|| format!("Command failed: `{command}`")); - tracing::debug!("Command failed: {ret:?}"); - ret - } - } - - fn spawn_checked(&mut self) -> eyre::Result { - let command = log(self); - let message = format!("Failed to execute `{command}`"); - - // This is where we implement `spawn_checked`, which is what we prefer to use instead of - // `spawn`, so we allow `Command::spawn` here. - #[allow(clippy::disallowed_methods)] - { - self.spawn().with_context(|| message.clone()) - } - } -} - -fn get_program_and_args(cmd: &Command) -> (String, String) { - // We're not doing anything weird with commands that are invalid UTF-8 so this is fine. - let program = cmd.get_program().to_string_lossy().into_owned(); - let args = shell_words::join(cmd.get_args().map(|arg| arg.to_string_lossy())); - (program, args) -} - -fn format_program_and_args(cmd: &Command) -> String { - let (program, args) = get_program_and_args(cmd); - if args.is_empty() { - program - } else { - format!("{program} {args}") - } -} - -fn log(cmd: &Command) -> String { - let command = format_program_and_args(cmd); - tracing::debug!("Executing command `{command}`"); - command -} diff --git a/src/config.rs b/src/config.rs index b98910d9..f032c2e2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,24 +1,21 @@ #![allow(dead_code)] +use anyhow::Context; +use anyhow::Result; +use clap::{ArgEnum, Parser}; +use directories::BaseDirs; +use log::debug; +use regex::Regex; +use serde::Deserialize; use std::collections::BTreeMap; use std::fs::write; use std::path::PathBuf; use std::process::Command; use std::{env, fs}; - -use clap::{ArgEnum, Parser}; -use color_eyre::eyre; -use color_eyre::eyre::Context; -use color_eyre::eyre::Result; -use directories::BaseDirs; -use regex::Regex; -use serde::Deserialize; use strum::{EnumIter, EnumString, EnumVariantNames, IntoEnumIterator}; -use tracing::debug; +use sys_info::hostname; use which_crate::which; -use crate::command::CommandExt; - -use super::utils::{editor, hostname}; +use super::utils::editor; pub static EXAMPLE_CONFIG: &str = include_str!("../config.example.toml"); @@ -351,12 +348,12 @@ impl ConfigFile { }; let contents = fs::read_to_string(&config_path).map_err(|e| { - tracing::error!("Unable to read {}", config_path.display()); + log::error!("Unable to read {}", config_path.display()); e })?; let mut result: Self = toml::from_str(&contents).map_err(|e| { - tracing::error!("Failed to deserialize {}", config_path.display()); + log::error!("Failed to deserialize {}", config_path.display()); e })?; @@ -392,8 +389,9 @@ impl ConfigFile { Command::new(command) .args(args) .arg(config_path) - .status_checked() - .context("Failed to open configuration file editor") + .spawn() + .and_then(|mut p| p.wait())?; + Ok(()) } } @@ -441,7 +439,7 @@ pub struct CommandLineArgs { #[clap(long = "env", value_name = "NAME=VALUE", multiple_values = true)] env: Vec, - /// Output debug logs. Alias for `--log-filter debug`. + /// Output logs #[clap(short = 'v', long = "verbose")] pub verbose: bool, @@ -479,12 +477,6 @@ pub struct CommandLineArgs { /// Show the reason for skipped steps #[clap(long = "show-skipped")] show_skipped: bool, - - /// Tracing filter directives. - /// - /// See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html - #[clap(long, default_value = "info")] - pub log_filter: String, } impl CommandLineArgs { @@ -499,14 +491,6 @@ impl CommandLineArgs { pub fn env_variables(&self) -> &Vec { &self.env } - - pub fn tracing_filter_directives(&self) -> String { - if self.verbose { - "debug".into() - } else { - self.log_filter.clone() - } - } } /// Represents the application configuration @@ -530,11 +514,11 @@ impl Config { ConfigFile::read(base_dirs, opt.config.clone()).unwrap_or_else(|e| { // Inform the user about errors when loading the configuration, // but fallback to the default config to at least attempt to do something - tracing::error!("failed to load configuration: {}", e); + log::error!("failed to load configuration: {}", e); ConfigFile::default() }) } else { - tracing::debug!("Configuration directory {} does not exist", config_directory.display()); + log::debug!("Configuration directory {} does not exist", config_directory.display()); ConfigFile::default() }; @@ -649,7 +633,7 @@ impl Config { } /// Extra Tmux arguments - pub fn tmux_arguments(&self) -> eyre::Result> { + pub fn tmux_arguments(&self) -> anyhow::Result> { let args = &self.config_file.tmux_arguments.as_deref().unwrap_or_default(); shell_words::split(args) // The only time the parse failed is in case of a missing close quote. diff --git a/src/ctrlc/windows.rs b/src/ctrlc/windows.rs index b2cf9e15..b5148ee7 100644 --- a/src/ctrlc/windows.rs +++ b/src/ctrlc/windows.rs @@ -16,6 +16,6 @@ extern "system" fn handler(ctrl_type: DWORD) -> BOOL { pub fn set_handler() { if 0 == unsafe { SetConsoleCtrlHandler(Some(handler), TRUE) } { - tracing::error!("Cannot set a control C handler") + log::error!("Cannot set a control C handler") } } diff --git a/src/error.rs b/src/error.rs index def711d7..5ec6ce75 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,11 +4,11 @@ use thiserror::Error; #[derive(Error, Debug, PartialEq, Eq)] pub enum TopgradeError { - #[error("`{0}` failed: {1}")] - ProcessFailed(String, ExitStatus), + #[error("{0}")] + ProcessFailed(ExitStatus), - #[error("`{0}` failed: {1}")] - ProcessFailedWithOutput(String, ExitStatus, String), + #[error("{0}: {1}")] + ProcessFailedWithOutput(ExitStatus, String), #[error("Sudo is required for this step")] #[allow(dead_code)] diff --git a/src/execution_context.rs b/src/execution_context.rs index 661e8bc8..6fe89e7a 100644 --- a/src/execution_context.rs +++ b/src/execution_context.rs @@ -3,10 +3,9 @@ use crate::executor::RunType; use crate::git::Git; use crate::utils::require_option; use crate::{config::Config, executor::Executor}; -use color_eyre::eyre::Result; +use anyhow::Result; use directories::BaseDirs; use std::path::{Path, PathBuf}; -use std::sync::Mutex; pub struct ExecutionContext<'a> { run_type: RunType, @@ -14,10 +13,6 @@ pub struct ExecutionContext<'a> { git: &'a Git, config: &'a Config, base_dirs: &'a BaseDirs, - /// Name of a tmux session to execute commands in, if any. - /// This is used in `./steps/remote/ssh.rs`, where we want to run `topgrade` in a new - /// tmux window for each remote. - tmux_session: Mutex>, } impl<'a> ExecutionContext<'a> { @@ -27,14 +22,13 @@ impl<'a> ExecutionContext<'a> { git: &'a Git, config: &'a Config, base_dirs: &'a BaseDirs, - ) -> Self { - Self { + ) -> ExecutionContext<'a> { + ExecutionContext { run_type, sudo, git, config, base_dirs, - tmux_session: Mutex::new(None), } } @@ -73,12 +67,4 @@ impl<'a> ExecutionContext<'a> { pub fn base_dirs(&self) -> &BaseDirs { self.base_dirs } - - pub fn set_tmux_session(&self, session_name: String) { - self.tmux_session.lock().unwrap().replace(session_name); - } - - pub fn get_tmux_session(&self) -> Option { - self.tmux_session.lock().unwrap().clone() - } } diff --git a/src/executor.rs b/src/executor.rs index a403a65e..b68b30a4 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,14 +1,11 @@ //! Utilities for command execution +use crate::error::{DryRun, TopgradeError}; +use crate::utils::{Check, CheckWithCodes}; +use anyhow::Result; +use log::{debug, trace}; use std::ffi::{OsStr, OsString}; use std::path::Path; -use std::process::{Child, Command, ExitStatus, Output}; - -use color_eyre::eyre; -use color_eyre::eyre::Result; -use tracing::debug; - -use crate::command::CommandExt; -use crate::error::DryRun; +use std::process::{Child, Command, ExitStatus}; /// An enum telling whether Topgrade should perform dry runs or actually perform the steps. #[derive(Clone, Copy, Debug)] @@ -59,16 +56,6 @@ pub enum Executor { } impl Executor { - /// Get the name of the program being run. - /// - /// Will give weird results for non-UTF-8 programs; see `to_string_lossy()`. - pub fn get_program(&self) -> String { - match self { - Executor::Wet(c) => c.get_program().to_string_lossy().into_owned(), - Executor::Dry(c) => c.program.to_string_lossy().into_owned(), - } - } - /// See `std::process::Command::arg` pub fn arg>(&mut self, arg: S) -> &mut Executor { match self { @@ -152,7 +139,7 @@ impl Executor { let result = match self { Executor::Wet(c) => { debug!("Running {:?}", c); - c.spawn_checked().map(ExecutorChild::Wet)? + c.spawn().map(ExecutorChild::Wet)? } Executor::Dry(c) => { c.dry_run(); @@ -166,7 +153,7 @@ impl Executor { /// See `std::process::Command::output` pub fn output(&mut self) -> Result { match self { - Executor::Wet(c) => Ok(ExecutorOutput::Wet(c.output_checked()?)), + Executor::Wet(c) => Ok(ExecutorOutput::Wet(c.output()?)), Executor::Dry(c) => { c.dry_run(); Ok(ExecutorOutput::Dry) @@ -174,28 +161,23 @@ impl Executor { } } - /// An extension of `status_checked` that allows you to set a sequence of codes + /// A convinence method for `spawn().wait().check()`. + /// Returns an error if something went wrong during the execution or if the + /// process exited with failure. + pub fn check_run(&mut self) -> Result<()> { + self.spawn()?.wait()?.check() + } + + /// An extension of `check_run` that allows you to set a sequence of codes /// that can indicate success of a script - #[cfg_attr(windows, allow(dead_code))] - pub fn status_checked_with_codes(&mut self, codes: &[i32]) -> Result<()> { - match self { - Executor::Wet(c) => c.status_checked_with(|status| { - if status.success() || status.code().as_ref().map(|c| codes.contains(c)).unwrap_or(false) { - Ok(()) - } else { - Err(()) - } - }), - Executor::Dry(c) => { - c.dry_run(); - Ok(()) - } - } + #[allow(dead_code)] + pub fn check_run_with_codes(&mut self, codes: &[i32]) -> Result<()> { + self.spawn()?.wait()?.check_with_codes(codes) } } pub enum ExecutorOutput { - Wet(Output), + Wet(std::process::Output), Dry, } @@ -232,33 +214,78 @@ pub enum ExecutorChild { Dry, } -impl CommandExt for Executor { - type Child = ExecutorChild; +impl ExecutorChild { + /// See `std::process::Child::wait` + pub fn wait(&mut self) -> Result { + let result = match self { + ExecutorChild::Wet(c) => c.wait().map(ExecutorExitStatus::Wet)?, + ExecutorChild::Dry => ExecutorExitStatus::Dry, + }; - // TODO: It might be nice to make `output_checked_with` return something that has a - // variant for wet/dry runs. - - fn output_checked_with(&mut self, succeeded: impl Fn(&Output) -> Result<(), ()>) -> eyre::Result { - match self { - Executor::Wet(c) => c.output_checked_with(succeeded), - Executor::Dry(c) => { - c.dry_run(); - Err(DryRun().into()) - } - } - } - - fn status_checked_with(&mut self, succeeded: impl Fn(ExitStatus) -> Result<(), ()>) -> eyre::Result<()> { - match self { - Executor::Wet(c) => c.status_checked_with(succeeded), - Executor::Dry(c) => { - c.dry_run(); - Ok(()) - } - } - } - - fn spawn_checked(&mut self) -> eyre::Result { - self.spawn() + Ok(result) + } +} + +/// The Result of wait. Contains an actual `std::process::ExitStatus` if executed by a wet command. +pub enum ExecutorExitStatus { + Wet(ExitStatus), + Dry, +} + +impl CheckWithCodes for ExecutorExitStatus { + fn check_with_codes(self, codes: &[i32]) -> Result<()> { + match self { + ExecutorExitStatus::Wet(e) => e.check_with_codes(codes), + ExecutorExitStatus::Dry => Ok(()), + } + } +} + +/// Extension methods for `std::process::Command` +pub trait CommandExt { + /// Run the command, wait for it to complete, check the return code and decode the output as UTF-8. + fn check_output(&mut self) -> Result; + fn string_output(&mut self) -> Result; +} + +impl CommandExt for Command { + fn check_output(&mut self) -> Result { + let output = self.output()?; + trace!("Output of {:?}: {:?}", self, output); + let status = output.status; + if !status.success() { + let stderr = String::from_utf8(output.stderr).unwrap_or_default(); + return Err(TopgradeError::ProcessFailedWithOutput(status, stderr).into()); + } + Ok(String::from_utf8(output.stdout)?) + } + + fn string_output(&mut self) -> Result { + let output = self.output()?; + trace!("Output of {:?}: {:?}", self, output); + Ok(String::from_utf8(output.stdout)?) + } +} + +impl CommandExt for Executor { + fn check_output(&mut self) -> Result { + let output = match self.output()? { + ExecutorOutput::Wet(output) => output, + ExecutorOutput::Dry => return Err(DryRun().into()), + }; + let status = output.status; + if !status.success() { + let stderr = String::from_utf8(output.stderr).unwrap_or_default(); + return Err(TopgradeError::ProcessFailedWithOutput(status, stderr).into()); + } + Ok(String::from_utf8(output.stdout)?) + } + + fn string_output(&mut self) -> Result { + let output = match self.output()? { + ExecutorOutput::Wet(output) => output, + ExecutorOutput::Dry => return Err(DryRun().into()), + }; + Ok(String::from_utf8(output.stdout)?) } } diff --git a/src/main.rs b/src/main.rs index f4dbcecf..d08d207e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,11 +4,12 @@ use std::env; use std::io; use std::process::exit; +use anyhow::{anyhow, Result}; use clap::{crate_version, Parser}; -use color_eyre::eyre::Context; -use color_eyre::eyre::{eyre, Result}; use console::Key; -use tracing::debug; +use log::debug; +use log::LevelFilter; +use pretty_env_logger::formatted_timed_builder; use self::config::{CommandLineArgs, Config, Step}; use self::error::StepFailed; @@ -17,7 +18,6 @@ use self::error::Upgraded; use self::steps::{remote::*, *}; use self::terminal::*; -mod command; mod config; mod ctrlc; mod error; @@ -34,15 +34,12 @@ mod terminal; mod utils; fn run() -> Result<()> { - color_eyre::install()?; ctrlc::set_handler(); - let base_dirs = directories::BaseDirs::new().ok_or_else(|| eyre!("No base directories"))?; + let base_dirs = directories::BaseDirs::new().ok_or_else(|| anyhow!("No base directories"))?; let opt = CommandLineArgs::parse(); - install_tracing(&opt.tracing_filter_directives())?; - for env in opt.env_variables() { let mut splitted = env.split('='); let var = splitted.next().unwrap(); @@ -50,6 +47,14 @@ fn run() -> Result<()> { env::set_var(var, value); } + let mut builder = formatted_timed_builder(); + + if opt.verbose { + builder.filter(Some("topgrade"), LevelFilter::Trace); + } + + builder.init(); + if opt.edit_config() { Config::edit(&base_dirs)?; return Ok(()); @@ -74,8 +79,7 @@ fn run() -> Result<()> { if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() { #[cfg(unix)] { - tmux::run_in_tmux(config.tmux_arguments()?)?; - return Ok(()); + tmux::run_in_tmux(config.tmux_arguments()?); } } @@ -202,7 +206,7 @@ fn run() -> Result<()> { #[cfg(target_os = "freebsd")] runner.execute(Step::Pkg, "FreeBSD Packages", || { - freebsd::upgrade_packages(&ctx, sudo.as_ref(), run_type) + freebsd::upgrade_packages(sudo.as_ref(), run_type) })?; #[cfg(target_os = "openbsd")] @@ -292,7 +296,7 @@ fn run() -> Result<()> { runner.execute(Step::Shell, "zi", || zsh::run_zi(&base_dirs, run_type))?; runner.execute(Step::Shell, "zim", || zsh::run_zim(&base_dirs, run_type))?; runner.execute(Step::Shell, "oh-my-zsh", || zsh::run_oh_my_zsh(&ctx))?; - runner.execute(Step::Shell, "fisher", || unix::run_fisher(run_type))?; + runner.execute(Step::Shell, "fisher", || unix::run_fisher(&base_dirs, run_type))?; runner.execute(Step::Shell, "bash-it", || unix::run_bashit(&ctx))?; runner.execute(Step::Shell, "oh-my-fish", || unix::run_oh_my_fish(&ctx))?; runner.execute(Step::Shell, "fish-plug", || unix::run_fish_plug(&ctx))?; @@ -323,8 +327,7 @@ fn run() -> Result<()> { runner.execute(Step::Choosenim, "choosenim", || generic::run_choosenim(&ctx))?; runner.execute(Step::Cargo, "cargo", || generic::run_cargo_update(&ctx))?; runner.execute(Step::Flutter, "Flutter", || generic::run_flutter_upgrade(run_type))?; - runner.execute(Step::Go, "go-global-update", || go::run_go_global_update(run_type))?; - runner.execute(Step::Go, "gup", || go::run_go_gup(run_type))?; + runner.execute(Step::Go, "Go", || generic::run_go(run_type))?; runner.execute(Step::Emacs, "Emacs", || emacs.upgrade(&ctx))?; runner.execute(Step::Opam, "opam", || generic::run_opam_update(&ctx))?; runner.execute(Step::Vcpkg, "vcpkg", || generic::run_vcpkg_update(run_type))?; @@ -468,10 +471,10 @@ fn run() -> Result<()> { loop { match get_key() { Ok(Key::Char('s')) | Ok(Key::Char('S')) => { - run_shell().context("Failed to execute shell")?; + run_shell(); } Ok(Key::Char('r')) | Ok(Key::Char('R')) => { - reboot().context("Failed to reboot")?; + reboot(); } Ok(Key::Char('q')) | Ok(Key::Char('Q')) => (), _ => { @@ -521,7 +524,7 @@ fn main() { .is_some()); if !skip_print { - // The `Debug` implementation of `eyre::Result` prints a multi-line + // The `Debug` implementation of `anyhow::Result` prints a multi-line // error message that includes all the 'causes' added with // `.with_context(...)` calls. println!("Error: {:?}", error); @@ -530,26 +533,3 @@ fn main() { } } } - -pub fn install_tracing(filter_directives: &str) -> Result<()> { - use tracing_subscriber::fmt; - use tracing_subscriber::fmt::format::FmtSpan; - use tracing_subscriber::layer::SubscriberExt; - use tracing_subscriber::util::SubscriberInitExt; - use tracing_subscriber::EnvFilter; - - let env_filter = EnvFilter::try_new(filter_directives) - .or_else(|_| EnvFilter::try_from_default_env()) - .or_else(|_| EnvFilter::try_new("info"))?; - - let fmt_layer = fmt::layer() - .with_target(false) - .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE) - .without_time(); - - let registry = tracing_subscriber::registry(); - - registry.with(env_filter).with(fmt_layer).init(); - - Ok(()) -} diff --git a/src/runner.rs b/src/runner.rs index 99175bda..2636a88a 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -2,12 +2,11 @@ use crate::ctrlc; use crate::error::{DryRun, SkipStep}; use crate::execution_context::ExecutionContext; use crate::report::{Report, StepResult}; -use crate::terminal::print_error; use crate::{config::Step, terminal::should_retry}; -use color_eyre::eyre::Result; +use anyhow::Result; +use log::debug; use std::borrow::Cow; use std::fmt::Debug; -use tracing::debug; pub struct Runner<'a> { ctx: &'a ExecutionContext<'a>, @@ -56,12 +55,7 @@ impl<'a> Runner<'a> { let ignore_failure = self.ctx.config().ignore_failure(step); let should_ask = interrupted || !(self.ctx.config().no_retry() || ignore_failure); - let should_retry = if should_ask { - print_error(&key, format!("{e:?}")); - should_retry(interrupted, key.as_ref())? - } else { - false - }; + let should_retry = should_ask && should_retry(interrupted, key.as_ref())?; if !should_retry { self.report.push_result(Some(( diff --git a/src/self_renamer.rs b/src/self_renamer.rs index 4a26b672..16186bac 100644 --- a/src/self_renamer.rs +++ b/src/self_renamer.rs @@ -1,8 +1,8 @@ #![cfg(windows)] -use color_eyre::eyre::Result; +use anyhow::Result; +use log::{debug, error}; use std::{env::current_exe, fs, path::PathBuf}; -use tracing::{debug, error}; pub struct SelfRenamer { exe_path: PathBuf, diff --git a/src/self_update.rs b/src/self_update.rs index 95274e99..9488fb58 100644 --- a/src/self_update.rs +++ b/src/self_update.rs @@ -1,15 +1,13 @@ -use std::env; -#[cfg(unix)] -use std::os::unix::process::CommandExt as _; -use std::process::Command; - -use color_eyre::eyre::{bail, Result}; -use self_update_crate::backends::github::Update; -use self_update_crate::update::UpdateStatus; - use super::terminal::*; #[cfg(windows)] use crate::error::Upgraded; +use anyhow::{bail, Result}; +use self_update_crate::backends::github::Update; +use self_update_crate::update::UpdateStatus; +use std::env; +#[cfg(unix)] +use std::os::unix::process::CommandExt; +use std::process::Command; pub fn self_update() -> Result<()> { print_separator("Self update"); @@ -51,8 +49,7 @@ pub fn self_update() -> Result<()> { #[cfg(windows)] { - #[allow(clippy::disallowed_methods)] - let status = command.status()?; + let status = command.spawn().and_then(|mut c| c.wait())?; bail!(Upgraded(status)); } } diff --git a/src/steps/containers.rs b/src/steps/containers.rs index 811cf7de..3ad4b53d 100644 --- a/src/steps/containers.rs +++ b/src/steps/containers.rs @@ -1,15 +1,12 @@ -use std::path::Path; -use std::process::Command; +use anyhow::Result; -use color_eyre::eyre::eyre; -use color_eyre::eyre::Context; -use color_eyre::eyre::Result; -use tracing::{debug, error, warn}; - -use crate::command::CommandExt; use crate::error::{self, TopgradeError}; +use crate::executor::CommandExt; use crate::terminal::print_separator; use crate::{execution_context::ExecutionContext, utils::require}; +use log::{debug, error, warn}; +use std::path::Path; +use std::process::Command; // 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 @@ -27,10 +24,11 @@ fn list_containers(crt: &Path) -> Result> { ); let output = Command::new(crt) .args(["image", "ls", "--format", "{{.Repository}}:{{.Tag}}"]) - .output_checked_with_utf8(|_| Ok(()))?; + .output()?; + let output_str = String::from_utf8(output.stdout)?; let mut retval = vec![]; - for line in output.stdout.lines() { + for line in output_str.lines() { if line.starts_with("localhost") { // Don't know how to update self-built containers debug!("Skipping self-built container '{}'", line); @@ -62,7 +60,7 @@ pub fn run_containers(ctx: &ExecutionContext) -> Result<()> { print_separator("Containers"); let mut success = true; - let containers = list_containers(&crt).context("Failed to list Docker containers")?; + let containers = list_containers(&crt)?; debug!("Containers to inspect: {:?}", containers); for container in containers.iter() { @@ -70,7 +68,7 @@ pub fn run_containers(ctx: &ExecutionContext) -> Result<()> { let args = vec!["pull", &container[..]]; let mut exec = ctx.run_type().execute(&crt); - if let Err(e) = exec.args(&args).status_checked() { + if let Err(e) = exec.args(&args).check_run() { error!("Pulling container '{}' failed: {}", container, e); // Find out if this is 'skippable' @@ -79,10 +77,10 @@ pub fn run_containers(ctx: &ExecutionContext) -> Result<()> { // practical consequence that all containers, whether self-built, created by // docker-compose or pulled from the docker hub, look exactly the same to us. We can // only find out what went wrong by manually parsing the output of the command... - if match exec.output_checked_utf8() { - Ok(s) => s.stdout.contains(NONEXISTENT_REPO) || s.stderr.contains(NONEXISTENT_REPO), + if match exec.check_output() { + Ok(s) => s.contains(NONEXISTENT_REPO), Err(e) => match e.downcast_ref::() { - Some(TopgradeError::ProcessFailedWithOutput(_, _, stderr)) => stderr.contains(NONEXISTENT_REPO), + Some(TopgradeError::ProcessFailedWithOutput(_, stderr)) => stderr.contains(NONEXISTENT_REPO), _ => false, }, } { @@ -97,12 +95,7 @@ pub fn run_containers(ctx: &ExecutionContext) -> Result<()> { if ctx.config().cleanup() { // Remove dangling images debug!("Removing dangling images"); - if let Err(e) = ctx - .run_type() - .execute(&crt) - .args(["image", "prune", "-f"]) - .status_checked() - { + if let Err(e) = ctx.run_type().execute(&crt).args(["image", "prune", "-f"]).check_run() { error!("Removing dangling images failed: {}", e); success = false; } @@ -111,6 +104,6 @@ pub fn run_containers(ctx: &ExecutionContext) -> Result<()> { if success { Ok(()) } else { - Err(eyre!(error::StepFailed)) + Err(anyhow::anyhow!(error::StepFailed)) } } diff --git a/src/steps/emacs.rs b/src/steps/emacs.rs index 89e04885..bbd80bb3 100644 --- a/src/steps/emacs.rs +++ b/src/steps/emacs.rs @@ -2,10 +2,9 @@ use std::env; use std::path::{Path, PathBuf}; -use color_eyre::eyre::Result; +use anyhow::Result; use directories::BaseDirs; -use crate::command::CommandExt; use crate::execution_context::ExecutionContext; use crate::terminal::print_separator; use crate::utils::{require, require_option, PathExt}; @@ -74,7 +73,7 @@ impl Emacs { command.args(["upgrade"]); - command.status_checked() + command.check_run() } pub fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> { @@ -106,6 +105,6 @@ impl Emacs { #[cfg(not(unix))] command.arg(EMACS_UPGRADE); - command.status_checked() + command.check_run() } } diff --git a/src/steps/generic.rs b/src/steps/generic.rs index 1646b0be..7694eada 100644 --- a/src/steps/generic.rs +++ b/src/steps/generic.rs @@ -5,16 +5,13 @@ use std::process::Command; use std::{env, path::Path}; use std::{fs, io::Write}; -use color_eyre::eyre::eyre; -use color_eyre::eyre::Context; -use color_eyre::eyre::Result; +use anyhow::Result; use directories::BaseDirs; +use log::debug; use tempfile::tempfile_in; -use tracing::debug; -use crate::command::{CommandExt, Utf8Output}; use crate::execution_context::ExecutionContext; -use crate::executor::{ExecutorOutput, RunType}; +use crate::executor::{CommandExt, ExecutorOutput, RunType}; use crate::terminal::{print_separator, shell}; use crate::utils::{self, require_option, PathExt}; use crate::{ @@ -56,14 +53,28 @@ pub fn run_cargo_update(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute(cargo_update) .args(["install-update", "--git", "--all"]) - .status_checked() + .check_run() } pub fn run_flutter_upgrade(run_type: RunType) -> Result<()> { let flutter = utils::require("flutter")?; print_separator("Flutter"); - run_type.execute(flutter).arg("upgrade").status_checked() + run_type.execute(flutter).arg("upgrade").check_run() +} + +pub fn run_go(run_type: RunType) -> Result<()> { + let go = utils::require("go")?; + let go_output = run_type.execute(go).args(["env", "GOPATH"]).check_output()?; + let gopath = go_output.trim(); + + let go_global_update = utils::require("go-global-update") + .unwrap_or_else(|_| PathBuf::from(gopath).join("bin/go-global-update")) + .require()?; + + print_separator("Go"); + + run_type.execute(go_global_update).check_run() } pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -80,15 +91,14 @@ pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { command.arg("--user-install"); } - command.status_checked() + command.check_run() } pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> { let haxelib = utils::require("haxelib")?; let haxelib_dir = - PathBuf::from(std::str::from_utf8(&Command::new(&haxelib).arg("config").output_checked()?.stdout)?.trim()) - .require()?; + PathBuf::from(std::str::from_utf8(&Command::new(&haxelib).arg("config").output()?.stdout)?.trim()).require()?; let directory_writable = tempfile_in(&haxelib_dir).is_ok(); debug!("{:?} writable: {}", haxelib_dir, directory_writable); @@ -105,7 +115,7 @@ pub fn run_haxelib_update(ctx: &ExecutionContext) -> Result<()> { c }; - command.arg("update").status_checked() + command.arg("update").check_run() } pub fn run_sheldon(ctx: &ExecutionContext) -> Result<()> { @@ -113,10 +123,7 @@ pub fn run_sheldon(ctx: &ExecutionContext) -> Result<()> { print_separator("Sheldon"); - ctx.run_type() - .execute(sheldon) - .args(["lock", "--update"]) - .status_checked() + ctx.run_type().execute(sheldon).args(["lock", "--update"]).check_run() } pub fn run_fossil(run_type: RunType) -> Result<()> { @@ -124,7 +131,7 @@ pub fn run_fossil(run_type: RunType) -> Result<()> { print_separator("Fossil"); - run_type.execute(fossil).args(["all", "sync"]).status_checked() + run_type.execute(fossil).args(["all", "sync"]).check_run() } pub fn run_micro(run_type: RunType) -> Result<()> { @@ -132,17 +139,13 @@ pub fn run_micro(run_type: RunType) -> Result<()> { print_separator("micro"); - let stdout = run_type - .execute(micro) - .args(["-plugin", "update"]) - .output_checked_utf8()? - .stdout; + let stdout = run_type.execute(micro).args(["-plugin", "update"]).string_output()?; std::io::stdout().write_all(stdout.as_bytes())?; if stdout.contains("Nothing to install / update") || stdout.contains("One or more plugins installed") { Ok(()) } else { - Err(eyre!("micro output does not indicate success: {}", stdout)) + Err(anyhow::anyhow!("micro output does not indicate success: {}", stdout)) } } @@ -157,10 +160,7 @@ pub fn run_apm(run_type: RunType) -> Result<()> { print_separator("Atom Package Manager"); - run_type - .execute(apm) - .args(["upgrade", "--confirm=false"]) - .status_checked() + run_type.execute(apm).args(["upgrade", "--confirm=false"]).check_run() } pub fn run_rustup(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -169,10 +169,10 @@ pub fn run_rustup(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("rustup"); if rustup.canonicalize()?.is_descendant_of(base_dirs.home_dir()) { - run_type.execute(&rustup).args(["self", "update"]).status_checked()?; + run_type.execute(&rustup).args(["self", "update"]).check_run()?; } - run_type.execute(&rustup).arg("update").status_checked() + run_type.execute(&rustup).arg("update").check_run() } pub fn run_choosenim(ctx: &ExecutionContext) -> Result<()> { @@ -181,8 +181,8 @@ pub fn run_choosenim(ctx: &ExecutionContext) -> Result<()> { print_separator("choosenim"); let run_type = ctx.run_type(); - run_type.execute(&choosenim).args(["update", "self"]).status_checked()?; - run_type.execute(&choosenim).args(["update", "stable"]).status_checked() + run_type.execute(&choosenim).args(["update", "self"]).check_run()?; + run_type.execute(&choosenim).args(["update", "stable"]).check_run() } pub fn run_krew_upgrade(run_type: RunType) -> Result<()> { @@ -190,7 +190,7 @@ pub fn run_krew_upgrade(run_type: RunType) -> Result<()> { print_separator("Krew"); - run_type.execute(krew).args(["upgrade"]).status_checked() + run_type.execute(krew).args(["upgrade"]).check_run() } pub fn run_gcloud_components_update(run_type: RunType) -> Result<()> { @@ -204,7 +204,7 @@ pub fn run_gcloud_components_update(run_type: RunType) -> Result<()> { run_type .execute(gcloud) .args(["components", "update", "--quiet"]) - .status_checked() + .check_run() } } @@ -213,7 +213,7 @@ pub fn run_jetpack(run_type: RunType) -> Result<()> { print_separator("Jetpack"); - run_type.execute(jetpack).args(["global", "update"]).status_checked() + run_type.execute(jetpack).args(["global", "update"]).check_run() } pub fn run_rtcl(ctx: &ExecutionContext) -> Result<()> { @@ -221,7 +221,7 @@ pub fn run_rtcl(ctx: &ExecutionContext) -> Result<()> { print_separator("rtcl"); - ctx.run_type().execute(rupdate).status_checked() + ctx.run_type().execute(rupdate).check_run() } pub fn run_opam_update(ctx: &ExecutionContext) -> Result<()> { @@ -229,11 +229,11 @@ pub fn run_opam_update(ctx: &ExecutionContext) -> Result<()> { print_separator("OCaml Package Manager"); - ctx.run_type().execute(&opam).arg("update").status_checked()?; - ctx.run_type().execute(&opam).arg("upgrade").status_checked()?; + ctx.run_type().execute(&opam).arg("update").check_run()?; + ctx.run_type().execute(&opam).arg("upgrade").check_run()?; if ctx.config().cleanup() { - ctx.run_type().execute(&opam).arg("clean").status_checked()?; + ctx.run_type().execute(&opam).arg("clean").check_run()?; } Ok(()) @@ -243,17 +243,14 @@ pub fn run_vcpkg_update(run_type: RunType) -> Result<()> { let vcpkg = utils::require("vcpkg")?; print_separator("vcpkg"); - run_type - .execute(vcpkg) - .args(["upgrade", "--no-dry-run"]) - .status_checked() + run_type.execute(vcpkg).args(["upgrade", "--no-dry-run"]).check_run() } pub fn run_pipx_update(run_type: RunType) -> Result<()> { let pipx = utils::require("pipx")?; print_separator("pipx"); - run_type.execute(pipx).arg("upgrade-all").status_checked() + run_type.execute(pipx).arg("upgrade-all").check_run() } pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> { @@ -261,9 +258,10 @@ pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> { let output = Command::new("conda") .args(["config", "--show", "auto_activate_base"]) - .output_checked_utf8()?; - debug!("Conda output: {}", output.stdout); - if output.stdout.contains("False") { + .output()?; + let string_output = String::from_utf8(output.stdout)?; + debug!("Conda output: {}", string_output); + if string_output.contains("False") { return Err(SkipStep("auto_activate_base is set to False".to_string()).into()); } @@ -272,14 +270,14 @@ pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute(conda) .args(["update", "--all", "-y"]) - .status_checked() + .check_run() } pub fn run_pip3_update(run_type: RunType) -> Result<()> { let python3 = utils::require("python3")?; Command::new(&python3) .args(["-m", "pip"]) - .output_checked_utf8() + .check_output() .map_err(|_| SkipStep("pip does not exists".to_string()))?; print_separator("pip3"); @@ -291,7 +289,7 @@ pub fn run_pip3_update(run_type: RunType) -> Result<()> { run_type .execute(&python3) .args(["-m", "pip", "install", "--upgrade", "--user", "pip"]) - .status_checked() + .check_run() } pub fn run_stack_update(run_type: RunType) -> Result<()> { @@ -305,14 +303,14 @@ pub fn run_stack_update(run_type: RunType) -> Result<()> { let stack = utils::require("stack")?; print_separator("stack"); - run_type.execute(stack).arg("upgrade").status_checked() + run_type.execute(stack).arg("upgrade").check_run() } pub fn run_ghcup_update(run_type: RunType) -> Result<()> { let ghcup = utils::require("ghcup")?; print_separator("ghcup"); - run_type.execute(ghcup).arg("upgrade").status_checked() + run_type.execute(ghcup).arg("upgrade").check_run() } pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> { @@ -328,11 +326,13 @@ pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> { let kpsewhich = utils::require("kpsewhich")?; let tlmgr_directory = { let mut d = PathBuf::from( - &Command::new(kpsewhich) - .arg("-var-value=SELFAUTOPARENT") - .output_checked_utf8()? - .stdout - .trim(), + std::str::from_utf8( + &Command::new(kpsewhich) + .arg("-var-value=SELFAUTOPARENT") + .output()? + .stdout, + )? + .trim(), ); d.push("tlpkg"); d @@ -355,7 +355,7 @@ pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> { }; command.args(["update", "--self", "--all"]); - command.status_checked() + command.check_run() } pub fn run_chezmoi_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -364,7 +364,7 @@ pub fn run_chezmoi_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> print_separator("chezmoi"); - run_type.execute(chezmoi).arg("update").status_checked() + run_type.execute(chezmoi).arg("update").check_run() } pub fn run_myrepos_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -378,27 +378,27 @@ pub fn run_myrepos_update(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> .arg("--directory") .arg(base_dirs.home_dir()) .arg("checkout") - .status_checked()?; + .check_run()?; run_type .execute(&myrepos) .arg("--directory") .arg(base_dirs.home_dir()) .arg("update") - .status_checked() + .check_run() } pub fn run_custom_command(name: &str, command: &str, ctx: &ExecutionContext) -> Result<()> { print_separator(name); - ctx.run_type().execute(shell()).arg("-c").arg(command).status_checked() + ctx.run_type().execute(shell()).arg("-c").arg(command).check_run() } pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> { let composer = utils::require("composer")?; let composer_home = Command::new(&composer) .args(["global", "config", "--absolute", "--quiet", "home"]) - .output_checked_utf8() + .check_output() .map_err(|e| (SkipStep(format!("Error getting the composer directory: {}", e)))) - .map(|s| PathBuf::from(s.stdout.trim()))? + .map(|s| PathBuf::from(s.trim()))? .require()?; if !composer_home.is_descendant_of(ctx.base_dirs().home_dir()) { @@ -425,22 +425,26 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> { .execute(ctx.sudo().as_ref().unwrap()) .arg(&composer) .arg("self-update") - .status_checked()?; + .check_run()?; } } else { - ctx.run_type().execute(&composer).arg("self-update").status_checked()?; + ctx.run_type().execute(&composer).arg("self-update").check_run()?; } } } - let output = ctx.run_type().execute(&composer).args(["global", "update"]).output()?; - if let ExecutorOutput::Wet(output) = output { - let output: Utf8Output = output.try_into()?; - print!("{}\n{}", output.stdout, output.stderr); - if output.stdout.contains("valet") || output.stderr.contains("valet") { - if let Some(valet) = utils::which("valet") { - ctx.run_type().execute(valet).arg("install").status_checked()?; - } + let output = Command::new(&composer).args(["global", "update"]).output()?; + let status = output.status; + if !status.success() { + return Err(TopgradeError::ProcessFailed(status).into()); + } + let stdout = String::from_utf8(output.stdout)?; + let stderr = String::from_utf8(output.stderr)?; + print!("{}\n{}", stdout, stderr); + + if stdout.contains("valet") || stderr.contains("valet") { + if let Some(valet) = utils::which("valet") { + ctx.run_type().execute(valet).arg("install").check_run()?; } } @@ -450,15 +454,18 @@ pub fn run_composer_update(ctx: &ExecutionContext) -> Result<()> { pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> { let dotnet = utils::require("dotnet")?; - let output = Command::new(dotnet) - .args(["tool", "list", "--global"]) - .output_checked_utf8()?; + let output = Command::new(dotnet).args(["tool", "list", "--global"]).output()?; - if !output.stdout.starts_with("Package Id") { + if !output.status.success() { + return Err(SkipStep(format!("dotnet failed with exit code {:?}", output.status)).into()); + } + + let output = String::from_utf8(output.stdout)?; + if !output.starts_with("Package Id") { return Err(SkipStep(String::from("dotnet did not output packages")).into()); } - let mut packages = output.stdout.lines().skip(2).filter(|line| !line.is_empty()).peekable(); + let mut packages = output.split('\n').skip(2).filter(|line| !line.is_empty()).peekable(); if packages.peek().is_none() { return Err(SkipStep(String::from("No dotnet global tools installed")).into()); @@ -471,8 +478,7 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute("dotnet") .args(["tool", "update", package_name, "--global"]) - .status_checked() - .with_context(|| format!("Failed to update .NET package {package_name}"))?; + .check_run()?; } Ok(()) @@ -483,26 +489,26 @@ pub fn run_raco_update(run_type: RunType) -> Result<()> { print_separator("Racket Package Manager"); - run_type.execute(raco).args(["pkg", "update", "--all"]).status_checked() + run_type.execute(raco).args(["pkg", "update", "--all"]).check_run() } pub fn bin_update(ctx: &ExecutionContext) -> Result<()> { let bin = utils::require("bin")?; print_separator("Bin"); - ctx.run_type().execute(bin).arg("update").status_checked() + ctx.run_type().execute(bin).arg("update").check_run() } pub fn spicetify_upgrade(ctx: &ExecutionContext) -> Result<()> { let spicetify = utils::require("spicetify")?; print_separator("Spicetify"); - ctx.run_type().execute(spicetify).arg("upgrade").status_checked() + ctx.run_type().execute(spicetify).arg("upgrade").check_run() } pub fn run_ghcli_extensions_upgrade(ctx: &ExecutionContext) -> Result<()> { let gh = utils::require("gh")?; - let result = Command::new(&gh).args(["extensions", "list"]).output_checked_utf8(); + let result = Command::new(&gh).args(["extensions", "list"]).check_output(); if result.is_err() { debug!("GH result {:?}", result); return Err(SkipStep(String::from("GH failed")).into()); @@ -512,7 +518,7 @@ pub fn run_ghcli_extensions_upgrade(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute(&gh) .args(["extension", "upgrade", "--all"]) - .status_checked() + .check_run() } pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> { @@ -523,5 +529,5 @@ pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute(julia) .args(["-e", "using Pkg; Pkg.update()"]) - .status_checked() + .check_run() } diff --git a/src/steps/git.rs b/src/steps/git.rs index 4bd0cbad..190b3552 100644 --- a/src/steps/git.rs +++ b/src/steps/git.rs @@ -3,18 +3,17 @@ use std::io; use std::path::{Path, PathBuf}; use std::process::{Command, Output, Stdio}; -use color_eyre::eyre::{eyre, Result}; +use anyhow::{anyhow, Result}; use console::style; use futures::stream::{iter, FuturesUnordered}; use futures::StreamExt; use glob::{glob_with, MatchOptions}; +use log::{debug, error}; use tokio::process::Command as AsyncCommand; use tokio::runtime; -use tracing::{debug, error}; -use crate::command::CommandExt; use crate::execution_context::ExecutionContext; -use crate::executor::RunType; +use crate::executor::{CommandExt, RunType}; use crate::terminal::print_separator; use crate::utils::{which, PathExt}; use crate::{error::SkipStep, terminal::print_warning}; @@ -34,10 +33,10 @@ pub struct Repositories<'a> { bad_patterns: Vec, } -fn output_checked_utf8(output: Output) -> Result<()> { +fn check_output(output: Output) -> Result<()> { if !(output.status.success()) { let stderr = String::from_utf8(output.stderr).unwrap(); - Err(eyre!(stderr)) + Err(anyhow!(stderr)) } else { Ok(()) } @@ -67,7 +66,7 @@ async fn pull_repository(repo: String, git: &Path, ctx: &ExecutionContext<'_>) - .stdin(Stdio::null()) .output() .await?; - let result = output_checked_utf8(pull_output).and_then(|_| output_checked_utf8(submodule_output)); + let result = check_output(pull_output).and_then(|_| check_output(submodule_output)); if let Err(message) = &result { println!("{} pulling {}", style("Failed").red().bold(), &repo); @@ -89,7 +88,10 @@ async fn pull_repository(repo: String, git: &Path, ctx: &ExecutionContext<'_>) - "--oneline", &format!("{}..{}", before, after), ]) - .status_checked()?; + .spawn() + .unwrap() + .wait() + .unwrap(); println!(); } _ => { @@ -106,8 +108,8 @@ fn get_head_revision(git: &Path, repo: &str) -> Option { .stdin(Stdio::null()) .current_dir(repo) .args(["rev-parse", "HEAD"]) - .output_checked_utf8() - .map(|output| output.stdout.trim().to_string()) + .check_output() + .map(|output| output.trim().to_string()) .map_err(|e| { error!("Error getting revision for {}: {}", repo, e); @@ -121,8 +123,8 @@ fn has_remotes(git: &Path, repo: &str) -> Option { .stdin(Stdio::null()) .current_dir(repo) .args(["remote", "show"]) - .output_checked_utf8() - .map(|output| output.stdout.lines().count() > 0) + .check_output() + .map(|output| output.lines().count() > 0) .map_err(|e| { error!("Error getting remotes for {}: {}", repo, e); e @@ -164,9 +166,9 @@ impl Git { .stdin(Stdio::null()) .current_dir(path) .args(["rev-parse", "--show-toplevel"]) - .output_checked_utf8() + .check_output() .ok() - .map(|output| output.stdout.trim().to_string()); + .map(|output| output.trim().to_string()); return output; } } diff --git a/src/steps/go.rs b/src/steps/go.rs deleted file mode 100644 index a80ee032..00000000 --- a/src/steps/go.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::path::PathBuf; -use std::process::Command; - -use color_eyre::eyre::Result; - -use crate::command::CommandExt; -use crate::executor::RunType; -use crate::terminal::print_separator; -use crate::utils; -use crate::utils::PathExt; - -/// -pub fn run_go_global_update(run_type: RunType) -> Result<()> { - let go_global_update = require_go_bin("go-global-update")?; - - print_separator("go-global-update"); - - run_type.execute(go_global_update).status_checked() -} - -/// -pub fn run_go_gup(run_type: RunType) -> Result<()> { - let gup = require_go_bin("gup")?; - - print_separator("gup"); - - run_type.execute(gup).arg("update").status_checked() -} - -/// Get the path of a Go binary. -fn require_go_bin(name: &str) -> Result { - utils::require(name).or_else(|_| { - let go = utils::require("go")?; - // TODO: Does this work? `go help gopath` says that: - // > The GOPATH environment variable lists places to look for Go code. - // > On Unix, the value is a colon-separated string. - // > On Windows, the value is a semicolon-separated string. - // > On Plan 9, the value is a list. - // Should we also fallback to the env variable? - let gopath_output = Command::new(go).args(["env", "GOPATH"]).output_checked_utf8()?; - let gopath = gopath_output.stdout.trim(); - - PathBuf::from(gopath).join("bin").join(name).require() - }) -} diff --git a/src/steps/kakoune.rs b/src/steps/kakoune.rs index d1b955aa..d2b5014b 100644 --- a/src/steps/kakoune.rs +++ b/src/steps/kakoune.rs @@ -1,8 +1,10 @@ +use crate::error::TopgradeError; use crate::terminal::print_separator; use crate::utils::require; -use color_eyre::eyre::Result; +use anyhow::Result; use crate::execution_context::ExecutionContext; +use crate::executor::ExecutorOutput; const UPGRADE_KAK: &str = include_str!("upgrade.kak"); @@ -11,13 +13,19 @@ pub fn upgrade_kak_plug(ctx: &ExecutionContext) -> Result<()> { print_separator("Kakoune"); - // TODO: Why supress output for this command? - ctx.run_type() - .execute(kak) - .args(["-ui", "dummy", "-e", UPGRADE_KAK]) - .output()?; + let mut command = ctx.run_type().execute(kak); + command.args(["-ui", "dummy", "-e", UPGRADE_KAK]); - println!("Plugins upgraded"); + let output = command.output()?; + + if let ExecutorOutput::Wet(output) = output { + let status = output.status; + if !status.success() { + return Err(TopgradeError::ProcessFailed(status).into()); + } else { + println!("Plugins upgraded") + } + } Ok(()) } diff --git a/src/steps/mod.rs b/src/steps/mod.rs index 9255dd81..b1b7e9ec 100644 --- a/src/steps/mod.rs +++ b/src/steps/mod.rs @@ -2,7 +2,6 @@ pub mod containers; pub mod emacs; pub mod generic; pub mod git; -pub mod go; pub mod kakoune; pub mod node; pub mod os; diff --git a/src/steps/node.rs b/src/steps/node.rs index 4909867b..c702076a 100644 --- a/src/steps/node.rs +++ b/src/steps/node.rs @@ -1,20 +1,20 @@ +#![allow(unused_imports)] + use std::fmt::Display; -#[cfg(target_os = "linux")] -use std::os::unix::fs::MetadataExt; +#[cfg(unix)] +use std::os::unix::prelude::MetadataExt; use std::path::PathBuf; use std::process::Command; -use crate::utils::require_option; -use color_eyre::eyre::Result; -#[cfg(target_os = "linux")] +use anyhow::Result; +use directories::BaseDirs; +use log::debug; +#[cfg(unix)] use nix::unistd::Uid; use semver::Version; -use tracing::debug; -use crate::command::CommandExt; -use crate::executor::RunType; +use crate::executor::{CommandExt, RunType}; use crate::terminal::print_separator; -use crate::utils::sudo; use crate::utils::{require, PathExt}; use crate::{error::SkipStep, execution_context::ExecutionContext}; @@ -24,6 +24,13 @@ enum NPMVariant { } impl NPMVariant { + const fn long_name(&self) -> &str { + match self { + NPMVariant::Npm => "Node Package Manager", + NPMVariant::Pnpm => "PNPM", + } + } + const fn short_name(&self) -> &str { match self { NPMVariant::Npm => "npm", @@ -78,26 +85,25 @@ impl NPM { let args = ["root", self.global_location_arg()]; Command::new(&self.command) .args(args) - .output_checked_utf8() - .map(|s| PathBuf::from(s.stdout.trim())) + .check_output() + .map(|s| PathBuf::from(s.trim())) } fn version(&self) -> Result { let version_str = Command::new(&self.command) .args(["--version"]) - .output_checked_utf8() - .map(|s| s.stdout.trim().to_owned()); + .check_output() + .map(|s| s.trim().to_owned()); Version::parse(&version_str?).map_err(|err| err.into()) } fn upgrade(&self, run_type: RunType, use_sudo: bool) -> Result<()> { + print_separator(self.variant.long_name()); let args = ["update", self.global_location_arg()]; if use_sudo { - let sudo_option = sudo(); - let sudo = require_option(sudo_option, String::from("sudo is not installed"))?; - run_type.execute(sudo).arg(&self.command).args(args).status_checked()?; + run_type.execute("sudo").args(args).check_run()?; } else { - run_type.execute(&self.command).args(args).status_checked()?; + run_type.execute(&self.command).args(args).check_run()?; } Ok(()) @@ -136,9 +142,9 @@ impl Yarn { // // As “yarn dlx” don't need to “upgrade”, we // ignore the whole task if Yarn is 2.x or above. - let version = Command::new(&self.command).args(["--version"]).output_checked_utf8(); + let version = Command::new(&self.command).args(["--version"]).check_output(); - matches!(version, Ok(ver) if ver.stdout.starts_with('1') || ver.stdout.starts_with('0')) + matches!(version, Ok(ver) if ver.starts_with('1') || ver.starts_with('0')) } #[cfg(target_os = "linux")] @@ -146,11 +152,12 @@ impl Yarn { let args = ["global", "dir"]; Command::new(&self.command) .args(args) - .output_checked_utf8() - .map(|s| PathBuf::from(s.stdout.trim())) + .check_output() + .map(|s| PathBuf::from(s.trim())) } fn upgrade(&self, run_type: RunType, use_sudo: bool) -> Result<()> { + print_separator("Yarn Package Manager"); let args = ["global", "upgrade"]; if use_sudo { @@ -158,9 +165,9 @@ impl Yarn { .execute("sudo") .arg(self.yarn.as_ref().unwrap_or(&self.command)) .args(args) - .status_checked()?; + .check_run()?; } else { - run_type.execute(&self.command).args(args).status_checked()?; + run_type.execute(&self.command).args(args).check_run()?; } Ok(()) @@ -211,8 +218,6 @@ fn should_use_sudo_yarn(yarn: &Yarn, ctx: &ExecutionContext) -> Result { pub fn run_npm_upgrade(ctx: &ExecutionContext) -> Result<()> { let npm = require("npm").map(|b| NPM::new(b, NPMVariant::Npm))?; - print_separator("Node Package Manager"); - #[cfg(target_os = "linux")] { npm.upgrade(ctx.run_type(), should_use_sudo(&npm, ctx)?) @@ -227,8 +232,6 @@ pub fn run_npm_upgrade(ctx: &ExecutionContext) -> Result<()> { pub fn run_pnpm_upgrade(ctx: &ExecutionContext) -> Result<()> { let pnpm = require("pnpm").map(|b| NPM::new(b, NPMVariant::Pnpm))?; - print_separator("Node Package Manager"); - #[cfg(target_os = "linux")] { pnpm.upgrade(ctx.run_type(), should_use_sudo(&pnpm, ctx)?) @@ -248,8 +251,6 @@ pub fn run_yarn_upgrade(ctx: &ExecutionContext) -> Result<()> { return Ok(()); } - print_separator("Yarn Package Manager"); - #[cfg(target_os = "linux")] { yarn.upgrade(ctx.run_type(), should_use_sudo_yarn(&yarn, ctx)?) @@ -271,5 +272,5 @@ pub fn deno_upgrade(ctx: &ExecutionContext) -> Result<()> { } print_separator("Deno"); - ctx.run_type().execute(&deno).arg("upgrade").status_checked() + ctx.run_type().execute(&deno).arg("upgrade").check_run() } diff --git a/src/steps/os/android.rs b/src/steps/os/android.rs index 76ed5468..e6706f9c 100644 --- a/src/steps/os/android.rs +++ b/src/steps/os/android.rs @@ -1,10 +1,8 @@ -use crate::command::CommandExt; use crate::execution_context::ExecutionContext; use crate::terminal::print_separator; use crate::utils::require; -use crate::utils::which; use crate::Step; -use color_eyre::eyre::Result; +use anyhow::Result; pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> { //let pkg = require("pkg")?; @@ -12,7 +10,7 @@ pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> { print_separator("Termux Packages"); - let is_nala = pkg.ends_with("nala"); + let is_nala = pkg.end_with("nala"); let mut command = ctx.run_type().execute(&pkg); command.arg("upgrade"); @@ -20,18 +18,20 @@ pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> { if ctx.config().yes(Step::System) { command.arg("-y"); } - command.status_checked()?; + command.check_run()?; - if !is_nala && ctx.config().cleanup() { - ctx.run_type().execute(&pkg).arg("clean").status_checked()?; + if !is_nala { + if ctx.config().cleanup() { + ctx.run_type().execute(&pkg).arg("clean").check_run()?; - let apt = require("apt")?; - let mut command = ctx.run_type().execute(&apt); - command.arg("autoremove"); - if ctx.config().yes(Step::System) { - command.arg("-y"); + let apt = require("apt")?; + let mut command = ctx.run_type().execute(&apt); + command.arg("autoremove"); + if ctx.config().yes(Step::System) { + command.arg("-y"); + } + command.check_run()?; } - command.status_checked()?; } Ok(()) diff --git a/src/steps/os/archlinux.rs b/src/steps/os/archlinux.rs index 7c4e938c..892a8994 100644 --- a/src/steps/os/archlinux.rs +++ b/src/steps/os/archlinux.rs @@ -3,11 +3,9 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::process::Command; -use color_eyre::eyre; -use color_eyre::eyre::Result; +use anyhow::Result; use walkdir::WalkDir; -use crate::command::CommandExt; use crate::error::TopgradeError; use crate::execution_context::ExecutionContext; use crate::utils::which; @@ -31,7 +29,11 @@ pub struct YayParu { impl ArchPackageManager for YayParu { fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> { if ctx.config().show_arch_news() { - Command::new(&self.executable).arg("-Pw").status_checked()?; + Command::new(&self.executable) + .arg("-Pw") + .spawn() + .and_then(|mut p| p.wait()) + .ok(); } let mut command = ctx.run_type().execute(&self.executable); @@ -46,7 +48,7 @@ impl ArchPackageManager for YayParu { if ctx.config().yes(Step::System) { command.arg("--noconfirm"); } - command.status_checked()?; + command.check_run()?; if ctx.config().cleanup() { let mut command = ctx.run_type().execute(&self.executable); @@ -54,7 +56,7 @@ impl ArchPackageManager for YayParu { if ctx.config().yes(Step::System) { command.arg("--noconfirm"); } - command.status_checked()?; + command.check_run()?; } Ok(()) @@ -86,7 +88,7 @@ impl ArchPackageManager for Trizen { if ctx.config().yes(Step::System) { command.arg("--noconfirm"); } - command.status_checked()?; + command.check_run()?; if ctx.config().cleanup() { let mut command = ctx.run_type().execute(&self.executable); @@ -94,7 +96,7 @@ impl ArchPackageManager for Trizen { if ctx.config().yes(Step::System) { command.arg("--noconfirm"); } - command.status_checked()?; + command.check_run()?; } Ok(()) @@ -124,7 +126,7 @@ impl ArchPackageManager for Pacman { if ctx.config().yes(Step::System) { command.arg("--noconfirm"); } - command.status_checked()?; + command.check_run()?; if ctx.config().cleanup() { let mut command = ctx.run_type().execute(&self.sudo); @@ -132,7 +134,7 @@ impl ArchPackageManager for Pacman { if ctx.config().yes(Step::System) { command.arg("--noconfirm"); } - command.status_checked()?; + command.check_run()?; } Ok(()) @@ -173,7 +175,7 @@ impl ArchPackageManager for Pikaur { command.arg("--noconfirm"); } - command.status_checked()?; + command.check_run()?; if ctx.config().cleanup() { let mut command = ctx.run_type().execute(&self.executable); @@ -181,7 +183,7 @@ impl ArchPackageManager for Pikaur { if ctx.config().yes(Step::System) { command.arg("--noconfirm"); } - command.status_checked()?; + command.check_run()?; } Ok(()) @@ -212,7 +214,7 @@ impl ArchPackageManager for Pamac { command.arg("--no-confirm"); } - command.status_checked()?; + command.check_run()?; if ctx.config().cleanup() { let mut command = ctx.run_type().execute(&self.executable); @@ -220,7 +222,7 @@ impl ArchPackageManager for Pamac { if ctx.config().yes(Step::System) { command.arg("--no-confirm"); } - command.status_checked()?; + command.check_run()?; } Ok(()) @@ -255,7 +257,7 @@ impl ArchPackageManager for Aura { aur_update.arg("--noconfirm"); } - aur_update.status_checked()?; + aur_update.check_run()?; } else { println!("Aura requires sudo installed to work with AUR packages") } @@ -268,7 +270,7 @@ impl ArchPackageManager for Aura { if ctx.config().yes(Step::System) { pacman_update.arg("--noconfirm"); } - pacman_update.status_checked()?; + pacman_update.check_run()?; Ok(()) } @@ -302,7 +304,7 @@ pub fn get_arch_package_manager(ctx: &ExecutionContext) -> Option Result<()> { let package_manager = - get_arch_package_manager(ctx).ok_or_else(|| eyre::Report::from(TopgradeError::FailedGettingPackageManager))?; + get_arch_package_manager(ctx).ok_or_else(|| anyhow::Error::from(TopgradeError::FailedGettingPackageManager))?; package_manager.upgrade(ctx) } diff --git a/src/steps/os/dragonfly.rs b/src/steps/os/dragonfly.rs index b314a47f..79003263 100644 --- a/src/steps/os/dragonfly.rs +++ b/src/steps/os/dragonfly.rs @@ -1,26 +1,26 @@ -use crate::command::CommandExt; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::require_option; -use color_eyre::eyre::Result; +use anyhow::Result; use std::path::PathBuf; use std::process::Command; pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo, String::from("No sudo detected"))?; - print_separator("DragonFly BSD Packages"); + print_separator("DrgaonFly BSD Packages"); run_type .execute(sudo) - .args(["/usr/local/sbin/pkg", "upgrade"]) - .status_checked() + .args(&["/usr/local/sbin/pkg", "upgrade"]) + .check_run() } pub fn audit_packages(sudo: &Option) -> Result<()> { if let Some(sudo) = sudo { println!(); Command::new(sudo) - .args(["/usr/local/sbin/pkg", "audit", "-Fr"]) - .status_checked()?; + .args(&["/usr/local/sbin/pkg", "audit", "-Fr"]) + .spawn()? + .wait()?; } Ok(()) } diff --git a/src/steps/os/freebsd.rs b/src/steps/os/freebsd.rs index 8109057a..bdd6ba76 100644 --- a/src/steps/os/freebsd.rs +++ b/src/steps/os/freebsd.rs @@ -1,10 +1,7 @@ -use crate::command::CommandExt; -use crate::execution_context::ExecutionContext; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::require_option; -use crate::Step; -use color_eyre::eyre::Result; +use anyhow::Result; use std::path::PathBuf; use std::process::Command; @@ -13,29 +10,23 @@ pub fn upgrade_freebsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> print_separator("FreeBSD Update"); run_type .execute(sudo) - .args(["/usr/sbin/freebsd-update", "fetch", "install"]) - .status_checked() + .args(&["/usr/sbin/freebsd-update", "fetch", "install"]) + .check_run() } -pub fn upgrade_packages(ctx: &ExecutionContext, sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { +pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo, String::from("No sudo detected"))?; print_separator("FreeBSD Packages"); - - let mut command = run_type.execute(sudo); - - command.args(["/usr/sbin/pkg", "upgrade"]); - if ctx.config().yes(Step::System) { - command.arg("-y"); - } - command.status_checked() + run_type.execute(sudo).args(&["/usr/sbin/pkg", "upgrade"]).check_run() } pub fn audit_packages(sudo: &Option) -> Result<()> { if let Some(sudo) = sudo { println!(); Command::new(sudo) - .args(["/usr/sbin/pkg", "audit", "-Fr"]) - .status_checked()?; + .args(&["/usr/sbin/pkg", "audit", "-Fr"]) + .spawn()? + .wait()?; } Ok(()) } diff --git a/src/steps/os/linux.rs b/src/steps/os/linux.rs index 25a40c0a..20f791dd 100644 --- a/src/steps/os/linux.rs +++ b/src/steps/os/linux.rs @@ -1,14 +1,13 @@ use std::path::{Path, PathBuf}; use std::process::Command; -use color_eyre::eyre::Result; +use anyhow::Result; use ini::Ini; -use tracing::{debug, warn}; +use log::{debug, warn}; -use crate::command::CommandExt; use crate::error::{SkipStep, TopgradeError}; use crate::execution_context::ExecutionContext; -use crate::executor::RunType; +use crate::executor::{CommandExt, RunType}; use crate::steps::os::archlinux; use crate::terminal::{print_separator, print_warning}; use crate::utils::{require, require_option, which, PathExt}; @@ -128,10 +127,11 @@ fn update_bedrock(ctx: &ExecutionContext) -> Result<()> { ctx.run_type().execute(sudo).args(["brl", "update"]); - let output = Command::new("brl").arg("list").output_checked_utf8()?; + let output = Command::new("brl").arg("list").output()?; debug!("brl list: {:?} {:?}", output.stdout, output.stderr); - for distribution in output.stdout.trim().lines() { + let parsed_output = String::from_utf8(output.stdout).unwrap(); + for distribution in parsed_output.trim().split('\n') { debug!("Bedrock distribution {}", distribution); match distribution { "arch" => archlinux::upgrade_arch_linux(ctx)?, @@ -148,7 +148,7 @@ fn update_bedrock(ctx: &ExecutionContext) -> Result<()> { } fn is_wsl() -> Result { - let output = Command::new("uname").arg("-r").output_checked_utf8()?.stdout; + let output = Command::new("uname").arg("-r").check_output()?; debug!("Uname output: {}", output); Ok(output.contains("microsoft")) } @@ -157,8 +157,8 @@ fn upgrade_alpine_linux(ctx: &ExecutionContext) -> Result<()> { let apk = require("apk")?; let sudo = ctx.sudo().as_ref().unwrap(); - 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("update").check_run()?; + ctx.run_type().execute(sudo).arg(&apk).arg("upgrade").check_run() } fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> { @@ -166,7 +166,7 @@ fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> { if ctx.config().rpm_ostree() { let mut command = ctx.run_type().execute(ostree); command.arg("upgrade"); - return command.status_checked(); + return command.check_run(); } }; @@ -188,7 +188,7 @@ fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> { command.arg("-y"); } - command.status_checked()?; + command.check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -198,7 +198,7 @@ fn upgrade_redhat(ctx: &ExecutionContext) -> Result<()> { fn upgrade_bedrock_strata(ctx: &ExecutionContext) -> Result<()> { if let Some(sudo) = ctx.sudo() { - ctx.run_type().execute(sudo).args(["brl", "update"]).status_checked()?; + ctx.run_type().execute(sudo).args(["brl", "update"]).check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -208,15 +208,12 @@ fn upgrade_bedrock_strata(ctx: &ExecutionContext) -> Result<()> { fn upgrade_suse(ctx: &ExecutionContext) -> Result<()> { if let Some(sudo) = ctx.sudo() { - ctx.run_type() - .execute(sudo) - .args(["zypper", "refresh"]) - .status_checked()?; + ctx.run_type().execute(sudo).args(["zypper", "refresh"]).check_run()?; ctx.run_type() .execute(sudo) .args(["zypper", "dist-upgrade"]) - .status_checked()?; + .check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -238,7 +235,7 @@ fn upgrade_openmandriva(ctx: &ExecutionContext) -> Result<()> { command.arg("-y"); } - command.status_checked()?; + command.check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -253,14 +250,14 @@ fn upgrade_void(ctx: &ExecutionContext) -> Result<()> { if ctx.config().yes(Step::System) { command.arg("-y"); } - command.status_checked()?; + command.check_run()?; let mut command = ctx.run_type().execute(sudo); command.args(["xbps-install", "-u"]); if ctx.config().yes(Step::System) { command.arg("-y"); } - command.status_checked()?; + command.check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -273,11 +270,7 @@ fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> { if let Some(sudo) = &ctx.sudo() { if let Some(layman) = which("layman") { - run_type - .execute(sudo) - .arg(layman) - .args(["-s", "ALL"]) - .status_checked()?; + run_type.execute(sudo).arg(layman).args(["-s", "ALL"]).check_run()?; } println!("Syncing portage"); @@ -290,10 +283,10 @@ fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> { .map(|s| s.split_whitespace().collect()) .unwrap_or_else(|| vec!["-q"]), ) - .status_checked()?; + .check_run()?; if let Some(eix_update) = which("eix-update") { - run_type.execute(sudo).arg(eix_update).status_checked()?; + run_type.execute(sudo).arg(eix_update).check_run()?; } run_type @@ -305,7 +298,7 @@ fn upgrade_gentoo(ctx: &ExecutionContext) -> Result<()> { .map(|s| s.split_whitespace().collect()) .unwrap_or_else(|| vec!["-uDNa", "--with-bdeps=y", "world"]), ) - .status_checked()?; + .check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -321,7 +314,7 @@ fn upgrade_debian(ctx: &ExecutionContext) -> Result<()> { let is_nala = apt.ends_with("nala"); if !is_nala { - ctx.run_type().execute(sudo).arg(&apt).arg("update").status_checked()?; + ctx.run_type().execute(sudo).arg(&apt).arg("update").check_run()?; } let mut command = ctx.run_type().execute(sudo); @@ -337,17 +330,17 @@ fn upgrade_debian(ctx: &ExecutionContext) -> Result<()> { if let Some(args) = ctx.config().apt_arguments() { command.args(args.split_whitespace()); } - command.status_checked()?; + command.check_run()?; if ctx.config().cleanup() { - ctx.run_type().execute(sudo).arg(&apt).arg("clean").status_checked()?; + ctx.run_type().execute(sudo).arg(&apt).arg("clean").check_run()?; let mut command = ctx.run_type().execute(sudo); command.arg(&apt).arg("autoremove"); if ctx.config().yes(Step::System) { command.arg("-y"); } - command.status_checked()?; + command.check_run()?; } } else { print_warning("No sudo detected. Skipping system upgrade"); @@ -361,11 +354,11 @@ pub fn run_deb_get(ctx: &ExecutionContext) -> Result<()> { print_separator("deb-get"); - ctx.execute_elevated(&deb_get, false)?.arg("update").status_checked()?; - ctx.execute_elevated(&deb_get, false)?.arg("upgrade").status_checked()?; + ctx.execute_elevated(&deb_get, false)?.arg("update").check_run()?; + ctx.execute_elevated(&deb_get, false)?.arg("upgrade").check_run()?; if ctx.config().cleanup() { - ctx.execute_elevated(&deb_get, false)?.arg("clean").status_checked()?; + ctx.execute_elevated(&deb_get, false)?.arg("clean").check_run()?; } Ok(()) @@ -373,10 +366,7 @@ pub fn run_deb_get(ctx: &ExecutionContext) -> Result<()> { fn upgrade_solus(ctx: &ExecutionContext) -> Result<()> { if let Some(sudo) = ctx.sudo() { - ctx.run_type() - .execute(sudo) - .args(["eopkg", "upgrade"]) - .status_checked()?; + ctx.run_type().execute(sudo).args(["eopkg", "upgrade"]).check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -389,10 +379,10 @@ pub fn run_pacdef(ctx: &ExecutionContext) -> Result<()> { print_separator("pacdef"); - ctx.run_type().execute(&pacdef).arg("sync").status_checked()?; + ctx.run_type().execute(&pacdef).arg("sync").check_run()?; println!(); - ctx.run_type().execute(&pacdef).arg("review").status_checked() + ctx.run_type().execute(&pacdef).arg("review").check_run() } pub fn run_pacstall(ctx: &ExecutionContext) -> Result<()> { @@ -400,16 +390,13 @@ pub fn run_pacstall(ctx: &ExecutionContext) -> Result<()> { print_separator("Pacstall"); - ctx.run_type().execute(&pacstall).arg("-U").status_checked()?; - ctx.run_type().execute(pacstall).arg("-Up").status_checked() + ctx.run_type().execute(&pacstall).arg("-U").check_run()?; + ctx.run_type().execute(pacstall).arg("-Up").check_run() } fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> { if let Some(sudo) = &ctx.sudo() { - ctx.run_type() - .execute(sudo) - .args(["swupd", "update"]) - .status_checked()?; + ctx.run_type().execute(sudo).args(["swupd", "update"]).check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -419,29 +406,26 @@ fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> { fn upgrade_exherbo(ctx: &ExecutionContext) -> Result<()> { if let Some(sudo) = ctx.sudo() { - ctx.run_type().execute(sudo).args(["cave", "sync"]).status_checked()?; + ctx.run_type().execute(sudo).args(["cave", "sync"]).check_run()?; ctx.run_type() .execute(sudo) .args(["cave", "resolve", "world", "-c1", "-Cs", "-km", "-Km", "-x"]) - .status_checked()?; + .check_run()?; if ctx.config().cleanup() { - ctx.run_type() - .execute(sudo) - .args(["cave", "purge", "-x"]) - .status_checked()?; + ctx.run_type().execute(sudo).args(["cave", "purge", "-x"]).check_run()?; } ctx.run_type() .execute(sudo) .args(["cave", "fix-linkage", "-x", "--", "-Cs"]) - .status_checked()?; + .check_run()?; ctx.run_type() .execute(sudo) .args(["eclectic", "config", "interactive"]) - .status_checked()?; + .check_run()?; } else { print_warning("No sudo detected. Skipping system upgrade"); } @@ -454,13 +438,13 @@ fn upgrade_nixos(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute(sudo) .args(["/run/current-system/sw/bin/nixos-rebuild", "switch", "--upgrade"]) - .status_checked()?; + .check_run()?; if ctx.config().cleanup() { ctx.run_type() .execute(sudo) .args(["/run/current-system/sw/bin/nix-collect-garbage", "-d"]) - .status_checked()?; + .check_run()?; } } else { print_warning("No sudo detected. Skipping system upgrade"); @@ -478,11 +462,7 @@ fn upgrade_neon(ctx: &ExecutionContext) -> Result<()> { if let Some(sudo) = &ctx.sudo() { let pkcon = which("pkcon").unwrap(); // pkcon ignores update with update and refresh provided together - ctx.run_type() - .execute(sudo) - .arg(&pkcon) - .arg("refresh") - .status_checked()?; + ctx.run_type().execute(sudo).arg(&pkcon).arg("refresh").check_run()?; let mut exe = ctx.run_type().execute(sudo); let cmd = exe.arg(&pkcon).arg("update"); if ctx.config().yes(Step::System) { @@ -492,7 +472,7 @@ fn upgrade_neon(ctx: &ExecutionContext) -> Result<()> { cmd.arg("--autoremove"); } // from pkcon man, exit code 5 is 'Nothing useful was done.' - cmd.status_checked_with_codes(&[5])?; + cmd.check_run_with_codes(&[5])?; } Ok(()) @@ -509,7 +489,7 @@ pub fn run_needrestart(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> print_separator("Check for needed restarts"); - run_type.execute(sudo).arg(needrestart).status_checked()?; + run_type.execute(sudo).arg(needrestart).check_run()?; Ok(()) } @@ -526,7 +506,7 @@ pub fn run_fwupdmgr(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute(&fwupdmgr) .arg("refresh") - .status_checked_with_codes(&[2])?; + .check_run_with_codes(&[2])?; let mut updmgr = ctx.run_type().execute(&fwupdmgr); @@ -538,7 +518,7 @@ pub fn run_fwupdmgr(ctx: &ExecutionContext) -> Result<()> { } else { updmgr.arg("get-updates"); } - updmgr.status_checked_with_codes(&[2]) + updmgr.check_run_with_codes(&[2]) } pub fn flatpak_update(ctx: &ExecutionContext) -> Result<()> { @@ -553,14 +533,14 @@ pub fn flatpak_update(ctx: &ExecutionContext) -> Result<()> { if yes { update_args.push("-y"); } - run_type.execute(&flatpak).args(&update_args).status_checked()?; + run_type.execute(&flatpak).args(&update_args).check_run()?; if cleanup { let mut cleanup_args = vec!["uninstall", "--user", "--unused"]; if yes { cleanup_args.push("-y"); } - run_type.execute(&flatpak).args(&cleanup_args).status_checked()?; + run_type.execute(&flatpak).args(&cleanup_args).check_run()?; } print_separator("Flatpak System Packages"); @@ -569,34 +549,26 @@ pub fn flatpak_update(ctx: &ExecutionContext) -> Result<()> { if yes { update_args.push("-y"); } - run_type - .execute(sudo) - .arg(&flatpak) - .args(&update_args) - .status_checked()?; + run_type.execute(sudo).arg(&flatpak).args(&update_args).check_run()?; if cleanup { let mut cleanup_args = vec!["uninstall", "--system", "--unused"]; if yes { cleanup_args.push("-y"); } - run_type - .execute(sudo) - .arg(flatpak) - .args(&cleanup_args) - .status_checked()?; + run_type.execute(sudo).arg(flatpak).args(&cleanup_args).check_run()?; } } else { let mut update_args = vec!["update", "--system"]; if yes { update_args.push("-y"); } - run_type.execute(&flatpak).args(&update_args).status_checked()?; + run_type.execute(&flatpak).args(&update_args).check_run()?; if cleanup { let mut cleanup_args = vec!["uninstall", "--system", "--unused"]; if yes { cleanup_args.push("-y"); } - run_type.execute(flatpak).args(&cleanup_args).status_checked()?; + run_type.execute(flatpak).args(&cleanup_args).check_run()?; } } @@ -612,7 +584,7 @@ pub fn run_snap(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { } print_separator("snap"); - run_type.execute(sudo).arg(snap).arg("refresh").status_checked() + run_type.execute(sudo).arg(snap).arg("refresh").check_run() } pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { @@ -622,7 +594,7 @@ pub fn run_pihole_update(sudo: Option<&PathBuf>, run_type: RunType) -> Result<() print_separator("pihole"); - run_type.execute(sudo).arg(pihole).arg("-up").status_checked() + run_type.execute(sudo).arg(pihole).arg("-up").check_run() } pub fn run_protonup_update(ctx: &ExecutionContext) -> Result<()> { @@ -630,7 +602,7 @@ pub fn run_protonup_update(ctx: &ExecutionContext) -> Result<()> { print_separator("protonup"); - ctx.run_type().execute(protonup).status_checked()?; + ctx.run_type().execute(protonup).check_run()?; Ok(()) } @@ -656,7 +628,8 @@ pub fn run_distrobox_update(ctx: &ExecutionContext) -> Result<()> { (r, true) => r.arg("--root"), (r, false) => r, } - .status_checked() + .check_run()?; + Ok(()) } pub fn run_config_update(ctx: &ExecutionContext) -> Result<()> { @@ -667,14 +640,14 @@ pub fn run_config_update(ctx: &ExecutionContext) -> Result<()> { if let Ok(etc_update) = require("etc-update") { print_separator("Configuration update"); - ctx.run_type().execute(sudo).arg(etc_update).status_checked()?; + ctx.run_type().execute(sudo).arg(etc_update).check_run()?; } else if let Ok(pacdiff) = require("pacdiff") { if std::env::var("DIFFPROG").is_err() { require("vim")?; } print_separator("Configuration update"); - ctx.execute_elevated(&pacdiff, false)?.status_checked()?; + ctx.execute_elevated(&pacdiff, false)?.check_run()?; } Ok(()) diff --git a/src/steps/os/macos.rs b/src/steps/os/macos.rs index f4265f4b..dab80d4b 100644 --- a/src/steps/os/macos.rs +++ b/src/steps/os/macos.rs @@ -1,30 +1,26 @@ -use crate::command::CommandExt; use crate::execution_context::ExecutionContext; -use crate::executor::RunType; +use crate::executor::{CommandExt, RunType}; use crate::terminal::{print_separator, prompt_yesno}; -use crate::{utils::require, Step}; -use color_eyre::eyre::Result; +use crate::{error::TopgradeError, utils::require, Step}; +use anyhow::Result; +use log::debug; use std::fs; use std::process::Command; -use tracing::debug; pub fn run_macports(ctx: &ExecutionContext) -> Result<()> { require("port")?; let sudo = ctx.sudo().as_ref().unwrap(); print_separator("MacPorts"); - ctx.run_type() - .execute(sudo) - .args(["port", "selfupdate"]) - .status_checked()?; + ctx.run_type().execute(sudo).args(["port", "selfupdate"]).check_run()?; ctx.run_type() .execute(sudo) .args(["port", "-u", "upgrade", "outdated"]) - .status_checked()?; + .check_run()?; if ctx.config().cleanup() { ctx.run_type() .execute(sudo) .args(["port", "-N", "reclaim"]) - .status_checked()?; + .check_run()?; } Ok(()) @@ -34,7 +30,7 @@ pub fn run_mas(run_type: RunType) -> Result<()> { let mas = require("mas")?; print_separator("macOS App Store"); - run_type.execute(mas).arg("upgrade").status_checked() + run_type.execute(mas).arg("upgrade").check_run() } pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> { @@ -62,15 +58,20 @@ pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> { command.arg("--no-scan"); } - command.status_checked() + command.check_run() } fn system_update_available() -> Result { - let output = Command::new("softwareupdate").arg("--list").output_checked_utf8()?; - + let output = Command::new("softwareupdate").arg("--list").output()?; debug!("{:?}", output); - Ok(!output.stderr.contains("No new software available")) + let status = output.status; + if !status.success() { + return Err(TopgradeError::ProcessFailed(status).into()); + } + let string_output = String::from_utf8(output.stderr)?; + debug!("{:?}", string_output); + Ok(!string_output.contains("No new software available")) } pub fn run_sparkle(ctx: &ExecutionContext) -> Result<()> { @@ -82,12 +83,12 @@ pub fn run_sparkle(ctx: &ExecutionContext) -> Result<()> { let probe = Command::new(&sparkle) .args(["--probe", "--application"]) .arg(application.path()) - .output_checked_utf8(); + .check_output(); if probe.is_ok() { let mut command = ctx.run_type().execute(&sparkle); command.args(["bundle", "--check-immediately", "--application"]); command.arg(application.path()); - command.status_checked()?; + command.spawn()?.wait()?; } } Ok(()) diff --git a/src/steps/os/openbsd.rs b/src/steps/os/openbsd.rs index b32c3e1e..dec83d22 100644 --- a/src/steps/os/openbsd.rs +++ b/src/steps/os/openbsd.rs @@ -1,23 +1,17 @@ use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::require_option; -use color_eyre::eyre::Result; +use anyhow::Result; use std::path::PathBuf; pub fn upgrade_openbsd(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo, String::from("No sudo detected"))?; print_separator("OpenBSD Update"); - run_type - .execute(sudo) - .args(&["/usr/sbin/sysupgrade", "-n"]) - .status_checked() + run_type.execute(sudo).args(&["/usr/sbin/sysupgrade", "-n"]).check_run() } pub fn upgrade_packages(sudo: Option<&PathBuf>, run_type: RunType) -> Result<()> { let sudo = require_option(sudo, String::from("No sudo detected"))?; print_separator("OpenBSD Packages"); - run_type - .execute(sudo) - .args(&["/usr/sbin/pkg_add", "-u"]) - .status_checked() + run_type.execute(sudo).args(&["/usr/sbin/pkg_add", "-u"]).check_run() } diff --git a/src/steps/os/unix.rs b/src/steps/os/unix.rs index 9f89df5b..6f20d17d 100644 --- a/src/steps/os/unix.rs +++ b/src/steps/os/unix.rs @@ -1,43 +1,33 @@ +use crate::error::{SkipStep, TopgradeError}; +use crate::execution_context::ExecutionContext; +use crate::executor::{CommandExt, Executor, ExecutorExitStatus, RunType}; +use crate::terminal::print_separator; +#[cfg(not(target_os = "macos"))] +use crate::utils::require_option; +use crate::utils::{require, PathExt}; +use crate::Step; +use anyhow::Result; +use directories::BaseDirs; +use home; +use ini::Ini; +use log::debug; use std::fs; use std::os::unix::fs::MetadataExt; use std::path::PathBuf; use std::process::Command; use std::{env, path::Path}; -use crate::command::CommandExt; -use crate::Step; -use color_eyre::eyre::Result; -use directories::BaseDirs; -use home; -use ini::Ini; -use tracing::debug; - -use crate::error::SkipStep; -use crate::execution_context::ExecutionContext; -#[cfg(any(target_os = "linux", target_os = "macos"))] -use crate::executor::Executor; -use crate::executor::RunType; -use crate::terminal::print_separator; -#[cfg(not(any(target_os = "android", target_os = "macos")))] -use crate::utils::require_option; -use crate::utils::{require, PathExt}; - -#[cfg(any(target_os = "linux", target_os = "macos"))] const INTEL_BREW: &str = "/usr/local/bin/brew"; - -#[cfg(any(target_os = "linux", target_os = "macos"))] const ARM_BREW: &str = "/opt/homebrew/bin/brew"; #[derive(Copy, Clone, Debug)] #[allow(dead_code)] -#[cfg(any(target_os = "linux", target_os = "macos"))] pub enum BrewVariant { Path, MacIntel, MacArm, } -#[cfg(any(target_os = "linux", target_os = "macos"))] impl BrewVariant { fn binary_name(self) -> &'static str { match self { @@ -87,36 +77,30 @@ impl BrewVariant { } } -pub fn run_fisher(run_type: RunType) -> Result<()> { +pub fn run_fisher(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let fish = require("fish")?; - Command::new(&fish) - .args(["-c", "type -t fisher"]) - .output_checked_utf8() - .map(|_| ()) - .map_err(|_| SkipStep("`fisher` is not defined in `fish`".to_owned()))?; - - Command::new(&fish) - .args(["-c", "echo \"$__fish_config_dir/fish_plugins\""]) - .output_checked_utf8() - .and_then(|output| Path::new(&output.stdout.trim()).require().map(|_| ())) - .map_err(|err| SkipStep(format!("`fish_plugins` path doesn't exist: {err}")))?; + if env::var("fisher_path").is_err() { + base_dirs + .home_dir() + .join(".config/fish/functions/fisher.fish") + .require()?; + } print_separator("Fisher"); let version_str = run_type .execute(&fish) .args(["-c", "fisher --version"]) - .output_checked_utf8()? - .stdout; + .check_output()?; debug!("Fisher version: {}", version_str); if version_str.starts_with("fisher version 3.") { // v3 - see https://github.com/topgrade-rs/topgrade/pull/37#issuecomment-1283844506 - run_type.execute(&fish).args(["-c", "fisher"]).status_checked() + run_type.execute(&fish).args(["-c", "fisher"]).check_run() } else { // v4 - run_type.execute(&fish).args(["-c", "fisher update"]).status_checked() + run_type.execute(&fish).args(["-c", "fisher update"]).check_run() } } @@ -128,7 +112,7 @@ pub fn run_bashit(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute("bash") .args(["-lic", &format!("bash-it update {}", ctx.config().bashit_branch())]) - .status_checked() + .check_run() } pub fn run_oh_my_fish(ctx: &ExecutionContext) -> Result<()> { @@ -140,7 +124,7 @@ pub fn run_oh_my_fish(ctx: &ExecutionContext) -> Result<()> { print_separator("oh-my-fish"); - ctx.run_type().execute(fish).args(["-c", "omf update"]).status_checked() + ctx.run_type().execute(fish).args(["-c", "omf update"]).check_run() } pub fn run_pkgin(ctx: &ExecutionContext) -> Result<()> { @@ -151,14 +135,14 @@ pub fn run_pkgin(ctx: &ExecutionContext) -> Result<()> { if ctx.config().yes(Step::Pkgin) { command.arg("-y"); } - command.status_checked()?; + command.check_run()?; let mut command = ctx.run_type().execute(ctx.sudo().as_ref().unwrap()); command.arg(&pkgin).arg("upgrade"); if ctx.config().yes(Step::Pkgin) { command.arg("-y"); } - command.status_checked() + command.check_run() } pub fn run_fish_plug(ctx: &ExecutionContext) -> Result<()> { @@ -170,10 +154,7 @@ pub fn run_fish_plug(ctx: &ExecutionContext) -> Result<()> { print_separator("fish-plug"); - ctx.run_type() - .execute(fish) - .args(["-c", "plug update"]) - .status_checked() + ctx.run_type().execute(fish).args(["-c", "plug update"]).check_run() } /// Upgrades `fundle` and `fundle` plugins. @@ -190,7 +171,7 @@ pub fn run_fundle(ctx: &ExecutionContext) -> Result<()> { ctx.run_type() .execute(fish) .args(["-c", "fundle self-update && fundle update"]) - .status_checked() + .check_run() } #[cfg(not(any(target_os = "android", target_os = "macos")))] @@ -211,10 +192,10 @@ pub fn upgrade_gnome_extensions(ctx: &ExecutionContext) -> Result<()> { "--method", "org.freedesktop.DBus.ListActivatableNames", ]) - .output_checked_utf8()?; + .check_output()?; debug!("Checking for gnome extensions: {}", output); - if !output.stdout.contains("org.gnome.Shell.Extensions") { + if !output.contains("org.gnome.Shell.Extensions") { return Err(SkipStep(String::from("Gnome shell extensions are unregistered in DBus")).into()); } @@ -232,10 +213,9 @@ pub fn upgrade_gnome_extensions(ctx: &ExecutionContext) -> Result<()> { "--method", "org.gnome.Shell.Extensions.CheckForUpdates", ]) - .status_checked() + .check_run() } -#[cfg(any(target_os = "linux", target_os = "macos"))] pub fn run_brew_formula(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()> { #[allow(unused_variables)] let binary_name = require(variant.binary_name())?; @@ -250,18 +230,18 @@ pub fn run_brew_formula(ctx: &ExecutionContext, variant: BrewVariant) -> Result< print_separator(variant.step_title()); let run_type = ctx.run_type(); - variant.execute(run_type).arg("update").status_checked()?; + variant.execute(run_type).arg("update").check_run()?; variant .execute(run_type) .args(["upgrade", "--ignore-pinned", "--formula"]) - .status_checked()?; + .check_run()?; if ctx.config().cleanup() { - variant.execute(run_type).arg("cleanup").status_checked()?; + variant.execute(run_type).arg("cleanup").check_run()?; } if ctx.config().brew_autoremove() { - variant.execute(run_type).arg("autoremove").status_checked()?; + variant.execute(run_type).arg("autoremove").check_run()?; } Ok(()) @@ -279,8 +259,8 @@ pub fn run_brew_cask(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()> let cask_upgrade_exists = variant .execute(RunType::Wet) .args(["--repository", "buo/cask-upgrade"]) - .output_checked_utf8() - .map(|p| Path::new(p.stdout.trim()).exists())?; + .check_output() + .map(|p| Path::new(p.trim()).exists())?; let mut brew_args = vec![]; @@ -296,10 +276,10 @@ pub fn run_brew_cask(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()> } } - variant.execute(run_type).args(&brew_args).status_checked()?; + variant.execute(run_type).args(&brew_args).check_run()?; if ctx.config().cleanup() { - variant.execute(run_type).arg("cleanup").status_checked()?; + variant.execute(run_type).arg("cleanup").check_run()?; } Ok(()) @@ -310,7 +290,7 @@ pub fn run_guix(ctx: &ExecutionContext) -> Result<()> { let run_type = ctx.run_type(); - let output = Command::new(&guix).arg("pull").output_checked_utf8(); + let output = Command::new(&guix).arg("pull").check_output(); debug!("guix pull output: {:?}", output); let should_upgrade = output.is_ok(); debug!("Can Upgrade Guix: {:?}", should_upgrade); @@ -318,7 +298,7 @@ pub fn run_guix(ctx: &ExecutionContext) -> Result<()> { print_separator("Guix"); if should_upgrade { - return run_type.execute(&guix).args(["package", "-u"]).status_checked(); + return run_type.execute(&guix).args(["package", "-u"]).check_run(); } Err(SkipStep(String::from("Guix Pull Failed, Skipping")).into()) } @@ -334,7 +314,7 @@ pub fn run_nix(ctx: &ExecutionContext) -> Result<()> { debug!("nix profile: {:?}", profile_path); let manifest_json_path = profile_path.join("manifest.json"); - let output = Command::new(&nix_env).args(["--query", "nix"]).output_checked_utf8(); + let output = Command::new(&nix_env).args(["--query", "nix"]).check_output(); debug!("nix-env output: {:?}", output); let should_self_upgrade = output.is_ok(); @@ -366,13 +346,13 @@ pub fn run_nix(ctx: &ExecutionContext) -> Result<()> { if should_self_upgrade { if multi_user { - ctx.execute_elevated(&nix, true)?.arg("upgrade-nix").status_checked()?; + ctx.execute_elevated(&nix, true)?.arg("upgrade-nix").check_run()?; } else { - run_type.execute(&nix).arg("upgrade-nix").status_checked()?; + run_type.execute(&nix).arg("upgrade-nix").check_run()?; } } - run_type.execute(nix_channel).arg("--update").status_checked()?; + run_type.execute(nix_channel).arg("--update").check_run()?; if std::path::Path::new(&manifest_json_path).exists() { run_type @@ -380,9 +360,9 @@ pub fn run_nix(ctx: &ExecutionContext) -> Result<()> { .arg("profile") .arg("upgrade") .arg(".*") - .status_checked() + .check_run() } else { - run_type.execute(&nix_env).arg("--upgrade").status_checked() + run_type.execute(&nix_env).arg("--upgrade").check_run() } } @@ -391,40 +371,42 @@ pub fn run_yadm(ctx: &ExecutionContext) -> Result<()> { print_separator("yadm"); - ctx.run_type().execute(yadm).arg("pull").status_checked() + ctx.run_type().execute(yadm).arg("pull").check_run() } pub fn run_asdf(run_type: RunType) -> Result<()> { let asdf = require("asdf")?; print_separator("asdf"); - run_type.execute(&asdf).arg("update").status_checked_with_codes(&[42])?; + let exit_status = run_type.execute(&asdf).arg("update").spawn()?.wait()?; - run_type - .execute(&asdf) - .args(["plugin", "update", "--all"]) - .status_checked() + if let ExecutorExitStatus::Wet(e) = exit_status { + if !(e.success() || e.code().map(|c| c == 42).unwrap_or(false)) { + return Err(TopgradeError::ProcessFailed(e).into()); + } + } + run_type.execute(&asdf).args(["plugin", "update", "--all"]).check_run() } pub fn run_home_manager(run_type: RunType) -> Result<()> { let home_manager = require("home-manager")?; print_separator("home-manager"); - run_type.execute(home_manager).arg("switch").status_checked() + run_type.execute(home_manager).arg("switch").check_run() } pub fn run_tldr(run_type: RunType) -> Result<()> { let tldr = require("tldr")?; print_separator("TLDR"); - run_type.execute(tldr).arg("--update").status_checked() + run_type.execute(tldr).arg("--update").check_run() } pub fn run_pearl(run_type: RunType) -> Result<()> { let pearl = require("pearl")?; print_separator("pearl"); - run_type.execute(pearl).arg("update").status_checked() + run_type.execute(pearl).arg("update").check_run() } pub fn run_sdkman(base_dirs: &BaseDirs, cleanup: bool, run_type: RunType) -> Result<()> { @@ -458,33 +440,27 @@ pub fn run_sdkman(base_dirs: &BaseDirs, cleanup: bool, run_type: RunType) -> Res run_type .execute(&bash) .args(["-c", cmd_selfupdate.as_str()]) - .status_checked()?; + .check_run()?; } let cmd_update = format!("source {} && sdk update", &sdkman_init_path); - run_type - .execute(&bash) - .args(["-c", cmd_update.as_str()]) - .status_checked()?; + run_type.execute(&bash).args(["-c", cmd_update.as_str()]).check_run()?; let cmd_upgrade = format!("source {} && sdk upgrade", &sdkman_init_path); - run_type - .execute(&bash) - .args(["-c", cmd_upgrade.as_str()]) - .status_checked()?; + run_type.execute(&bash).args(["-c", cmd_upgrade.as_str()]).check_run()?; if cleanup { let cmd_flush_archives = format!("source {} && sdk flush archives", &sdkman_init_path); run_type .execute(&bash) .args(["-c", cmd_flush_archives.as_str()]) - .status_checked()?; + .check_run()?; let cmd_flush_temp = format!("source {} && sdk flush temp", &sdkman_init_path); run_type .execute(&bash) .args(["-c", cmd_flush_temp.as_str()]) - .status_checked()?; + .check_run()?; } Ok(()) @@ -495,7 +471,7 @@ pub fn run_bun(ctx: &ExecutionContext) -> Result<()> { print_separator("Bun"); - ctx.run_type().execute(bun).arg("upgrade").status_checked() + ctx.run_type().execute(bun).arg("upgrade").check_run() } /// Update dotfiles with `rcm(7)`. @@ -505,10 +481,10 @@ pub fn run_rcm(ctx: &ExecutionContext) -> Result<()> { let rcup = require("rcup")?; print_separator("rcm"); - ctx.run_type().execute(rcup).arg("-v").status_checked() + ctx.run_type().execute(rcup).arg("-v").check_run() } -pub fn reboot() -> Result<()> { +pub fn reboot() { print!("Rebooting..."); - Command::new("sudo").arg("reboot").status_checked() + Command::new("sudo").arg("reboot").spawn().unwrap().wait().unwrap(); } diff --git a/src/steps/os/windows.rs b/src/steps/os/windows.rs index 909847c9..14d57595 100644 --- a/src/steps/os/windows.rs +++ b/src/steps/os/windows.rs @@ -2,12 +2,11 @@ use std::convert::TryFrom; use std::path::Path; use std::{ffi::OsStr, process::Command}; -use color_eyre::eyre::Result; -use tracing::debug; +use anyhow::Result; +use log::debug; -use crate::command::CommandExt; use crate::execution_context::ExecutionContext; -use crate::executor::RunType; +use crate::executor::{CommandExt, RunType}; use crate::terminal::{print_separator, print_warning}; use crate::utils::require; use crate::{error::SkipStep, steps::git::Repositories}; @@ -35,7 +34,7 @@ pub fn run_chocolatey(ctx: &ExecutionContext) -> Result<()> { command.arg("--yes"); } - command.status_checked() + command.check_run() } pub fn run_winget(ctx: &ExecutionContext) -> Result<()> { @@ -48,10 +47,7 @@ pub fn run_winget(ctx: &ExecutionContext) -> Result<()> { return Err(SkipStep(String::from("Winget is disabled by default")).into()); } - ctx.run_type() - .execute(&winget) - .args(["upgrade", "--all"]) - .status_checked() + ctx.run_type().execute(&winget).args(["upgrade", "--all"]).check_run() } pub fn run_scoop(cleanup: bool, run_type: RunType) -> Result<()> { @@ -59,18 +55,18 @@ pub fn run_scoop(cleanup: bool, run_type: RunType) -> Result<()> { print_separator("Scoop"); - run_type.execute(&scoop).args(["update"]).status_checked()?; - run_type.execute(&scoop).args(["update", "*"]).status_checked()?; + run_type.execute(&scoop).args(["update"]).check_run()?; + run_type.execute(&scoop).args(["update", "*"]).check_run()?; if cleanup { - run_type.execute(&scoop).args(["cleanup", "*"]).status_checked()?; + run_type.execute(&scoop).args(["cleanup", "*"]).check_run()?; } Ok(()) } fn get_wsl_distributions(wsl: &Path) -> Result> { - let output = Command::new(wsl).args(["--list", "-q"]).output_checked_utf8()?.stdout; + let output = Command::new(wsl).args(["--list", "-q"]).check_output()?; Ok(output .lines() .filter(|s| !s.is_empty()) @@ -81,7 +77,7 @@ fn get_wsl_distributions(wsl: &Path) -> Result> { fn upgrade_wsl_distribution(wsl: &Path, dist: &str, ctx: &ExecutionContext) -> Result<()> { let topgrade = Command::new(wsl) .args(["-d", dist, "bash", "-lc", "which topgrade"]) - .output_checked_utf8() + .check_output() .map_err(|_| SkipStep(String::from("Could not find Topgrade installed in WSL")))?; let mut command = ctx.run_type().execute(wsl); @@ -93,7 +89,7 @@ fn upgrade_wsl_distribution(wsl: &Path, dist: &str, ctx: &ExecutionContext) -> R command.arg("-y"); } - command.status_checked() + command.check_run() } pub fn run_wsl_topgrade(ctx: &ExecutionContext) -> Result<()> { @@ -133,17 +129,12 @@ pub fn windows_update(ctx: &ExecutionContext) -> Result<()> { print_separator("Windows Update"); println!("Running Windows Update. Check the control panel for progress."); - ctx.run_type() - .execute(&usoclient) - .arg("ScanInstallWait") - .status_checked()?; - ctx.run_type().execute(&usoclient).arg("StartInstall").status_checked() + ctx.run_type().execute(&usoclient).arg("ScanInstallWait").check_run()?; + ctx.run_type().execute(&usoclient).arg("StartInstall").check_run() } -pub fn reboot() -> Result<()> { - // If this works, it won't return, but if it doesn't work, it may return a useful error - // message. - Command::new("shutdown").args(["/R", "/T", "0"]).status_checked() +pub fn reboot() { + Command::new("shutdown").args(["/R", "/T", "0"]).spawn().ok(); } pub fn insert_startup_scripts(ctx: &ExecutionContext, git_repos: &mut Repositories) -> Result<()> { diff --git a/src/steps/powershell.rs b/src/steps/powershell.rs index f6fe50e3..3d60ea8d 100644 --- a/src/steps/powershell.rs +++ b/src/steps/powershell.rs @@ -3,10 +3,10 @@ use std::path::Path; use std::path::PathBuf; use std::process::Command; -use color_eyre::eyre::Result; +use anyhow::Result; -use crate::command::CommandExt; use crate::execution_context::ExecutionContext; +use crate::executor::CommandExt; use crate::terminal::{is_dumb, print_separator}; use crate::utils::{require_option, which, PathExt}; use crate::Step; @@ -27,8 +27,8 @@ impl Powershell { let profile = path.as_ref().and_then(|path| { Command::new(path) .args(["-NoProfile", "-Command", "Split-Path $profile"]) - .output_checked_utf8() - .map(|output| PathBuf::from(output.stdout.trim())) + .check_output() + .map(|output| PathBuf::from(output.trim())) .and_then(|p| p.require()) .ok() }); @@ -52,8 +52,8 @@ impl Powershell { "-Command", &format!("Get-Module -ListAvailable {}", command), ]) - .output_checked_utf8() - .map(|result| !result.stdout.is_empty()) + .check_output() + .map(|result| !result.is_empty()) .unwrap_or(false) } @@ -81,7 +81,7 @@ impl Powershell { .execute(powershell) // This probably doesn't need `shell_words::join`. .args(["-NoProfile", "-Command", &cmd.join(" ")]) - .status_checked() + .check_run() } #[cfg(windows)] @@ -119,6 +119,6 @@ impl Powershell { } ), ]) - .status_checked() + .check_run() } } diff --git a/src/steps/remote/ssh.rs b/src/steps/remote/ssh.rs index c274766d..c94552fb 100644 --- a/src/steps/remote/ssh.rs +++ b/src/steps/remote/ssh.rs @@ -1,8 +1,6 @@ -use color_eyre::eyre::Result; +use anyhow::Result; -use crate::{ - command::CommandExt, error::SkipStep, execution_context::ExecutionContext, terminal::print_separator, utils, -}; +use crate::{error::SkipStep, execution_context::ExecutionContext, terminal::print_separator, utils}; fn prepare_async_ssh_command(args: &mut Vec<&str>) { args.insert(0, "ssh"); @@ -26,7 +24,7 @@ pub fn ssh_step(ctx: &ExecutionContext, hostname: &str) -> Result<()> { #[cfg(unix)] { prepare_async_ssh_command(&mut args); - crate::tmux::run_command(ctx, hostname, &shell_words::join(args))?; + crate::tmux::run_command(ctx, &shell_words::join(args))?; Err(SkipStep(String::from("Remote Topgrade launched in Tmux")).into()) } @@ -49,6 +47,6 @@ pub fn ssh_step(ctx: &ExecutionContext, hostname: &str) -> Result<()> { print_separator(format!("Remote ({})", hostname)); println!("Connecting to {}...", hostname); - ctx.run_type().execute(ssh).args(&args).status_checked() + ctx.run_type().execute(ssh).args(&args).check_run() } } diff --git a/src/steps/remote/vagrant.rs b/src/steps/remote/vagrant.rs index b7fbfb18..712a2be9 100644 --- a/src/steps/remote/vagrant.rs +++ b/src/steps/remote/vagrant.rs @@ -2,13 +2,13 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::{fmt::Display, rc::Rc, str::FromStr}; -use color_eyre::eyre::Result; +use anyhow::Result; +use log::{debug, error}; use regex::Regex; use strum::EnumString; -use tracing::{debug, error}; -use crate::command::CommandExt; use crate::execution_context::ExecutionContext; +use crate::executor::CommandExt; use crate::terminal::print_separator; use crate::{error::SkipStep, utils, Step}; @@ -61,11 +61,10 @@ impl Vagrant { let output = Command::new(&self.path) .arg("status") .current_dir(directory) - .output_checked_utf8()?; + .check_output()?; debug!("Vagrant output in {}: {}", directory, output); let boxes = output - .stdout .split('\n') .skip(2) .take_while(|line| !(line.is_empty() || line.starts_with('\r'))) @@ -116,7 +115,7 @@ impl<'a> TemporaryPowerOn<'a> { .execute(vagrant) .args([subcommand, &vagrant_box.name]) .current_dir(vagrant_box.path.clone()) - .status_checked()?; + .check_run()?; Ok(TemporaryPowerOn { vagrant, vagrant_box, @@ -143,7 +142,7 @@ impl<'a> Drop for TemporaryPowerOn<'a> { .execute(self.vagrant) .args([subcommand, &self.vagrant_box.name]) .current_dir(self.vagrant_box.path.clone()) - .status_checked() + .check_run() .ok(); } } @@ -200,7 +199,7 @@ pub fn topgrade_vagrant_box(ctx: &ExecutionContext, vagrant_box: &VagrantBox) -> .execute(&vagrant.path) .current_dir(&vagrant_box.path) .args(["ssh", "-c", &command]) - .status_checked() + .check_run() } pub fn upgrade_vagrant_boxes(ctx: &ExecutionContext) -> Result<()> { @@ -209,12 +208,12 @@ pub fn upgrade_vagrant_boxes(ctx: &ExecutionContext) -> Result<()> { let outdated = Command::new(&vagrant) .args(["box", "outdated", "--global"]) - .output_checked_utf8()?; + .check_output()?; let re = Regex::new(r"\* '(.*?)' for '(.*?)' is outdated").unwrap(); let mut found = false; - for ele in re.captures_iter(&outdated.stdout) { + for ele in re.captures_iter(&outdated) { found = true; let _ = ctx .run_type() @@ -223,16 +222,13 @@ pub fn upgrade_vagrant_boxes(ctx: &ExecutionContext) -> Result<()> { .arg(ele.get(1).unwrap().as_str()) .arg("--provider") .arg(ele.get(2).unwrap().as_str()) - .status_checked(); + .check_run(); } if !found { println!("No outdated boxes") } else { - ctx.run_type() - .execute(&vagrant) - .args(["box", "prune"]) - .status_checked()?; + ctx.run_type().execute(&vagrant).args(["box", "prune"]).check_run()?; } Ok(()) diff --git a/src/steps/tmux.rs b/src/steps/tmux.rs index 61d3bb46..388b65b4 100644 --- a/src/steps/tmux.rs +++ b/src/steps/tmux.rs @@ -1,22 +1,16 @@ -use std::env; -use std::path::PathBuf; -use std::process::Command; - -use color_eyre::eyre::eyre; -use color_eyre::eyre::Context; -use color_eyre::eyre::Result; -use directories::BaseDirs; - -use crate::command::CommandExt; use crate::executor::RunType; use crate::terminal::print_separator; use crate::{ execution_context::ExecutionContext, - utils::{which, PathExt}, + utils::{which, Check, PathExt}, }; - -#[cfg(unix)] -use std::os::unix::process::CommandExt as _; +use anyhow::Result; +use directories::BaseDirs; +use std::env; +use std::io; +use std::os::unix::process::CommandExt; +use std::path::PathBuf; +use std::process::{exit, Command}; pub fn run_tpm(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let tpm = base_dirs @@ -26,7 +20,7 @@ pub fn run_tpm(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("tmux plugins"); - run_type.execute(tpm).arg("all").status_checked() + run_type.execute(tpm).arg("all").check_run() } struct Tmux { @@ -50,130 +44,73 @@ impl Tmux { command } - fn has_session(&self, session_name: &str) -> Result { + fn has_session(&self, session_name: &str) -> Result { Ok(self .build() .args(["has-session", "-t", session_name]) - .output_checked_with(|_| Ok(()))? + .output()? .status .success()) } - /// Create a new tmux session with the given name, running the given command. - /// The command is passed to `sh` (see "shell-command arguments are sh(1) commands" in the - /// `tmux(1)` man page). - fn new_session(&self, session_name: &str, window_name: &str, command: &str) -> Result<()> { - let _ = self + fn new_session(&self, session_name: &str) -> Result { + Ok(self .build() - // `-d`: initial size comes from the global `default-size` option (instead - // of passing `-x` and `-y` arguments. - // (What do those even do?) - // `-s`: session name - // `-n`: window name (always `topgrade`) - .args(["new-session", "-d", "-s", session_name, "-n", window_name, command]) - .output_checked()?; + .args(["new-session", "-d", "-s", session_name, "-n", "dummy"]) + .spawn()? + .wait()? + .success()) + } + + fn run_in_session(&self, command: &str) -> Result<()> { + self.build() + .args(["new-window", "-t", "topgrade", command]) + .spawn()? + .wait()? + .check()?; + Ok(()) } - - /// Like [`new_session`] but it appends a digit to the session name (if necessary) to - /// avoid duplicate session names. - /// - /// The session name is returned. - fn new_unique_session(&self, session_name: &str, window_name: &str, command: &str) -> Result { - let mut session = session_name.to_owned(); - for i in 1.. { - if !self - .has_session(&session) - .context("Error determining if a tmux session exists")? - { - self.new_session(&session, window_name, command) - .context("Error running Topgrade in tmux")?; - return Ok(session); - } - session = format!("{session_name}-{i}"); - } - unreachable!() - } - - /// Create a new window in the given tmux session, running the given command. - fn new_window(&self, session_name: &str, window_name: &str, command: &str) -> Result<()> { - self.build() - // `-d`: initial size comes from the global `default-size` option (instead - // of passing `-x` and `-y` arguments. - // (What do those even do?) - // `-s`: session name - // `-n`: window name - .args([ - "new-window", - "-a", - "-t", - &format!("{session_name}:{window_name}"), - "-n", - window_name, - command, - ]) - .env_remove("TMUX") - .status_checked() - } - - fn window_indices(&self, session_name: &str) -> Result> { - self.build() - .args(["list-windows", "-F", "#{window_index}", "-t", session_name]) - .output_checked_utf8()? - .stdout - .lines() - .map(|l| l.parse()) - .collect::, _>>() - .context("Failed to compute tmux windows") - } } -pub fn run_in_tmux(args: Vec) -> Result<()> { +pub fn run_in_tmux(args: Vec) -> ! { let command = { let mut command = vec![ String::from("env"), String::from("TOPGRADE_KEEP_END=1"), String::from("TOPGRADE_INSIDE_TMUX=1"), ]; - // TODO: Should we use `topgrade` instead of the first argument here, which may be - // a local path? command.extend(env::args()); shell_words::join(command) }; let tmux = Tmux::new(args); - // Find an unused session and run `topgrade` in it with the current command's arguments. - let session_name = "topgrade"; - let window_name = "topgrade"; - let session = tmux.new_unique_session(session_name, window_name, &command)?; + if !tmux.has_session("topgrade").expect("Error detecting a tmux session") { + tmux.new_session("topgrade").expect("Error creating a tmux session"); + } + + tmux.run_in_session(&command).expect("Error running Topgrade in tmux"); + tmux.build() + .args(["kill-window", "-t", "topgrade:dummy"]) + .output() + .expect("Error killing the dummy tmux window"); - // Only attach to the newly-created session if we're not currently in a tmux session. if env::var("TMUX").is_err() { - let err = tmux.build().args(["attach-session", "-t", &session]).exec(); - Err(eyre!("{err}")).context("Failed to `execvp(3)` tmux") + let err = tmux.build().args(["attach", "-t", "topgrade"]).exec(); + panic!("{:?}", err); } else { println!("Topgrade launched in a new tmux session"); - Ok(()) + exit(0); } } -pub fn run_command(ctx: &ExecutionContext, window_name: &str, command: &str) -> Result<()> { - let tmux = Tmux::new(ctx.config().tmux_arguments()?); - - match ctx.get_tmux_session() { - Some(session_name) => { - let indices = tmux.window_indices(&session_name)?; - let last_window = indices - .iter() - .last() - .ok_or_else(|| eyre!("tmux session {session_name} has no windows"))?; - tmux.new_window(&session_name, &format!("{last_window}"), command)?; - } - None => { - let name = tmux.new_unique_session("topgrade", window_name, command)?; - ctx.set_tmux_session(name); - } - } - Ok(()) +pub fn run_command(ctx: &ExecutionContext, command: &str) -> Result<()> { + Tmux::new(ctx.config().tmux_arguments()?) + .build() + .args(["new-window", "-a", "-t", "topgrade:1", command]) + .env_remove("TMUX") + .spawn()? + .wait()? + .check() } diff --git a/src/steps/toolbx.rs b/src/steps/toolbx.rs index 1c8a7583..fcf6b247 100644 --- a/src/steps/toolbx.rs +++ b/src/steps/toolbx.rs @@ -1,20 +1,17 @@ -use color_eyre::eyre::Result; +use anyhow::Result; -use crate::command::CommandExt; use crate::config::Step; use crate::terminal::print_separator; use crate::{execution_context::ExecutionContext, utils::require}; +use log::debug; use std::path::Path; use std::{path::PathBuf, process::Command}; -use tracing::debug; fn list_toolboxes(toolbx: &Path) -> Result> { - let output = Command::new(toolbx) - .args(["list", "--containers"]) - .output_checked_utf8()?; + let output = Command::new(toolbx).args(["list", "--containers"]).output()?; + let output_str = String::from_utf8(output.stdout)?; - let proc: Vec = output - .stdout + let proc: Vec = output_str .lines() // Skip the first line since that contains only status information .skip(1) @@ -57,7 +54,7 @@ pub fn run_toolbx(ctx: &ExecutionContext) -> Result<()> { args.push("--yes"); } - ctx.run_type().execute(&toolbx).args(&args).status_checked()?; + let _output = ctx.run_type().execute(&toolbx).args(&args).check_run(); } Ok(()) diff --git a/src/steps/upgrade.vim b/src/steps/upgrade.vim index 5eac813e..28e3df9f 100644 --- a/src/steps/upgrade.vim +++ b/src/steps/upgrade.vim @@ -38,14 +38,6 @@ if exists(":CocUpdateSync") CocUpdateSync endif -" TODO: Should this be after `PackerSync`? -" Not sure how to sequence this after Packer without doing something weird -" with that `PackerComplete` autocommand. -if exists(":TSUpdate") - echo "TreeSitter Update" - TSUpdate -endif - if exists(':PackerSync') echo "Packer" autocmd User PackerComplete quitall diff --git a/src/steps/vim.rs b/src/steps/vim.rs index 42b1a389..4a7538fc 100644 --- a/src/steps/vim.rs +++ b/src/steps/vim.rs @@ -1,20 +1,19 @@ -use crate::command::CommandExt; use crate::error::{SkipStep, TopgradeError}; -use color_eyre::eyre::Result; +use anyhow::Result; -use crate::executor::{Executor, ExecutorOutput, RunType}; +use crate::executor::{CommandExt, Executor, ExecutorOutput, RunType}; use crate::terminal::print_separator; use crate::{ execution_context::ExecutionContext, utils::{require, PathExt}, }; use directories::BaseDirs; +use log::debug; use std::path::PathBuf; use std::{ io::{self, Write}, process::Command, }; -use tracing::debug; const UPGRADE_VIM: &str = include_str!("upgrade.vim"); @@ -64,7 +63,7 @@ fn upgrade(command: &mut Executor, ctx: &ExecutionContext) -> Result<()> { } if !status.success() { - return Err(TopgradeError::ProcessFailed(command.get_program(), status).into()); + return Err(TopgradeError::ProcessFailed(status).into()); } else { println!("Plugins upgraded") } @@ -85,22 +84,22 @@ pub fn upgrade_ultimate_vimrc(ctx: &ExecutionContext) -> Result<()> { .execute(&git) .current_dir(&config_dir) .args(["reset", "--hard"]) - .status_checked()?; + .check_run()?; ctx.run_type() .execute(&git) .current_dir(&config_dir) .args(["clean", "-d", "--force"]) - .status_checked()?; + .check_run()?; ctx.run_type() .execute(&git) .current_dir(&config_dir) .args(["pull", "--rebase"]) - .status_checked()?; + .check_run()?; ctx.run_type() .execute(python) .current_dir(config_dir) .arg(update_plugins) - .status_checked()?; + .check_run()?; Ok(()) } @@ -108,8 +107,8 @@ pub fn upgrade_ultimate_vimrc(ctx: &ExecutionContext) -> Result<()> { pub fn upgrade_vim(base_dirs: &BaseDirs, ctx: &ExecutionContext) -> Result<()> { let vim = require("vim")?; - let output = Command::new(&vim).arg("--version").output_checked_utf8()?; - if !output.stdout.starts_with("VIM") { + let output = Command::new(&vim).arg("--version").check_output()?; + if !output.starts_with("VIM") { return Err(SkipStep(String::from("vim binary might be actually nvim")).into()); } @@ -148,5 +147,5 @@ pub fn run_voom(_base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("voom"); - run_type.execute(voom).arg("update").status_checked() + run_type.execute(voom).arg("update").check_run() } diff --git a/src/steps/zsh.rs b/src/steps/zsh.rs index 4ffdcc30..9787c259 100644 --- a/src/steps/zsh.rs +++ b/src/steps/zsh.rs @@ -1,18 +1,15 @@ -use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; - -use color_eyre::eyre::Result; -use directories::BaseDirs; -use tracing::debug; -use walkdir::WalkDir; - -use crate::command::CommandExt; use crate::execution_context::ExecutionContext; -use crate::executor::RunType; +use crate::executor::{CommandExt, RunType}; use crate::git::Repositories; use crate::terminal::print_separator; use crate::utils::{require, PathExt}; +use anyhow::Result; +use directories::BaseDirs; +use log::debug; +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; +use walkdir::WalkDir; pub fn run_zr(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let zsh = require("zsh")?; @@ -22,7 +19,7 @@ pub fn run_zr(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("zr"); let cmd = format!("source {} && zr --update", zshrc(base_dirs).display()); - run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).status_checked() + run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).check_run() } pub fn zshrc(base_dirs: &BaseDirs) -> PathBuf { @@ -37,7 +34,7 @@ pub fn run_antibody(run_type: RunType) -> Result<()> { print_separator("antibody"); - run_type.execute(antibody).arg("update").status_checked() + run_type.execute(antibody).arg("update").check_run() } pub fn run_antigen(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -51,7 +48,7 @@ pub fn run_antigen(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("antigen"); let cmd = format!("source {} && (antigen selfupdate ; antigen update)", zshrc.display()); - run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).status_checked() + run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).check_run() } pub fn run_zgenom(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -65,7 +62,7 @@ pub fn run_zgenom(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("zgenom"); let cmd = format!("source {} && zgenom selfupdate && zgenom update", zshrc.display()); - run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).status_checked() + run_type.execute(zsh).args(["-l", "-c", cmd.as_str()]).check_run() } pub fn run_zplug(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -79,10 +76,7 @@ pub fn run_zplug(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("zplug"); - run_type - .execute(zsh) - .args(["-i", "-c", "zplug update"]) - .status_checked() + run_type.execute(zsh).args(["-i", "-c", "zplug update"]).check_run() } pub fn run_zinit(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -97,7 +91,7 @@ pub fn run_zinit(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("zinit"); let cmd = format!("source {} && zinit self-update && zinit update --all", zshrc.display(),); - run_type.execute(zsh).args(["-i", "-c", cmd.as_str()]).status_checked() + run_type.execute(zsh).args(["-i", "-c", cmd.as_str()]).check_run() } pub fn run_zi(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -109,7 +103,7 @@ pub fn run_zi(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { print_separator("zi"); let cmd = format!("source {} && zi self-update && zi update --all", zshrc.display(),); - run_type.execute(zsh).args(["-i", "-c", &cmd]).status_checked() + run_type.execute(zsh).args(["-i", "-c", &cmd]).check_run() } pub fn run_zim(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { @@ -117,10 +111,8 @@ pub fn run_zim(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { env::var("ZIM_HOME") .or_else(|_| { Command::new("zsh") - // TODO: Should these be quoted? .args(["-c", "[[ -n ${ZIM_HOME} ]] && print -n ${ZIM_HOME}"]) - .output_checked_utf8() - .map(|o| o.stdout) + .check_output() }) .map(PathBuf::from) .unwrap_or_else(|_| base_dirs.home_dir().join(".zim")) @@ -131,7 +123,7 @@ pub fn run_zim(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { run_type .execute(zsh) .args(["-i", "-c", "zimfw upgrade && zimfw update"]) - .status_checked() + .check_run() } pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> { @@ -143,10 +135,8 @@ pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> { let custom_dir = env::var::<_>("ZSH_CUSTOM") .or_else(|_| { Command::new("zsh") - // TODO: Should these be quoted? .args(["-c", "test $ZSH_CUSTOM && echo -n $ZSH_CUSTOM"]) - .output_checked_utf8() - .map(|o| o.stdout) + .check_output() }) .map(PathBuf::from) .unwrap_or_else(|e| { @@ -178,5 +168,5 @@ pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> { .execute("zsh") .env("ZSH", &oh_my_zsh) .arg(&oh_my_zsh.join("tools/upgrade.sh")) - .status_checked_with_codes(&[80]) + .check_run_with_codes(&[80]) } diff --git a/src/terminal.rs b/src/terminal.rs index 8d17b3ad..b6273a8a 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -8,17 +8,14 @@ use std::sync::Mutex; use std::time::Duration; use chrono::{Local, Timelike}; -use color_eyre::eyre; -use color_eyre::eyre::Context; use console::{style, Key, Term}; use lazy_static::lazy_static; +use log::{debug, error}; #[cfg(target_os = "macos")] use notify_rust::{Notification, Timeout}; -use tracing::{debug, error}; #[cfg(windows)] use which_crate::which; -use crate::command::CommandExt; use crate::report::StepResult; #[cfg(target_os = "linux")] use crate::utils::which; @@ -37,8 +34,13 @@ pub fn shell() -> &'static str { which("pwsh").map(|_| "pwsh").unwrap_or("powershell") } -pub fn run_shell() -> eyre::Result<()> { - Command::new(shell()).env("IN_TOPGRADE", "1").status_checked() +pub fn run_shell() { + Command::new(shell()) + .env("IN_TOPGRADE", "1") + .spawn() + .unwrap() + .wait() + .unwrap(); } struct Terminal { @@ -104,9 +106,7 @@ impl Terminal { } command.args(["-a", "Topgrade", "Topgrade"]); command.arg(message.as_ref()); - if let Err(err) = command.output_checked() { - tracing::error!("{err:?}"); - } + command.output().ok(); } } } @@ -163,19 +163,6 @@ impl Terminal { } } - #[allow(dead_code)] - fn print_error, Q: AsRef>(&mut self, key: Q, message: P) { - let key = key.as_ref(); - let message = message.as_ref(); - self.term - .write_fmt(format_args!( - "{} {}", - style(format!("{} failed:", key)).red().bold(), - message - )) - .ok(); - } - #[allow(dead_code)] fn print_warning>(&mut self, message: P) { let message = message.as_ref(); @@ -227,7 +214,7 @@ impl Terminal { } } #[allow(unused_variables)] - fn should_retry(&mut self, interrupted: bool, step_name: &str) -> eyre::Result { + fn should_retry(&mut self, interrupted: bool, step_name: &str) -> Result { if self.width.is_none() { return Ok(false); } @@ -238,31 +225,29 @@ impl Terminal { self.notify_desktop(format!("{} failed", step_name), None); - let prompt_inner = style(format!("{}Retry? (y)es/(N)o/(s)hell/(q)uit", self.prefix)) - .yellow() - .bold(); - - self.term.write_fmt(format_args!("\n{}", prompt_inner)).ok(); + self.term + .write_fmt(format_args!( + "\n{}", + style(format!("{}Retry? (y)es/(N)o/(s)hell/(q)uit", self.prefix)) + .yellow() + .bold() + )) + .ok(); let answer = loop { match self.term.read_key() { Ok(Key::Char('y')) | Ok(Key::Char('Y')) => break Ok(true), Ok(Key::Char('s')) | Ok(Key::Char('S')) => { println!("\n\nDropping you to shell. Fix what you need and then exit the shell.\n"); - if let Err(err) = run_shell().context("Failed to run shell") { - self.term.write_fmt(format_args!("{err:?}\n{}", prompt_inner)).ok(); - } else { - break Ok(true); - } + run_shell(); + break Ok(true); } Ok(Key::Char('n')) | Ok(Key::Char('N')) | Ok(Key::Enter) => break Ok(false), Err(e) => { error!("Error reading from terminal: {}", e); break Ok(false); } - Ok(Key::Char('q')) | Ok(Key::Char('Q')) => { - return Err(io::Error::from(io::ErrorKind::Interrupted)).context("Quit from user input") - } + Ok(Key::Char('q')) | Ok(Key::Char('Q')) => return Err(io::Error::from(io::ErrorKind::Interrupted)), _ => (), } }; @@ -283,7 +268,7 @@ impl Default for Terminal { } } -pub fn should_retry(interrupted: bool, step_name: &str) -> eyre::Result { +pub fn should_retry(interrupted: bool, step_name: &str) -> Result { TERMINAL.lock().unwrap().should_retry(interrupted, step_name) } @@ -291,11 +276,6 @@ pub fn print_separator>(message: P) { TERMINAL.lock().unwrap().print_separator(message) } -#[allow(dead_code)] -pub fn print_error, Q: AsRef>(key: Q, message: P) { - TERMINAL.lock().unwrap().print_error(key, message) -} - #[allow(dead_code)] pub fn print_warning>(message: P) { TERMINAL.lock().unwrap().print_warning(message) diff --git a/src/utils.rs b/src/utils.rs index 326e4ad6..3ea1fb3f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,11 +1,46 @@ -use crate::error::SkipStep; -use color_eyre::eyre::Result; +use crate::error::{SkipStep, TopgradeError}; +use anyhow::Result; +use log::{debug, error}; use std::env; use std::ffi::OsStr; use std::fmt::Debug; use std::path::{Path, PathBuf}; -use tracing::{debug, error}; +use std::process::{ExitStatus, Output}; + +pub trait Check { + fn check(self) -> Result<()>; +} + +impl Check for Output { + fn check(self) -> Result<()> { + self.status.check() + } +} + +pub trait CheckWithCodes { + fn check_with_codes(self, codes: &[i32]) -> Result<()>; +} + +// Anything that implements CheckWithCodes also implements check +// if check_with_codes is given an empty array of codes to check +impl Check for T { + fn check(self) -> Result<()> { + self.check_with_codes(&[]) + } +} + +impl CheckWithCodes for ExitStatus { + fn check_with_codes(self, codes: &[i32]) -> Result<()> { + // Set the default to be -1 because the option represents a signal termination + let code = self.code().unwrap_or(-1); + if self.success() || codes.contains(&code) { + Ok(()) + } else { + Err(TopgradeError::ProcessFailed(self).into()) + } + } +} pub trait PathExt where @@ -107,55 +142,3 @@ pub fn require_option(option: Option, cause: String) -> Result { Err(SkipStep(cause).into()) } } - -/* sys-info-rs - * - * Copyright (c) 2015 Siyu Wang - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#[cfg(target_family = "unix")] -pub fn hostname() -> Result { - use std::ffi; - extern crate libc; - - unsafe { - let buf_size = libc::sysconf(libc::_SC_HOST_NAME_MAX) as usize; - let mut buf = Vec::::with_capacity(buf_size + 1); - - if libc::gethostname(buf.as_mut_ptr() as *mut libc::c_char, buf_size) < 0 { - return Err(SkipStep(format!("Failed to get hostname: {}", std::io::Error::last_os_error())).into()); - } - let hostname_len = libc::strnlen(buf.as_ptr() as *const libc::c_char, buf_size); - buf.set_len(hostname_len); - - Ok(ffi::CString::new(buf).unwrap().into_string().unwrap()) - } -} - -#[cfg(target_family = "windows")] -pub fn hostname() -> Result { - use crate::command::CommandExt; - use std::process::Command; - - Command::new("hostname") - .output_checked_utf8() - .map_err(|err| SkipStep(format!("Failed to get hostname: {}", err)).into()) - .map(|output| output.stdout.trim().to_owned()) -}