Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package topgrade for openSUSE:Factory 
checked in at 2026-02-03 21:34:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/topgrade (Old)
 and      /work/SRC/openSUSE:Factory/.topgrade.new.1995 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "topgrade"

Tue Feb  3 21:34:23 2026 rev:19 rq:1330741 version:16.9.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/topgrade/topgrade.changes        2026-01-12 
10:31:18.346163160 +0100
+++ /work/SRC/openSUSE:Factory/.topgrade.new.1995/topgrade.changes      
2026-02-03 21:35:13.866021983 +0100
@@ -1,0 +2,36 @@
+Tue Feb 03 11:11:40 UTC 2026 - Gerald Chen <[email protected]>
+
+- Update to version 16.9.0:
+  * chore: release v16.9.0 (#1710)
+  * chore(pre-commit): autoupdate (#1723)
+  * chore(deps): update github/codeql-action action to v4.32.1 (#1722)
+  * chore(deps): update vmactions/openbsd-vm digest to a23337e (#1720)
+  * chore(deps): update rust crate notify-rust to ~4.12.0 (#1721)
+  * refactor(system,home-manager): make matches more explicit (#1719)
+  * fix: add missing import (#1718)
+  * fix(nix): make nix-{channel,env} optional for flake-enabled systems (#1716)
+  * feat(system,home_manager): integrate nh into nix-based updaters (#1712)
+  * chore(pre-commit): autoupdate (#1704)
+  * chore(deps): update release-plz/action digest to e592230 (#1698)
+  * chore(deps): update actions/checkout action to v6.0.2 (#1699)
+  * chore(deps): update github/codeql-action action to v4.32.0 (#1700)
+  * chore(deps): update actions/attest-build-provenance action to v3.2.0 
(#1703)
+  * chore(deps): update vmactions/openbsd-vm digest to f5b9bc1 (#1706)
+  * chore(renovate): disable rate-limiting (#1697)
+  * chore(renovate): disable rate-limiting (#1696)
+  * chore(deps): update dependency rust to v1.93.0 (#1695)
+  * chore(renovate): add rust-toolchain updating (#1694)
+  * chore: pin toolchain to stable instead of MSRV, add MSRV testing, simplify 
CI workflow (#1690)
+  * chore(pre-commit): autoupdate (#1687)
+  * feat(cargo): add `git` and `quiet` options (#1685)
+  * chore(renovate): use preset for lockfile config (#1684)
+  * chore(pre-commit): autoupdate (#1682)
+  * chore(deps): update github/codeql-action action to v4.31.10 (#1680)
+  * chore(deb): update copyright
+  * chore(deps): unpin toml
+  * docs(installation): add Pacstall Ubuntu package to README (#1676)
+  * chore(deps): update rust crate toml to v0.9.11 (#1675)
+  * ci(release): fix OpenBSD release job (#1674)
+  * ci(release): fix OpenBSD release job (#1672)
+
+-------------------------------------------------------------------

Old:
----
  topgrade-16.8.0.tar.zst

New:
----
  topgrade-16.9.0.tar.zst

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ topgrade.spec ++++++
--- /var/tmp/diff_new_pack.376kzC/_old  2026-02-03 21:35:15.678098138 +0100
+++ /var/tmp/diff_new_pack.376kzC/_new  2026-02-03 21:35:15.682098307 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           topgrade
-Version:        16.8.0
+Version:        16.9.0
 Release:        0
 Summary:        Upgrade all the things
 License:        GPL-3.0-only

++++++ _service ++++++
--- /var/tmp/diff_new_pack.376kzC/_old  2026-02-03 21:35:15.726100156 +0100
+++ /var/tmp/diff_new_pack.376kzC/_new  2026-02-03 21:35:15.734100492 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/topgrade-rs/topgrade.git</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="scm">git</param>
-    <param name="revision">v16.8.0</param>
+    <param name="revision">v16.9.0</param>
     <param name="match-tag">*</param>
     <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param>
     <param name="versionrewrite-replacement">\1</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.376kzC/_old  2026-02-03 21:35:15.774102173 +0100
+++ /var/tmp/diff_new_pack.376kzC/_new  2026-02-03 21:35:15.782102510 +0100
@@ -1,7 +1,7 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/topgrade-rs/topgrade.git</param>
-              <param 
name="changesrevision">0dfc6dd72c59d7628d6d0c8b6bcc018b729f1fc3</param></service><service
 name="tar_scm">
+              <param 
name="changesrevision">dce9b6bf8933ba2b3da186bc19ab3462b65f2395</param></service><service
 name="tar_scm">
                 <param 
name="url">https://ghproxy.net/https://github.com/topgrade-rs/topgrade.git</param>
               <param 
name="changesrevision">ef0a0d69bbe0cb08d6c4930ee18b734e03c215fb</param></service></servicedata>
 (No newline at EOF)

++++++ topgrade-16.8.0.tar.zst -> topgrade-16.9.0.tar.zst ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/.github/renovate.json5 
new/topgrade-16.9.0/.github/renovate.json5
--- old/topgrade-16.8.0/.github/renovate.json5  2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/.github/renovate.json5  2026-02-03 11:49:42.000000000 
+0100
@@ -4,8 +4,21 @@
         "config:best-practices",
         ":semanticCommits",
         ":semanticCommitTypeAll(chore)",
+        ":maintainLockFilesWeekly",
+        ":disableRateLimiting",
+    ],
+    customManagers: [
+        {
+            customType: "regex",
+            managerFilePatterns: [
+                "/(^|/)rust-toolchain\\.toml?$/",
+            ],
+            matchStrings: [
+                "channel\\s*=\\s*\"(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)\"",
+            ],
+            depNameTemplate: "rust",
+            packageNameTemplate: "rust-lang/rust",
+            datasourceTemplate: "github-tags",
+        },
     ],
-    lockFileMaintenance: {
-        enabled: true,
-    },
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/topgrade-16.8.0/.github/workflows/check_config_creation_if_not_exists.yml 
new/topgrade-16.9.0/.github/workflows/check_config_creation_if_not_exists.yml
--- 
old/topgrade-16.8.0/.github/workflows/check_config_creation_if_not_exists.yml   
    2026-01-09 13:43:16.000000000 +0100
+++ 
new/topgrade-16.9.0/.github/workflows/check_config_creation_if_not_exists.yml   
    2026-02-03 11:49:42.000000000 +0100
@@ -15,7 +15,7 @@
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/.github/workflows/check_i18n.yml 
new/topgrade-16.9.0/.github/workflows/check_i18n.yml
--- old/topgrade-16.8.0/.github/workflows/check_i18n.yml        2026-01-09 
13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/.github/workflows/check_i18n.yml        2026-02-03 
11:49:42.000000000 +0100
@@ -14,7 +14,7 @@
     runs-on: ubuntu-latest
     steps:
       - name: Checkout code
-        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/topgrade-16.8.0/.github/workflows/check_security_vulnerability.yml 
new/topgrade-16.9.0/.github/workflows/check_security_vulnerability.yml
--- old/topgrade-16.8.0/.github/workflows/check_security_vulnerability.yml      
2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/.github/workflows/check_security_vulnerability.yml      
2026-02-03 11:49:42.000000000 +0100
@@ -24,7 +24,7 @@
       security-events: write
     steps:
       - name: Checkout code
-        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -32,6 +32,6 @@
         uses: 
microsoft/DevSkim-Action@4b5047945a44163b94642a1cecc0d93a3f428cc6 # v1.0.16
 
       - name: Upload DevSkim scan results to GitHub Security tab
-        uses: 
github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # 
v4.31.9
+        uses: 
github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # 
v4.32.1
         with:
           sarif_file: devskim-results.sarif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/.github/workflows/ci.yml 
new/topgrade-16.9.0/.github/workflows/ci.yml
--- old/topgrade-16.8.0/.github/workflows/ci.yml        2026-01-09 
13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/.github/workflows/ci.yml        2026-02-03 
11:49:42.000000000 +0100
@@ -1,14 +1,20 @@
+name: Rust CI
+
 on:
   pull_request:
   push:
     branches:
       - main
 
-name: CI
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
 
 env:
   CROSS_VER: '0.2.5'
   CARGO_NET_RETRY: 3
+  CARGO_TERM_COLOR: always
+  RUST_BACKTRACE: full
 
 permissions:
   contents: read
@@ -18,28 +24,24 @@
     shell: bash
 
 jobs:
-  fmt:
-    name: Rustfmt
+  format:
+    name: Format (cargo fmt)
     runs-on: ubuntu-latest
     steps:
-      - name: Checkout code
-        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
-      - name: Run cargo fmt
-        env:
-          TERM: xterm-256color
-        run: |
-          rustup component add rustfmt
-          cargo fmt --all -- --check
+      - run: rustup component add rustfmt
+
+      - name: cargo fmt
+        run: cargo fmt --all -- --check
 
   custom-checks:
     name: Custom checks
     runs-on: ubuntu-latest
     steps:
-      - name: Checkout code
-        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -92,74 +94,92 @@
             # exit 1
           fi
 
+  msrv:
+    name: MSRV check
+    needs: [ format, custom-checks ]
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+        with:
+          persist-credentials: false
+
+      - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
+
+      - name: Pin toolchain to MSRV
+        run: |
+          msrv=$(cargo metadata --no-deps --format-version=1 | jq -r 
'.packages[0].rust_version')
+          rustup override set "$msrv"
+
+      - name: Print toolchain version
+        run: rustc --version
+
+      - name: cargo check
+        run: cargo check --locked --all-features
+
   main:
-    needs: [ fmt, custom-checks ]
-    name: ${{ matrix.target_name }} (check, clippy)
+    needs: [ format, custom-checks ]
+    name: ${{ matrix.target_name }}
     runs-on: ${{ matrix.os }}
     strategy:
       fail-fast: false
       matrix:
         include:
-          - target: x86_64-linux-android
-            target_name: Android
-            use_cross: true
+          - target_name: Linux
+            target: x86_64-unknown-linux-gnu
             os: ubuntu-latest
 
-          - target: x86_64-unknown-freebsd
-            target_name: FreeBSD
-            use_cross: true
-            os: ubuntu-latest
+          - target_name: Windows
+            target: x86_64-pc-windows-msvc
+            os: windows-latest
 
-          - target: x86_64-unknown-linux-gnu
-            target_name: Linux
-            os: ubuntu-latest
+          - target_name: macOS-aarch64
+            target: aarch64-apple-darwin
+            os: macos-latest
 
-          - target: x86_64-apple-darwin
-            target_name: macOS-x86_64
+          - target_name: macOS-x86_64
+            target: x86_64-apple-darwin
             os: macos-15-intel
 
-          - target: aarch64-apple-darwin
-            target_name: macOS-aarch64
-            os: macos-latest
+          - target_name: FreeBSD
+            target: x86_64-unknown-freebsd
+            use_cross: true
+            os: ubuntu-latest
 
-          - target: x86_64-unknown-netbsd
-            target_name: NetBSD
+          - target_name: NetBSD
+            target: x86_64-unknown-netbsd
             use_cross: true
             os: ubuntu-latest
 
-          - target: x86_64-pc-windows-msvc
-            target_name: Windows
-            os: windows-latest
+          - target_name: Android
+            target: x86_64-linux-android
+            use_cross: true
+            os: ubuntu-latest
     env:
       cargo_cmd: ${{ matrix.use_cross == true && 'cross' || 'cargo' }}
       matrix_target: ${{ matrix.target }}
     steps:
-      - name: Checkout code
-        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
-      - name: Setup Rust Cache
-        uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # 
v2.8.2
+      - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # 
v2.8.2
         with:
           prefix-key: ${{ matrix.target }}
 
       - name: Setup cross
         if: matrix.use_cross == true
-        run: |
-          curl -fL --retry 3 
"https://github.com/cross-rs/cross/releases/download/v${CROSS_VER}/cross-x86_64-unknown-linux-musl.tar.gz";
 | tar vxz -C /usr/local/bin
+        run: curl -fL --retry 3 
"https://github.com/cross-rs/cross/releases/download/v${CROSS_VER}/cross-x86_64-unknown-linux-musl.tar.gz";
 | tar vxz -C /usr/local/bin
 
-      - name: Run cargo/cross check
-        run: |
-          "${cargo_cmd}" check --locked --target "${matrix_target}"
+      - run: rustup component add clippy
+
+      - name: Print toolchain version
+        run: rustc --version
 
-      - name: Run cargo/cross clippy
+      - name: cargo clippy
         run: |
-          rustup component add clippy
-          "${cargo_cmd}" clippy --locked --target "${matrix_target}" 
--all-features -- -D warnings
+          "${cargo_cmd}" clippy --locked --target "${matrix_target}" 
--all-features -- --deny warnings
 
-      - name: Run cargo test
+      - name: cargo test
         # ONLY run test with cargo
         if: matrix.use_cross == false
-        run: |
-          cargo test --locked --target "${matrix_target}"
+        run: cargo test --locked --target "${matrix_target}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/topgrade-16.8.0/.github/workflows/create_release_assets.yml 
new/topgrade-16.9.0/.github/workflows/create_release_assets.yml
--- old/topgrade-16.8.0/.github/workflows/create_release_assets.yml     
2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/.github/workflows/create_release_assets.yml     
2026-02-03 11:49:42.000000000 +0100
@@ -33,7 +33,7 @@
     env:
       tag: ${{ github.event.client_payload.tag }}
     steps:
-      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -137,7 +137,7 @@
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Generate artifact attestations
-        uses: 
actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # 
v3.1.0
+        uses: 
actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # 
v3.2.0
         with:
           subject-path: assets/*
 
@@ -169,7 +169,7 @@
       matrix_target: ${{ matrix.target }}
       tag: ${{ github.event.client_payload.tag }}
     steps:
-      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -284,7 +284,7 @@
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Generate artifact attestations
-        uses: 
actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # 
v3.1.0
+        uses: 
actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # 
v3.2.0
         with:
           subject-path: assets/*
 
@@ -303,12 +303,12 @@
         shell: openbsd {0}
 
     steps:
-      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
       - name: Start OpenBSD VM
-        uses: vmactions/openbsd-vm@00753f2835e62704570734312de6f212069dfef7 # 
v1
+        uses: vmactions/openbsd-vm@a23337e408f7990a9369882eafbc24af55f5d3af # 
v1
         with:
           arch: x86_64
           sync: nfs
@@ -327,25 +327,37 @@
           pkg_add -v rust-rustfmt rust-clippy
 
       - name: Check format
-        run: cargo fmt --all -- --check
+        run: |
+          cd $GITHUB_WORKSPACE
+          cargo fmt --all -- --check
 
       - name: Run clippy
-        run: cargo clippy --all-targets --locked -- -D warnings
+        run: |
+          cd $GITHUB_WORKSPACE
+          cargo clippy --all-targets --locked -- -D warnings
 
       - name: Run clippy (All features)
-        run: cargo clippy --all-targets --locked --all-features -- -D warnings
+        run: |
+          cd $GITHUB_WORKSPACE
+          cargo clippy --all-targets --locked --all-features -- -D warnings
 
       - name: Run tests
-        run: cargo test
+        run: |
+          cd $GITHUB_WORKSPACE
+          cargo test
 
       - name: Build Release binary
-        run: cargo build --release --all-features
+        run: |
+          cd $GITHUB_WORKSPACE
+          cargo build --release --all-features
 
       - name: Package OpenBSD release
         run: |
+          cd $GITHUB_WORKSPACE
           cargo install default-target
           mkdir -p assets
-          FILENAME=topgrade-${tag}-$(default-target)
+          # TODO: this should use the env var (${tag}), but can't because it's 
not set in the VM.
+          FILENAME=topgrade-${{ github.event.client_payload.tag 
}}-$(/root/.cargo/bin/default-target)
           mv target/release/topgrade assets
           cd assets
           tar -F ustar -czf "$FILENAME.tar.gz" topgrade
@@ -353,12 +365,13 @@
           ls .
 
       - name: Upload assets
+        shell: bash
         run: gh release upload "${tag}" assets/*
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Generate artifact attestations
-        uses: 
actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # 
v3.1.0
+        uses: 
actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # 
v3.2.0
         with:
           subject-path: assets/*
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/topgrade-16.8.0/.github/workflows/dependency-review.yml 
new/topgrade-16.9.0/.github/workflows/dependency-review.yml
--- old/topgrade-16.8.0/.github/workflows/dependency-review.yml 2026-01-09 
13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/.github/workflows/dependency-review.yml 2026-02-03 
11:49:42.000000000 +0100
@@ -17,7 +17,7 @@
     runs-on: ubuntu-latest
     steps:
       - name: 'Checkout Repository'
-        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/.github/workflows/release-plz.yml 
new/topgrade-16.9.0/.github/workflows/release-plz.yml
--- old/topgrade-16.8.0/.github/workflows/release-plz.yml       2026-01-09 
13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/.github/workflows/release-plz.yml       2026-02-03 
11:49:42.000000000 +0100
@@ -25,7 +25,7 @@
         uses: dtolnay/rust-toolchain@stable
       - name: Run release-plz
         id: release-plz
-        uses: release-plz/action@487eb7b5c085a664d5c5ca05f4159bd9b591182a # 
v0.5
+        uses: release-plz/action@e592230ad39e3ec735402572601fc621aa24355c # 
v0.5
         with:
           command: release
         env:
@@ -60,7 +60,7 @@
       - name: Install Rust toolchain
         uses: dtolnay/rust-toolchain@stable
       - name: Run release-plz
-        uses: release-plz/action@487eb7b5c085a664d5c5ca05f4159bd9b591182a # 
v0.5
+        uses: release-plz/action@e592230ad39e3ec735402572601fc621aa24355c # 
v0.5
         with:
           command: release-pr
         env:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/topgrade-16.8.0/.github/workflows/release_to_pypi.yml 
new/topgrade-16.9.0/.github/workflows/release_to_pypi.yml
--- old/topgrade-16.8.0/.github/workflows/release_to_pypi.yml   2026-01-09 
13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/.github/workflows/release_to_pypi.yml   2026-02-03 
11:49:42.000000000 +0100
@@ -15,7 +15,7 @@
       matrix:
         target: [x86_64, x86, aarch64]
     steps:
-      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -37,7 +37,7 @@
       matrix:
         target: [x64, x86]
     steps:
-      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -58,7 +58,7 @@
       matrix:
         target: [x86_64, aarch64]
     steps:
-      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -76,7 +76,7 @@
   sdist:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -108,7 +108,7 @@
       - uses: 
actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
 
       - name: Generate artifact attestation
-        uses: 
actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # 
v3.1.0
+        uses: 
actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # 
v3.2.0
         with:
           subject-path: 'wheels-*/*'
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/.github/workflows/scorecards.yml 
new/topgrade-16.9.0/.github/workflows/scorecards.yml
--- old/topgrade-16.8.0/.github/workflows/scorecards.yml        2026-01-09 
13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/.github/workflows/scorecards.yml        2026-02-03 
11:49:42.000000000 +0100
@@ -36,7 +36,7 @@
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 
v6.0.1
+        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           persist-credentials: false
 
@@ -71,6 +71,6 @@
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: 
github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # 
v4.31.9
+        uses: 
github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # 
v4.32.1
         with:
           sarif_file: results.sarif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/.pre-commit-config.yaml 
new/topgrade-16.9.0/.pre-commit-config.yaml
--- old/topgrade-16.8.0/.pre-commit-config.yaml 2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/.pre-commit-config.yaml 2026-02-03 11:49:42.000000000 
+0100
@@ -16,7 +16,7 @@
   - id: trailing-whitespace
 
 - repo: https://github.com/crate-ci/typos
-  rev: v1.41.0
+  rev: v1.43.0
   hooks:
   - id: typos
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/BREAKINGCHANGES_dev.md 
new/topgrade-16.9.0/BREAKINGCHANGES_dev.md
--- old/topgrade-16.8.0/BREAKINGCHANGES_dev.md  2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/BREAKINGCHANGES_dev.md  2026-02-03 11:49:42.000000000 
+0100
@@ -1,3 +1,9 @@
 1. The `jet_brains_toolbox` step was renamed to `jetbrains_toolbox`. If you're
    using the old name in your configuration file in the `disable` or `only`
    fields, simply change it to `jetbrains_toolbox`.
+2. The `nix_helper` step was deprecated. Its `home_manager`-replacing
+   functionality has been merged into that step. When on NixOS, the `system`
+   step will decide whether to use `nh` according to the new config option
+   `misc.nix_handler`.
+3. Since the `nix_helper` step is gone and `nix-darwin` support is otherwise
+   untested, `nh darwin` support has been removed for the time being as well.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/CHANGELOG.md 
new/topgrade-16.9.0/CHANGELOG.md
--- old/topgrade-16.8.0/CHANGELOG.md    2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/CHANGELOG.md    2026-02-03 11:49:42.000000000 +0100
@@ -7,6 +7,47 @@
 
 ## [Unreleased]
 
+## [16.9.0](https://github.com/topgrade-rs/topgrade/compare/v16.8.0...v16.9.0) 
- 2026-02-03
+
+### Added
+
+- *(system,home_manager)* integrate nh into nix-based updaters 
([#1712](https://github.com/topgrade-rs/topgrade/pull/1712))
+- *(cargo)* add `git` and `quiet` options 
([#1685](https://github.com/topgrade-rs/topgrade/pull/1685))
+
+### Fixed
+
+- add missing import 
([#1718](https://github.com/topgrade-rs/topgrade/pull/1718))
+- *(nix)* make nix-{channel,env} optional for flake-enabled systems 
([#1716](https://github.com/topgrade-rs/topgrade/pull/1716))
+
+### Other
+
+- *(pre-commit)* autoupdate 
([#1723](https://github.com/topgrade-rs/topgrade/pull/1723))
+- *(deps)* update github/codeql-action action to v4.32.1 
([#1722](https://github.com/topgrade-rs/topgrade/pull/1722))
+- *(deps)* update vmactions/openbsd-vm digest to a23337e 
([#1720](https://github.com/topgrade-rs/topgrade/pull/1720))
+- *(deps)* update rust crate notify-rust to ~4.12.0 
([#1721](https://github.com/topgrade-rs/topgrade/pull/1721))
+- *(system,home-manager)* make matches more explicit 
([#1719](https://github.com/topgrade-rs/topgrade/pull/1719))
+- *(pre-commit)* autoupdate 
([#1704](https://github.com/topgrade-rs/topgrade/pull/1704))
+- *(deps)* update release-plz/action digest to e592230 
([#1698](https://github.com/topgrade-rs/topgrade/pull/1698))
+- *(deps)* update actions/checkout action to v6.0.2 
([#1699](https://github.com/topgrade-rs/topgrade/pull/1699))
+- *(deps)* update github/codeql-action action to v4.32.0 
([#1700](https://github.com/topgrade-rs/topgrade/pull/1700))
+- *(deps)* update actions/attest-build-provenance action to v3.2.0 
([#1703](https://github.com/topgrade-rs/topgrade/pull/1703))
+- *(deps)* update vmactions/openbsd-vm digest to f5b9bc1 
([#1706](https://github.com/topgrade-rs/topgrade/pull/1706))
+- *(renovate)* disable rate-limiting 
([#1697](https://github.com/topgrade-rs/topgrade/pull/1697))
+- *(renovate)* disable rate-limiting 
([#1696](https://github.com/topgrade-rs/topgrade/pull/1696))
+- *(deps)* update dependency rust to v1.93.0 
([#1695](https://github.com/topgrade-rs/topgrade/pull/1695))
+- *(renovate)* add rust-toolchain updating 
([#1694](https://github.com/topgrade-rs/topgrade/pull/1694))
+- pin toolchain to stable instead of MSRV, add MSRV testing, simplify CI 
workflow ([#1690](https://github.com/topgrade-rs/topgrade/pull/1690))
+- *(pre-commit)* autoupdate 
([#1687](https://github.com/topgrade-rs/topgrade/pull/1687))
+- *(renovate)* use preset for lockfile config 
([#1684](https://github.com/topgrade-rs/topgrade/pull/1684))
+- *(pre-commit)* autoupdate 
([#1682](https://github.com/topgrade-rs/topgrade/pull/1682))
+- *(deps)* update github/codeql-action action to v4.31.10 
([#1680](https://github.com/topgrade-rs/topgrade/pull/1680))
+- *(deb)* update copyright
+- *(deps)* unpin toml
+- *(installation)* add Pacstall Ubuntu package to README 
([#1676](https://github.com/topgrade-rs/topgrade/pull/1676))
+- *(deps)* update rust crate toml to v0.9.11 
([#1675](https://github.com/topgrade-rs/topgrade/pull/1675))
+- *(release)* fix OpenBSD release job 
([#1674](https://github.com/topgrade-rs/topgrade/pull/1674))
+- *(release)* fix OpenBSD release job 
([#1672](https://github.com/topgrade-rs/topgrade/pull/1672))
+
 ## [16.8.0](https://github.com/topgrade-rs/topgrade/compare/v16.7.0...v16.8.0) 
- 2026-01-07
 
 ### Added
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/Cargo.lock 
new/topgrade-16.9.0/Cargo.lock
--- old/topgrade-16.8.0/Cargo.lock      2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/Cargo.lock      2026-02-03 11:49:42.000000000 +0100
@@ -1665,9 +1665,9 @@
 
 [[package]]
 name = "notify-rust"
-version = "4.11.7"
+version = "4.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400"
+checksum = "21af20a1b50be5ac5861f74af1a863da53a11c38684d9818d82f1c42f7fdc6c2"
 dependencies = [
  "futures-lite",
  "log",
@@ -2907,9 +2907,9 @@
 
 [[package]]
 name = "toml"
-version = "0.9.10+spec-1.1.0"
+version = "0.9.11+spec-1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48"
+checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
 dependencies = [
  "indexmap",
  "serde_core",
@@ -2987,7 +2987,7 @@
 
 [[package]]
 name = "topgrade"
-version = "16.8.0"
+version = "16.9.0"
 dependencies = [
  "async-lock",
  "base64ct",
@@ -3027,7 +3027,7 @@
  "tempfile",
  "thiserror 2.0.17",
  "tokio",
- "toml 0.9.10+spec-1.1.0",
+ "toml 0.9.11+spec-1.1.0",
  "tracing",
  "tracing-subscriber",
  "walkdir",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/Cargo.toml 
new/topgrade-16.9.0/Cargo.toml
--- old/topgrade-16.8.0/Cargo.toml      2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/Cargo.toml      2026-02-03 11:49:42.000000000 +0100
@@ -6,7 +6,7 @@
 license = "GPL-3.0-or-later"
 repository = "https://github.com/topgrade-rs/topgrade";
 rust-version = "1.84.1"
-version = "16.8.0"
+version = "16.9.0"
 exclude = ["doc/screenshot.gif", "BREAKINGCHANGES_dev.md"]
 edition = "2021"
 
@@ -16,7 +16,7 @@
 home = "=0.5.11"
 etcetera = "=0.10.0"
 serde = { version = "~1.0", features = ["derive"] }
-toml = { version = "=0.9.10", features = ["preserve_order"] }
+toml = { version = "~0.9.11", features = ["preserve_order"] }
 which_crate = { version = "~8.0", package = "which" }
 shellexpand = "~3.1"
 clap = { version = "~4.5", features = ["cargo", "derive"] }
@@ -39,7 +39,7 @@
 tracing-subscriber = { version = "~0.3.20", features = ["env-filter", "time"] }
 merge = "~0.1"
 regex-split = "~0.1"
-notify-rust = "~4.11"
+notify-rust = "~4.12.0"
 wildmatch = "2.3.0"
 rust-i18n = "3.0.1"
 sys-locale = "0.3.1"
@@ -63,7 +63,7 @@
 [package.metadata.deb]
 name = "topgrade"
 maintainer = "Chris Gelatt <[email protected]>"
-copyright = "2024, Topgrade Team"
+copyright = "2026, Topgrade Team"
 license-file = ["LICENSE", "0"]
 depends = "$auto"
 extended-description = "Keeping your system up to date usually involves 
invoking multiple package managers. This results in big, non-portable shell 
one-liners saved in your shell. To remedy this, Topgrade detects which tools 
you use and runs the appropriate commands to update them."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/README.md 
new/topgrade-16.9.0/README.md
--- old/topgrade-16.8.0/README.md       2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/README.md       2026-02-03 11:49:42.000000000 +0100
@@ -48,6 +48,8 @@
   `scoop bucket add main && scoop install 
main/topgrade`](https://scoop.sh/#/apps?q=topgrade)
 - macOS ([MacPorts](https://www.macports.org/)): [
   `sudo port install topgrade`](https://ports.macports.org/port/topgrade/)
+- Ubuntu ([Pacstall](https://pacstall.dev/)):
+  [`pacstall -I 
topgrade-bin`](https://github.com/pacstall/pacstall-programs/blob/master/packages/topgrade-bin/topgrade-bin.pacscript)
 - NixOS or Nix (nixpkgs): 
[topgrade](https://search.nixos.org/packages?show=topgrade)
 - Void Linux: [`sudo xbps-install -S 
topgrade`](https://voidlinux.org/packages/?arch=x86_64&q=topgrade)
 
@@ -59,6 +61,10 @@
 
 See 
[`config.example.toml`](https://github.com/topgrade-rs/topgrade/blob/main/config.example.toml)
 for an example configuration file.
 
+## MSRV
+
+Find the current MSRV in `Cargo.toml` under `rust-version`. This MSRV will 
only be bumped in a major release.
+
 ## Migration and Breaking Changes
 
 Whenever there is a **breaking change**, the major version number will be 
bumped,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/config.example.toml 
new/topgrade-16.9.0/config.example.toml
--- old/topgrade-16.8.0/config.example.toml     2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/config.example.toml     2026-02-03 11:49:42.000000000 
+0100
@@ -96,6 +96,12 @@
 # (default: true)
 # show_distribution_summary = false
 
+# For NixOS/home-manager, there are multiple ways to switch to newer 
configurations.
+# When set to autodetect: use nh when available, fall back to vanilla
+# Allowed values:
+#   autodetect, nh, vanilla
+# nix_handler = "autodetect"
+
 
 # Commands to run before anything
 [pre_commands]
@@ -424,6 +430,18 @@
 # (default: false)
 # aot = true
 
+[cargo]
+# If this is set to true, `cargo install-update` also updates git-originating 
packages.
+# (default: true)
+# git = false
+
+# If this is set to true, `cargo install-update` is run quietly.
+# (default: false)
+# quiet = true
+
+# Other options like `--locked` or `--jobs` can be passed to `cargo install`
+# using the `CARGO_INSTALL_OPTS` environment variable.
+
 [rustup]
 # If set, updates only these channels.
 # (default: [] (all channels))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/locales/app.yml 
new/topgrade-16.9.0/locales/app.yml
--- old/topgrade-16.8.0/locales/app.yml 2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/locales/app.yml 2026-02-03 11:49:42.000000000 +0100
@@ -1402,22 +1402,30 @@
   zh_CN: "<省略了 `deb-get clean` 的输出>"
   zh_TW: "<省略了 `deb-get clean` 的輸出>"
   de: "<Ausgabe von `deb-get clean` ausgelassen>"
-"You have a flake inside of $FLAKE. This is deprecated for nh.":
-  en: "You have a flake inside of $FLAKE. This is deprecated for nh."
-  lt: "Jūs turite flake viduje $FLAKE. Tai yra pasenę nh."
-  es: "Tienes un flake dentro de $FLAKE. Esto está en desuso para nh."
-  fr: "Vous avez un flake à l'intérieur de $FLAKE. Ceci est obsolète pour nh."
-  zh_CN: "你在 $FLAKE 里有一个 flake。这在 nh 中已被弃用。"
-  zh_TW: "你在 $FLAKE 裡有一個 flake。這在 nh 中已被棄用。"
-  de: "Sie haben ein flake in $FLAKE. Dies ist für nh veraltet."
-"nh cannot find any configured flakes":
-  en: "nh cannot find any configured flakes"
-  lt: "nh nepavyksta rasti jokių sukonfigūruotų flake"
-  es: "nh no puede encontrar ningún flake configurado"
-  fr: "nh ne peut trouver aucun flake configuré"
-  zh_CN: "nh 无法找到任何已配置的 flake"
-  zh_TW: "nh 找不到任何已設定的 flake"
-  de: "nh kann keine konfigurierten flakes finden"
+"linux.nix_handler = \"{value}\", but {resulting_tool} is not available":
+  en: "linux.nix_handler = \"%{value}\", but %{resulting_tool} is not 
available"
+  lt: "linux.nix_handler = \"%{value}\", bet %{resulting_tool} nėra 
pasiekiamas"
+  es: "linux.nix_handler = \"%{value}\", pero %{resulting_tool} no está 
disponible"
+  fr: "linux.nix_handler = \"%{value}\", mais %{resulting_tool} n’est pas 
disponible"
+  zh_CN: "linux.nix_handler = \"%{value}\",但 %{resulting_tool} 不可用"
+  zh_TW: "linux.nix_handler = \"%{value}\",但 %{resulting_tool} 不可用"
+  de: "linux.nix_handler = \"%{value}\", aber %{resulting_tool} ist nicht 
verfügbar"
+"{step}: linux.nix_handler = \"nh\", but neither $NH_FLAKE nor ${specific_var} 
were set":
+  en: "%{step}: linux.nix_handler = \"nh\", but neither $NH_FLAKE nor 
$%{specific_var} were set"
+  lt: "%{step}: linux.nix_handler = \"nh\", tačiau nebuvo nustatyti nei 
$NH_FLAKE, nei $%{specific_var}"
+  es: "%{step}: linux.nix_handler = \"nh\", pero no se establecieron ni 
$NH_FLAKE ni $%{specific_var}"
+  fr: "%{step} : linux.nix_handler = \"nh\", mais ni $NH_FLAKE ni 
$%{specific_var} n’ont été définis"
+  zh_CN: "%{step}:linux.nix_handler = \"nh\", 但未设置 $NH_FLAKE 和 
$%{specific_var}"
+  zh_TW: "%{step}:linux.nix_handler = \"nh\",但未設定 $NH_FLAKE 與 $%{specific_var}"
+  de: "%{step}: linux.nix_handler = \"nh\", aber weder $NH_FLAKE noch 
$%{specific_var} wurden gesetzt"
+"`{step}` step is deprecated":
+  en: "`%{step}` step is deprecated"
+  lt: "`%{step}` žingsnis yra nebenaudojamas"
+  es: "El paso `%{step}` está obsoleto"
+  fr: "L’étape `%{step}` est obsolète"
+  zh_CN: "`%{step}` 步骤已弃用"
+  zh_TW: "`%{step}` 步驟已棄用"
+  de: "Der Schritt `%{step}` ist veraltet"
 "System Manuals":
   en: "System Manuals"
   lt: "Sistemos Vadovai"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/rust-toolchain.toml 
new/topgrade-16.9.0/rust-toolchain.toml
--- old/topgrade-16.8.0/rust-toolchain.toml     2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/rust-toolchain.toml     2026-02-03 11:49:42.000000000 
+0100
@@ -1,2 +1,2 @@
 [toolchain]
-channel = "1.84.1"
+channel = "1.93.0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/src/config.rs 
new/topgrade-16.9.0/src/config.rs
--- old/topgrade-16.8.0/src/config.rs   2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/src/config.rs   2026-02-03 11:49:42.000000000 +0100
@@ -15,6 +15,7 @@
 use merge::Merge;
 use regex::Regex;
 use regex_split::RegexSplit;
+use rust_i18n::t;
 use serde::Deserialize;
 use strum::IntoEnumIterator;
 use tracing::{debug, error};
@@ -23,8 +24,9 @@
 use super::utils::editor;
 use crate::command::CommandExt;
 use crate::execution_context::RunType;
-use crate::step::Step;
+use crate::step::{Step, DEPRECATED_STEPS};
 use crate::sudo::SudoKind;
+use crate::terminal::print_warning;
 use crate::utils::string_prepend_str;
 
 // TODO: Add i18n to this. Tracking issue: 
https://github.com/topgrade-rs/topgrade/issues/859
@@ -248,6 +250,15 @@
     }
 }
 
+#[derive(Debug, Deserialize, Clone, Copy, Default, PartialEq)]
+#[serde(rename_all = "kebab-case")]
+pub enum NixHandler {
+    #[default]
+    Autodetect,
+    Nh,
+    Vanilla,
+}
+
 #[derive(Deserialize, Default, Debug, Merge)]
 #[serde(deny_unknown_fields)]
 pub struct Linux {
@@ -370,6 +381,8 @@
     log_filters: Option<Vec<String>>,
 
     show_distribution_summary: Option<bool>,
+
+    nix_handler: Option<NixHandler>,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, ValueEnum, Default)]
@@ -421,6 +434,13 @@
 
 #[derive(Deserialize, Default, Debug, Merge)]
 #[serde(deny_unknown_fields)]
+pub struct Cargo {
+    git: Option<bool>,
+    quiet: Option<bool>,
+}
+
+#[derive(Deserialize, Default, Debug, Merge)]
+#[serde(deny_unknown_fields)]
 pub struct Rustup {
     channels: Option<Vec<String>>,
 }
@@ -526,6 +546,9 @@
     doom: Option<DoomConfig>,
 
     #[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)]
+    cargo: Option<Cargo>,
+
+    #[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)]
     rustup: Option<Rustup>,
 
     #[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)]
@@ -1023,10 +1046,15 @@
             }
         }
 
+        let step_is_deprecated = |x| DEPRECATED_STEPS.contains(&x);
+
         // If neither of those contain anything
         if enabled_steps.is_empty() {
             // All steps are enabled
             enabled_steps.extend(Step::iter());
+            // Handle deprecated steps. Disable automatically when not 
explicitly mentioned in the
+            // config, so that the warning doesn't print.
+            enabled_steps.retain(|x| !step_is_deprecated(*x));
         }
 
         let mut disabled_steps: Vec<Step> = Vec::new();
@@ -1037,6 +1065,15 @@
             }
         }
 
+        // When a deprecated step is mentioned,
+        for step in enabled_steps
+            .iter()
+            .chain(disabled_steps.iter())
+            .filter(|x| step_is_deprecated(**x))
+        {
+            print_warning(t!("`{step}` step is deprecated", step = 
format!("{step:?}")));
+        }
+
         // All steps that are disabled are not enabled, except ones that are 
passed to `--only`
         enabled_steps.retain(|e| !disabled_steps.contains(e) || 
opt.only.contains(e));
         enabled_steps
@@ -1456,6 +1493,15 @@
             .and_then(|linux| linux.dnf_arguments.as_deref())
     }
 
+    /// Get the handler to use for NixOS/home-manager
+    pub fn nix_handler(&self) -> NixHandler {
+        self.config_file
+            .misc
+            .as_ref()
+            .and_then(|s| s.nix_handler)
+            .unwrap_or_default()
+    }
+
     /// Extra nix arguments
     pub fn nix_arguments(&self) -> Option<&str> {
         self.config_file
@@ -1584,6 +1630,22 @@
                 .unwrap_or(true)
     }
 
+    pub fn cargo_update_git(&self) -> bool {
+        self.config_file
+            .cargo
+            .as_ref()
+            .and_then(|cargo| cargo.git)
+            .unwrap_or(true)
+    }
+
+    pub fn cargo_update_quiet(&self) -> bool {
+        self.config_file
+            .cargo
+            .as_ref()
+            .and_then(|cargo| cargo.quiet)
+            .unwrap_or(false)
+    }
+
     pub fn rustup_channels(&self) -> Vec<String> {
         self.config_file
             .rustup
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/src/step.rs 
new/topgrade-16.9.0/src/step.rs
--- old/topgrade-16.8.0/src/step.rs     2026-01-09 13:43:16.000000000 +0100
+++ new/topgrade-16.9.0/src/step.rs     2026-02-03 11:49:42.000000000 +0100
@@ -14,6 +14,8 @@
 use crate::steps::*;
 use crate::utils::hostname;
 
+pub const DEPRECATED_STEPS: [Step; 1] = [Step::NixHelper];
+
 #[derive(ValueEnum, EnumString, VariantNames, Debug, Clone, PartialEq, Eq, 
Deserialize, EnumIter, Copy, EnumCount)]
 #[clap(rename_all = "snake_case")]
 #[serde(rename_all = "snake_case")]
@@ -445,11 +447,7 @@
                 #[cfg(unix)]
                 runner.execute(*self, "nix upgrade-nix", || 
unix::run_nix_self_upgrade(ctx))?
             }
-            NixHelper =>
-            {
-                #[cfg(unix)]
-                runner.execute(*self, "nh", || unix::run_nix_helper(ctx))?
-            }
+            NixHelper => {}
             Node => runner.execute(*self, "npm", || 
node::run_npm_upgrade(ctx))?,
             Opam => runner.execute(*self, "opam", || 
generic::run_opam_update(ctx))?,
             Pacdef =>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/src/steps/generic.rs 
new/topgrade-16.9.0/src/steps/generic.rs
--- old/topgrade-16.8.0/src/steps/generic.rs    2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/src/steps/generic.rs    2026-02-03 11:49:42.000000000 
+0100
@@ -70,9 +70,15 @@
         return Err(SkipStep(message).into());
     };
 
-    ctx.execute(cargo_update)
-        .args(["install-update", "--git", "--all"])
-        .status_checked()?;
+    let mut command = ctx.execute(cargo_update);
+    command.args(["install-update", "--all"]);
+    if ctx.config().cargo_update_git() {
+        command.arg("--git");
+    }
+    if ctx.config().cargo_update_quiet() {
+        command.arg("--quiet");
+    }
+    command.status_checked()?;
 
     if ctx.config().cleanup() {
         let cargo_cache = require("cargo-cache")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/src/steps/os/linux.rs 
new/topgrade-16.9.0/src/steps/os/linux.rs
--- old/topgrade-16.8.0/src/steps/os/linux.rs   2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/src/steps/os/linux.rs   2026-02-03 11:49:42.000000000 
+0100
@@ -7,11 +7,13 @@
 use tracing::{debug, warn};
 
 use crate::command::CommandExt;
+use crate::config::NixHandler;
 use crate::error::{SkipStep, TopgradeError};
 use crate::execution_context::ExecutionContext;
 use crate::step::Step;
 use crate::steps::generic::is_wsl;
 use crate::steps::os::archlinux;
+use crate::steps::unix::{can_nh_switch, nh_switch, NhSwitchArgs};
 use crate::sudo::SudoExecuteOpts;
 use crate::terminal::{print_separator, prompt_yesno};
 use crate::utils::{require, require_one, which, PathExt};
@@ -799,15 +801,37 @@
 
 fn upgrade_nixos(ctx: &ExecutionContext) -> Result<()> {
     let sudo = ctx.require_sudo()?;
+    let nix_handler = ctx.config().nix_handler();
+    let nh_switch_args = NhSwitchArgs {
+        step: "system",
+        installable_type: "os",
+        specific_var: "NH_OS_FLAKE",
+        print_separator: false,
+    };
+    let can_nh_switch = can_nh_switch(&nh_switch_args);
 
-    let mut command = sudo.execute(ctx, 
"/run/current-system/sw/bin/nixos-rebuild")?;
-    command.args(["switch", "--upgrade"]);
+    match (nix_handler, can_nh_switch) {
+        // nh is available and we want it
+        (NixHandler::Autodetect | NixHandler::Nh, Ok(nh)) => {
+            nh_switch(ctx, &nh, &nh_switch_args)?;
+        }
+        // nh is not available but we need it
+        (NixHandler::Nh, Err(e)) => return Err(e),
+        // nh is not available and we don't need it, so we fall back
+        (NixHandler::Autodetect, Err(_))
+        // We need vanilla
+        | (NixHandler::Vanilla, _) => {
+            let mut command = sudo.execute(ctx, 
"/run/current-system/sw/bin/nixos-rebuild")?;
+            command.args(["switch", "--upgrade"]);
 
-    if let Some(args) = ctx.config().nix_arguments() {
-        command.args(args.split_whitespace());
+            if let Some(args) = ctx.config().nix_arguments() {
+                command.args(args.split_whitespace());
+            }
+            command.status_checked()?;
+        }
     }
-    command.status_checked()?;
 
+    // TODO: maybe use `nh clean` when available&&wanted ?
     if ctx.config().cleanup() {
         sudo.execute(ctx, "/run/current-system/sw/bin/nix-collect-garbage")?
             .arg("-d")
@@ -1125,6 +1149,9 @@
 pub fn run_auto_cpufreq(ctx: &ExecutionContext) -> Result<()> {
     let auto_cpu_freq = require("auto-cpufreq")?.canonicalize()?;
 
+    // The fix is *auto_cpu_freq != *"/usr/local/bin/auto-cpufreq", but that 
impl is not available in MSRV yet
+    // TODO: once MSRV is bumped high enough that it is, remove this and apply 
lint
+    #[allow(clippy::cmp_owned)]
     if auto_cpu_freq != PathBuf::from("/usr/local/bin/auto-cpufreq") {
         return Err(SkipStep(String::from(
             "`auto-cpufreq` was not installed by the official installer, but 
presumably by a package manager.",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/src/steps/os/macos.rs 
new/topgrade-16.9.0/src/steps/os/macos.rs
--- old/topgrade-16.8.0/src/steps/os/macos.rs   2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/src/steps/os/macos.rs   2026-02-03 11:49:42.000000000 
+0100
@@ -200,7 +200,7 @@
 pub fn process_xcodes_releases(releases_filtered: Vec<String>, should_ask: 
bool, ctx: &ExecutionContext) -> Result<()> {
     let xcodes = require("xcodes")?;
 
-    if releases_filtered.last().map_or(true, |s| !s.contains("(Installed)")) 
&& !releases_filtered.is_empty() {
+    if releases_filtered.last().is_none_or(|s| !s.contains("(Installed)")) && 
!releases_filtered.is_empty() {
         println!(
             "{} {}",
             t!("New Xcode release detected:"),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/topgrade-16.8.0/src/steps/os/unix.rs 
new/topgrade-16.9.0/src/steps/os/unix.rs
--- old/topgrade-16.8.0/src/steps/os/unix.rs    2026-01-09 13:43:16.000000000 
+0100
+++ new/topgrade-16.9.0/src/steps/os/unix.rs    2026-02-03 11:49:42.000000000 
+0100
@@ -19,7 +19,9 @@
 use tracing::{debug, warn};
 
 use crate::command::CommandExt;
+use crate::config::NixHandler;
 use crate::sudo::SudoExecuteOpts;
+use crate::utils::require_one;
 use crate::XDG_DIRS;
 use crate::{output_changed_message, HOME_DIR};
 
@@ -551,8 +553,6 @@
 
 pub fn run_nix(ctx: &ExecutionContext) -> Result<()> {
     let nix = require("nix")?;
-    let nix_channel = require("nix-channel")?;
-    let nix_env = require("nix-env")?;
     // TODO: Is None possible here?
     let profile_path = match home::home_dir() {
         Some(home) => XDG_DIRS
@@ -565,8 +565,6 @@
     debug!("nix profile: {:?}", profile_path);
     let manifest_json_path = profile_path.join("manifest.json");
 
-    print_separator("Nix");
-
     #[cfg(target_os = "macos")]
     {
         if require("darwin-rebuild").is_ok() {
@@ -576,8 +574,6 @@
         }
     }
 
-    ctx.execute(nix_channel).arg("--update").status_checked()?;
-
     let nix_version = NixVersion::new(ctx, &nix)?;
 
     // Nix since 2.21.0 uses `--all --impure` rather than `.*` to upgrade all 
packages.
@@ -588,7 +584,20 @@
         vec![".*"]
     };
 
+    // nix-channel might not be available and isn't always necessary to 
perform profile updates
+    let nix_channel_result = if let Ok(nix_channel) = require("nix-channel") {
+        print_separator("Nix Channels");
+        ctx.execute(nix_channel).arg("--update").status_checked()
+    } else {
+        Ok(())
+    };
+
     if Path::new(&manifest_json_path).exists() {
+        // nix-channel doesn't have to succeed when upgrading nix profiles, 
just warn about it
+        if let Err(e) = nix_channel_result {
+            warn!("`nix-channel --update` failed: {e}");
+        }
+        print_separator("Nix Profiles");
         ctx.execute(nix)
             .args(nix_args())
             .arg("profile")
@@ -597,6 +606,11 @@
             .arg("--verbose")
             .status_checked()
     } else {
+        // a successful nix-channel run is expected to perform nix-env upgrades
+        nix_channel_result?;
+        let nix_env = require("nix-env")?;
+        print_separator("Nix");
+
         let mut command = ctx.execute(nix_env);
         command.arg("--upgrade");
         if let Some(args) = ctx.config().nix_env_arguments() {
@@ -731,79 +745,63 @@
 
 /// Returns a directory from an environment variable, if and only if it is a 
directory which
 /// contains a flake.nix
-fn flake_dir(var: &'static str) -> Option<PathBuf> {
+pub(super) fn flake_dir(var: &'static str) -> Option<PathBuf> {
     std::env::var_os(var)
         .map(PathBuf::from)
         .take_if(|x| std::fs::exists(x.join("flake.nix")).is_ok_and(|x| x))
 }
 
-/// Update NixOS and home-manager through a flake using `nh`
-///
-/// See: https://github.com/viperML/nh
-pub fn run_nix_helper(ctx: &ExecutionContext) -> Result<()> {
-    require("nix")?;
-    let nix_helper = require("nh")?;
-
-    let fallback_flake_path = flake_dir("NH_FLAKE");
-    let darwin_flake_path = flake_dir("NH_DARWIN_FLAKE");
-    let home_flake_path = flake_dir("NH_HOME_FLAKE");
-    let nixos_flake_path = flake_dir("NH_OS_FLAKE");
-
-    let all_flake_paths: Vec<_> = [
-        fallback_flake_path.as_ref(),
-        darwin_flake_path.as_ref(),
-        home_flake_path.as_ref(),
-        nixos_flake_path.as_ref(),
-    ]
-    .into_iter()
-    .flatten()
-    .collect();
-
-    // if none of the paths exist AND contain a `flake.nix`, skip
-    if all_flake_paths.is_empty() {
-        if flake_dir("FLAKE").is_some() {
-            warn!(
-                "{}",
-                t!("You have a flake inside of $FLAKE. This is deprecated for 
nh.")
-            );
-        }
-        return Err(SkipStep(t!("nh cannot find any configured 
flakes").into()).into());
-    }
-
-    let nh_switch = |ty: &'static str| -> Result<()> {
-        print_separator(format!("nh {ty}"));
-
-        let mut cmd = ctx.execute(&nix_helper);
-        cmd.arg(ty);
-        cmd.arg("switch");
-        cmd.arg("-u");
-
-        if !ctx.config().yes(Step::NixHelper) {
-            cmd.arg("--ask");
-        }
-        cmd.status_checked()?;
-        Ok(())
+pub(super) struct NhSwitchArgs<'a> {
+    pub step: &'a str,
+    pub installable_type: &'a str,
+    pub specific_var: &'a str,
+    pub print_separator: bool,
+}
+
+pub(super) fn can_nh_switch(args: &NhSwitchArgs<'static>) -> Result<PathBuf> {
+    let nix_helper = require("nh");
+
+    if nix_helper.is_err() {
+        return Err(SkipStep(
+            t!(
+                "linux.nix_handler = \"{value}\", but {resulting_tool} is not 
available",
+                value = "nh",
+                resulting_tool = "nh"
+            )
+            .into(),
+        )
+        .into());
     };
 
-    // We assume that if the user has set these variables, we can throw an 
error if nh cannot find
-    // a flake there. So we do not anymore perform an eval check to find out 
whether we should skip
-    // or not.
-    #[cfg(target_os = "macos")]
-    if darwin_flake_path.is_some() || fallback_flake_path.is_some() {
-        nh_switch("darwin")?;
+    if flake_dir("NH_FLAKE").is_none() && 
flake_dir(args.specific_var).is_none() {
+        return Err(SkipStep(
+            t!(
+                "{step}: linux.nix_handler = \"nh\", but neither $NH_FLAKE nor 
${specific_var} were set",
+                step = args.step,
+                specific_var = args.specific_var
+            )
+            .into(),
+        )
+        .into());
     }
 
-    if home_flake_path.is_some() || fallback_flake_path.is_some() {
-        nh_switch("home")?;
-    }
+    nix_helper
+}
 
-    #[cfg(target_os = "linux")]
-    if matches!(Distribution::detect(), Ok(Distribution::NixOS))
-        && (nixos_flake_path.is_some() || fallback_flake_path.is_some())
-    {
-        nh_switch("os")?;
+pub(super) fn nh_switch(ctx: &ExecutionContext, nh: &PathBuf, args: 
&NhSwitchArgs<'static>) -> Result<()> {
+    if args.print_separator {
+        print_separator(format!("nh {}", args.installable_type));
     }
 
+    let mut cmd = ctx.execute(nh);
+    cmd.arg(args.installable_type);
+    cmd.arg("switch");
+    cmd.arg("-u");
+
+    if !ctx.config().yes(Step::System) {
+        cmd.arg("--ask");
+    }
+    cmd.status_checked()?;
     Ok(())
 }
 
@@ -906,18 +904,49 @@
 }
 
 pub fn run_home_manager(ctx: &ExecutionContext) -> Result<()> {
-    let home_manager = require("home-manager")?;
+    require_one(["home-manager", "nh"])?;
+    let home_manager = require("home-manager");
+    let nix_handler = ctx.config().nix_handler();
+    let nh_switch_args = NhSwitchArgs {
+        step: "home_manager",
+        installable_type: "home",
+        specific_var: "NH_HOME_FLAKE",
+        print_separator: true,
+    };
+    let can_nh_switch = can_nh_switch(&nh_switch_args);
 
-    print_separator("home-manager");
+    match (home_manager, nix_handler, can_nh_switch) {
+        // nh is available and we want it
+        (_, NixHandler::Autodetect | NixHandler::Nh, Ok(nh)) => nh_switch(ctx, 
&nh, &nh_switch_args),
+        // nh is not available but we need it
+        (_, NixHandler::Nh, Err(e)) => Err(e),
+        // home-manager is not available but we need it
+        (Err(_), NixHandler::Vanilla, _) => Err(SkipStep(
+            t!(
+                "linux.nix_handler = \"{value}\", but {resulting_tool} is not 
available",
+                value = "vanilla",
+                resulting_tool = "home-manager"
+            )
+            .into(),
+        )
+        .into()),
+        // nh is not available and we don't need it, so we fall back
+        (Ok(home_manager), NixHandler::Autodetect, Err(_))
+        // We need home-manager
+        | (Ok(home_manager), NixHandler::Vanilla, _) => {
+            print_separator("home-manager");
 
-    let mut cmd = ctx.execute(home_manager);
-    cmd.arg("switch");
+            let mut cmd = ctx.execute(home_manager);
+            cmd.arg("switch");
 
-    if let Some(extra_args) = ctx.config().home_manager() {
-        cmd.args(extra_args);
-    }
+            if let Some(extra_args) = ctx.config().home_manager() {
+                cmd.args(extra_args);
+            }
 
-    cmd.status_checked()
+            cmd.status_checked()
+        }
+        (Err(_), _, Err(_)) => unreachable!("require_one([\"home-manager\", 
\"nh\"])?; was called, so either tool must be available"),
+    }
 }
 
 pub fn run_pearl(ctx: &ExecutionContext) -> Result<()> {

++++++ vendor.tar.zst ++++++
/work/SRC/openSUSE:Factory/topgrade/vendor.tar.zst 
/work/SRC/openSUSE:Factory/.topgrade.new.1995/vendor.tar.zst differ: char 7, 
line 1

Reply via email to