Compare commits
1 Commits
v10.2.0
...
last-topgr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e694d8e13 |
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: GitHub Discussions
|
||||
url: https://github.com/topgrade-rs/topgrade/discussions
|
||||
about: Please ask and answer questions here.
|
||||
24
.github/workflows/build-and-test.yml
vendored
Normal file
24
.github/workflows/build-and-test.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Cargo Build & Test
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
name: Rust project - latest
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
toolchain:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
|
||||
- run: cargo build --verbose
|
||||
- run: cargo test --verbose
|
||||
112
.github/workflows/check-and-lint.yaml
vendored
112
.github/workflows/check-and-lint.yaml
vendored
@@ -4,88 +4,52 @@ 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
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- run: rustup component add rustfmt
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- 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
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
toolchain: stable
|
||||
components: clippy
|
||||
override: true
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
prefix-key: ${{ matrix.target }}
|
||||
|
||||
- name: Setup cross
|
||||
if: matrix.use_cross == true
|
||||
run: curl -fL --retry 3 https://github.com/cross-rs/cross/releases/download/v${{ env.CROSS_VER }}/cross-x86_64-unknown-linux-musl.tar.gz | tar vxz -C /usr/local/bin
|
||||
|
||||
- name: Run cargo check
|
||||
run: ${{ matrix.use_cross == true && 'cross' || 'cargo' }} check --locked --target ${{ matrix.target }}
|
||||
|
||||
- name: Run cargo clippy
|
||||
run: ${{ matrix.use_cross == true && 'cross' || 'cargo' }} clippy --locked --target ${{ matrix.target }} --all-features -- -D warnings
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
name: Clippy Output
|
||||
|
||||
27
.github/workflows/check-semver.yml
vendored
27
.github/workflows/check-semver.yml
vendored
@@ -1,27 +0,0 @@
|
||||
on:
|
||||
release:
|
||||
types: [published, edited]
|
||||
|
||||
name: Check SemVer compliance
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2022-08-03
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
semver:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: --git https://github.com/rust-lang/rust-semverver
|
||||
- run: eval "current_version=$(grep -e '^version = .*$' Cargo.toml | cut -d ' ' -f 3)"
|
||||
- run: cargo semver | tee semver_out
|
||||
- run: (head -n 1 semver_out | grep "\-> $current_version") || (echo "versioning mismatch" && return 1)
|
||||
21
.github/workflows/crates-publish.yml
vendored
21
.github/workflows/crates-publish.yml
vendored
@@ -1,25 +1,30 @@
|
||||
on:
|
||||
# workflow_run:
|
||||
# workflows: ["Check SemVer compliance"]
|
||||
# types:
|
||||
# - completed
|
||||
release:
|
||||
types: [published, edited]
|
||||
|
||||
name: Publish to crates.io on release
|
||||
name: Check SemVer compliance and publish on release
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: nightly-2022-08-03
|
||||
override: true
|
||||
|
||||
publish:
|
||||
semver:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: --git https://github.com/rust-lang/rust-semverver
|
||||
- run: eval "current_version=$(grep -e '^version = .*$' Cargo.toml | cut -d ' ' -f 3)"
|
||||
- run: cargo semver | tee semver_out
|
||||
- run: (head -n 1 semver_out | grep "\-> $current_version") || (echo "versioning mismatch" && return 1)
|
||||
|
||||
publish:
|
||||
steps:
|
||||
- uses: katyo/publish-crates@v1
|
||||
with:
|
||||
|
||||
70
.github/workflows/release-cross.yml
vendored
70
.github/workflows/release-cross.yml
vendored
@@ -1,70 +0,0 @@
|
||||
name: Publish release files for non-cd-native environments
|
||||
|
||||
on:
|
||||
# workflow_run:
|
||||
# workflows: ["Check SemVer compliance"]
|
||||
# types:
|
||||
# - completed
|
||||
release:
|
||||
types: [ created ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [ "aarch64-unknown-linux-gnu", "armv7-unknown-linux-gnueabihf", "x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl", "x86_64-unknown-freebsd", ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
override: true
|
||||
target: ${{ matrix.target }}
|
||||
components: rustfmt, clippy
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Check format
|
||||
with:
|
||||
use-cross: true
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Run clippy
|
||||
with:
|
||||
command: clippy
|
||||
use-cross: true
|
||||
args: --all-targets --locked --target ${{matrix.target}} -- -D warnings
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Run clippy (All features)
|
||||
with:
|
||||
command: clippy
|
||||
use-cross: true
|
||||
args: --locked --all-features --target ${{matrix.target}} -- -D warnings
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Run tests
|
||||
with:
|
||||
command: test
|
||||
use-cross: true
|
||||
args: --target ${{matrix.target}}
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Build
|
||||
with:
|
||||
command: build
|
||||
use-cross: true
|
||||
args: --release --all-features --target ${{matrix.target}}
|
||||
- name: Rename Release
|
||||
run: |
|
||||
mkdir assets
|
||||
FILENAME=topgrade-${{github.event.release.tag_name}}-${{matrix.target}}
|
||||
mv target/${{matrix.target}}/release/topgrade assets
|
||||
cd assets
|
||||
tar --format=ustar -czf $FILENAME.tar.gz topgrade
|
||||
rm topgrade
|
||||
ls .
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: assets/*
|
||||
28
.github/workflows/release-packaging.yaml
vendored
Normal file
28
.github/workflows/release-packaging.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
name: Release Packaging
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release Packaging
|
||||
env:
|
||||
PROJECT_NAME_UNDERSCORE: topgrade-rs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Release Build
|
||||
run: cargo build --release
|
||||
- name: 'Upload Artifact'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ env.PROJECT_NAME_UNDERSCORE }}
|
||||
path: target/release/${{ env.PROJECT_NAME_UNDERSCORE }}
|
||||
@@ -1,10 +1,7 @@
|
||||
name: Publish release files for CD native environments
|
||||
name: CD Win/MacOS Native
|
||||
|
||||
on:
|
||||
# workflow_run:
|
||||
# workflows: ["Check SemVer compliance"]
|
||||
# types:
|
||||
# - completed
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [ created ]
|
||||
|
||||
@@ -13,7 +10,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ ubuntu-latest, macos-latest, windows-latest ]
|
||||
platform: [ macos-latest, windows-latest ]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -23,21 +20,6 @@ jobs:
|
||||
profile: minimal
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Check format
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Run clippy
|
||||
with:
|
||||
command: clippy
|
||||
args: --all-targets --locked -- -D warnings
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Run clippy (All features)
|
||||
with:
|
||||
command: clippy
|
||||
args: --all-targets --locked --all-features -- -D warnings
|
||||
- uses: actions-rs/cargo@v1.0.1
|
||||
name: Run tests
|
||||
with:
|
||||
24
.github/workflows/rust-ubuntu.yml
vendored
Normal file
24
.github/workflows/rust-ubuntu.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "dev" ]
|
||||
pull_request:
|
||||
branches: [ "master", "dev" ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Fmt
|
||||
run: cargo fmt --check --all
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
4
.github/workflows/test.yaml
vendored
4
.github/workflows/test.yaml
vendored
@@ -1,12 +1,10 @@
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
name: Test with Code Coverage
|
||||
|
||||
jobs:
|
||||
|
||||
22
.github/workflows/update_aur.yml
vendored
22
.github/workflows/update_aur.yml
vendored
@@ -1,22 +0,0 @@
|
||||
name: Publish to AUR
|
||||
|
||||
on:
|
||||
# workflow_run:
|
||||
# workflows: ["Check SemVer compliance"]
|
||||
# types:
|
||||
# - completed
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
aur-publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Publish AUR package
|
||||
uses: ATiltedTree/create-aur-release@v1
|
||||
with:
|
||||
package_name: topgrade
|
||||
commit_username: "Thomas Schönauer"
|
||||
commit_email: t.schoenauer@hgs-wt.at
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
38
.github/workflows/update_homebrew.yml
vendored
38
.github/workflows/update_homebrew.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Publish to Homebrew
|
||||
|
||||
on:
|
||||
# workflow_run:
|
||||
# workflows: ["Check SemVer compliance"]
|
||||
# types:
|
||||
# - completed
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
homebrew-publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Cache Homebrew Bundler RubyGems
|
||||
id: cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
|
||||
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
|
||||
restore-keys: ${{ runner.os }}-rubygems-
|
||||
|
||||
- name: Install Homebrew Bundler RubyGems
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: brew install-bundler-gems
|
||||
- name: Bump formulae
|
||||
uses: Homebrew/actions/bump-formulae@master
|
||||
with:
|
||||
# Custom GitHub access token with only the 'public_repo' scope enabled
|
||||
token: ${{secrets.HOMEBREW_ACCESS_TOKEN}}
|
||||
# Bump only these formulae if outdated
|
||||
formulae: |
|
||||
topgrade
|
||||
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@@ -12,11 +12,11 @@
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=topgrade-rs",
|
||||
"--package=topgrade-rs"
|
||||
"--bin=topgrade",
|
||||
"--package=topgrade"
|
||||
],
|
||||
"filter": {
|
||||
"name": "topgrade-rs",
|
||||
"name": "topgrade",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
open an issue on GitHub .
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
645
Cargo.lock
generated
645
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
82
Cargo.toml
82
Cargo.toml
@@ -1,12 +1,11 @@
|
||||
[package]
|
||||
name = "topgrade"
|
||||
description = "Upgrade all the things"
|
||||
name = "topgrade-rs"
|
||||
description = "Upgrade all the things, successor of topgrade"
|
||||
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.0.1"
|
||||
authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"]
|
||||
exclude = ["doc/screenshot.gif"]
|
||||
edition = "2021"
|
||||
@@ -17,57 +16,44 @@ readme = "README.md"
|
||||
name = "topgrade"
|
||||
path = "src/main.rs"
|
||||
|
||||
##[lib]
|
||||
##name = "topgrade_lib"
|
||||
|
||||
[dependencies]
|
||||
home = "~0.5"
|
||||
directories = "~4.0"
|
||||
serde = { version = "~1.0", features = ["derive"] }
|
||||
home = "0.5"
|
||||
directories = "4.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.5"
|
||||
which_crate = { version = "~4.1", package = "which" }
|
||||
shellexpand = "~2.1"
|
||||
clap = { version = "~3.1", features = ["cargo", "derive"] }
|
||||
walkdir = "~2.3"
|
||||
console = "~0.15"
|
||||
lazy_static = "~1.4"
|
||||
chrono = "~0.4"
|
||||
glob = "~0.3"
|
||||
strum = { version = "~0.24", features = ["derive"] }
|
||||
thiserror = "~1.0"
|
||||
tempfile = "~3.2"
|
||||
cfg-if = "~1.0"
|
||||
tokio = { version = "~1.5", features = ["process", "rt-multi-thread"] }
|
||||
futures = "~0.3"
|
||||
regex = "~1.5"
|
||||
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"] }
|
||||
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"
|
||||
|
||||
[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"
|
||||
notify-rust = "4.5"
|
||||
|
||||
[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"] }
|
||||
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"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
self_update_crate = { version = "~0.30", default-features = false, optional = true, package = "self_update", features = ["archive-zip", "compression-zip-deflate", "rustls"] }
|
||||
winapi = "~0.3"
|
||||
parselnk = "~0.1"
|
||||
self_update_crate = { version = "0.30", default-features = false, optional = true, package = "self_update", features = ["archive-zip", "compression-zip-deflate", "rustls"] }
|
||||
winapi = "0.3"
|
||||
parselnk = "0.1"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
@@ -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"]
|
||||
92
README.md
92
README.md
@@ -1,89 +1,59 @@
|
||||
<div align="center">
|
||||
<h1>
|
||||
<img alt="Topgrade" src="doc/topgrade_transparent.png" width="850px">
|
||||
</h1>
|
||||

|
||||
<!---
|
||||

|
||||
[](https://crates.io/crates/topgrade)
|
||||
[](https://aur.archlinux.org/packages/topgrade/)
|
||||
 -->
|
||||
--->
|
||||
|
||||
<a href="https://github.com/topgrade-rs/topgrade/releases"><img alt="GitHub Release" src="https://img.shields.io/github/release/topgrade-rs/topgrade.svg"></a>
|
||||
<a href="https://crates.io/crates/topgrade"><img alt="crates.io" src="https://img.shields.io/crates/v/topgrade.svg"></a>
|
||||
<a href="https://aur.archlinux.org/packages/topgrade"><img alt="AUR" src="https://img.shields.io/aur/version/topgrade.svg"></a>
|
||||
<a href="https://formulae.brew.sh/formula/topgrade"><img alt="Homebrew" src="https://img.shields.io/homebrew/v/topgrade.svg"></a>
|
||||

|
||||
|
||||
<img alt="Demo" src="doc/screenshot.gif" width="550px">
|
||||
</div>
|
||||
## Fork
|
||||
This is a fork of [topgrade by r-darwish](https://github.com/r-darwish/topgrade) to keep it maintained.
|
||||
|
||||
## Introduction
|
||||
|
||||
> **Note**
|
||||
> This is a fork of [topgrade by r-darwish](https://github.com/r-darwish/topgrade) to keep it maintained.
|
||||
|
||||
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.
|
||||
To remedy this, _topgrade_ detects which tools you use and runs the appropriate commands to update them.
|
||||
|
||||
## Installation
|
||||
- Arch Linux: [AUR](https://aur.archlinux.org/packages/topgrade/) package.
|
||||
- NixOS: _topgrade_ package in `nixpkgs`.
|
||||
- macOS: [Homebrew](https://brew.sh/) or [MacPorts](https://www.macports.org/install.php).
|
||||
|
||||
[](https://repology.org/project/topgrade/versions)
|
||||
|
||||
- Arch Linux: [AUR](https://aur.archlinux.org/packages/topgrade)
|
||||
- NixOS: [Nixpkgs](https://search.nixos.org/packages?show=topgrade)
|
||||
- Void Linux: [XBPS](https://voidlinux.org/packages/?arch=x86_64&q=topgrade)
|
||||
- macOS: [Homebrew](https://formulae.brew.sh/formula/topgrade) or [MacPorts](https://ports.macports.org/port/topgrade/)
|
||||
|
||||
Other systems users can either use `cargo install` or the compiled binaries from the release page.
|
||||
Other systems users can either use `cargo install` or use 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.
|
||||
|
||||
## Documentation[WIP]
|
||||
You can visit the documentation at [topgrade-rs.github.io](https://topgrade-rs.github.io/) .
|
||||
|
||||
## Usage
|
||||
|
||||
Just run `topgrade`.
|
||||
|
||||
Visit the documentation at [topgrade-rs.github.io](https://topgrade-rs.github.io/) for more information.
|
||||
|
||||
> **Warning**
|
||||
> Work in Progress
|
||||
See [the wiki](https://github.com/r-darwish/topgrade/wiki/Step-list) for the list of things Topgrade supports.
|
||||
|
||||
## Customization
|
||||
|
||||
See `config.example.toml` for an example configuration file.
|
||||
|
||||
### Configuration Path
|
||||
### Configuration path
|
||||
|
||||
The configuration should be placed in the following paths depending on the operating system:
|
||||
The configuration should be placed in the following paths depending by the operating system:
|
||||
|
||||
- **Windows** - `%APPDATA%/topgrade.toml`
|
||||
- **macOS** and **other Unix systems** - `${XDG_CONFIG_HOME:-~/.config}/topgrade.toml`
|
||||
|
||||
## Remote Execution
|
||||
|
||||
You can specify a key called `remote_topgrades` in the configuration file.
|
||||
This key should contain a list of hostnames that have Topgrade installed on them.
|
||||
Topgrade will use `ssh` to run `topgrade` on remote hosts before acting locally.
|
||||
To limit the execution only to specific hosts use the `--remote-host-limit` parameter.
|
||||
* **Windows** - `%APPDATA%/topgrade.toml`
|
||||
* **macOS** and **other Unix systems** - `${XDG_CONFIG_HOME:-~/.config}/topgrade.toml`
|
||||
|
||||
## Contribution
|
||||
|
||||
### Problems or missing features?
|
||||
|
||||
Open a new issue describing your problem and if possible provide a solution.
|
||||
|
||||
Open a new Issue describing your problem and if possible with a possible solution.
|
||||
### Missing a feature or found an unsupported tool/distro?
|
||||
|
||||
Just let us now what you are missing by opening an issue.
|
||||
For tools, please open an issue describing the tool, which platforms it supports and if possible, give us an example of its usage.
|
||||
|
||||
For tools please open an Issue describing the tool, which platforms it supports and if possible, give us an example of its usage.
|
||||
### Want to contribute to the code?
|
||||
|
||||
Just fork the repository and start coding.
|
||||
|
||||
### Contribution Guidelines
|
||||
|
||||
- Check if your code passes `cargo fmt` and `cargo clippy`.
|
||||
- Check if your code is self explanatory, if not it should be documented by comments.
|
||||
- Make a pull request to the `dev` branch for new features or to the `bug-fixes` branch for bug fixes.
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] Add a proper testing framework to the code base.
|
||||
- [ ] Add unit tests for package managers.
|
||||
- [ ] Split up code into more maintainable parts, eg. putting every linux package manager in a own submodule of linux.rs.
|
||||
## Remote execution
|
||||
You can specify a key called `remote_topgrades` in the configuration file.
|
||||
This key should contain a list of hostnames that have topgrade installed on them.
|
||||
Topgrade will use `ssh` to run `topgrade` on remote hosts before acting locally.
|
||||
To limit the execution only to specific hosts use the `--remote-host-limit` parameter.
|
||||
|
||||
11
SECURITY.md
11
SECURITY.md
@@ -1,11 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We only support the latest major version and each subversion.
|
||||
|
||||
| Version | Supported |
|
||||
| -------- | ------------------ |
|
||||
| 10.0.x | :white_check_mark: |
|
||||
| < 10.0 | :x: |
|
||||
|
||||
@@ -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`" },
|
||||
]
|
||||
@@ -107,7 +107,3 @@
|
||||
[flatpak]
|
||||
# Use sudo for updating the system-wide installation
|
||||
#use_sudo = true
|
||||
|
||||
[distrobox]
|
||||
#use_root = false
|
||||
#containers = ["archlinux-latest"]
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB |
244
src/command.rs
244
src/command.rs
@@ -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<Output> for Utf8Output {
|
||||
type Error = eyre::Error;
|
||||
|
||||
fn try_from(Output { status, stdout, stderr }: Output) -> Result<Self, Self::Error> {
|
||||
let stdout = String::from_utf8(stdout).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<Self, Self::Error> {
|
||||
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<Output> {
|
||||
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<Utf8Output> {
|
||||
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<Output>;
|
||||
|
||||
/// 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<Utf8Output> {
|
||||
// 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<Self::Child>;
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
type Child = Child;
|
||||
|
||||
fn output_checked_with(&mut self, succeeded: impl Fn(&Output) -> Result<(), ()>) -> eyre::Result<Output> {
|
||||
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<Self::Child> {
|
||||
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
|
||||
}
|
||||
100
src/config.rs
100
src/config.rs
@@ -5,20 +5,17 @@ use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::{env, fs};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{ArgEnum, Parser};
|
||||
use color_eyre::eyre;
|
||||
use color_eyre::eyre::Context;
|
||||
use color_eyre::eyre::Result;
|
||||
use directories::BaseDirs;
|
||||
use log::debug;
|
||||
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");
|
||||
|
||||
@@ -86,7 +83,6 @@ pub enum Step {
|
||||
Containers,
|
||||
CustomCommands,
|
||||
DebGet,
|
||||
Distrobox,
|
||||
Deno,
|
||||
Dotnet,
|
||||
Emacs,
|
||||
@@ -96,7 +92,6 @@ pub enum Step {
|
||||
Fossil,
|
||||
Gcloud,
|
||||
Gem,
|
||||
Ghcup,
|
||||
GithubCliExtensions,
|
||||
GitRepos,
|
||||
Go,
|
||||
@@ -125,7 +120,6 @@ pub enum Step {
|
||||
Powershell,
|
||||
Protonup,
|
||||
Raco,
|
||||
Rcm,
|
||||
Remotes,
|
||||
Restarts,
|
||||
Rtcl,
|
||||
@@ -177,14 +171,6 @@ pub struct Windows {
|
||||
enable_winget: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct Distrobox {
|
||||
use_root: Option<bool>,
|
||||
containers: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
@@ -306,7 +292,6 @@ pub struct ConfigFile {
|
||||
firmware: Option<Firmware>,
|
||||
vagrant: Option<Vagrant>,
|
||||
flatpak: Option<Flatpak>,
|
||||
distrobox: Option<Distrobox>,
|
||||
}
|
||||
|
||||
fn config_directory(base_dirs: &BaseDirs) -> PathBuf {
|
||||
@@ -351,12 +336,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 +377,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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,22 +412,22 @@ pub struct CommandLineArgs {
|
||||
no_retry: bool,
|
||||
|
||||
/// Do not perform upgrades for the given steps
|
||||
#[clap(long = "disable", value_name = "STEP", arg_enum, multiple_values = true)]
|
||||
#[clap(long = "disable", arg_enum, multiple_values = true)]
|
||||
disable: Vec<Step>,
|
||||
|
||||
/// Perform only the specified steps (experimental)
|
||||
#[clap(long = "only", value_name = "STEP", arg_enum, multiple_values = true)]
|
||||
#[clap(long = "only", arg_enum, multiple_values = true)]
|
||||
only: Vec<Step>,
|
||||
|
||||
/// Run only specific custom commands
|
||||
#[clap(long = "custom-commands", value_name = "NAME", multiple_values = true)]
|
||||
#[clap(long = "custom-commands")]
|
||||
custom_commands: Vec<String>,
|
||||
|
||||
/// Set environment variables
|
||||
#[clap(long = "env", value_name = "NAME=VALUE", multiple_values = true)]
|
||||
#[clap(long = "env", multiple_values = true)]
|
||||
env: Vec<String>,
|
||||
|
||||
/// Output debug logs. Alias for `--log-filter debug`.
|
||||
/// Output logs
|
||||
#[clap(short = 'v', long = "verbose")]
|
||||
pub verbose: bool,
|
||||
|
||||
@@ -454,14 +440,7 @@ pub struct CommandLineArgs {
|
||||
skip_notify: bool,
|
||||
|
||||
/// Say yes to package manager's prompt
|
||||
#[clap(
|
||||
short = 'y',
|
||||
long = "yes",
|
||||
value_name = "STEP",
|
||||
arg_enum,
|
||||
multiple_values = true,
|
||||
min_values = 0
|
||||
)]
|
||||
#[clap(short = 'y', long = "yes", arg_enum, multiple_values = true, min_values = 0)]
|
||||
yes: Option<Vec<Step>>,
|
||||
|
||||
/// Don't pull the predefined git repos
|
||||
@@ -469,22 +448,16 @@ pub struct CommandLineArgs {
|
||||
disable_predefined_git_repos: bool,
|
||||
|
||||
/// Alternative configuration file
|
||||
#[clap(long = "config", value_name = "PATH")]
|
||||
#[clap(long = "config")]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
/// A regular expression for restricting remote host execution
|
||||
#[clap(long = "remote-host-limit", value_name = "REGEX")]
|
||||
#[clap(long = "remote-host-limit")]
|
||||
remote_host_limit: Option<Regex>,
|
||||
|
||||
/// 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 +472,6 @@ impl CommandLineArgs {
|
||||
pub fn env_variables(&self) -> &Vec<String> {
|
||||
&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 +495,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,16 +614,9 @@ impl Config {
|
||||
}
|
||||
|
||||
/// Extra Tmux arguments
|
||||
pub fn tmux_arguments(&self) -> eyre::Result<Vec<String>> {
|
||||
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.
|
||||
// The error message looks like this:
|
||||
// Error: Failed to parse `tmux_arguments`: `'foo`
|
||||
//
|
||||
// Caused by:
|
||||
// missing closing quote
|
||||
.with_context(|| format!("Failed to parse `tmux_arguments`: `{args}`"))
|
||||
|
||||
pub fn tmux_arguments(&self) -> &Option<String> {
|
||||
&self.config_file.tmux_arguments
|
||||
}
|
||||
|
||||
/// Prompt for a key before exiting
|
||||
@@ -850,20 +808,6 @@ impl Config {
|
||||
.and_then(|linux| linux.dnf_arguments.as_deref())
|
||||
}
|
||||
|
||||
/// Distrobox use root
|
||||
pub fn distrobox_root(&self) -> bool {
|
||||
self.config_file
|
||||
.distrobox
|
||||
.as_ref()
|
||||
.and_then(|r| r.use_root)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Distrobox containers
|
||||
pub fn distrobox_containers(&self) -> Option<&Vec<String>> {
|
||||
self.config_file.distrobox.as_ref().and_then(|r| r.containers.as_ref())
|
||||
}
|
||||
|
||||
/// Concurrency limit for git
|
||||
pub fn git_concurrency_limit(&self) -> Option<usize> {
|
||||
self.config_file.git.as_ref().and_then(|git| git.max_concurrency)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
/// A global variable telling whether the application has been interrupted.
|
||||
static INTERRUPTED: AtomicBool = AtomicBool::new(false);
|
||||
lazy_static! {
|
||||
/// A global variable telling whether the application has been interrupted.
|
||||
static ref INTERRUPTED: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
|
||||
/// Tells whether the program has been interrupted
|
||||
pub fn interrupted() -> bool {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<Option<String>>,
|
||||
}
|
||||
|
||||
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<String> {
|
||||
self.tmux_session.lock().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
158
src/executor.rs
158
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<S: AsRef<OsStr>>(&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<ExecutorOutput> {
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -212,12 +194,11 @@ impl DryCommand {
|
||||
print!(
|
||||
"Dry running: {} {}",
|
||||
self.program.to_string_lossy(),
|
||||
shell_words::join(
|
||||
self.args
|
||||
.iter()
|
||||
.map(|a| String::from(a.to_string_lossy()))
|
||||
.collect::<Vec<String>>()
|
||||
)
|
||||
.join(" ")
|
||||
);
|
||||
match &self.directory {
|
||||
Some(dir) => println!(" in {}", dir.to_string_lossy()),
|
||||
@@ -232,33 +213,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<ExecutorExitStatus> {
|
||||
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<Output> {
|
||||
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::Child> {
|
||||
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<String>;
|
||||
fn string_output(&mut self) -> Result<String>;
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
fn check_output(&mut self) -> Result<String> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
let output = match self.output()? {
|
||||
ExecutorOutput::Wet(output) => output,
|
||||
ExecutorOutput::Dry => return Err(DryRun().into()),
|
||||
};
|
||||
Ok(String::from_utf8(output.stdout)?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
//pub mod steps;
|
||||
//pub mod utils;
|
||||
85
src/main.rs
85
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,12 +206,7 @@ fn run() -> Result<()> {
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
runner.execute(Step::Pkg, "FreeBSD Packages", || {
|
||||
freebsd::upgrade_packages(&ctx, sudo.as_ref(), run_type)
|
||||
})?;
|
||||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
runner.execute(Step::Pkg, "OpenBSD Packages", || {
|
||||
openbsd::upgrade_packages(sudo.as_ref(), run_type)
|
||||
freebsd::upgrade_packages(sudo.as_ref(), run_type)
|
||||
})?;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
@@ -232,10 +231,6 @@ fn run() -> Result<()> {
|
||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".ideavimrc"));
|
||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".intellimacs"));
|
||||
|
||||
if config.should_run(Step::Rcm) {
|
||||
git_repos.insert_if_repo(base_dirs.home_dir().join(".dotfiles"));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
git_repos.insert_if_repo(zsh::zshrc(&base_dirs));
|
||||
@@ -292,11 +287,10 @@ 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))?;
|
||||
runner.execute(Step::Shell, "fundle", || unix::run_fundle(&ctx))?;
|
||||
runner.execute(Step::Tmux, "tmux", || tmux::run_tpm(&base_dirs, run_type))?;
|
||||
runner.execute(Step::Tldr, "TLDR", || unix::run_tldr(run_type))?;
|
||||
runner.execute(Step::Pearl, "pearl", || unix::run_pearl(run_type))?;
|
||||
@@ -307,7 +301,6 @@ fn run() -> Result<()> {
|
||||
runner.execute(Step::Sdkman, "SDKMAN!", || {
|
||||
unix::run_sdkman(&base_dirs, config.cleanup(), run_type)
|
||||
})?;
|
||||
runner.execute(Step::Rcm, "rcm", || unix::run_rcm(&ctx))?;
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
@@ -323,15 +316,13 @@ 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))?;
|
||||
runner.execute(Step::Pipx, "pipx", || generic::run_pipx_update(run_type))?;
|
||||
runner.execute(Step::Conda, "conda", || generic::run_conda_update(&ctx))?;
|
||||
runner.execute(Step::Pip3, "pip3", || generic::run_pip3_update(run_type))?;
|
||||
runner.execute(Step::Ghcup, "ghcup", || generic::run_ghcup_update(run_type))?;
|
||||
runner.execute(Step::Stack, "stack", || generic::run_stack_update(run_type))?;
|
||||
runner.execute(Step::Tlmgr, "tlmgr", || generic::run_tlmgr_update(&ctx))?;
|
||||
runner.execute(Step::Myrepos, "myrepos", || {
|
||||
@@ -348,7 +339,6 @@ fn run() -> Result<()> {
|
||||
runner.execute(Step::Kakoune, "Kakoune", || kakoune::upgrade_kak_plug(&ctx))?;
|
||||
runner.execute(Step::Node, "npm", || node::run_npm_upgrade(&ctx))?;
|
||||
runner.execute(Step::Node, "yarn", || node::run_yarn_upgrade(&ctx))?;
|
||||
runner.execute(Step::Node, "pnpm", || node::run_pnpm_upgrade(&ctx))?;
|
||||
runner.execute(Step::Containers, "Containers", || containers::run_containers(&ctx))?;
|
||||
runner.execute(Step::Deno, "deno", || node::deno_upgrade(&ctx))?;
|
||||
runner.execute(Step::Composer, "composer", || generic::run_composer_update(&ctx))?;
|
||||
@@ -378,7 +368,6 @@ fn run() -> Result<()> {
|
||||
runner.execute(Step::Pacstall, "pacstall", || linux::run_pacstall(&ctx))?;
|
||||
runner.execute(Step::Pacdef, "pacdef", || linux::run_pacdef(&ctx))?;
|
||||
runner.execute(Step::Protonup, "protonup", || linux::run_protonup_update(&ctx))?;
|
||||
runner.execute(Step::Distrobox, "distrobox", || linux::run_distrobox_update(&ctx))?;
|
||||
}
|
||||
|
||||
if let Some(commands) = config.commands() {
|
||||
@@ -414,11 +403,6 @@ fn run() -> Result<()> {
|
||||
freebsd::upgrade_freebsd(sudo.as_ref(), run_type)
|
||||
})?;
|
||||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
runner.execute(Step::System, "OpenBSD Upgrade", || {
|
||||
openbsd::upgrade_openbsd(sudo.as_ref(), run_type)
|
||||
})?;
|
||||
|
||||
#[cfg(windows)]
|
||||
runner.execute(Step::System, "Windows update", || windows::windows_update(&ctx))?;
|
||||
|
||||
@@ -468,10 +452,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')) => (),
|
||||
_ => {
|
||||
@@ -502,6 +486,9 @@ fn run() -> Result<()> {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print_info("Due to r-darwish giving this project access to the original topgrade crate, this crate will no longer receive any updates beyond the 10.0.1 update. To install the supported version, please run the following command: \n");
|
||||
println!("cargo uninstall topgrade-rs");
|
||||
println!("cargo install topgrade");
|
||||
match run() {
|
||||
Ok(()) => {
|
||||
exit(0);
|
||||
@@ -521,35 +508,9 @@ fn main() {
|
||||
.is_some());
|
||||
|
||||
if !skip_print {
|
||||
// The `Debug` implementation of `eyre::Result` prints a multi-line
|
||||
// error message that includes all the 'causes' added with
|
||||
// `.with_context(...)` calls.
|
||||
println!("Error: {:?}", error);
|
||||
println!("Error: {}", error);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_tracing(filter_directives: &str) -> Result<()> {
|
||||
use tracing_subscriber::fmt;
|
||||
use tracing_subscriber::fmt::format::FmtSpan;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
let env_filter = EnvFilter::try_new(filter_directives)
|
||||
.or_else(|_| EnvFilter::try_from_default_env())
|
||||
.or_else(|_| EnvFilter::try_new("info"))?;
|
||||
|
||||
let fmt_layer = fmt::layer()
|
||||
.with_target(false)
|
||||
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
||||
.without_time();
|
||||
|
||||
let registry = tracing_subscriber::registry();
|
||||
|
||||
registry.with(env_filter).with(fmt_layer).init();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -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((
|
||||
|
||||
@@ -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,
|
||||
@@ -31,7 +31,7 @@ impl Drop for SelfRenamer {
|
||||
}
|
||||
|
||||
match fs::rename(&self.temp_path, &self.exe_path) {
|
||||
Ok(_) => debug!("Moved Topgrade back from {:?} to {:?}", self.temp_path, self.exe_path),
|
||||
Ok(_) => debug!("Moved topgrade back from {:?} to {:?}", self.temp_path, self.exe_path),
|
||||
Err(e) => error!(
|
||||
"Could not move Topgrade from {} back to {}: {}",
|
||||
self.temp_path.display(),
|
||||
|
||||
@@ -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");
|
||||
@@ -17,7 +15,7 @@ pub fn self_update() -> Result<()> {
|
||||
|
||||
let target = self_update_crate::get_target();
|
||||
let result = Update::configure()
|
||||
.repo_owner("topgrade-rs")
|
||||
.repo_owner("r-darwish")
|
||||
.repo_name("topgrade")
|
||||
.target(target)
|
||||
.bin_name(if cfg!(windows) { "topgrade.exe" } else { "topgrade" })
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Vec<String>> {
|
||||
);
|
||||
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::<TopgradeError>() {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<()> {
|
||||
@@ -88,7 +87,7 @@ impl Emacs {
|
||||
|
||||
print_separator("Emacs");
|
||||
|
||||
let mut command = ctx.run_type().execute(emacs);
|
||||
let mut command = ctx.run_type().execute(&emacs);
|
||||
|
||||
command
|
||||
.args(["--batch", "--debug-init", "-l"])
|
||||
@@ -106,6 +105,6 @@ impl Emacs {
|
||||
#[cfg(not(unix))]
|
||||
command.arg(EMACS_UPGRADE);
|
||||
|
||||
command.status_checked()
|
||||
command.check_run()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<()> {
|
||||
@@ -72,7 +83,7 @@ pub fn run_gem(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
|
||||
|
||||
print_separator("RubyGems");
|
||||
|
||||
let mut command = run_type.execute(gem);
|
||||
let mut command = run_type.execute(&gem);
|
||||
command.arg("update");
|
||||
|
||||
if env::var_os("RBENV_SHELL").is_none() {
|
||||
@@ -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(µ).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,22 +190,18 @@ 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<()> {
|
||||
let gcloud = utils::require("gcloud")?;
|
||||
|
||||
if gcloud.starts_with("/snap") {
|
||||
Ok(())
|
||||
} else {
|
||||
print_separator("gcloud");
|
||||
|
||||
run_type
|
||||
.execute(gcloud)
|
||||
.execute(&gcloud)
|
||||
.args(["components", "update", "--quiet"])
|
||||
.status_checked()
|
||||
}
|
||||
.check_run()
|
||||
}
|
||||
|
||||
pub fn run_jetpack(run_type: RunType) -> Result<()> {
|
||||
@@ -213,7 +209,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 +217,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 +225,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 +239,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,25 +254,26 @@ 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());
|
||||
}
|
||||
|
||||
print_separator("Conda");
|
||||
|
||||
ctx.run_type()
|
||||
.execute(conda)
|
||||
.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,28 +285,14 @@ 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<()> {
|
||||
if utils::require("ghcup").is_ok() {
|
||||
// `ghcup` is present and probably(?) being used to install `stack`.
|
||||
// Don't upgrade `stack`, let `ghcup` handle it. Per `ghcup install stack`:
|
||||
// !!! Additionally, you should upgrade stack only through ghcup and not use 'stack upgrade' !!!
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let stack = utils::require("stack")?;
|
||||
print_separator("stack");
|
||||
|
||||
run_type.execute(stack).arg("upgrade").status_checked()
|
||||
}
|
||||
|
||||
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(&stack).arg("upgrade").check_run()
|
||||
}
|
||||
|
||||
pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> {
|
||||
@@ -328,10 +308,12 @@ pub fn run_tlmgr_update(ctx: &ExecutionContext) -> Result<()> {
|
||||
let kpsewhich = utils::require("kpsewhich")?;
|
||||
let tlmgr_directory = {
|
||||
let mut d = PathBuf::from(
|
||||
&Command::new(kpsewhich)
|
||||
std::str::from_utf8(
|
||||
&Command::new(&kpsewhich)
|
||||
.arg("-var-value=SELFAUTOPARENT")
|
||||
.output_checked_utf8()?
|
||||
.stdout
|
||||
.output()?
|
||||
.stdout,
|
||||
)?
|
||||
.trim(),
|
||||
);
|
||||
d.push("tlpkg");
|
||||
@@ -355,7 +337,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 +346,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 +360,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 +407,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 +436,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 +460,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 +471,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 +500,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<()> {
|
||||
@@ -521,7 +509,7 @@ pub fn update_julia_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||
print_separator("Julia Packages");
|
||||
|
||||
ctx.run_type()
|
||||
.execute(julia)
|
||||
.execute(&julia)
|
||||
.args(["-e", "using Pkg; Pkg.update()"])
|
||||
.status_checked()
|
||||
.check_run()
|
||||
}
|
||||
|
||||
@@ -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<String>,
|
||||
}
|
||||
|
||||
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<String> {
|
||||
.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<bool> {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <https://github.com/Gelio/go-global-update>
|
||||
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()
|
||||
}
|
||||
|
||||
/// <https://github.com/nao1215/gup>
|
||||
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<PathBuf> {
|
||||
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()
|
||||
})
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,103 +1,66 @@
|
||||
use std::fmt::Display;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
#![allow(unused_imports)]
|
||||
|
||||
#[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};
|
||||
|
||||
enum NPMVariant {
|
||||
Npm,
|
||||
Pnpm,
|
||||
}
|
||||
|
||||
impl NPMVariant {
|
||||
const fn short_name(&self) -> &str {
|
||||
match self {
|
||||
NPMVariant::Npm => "npm",
|
||||
NPMVariant::Pnpm => "pnpm",
|
||||
}
|
||||
}
|
||||
|
||||
const fn is_npm(&self) -> bool {
|
||||
matches!(self, NPMVariant::Npm)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NPMVariant {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.short_name())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
struct NPM {
|
||||
command: PathBuf,
|
||||
variant: NPMVariant,
|
||||
}
|
||||
|
||||
impl NPM {
|
||||
fn new(command: PathBuf, variant: NPMVariant) -> Self {
|
||||
Self { command, variant }
|
||||
}
|
||||
|
||||
/// Is the “NPM” version larger than 8.11.0?
|
||||
fn is_npm_8(&self) -> bool {
|
||||
let v = self.version();
|
||||
|
||||
self.variant.is_npm() && matches!(v, Ok(v) if v >= Version::new(8, 11, 0))
|
||||
}
|
||||
|
||||
/// Get the most suitable “global location” argument
|
||||
/// of this NPM instance.
|
||||
///
|
||||
/// If the “NPM” version is larger than 8.11.0, we use
|
||||
/// `--location=global`; otherwise, use `-g`.
|
||||
fn global_location_arg(&self) -> &str {
|
||||
if self.is_npm_8() {
|
||||
"--location=global"
|
||||
} else {
|
||||
"-g"
|
||||
}
|
||||
fn new(command: PathBuf) -> Self {
|
||||
Self { command }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn root(&self) -> Result<PathBuf> {
|
||||
let args = ["root", self.global_location_arg()];
|
||||
let version = self.version()?;
|
||||
let args = if version < Version::new(8, 11, 0) {
|
||||
["root", "-g"]
|
||||
} else {
|
||||
["root", "--location=global"]
|
||||
};
|
||||
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<Version> {
|
||||
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<()> {
|
||||
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()?;
|
||||
print_separator("Node Package Manager");
|
||||
let version = self.version()?;
|
||||
let args = if version < Version::new(8, 11, 0) {
|
||||
["update", "-g"]
|
||||
} else {
|
||||
run_type.execute(&self.command).args(args).status_checked()?;
|
||||
["update", "--location=global"]
|
||||
};
|
||||
if use_sudo {
|
||||
run_type.execute("sudo").args(args).check_run()?;
|
||||
} else {
|
||||
run_type.execute(&self.command).args(args).check_run()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -107,7 +70,7 @@ impl NPM {
|
||||
pub fn should_use_sudo(&self) -> Result<bool> {
|
||||
let npm_root = self.root()?;
|
||||
if !npm_root.exists() {
|
||||
return Err(SkipStep(format!("{} root at {} doesn't exist", self.variant, npm_root.display())).into());
|
||||
return Err(SkipStep(format!("NPM root at {} doesn't exist", npm_root.display(),)).into());
|
||||
}
|
||||
|
||||
let metadata = std::fs::metadata(&npm_root)?;
|
||||
@@ -130,27 +93,17 @@ impl Yarn {
|
||||
}
|
||||
}
|
||||
|
||||
fn has_global_subcmd(&self) -> bool {
|
||||
// Get the version of Yarn. After Yarn 2.x (berry),
|
||||
// “yarn global” has been replaced with “yarn dlx”.
|
||||
//
|
||||
// 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();
|
||||
|
||||
matches!(version, Ok(ver) if ver.stdout.starts_with('1') || ver.stdout.starts_with('0'))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn root(&self) -> Result<PathBuf> {
|
||||
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 +111,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(())
|
||||
@@ -170,7 +123,7 @@ impl Yarn {
|
||||
pub fn should_use_sudo(&self) -> Result<bool> {
|
||||
let yarn_root = self.root()?;
|
||||
if !yarn_root.exists() {
|
||||
return Err(SkipStep(format!("Yarn root at {} doesn't exist", yarn_root.display(),)).into());
|
||||
return Err(SkipStep(format!("NPM root at {} doesn't exist", yarn_root.display(),)).into());
|
||||
}
|
||||
|
||||
let metadata = std::fs::metadata(&yarn_root)?;
|
||||
@@ -209,9 +162,7 @@ fn should_use_sudo_yarn(yarn: &Yarn, ctx: &ExecutionContext) -> Result<bool> {
|
||||
}
|
||||
|
||||
pub fn run_npm_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
||||
let npm = require("npm").map(|b| NPM::new(b, NPMVariant::Npm))?;
|
||||
|
||||
print_separator("Node Package Manager");
|
||||
let npm = require("pnpm").or_else(|_| require("npm")).map(NPM::new)?;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
@@ -224,32 +175,9 @@ 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)?)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
pnpm.upgrade(ctx.run_type(), false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_yarn_upgrade(ctx: &ExecutionContext) -> Result<()> {
|
||||
let yarn = require("yarn").map(Yarn::new)?;
|
||||
|
||||
if !yarn.has_global_subcmd() {
|
||||
debug!("Yarn is 2.x or above, skipping global upgrade");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
print_separator("Yarn Package Manager");
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
yarn.upgrade(ctx.run_type(), should_use_sudo_yarn(&yarn, ctx)?)
|
||||
@@ -271,5 +199,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()
|
||||
}
|
||||
|
||||
@@ -1,29 +1,23 @@
|
||||
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")?;
|
||||
let pkg = which("nala").or_else(|| which("pkg")).unwrap();
|
||||
let pkg = require("pkg")?;
|
||||
|
||||
print_separator("Termux Packages");
|
||||
|
||||
let is_nala = pkg.ends_with("nala");
|
||||
|
||||
let mut command = ctx.run_type().execute(&pkg);
|
||||
command.arg("upgrade");
|
||||
|
||||
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 ctx.config().cleanup() {
|
||||
ctx.run_type().execute(&pkg).arg("clean").check_run()?;
|
||||
|
||||
let apt = require("apt")?;
|
||||
let mut command = ctx.run_type().execute(&apt);
|
||||
@@ -31,7 +25,7 @@ pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
|
||||
if ctx.config().yes(Step::System) {
|
||||
command.arg("-y");
|
||||
}
|
||||
command.status_checked()?;
|
||||
command.check_run()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -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(())
|
||||
@@ -243,7 +245,7 @@ impl Aura {
|
||||
|
||||
impl ArchPackageManager for Aura {
|
||||
fn upgrade(&self, ctx: &ExecutionContext) -> Result<()> {
|
||||
let sudo = which("sudo").unwrap_or_else(PathBuf::new);
|
||||
let sudo = which("sudo").unwrap_or(PathBuf::new());
|
||||
let mut aur_update = ctx.run_type().execute(&sudo);
|
||||
|
||||
if sudo.ends_with("sudo") {
|
||||
@@ -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<Box<dyn ArchPa
|
||||
|
||||
pub fn upgrade_arch_linux(ctx: &ExecutionContext) -> 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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<PathBuf>) -> 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(())
|
||||
}
|
||||
|
||||
@@ -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<PathBuf>) -> 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(())
|
||||
}
|
||||
|
||||
@@ -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<bool> {
|
||||
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,35 +602,10 @@ 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(())
|
||||
}
|
||||
|
||||
pub fn run_distrobox_update(ctx: &ExecutionContext) -> Result<()> {
|
||||
let distrobox = require("distrobox")?;
|
||||
|
||||
print_separator("Distrobox");
|
||||
match (
|
||||
match (
|
||||
ctx.run_type().execute(distrobox).arg("upgrade"),
|
||||
ctx.config().distrobox_containers(),
|
||||
) {
|
||||
(r, Some(c)) => {
|
||||
if c.is_empty() {
|
||||
return Err(SkipStep("You need to specify at least one container".to_string()).into());
|
||||
}
|
||||
r.args(c)
|
||||
}
|
||||
(r, None) => r.arg("--all"),
|
||||
},
|
||||
ctx.config().distrobox_root(),
|
||||
) {
|
||||
(r, true) => r.arg("--root"),
|
||||
(r, false) => r,
|
||||
}
|
||||
.status_checked()
|
||||
}
|
||||
|
||||
pub fn run_config_update(ctx: &ExecutionContext) -> Result<()> {
|
||||
let sudo = require_option(ctx.sudo().as_ref(), String::from("sudo is not installed"))?;
|
||||
if ctx.config().yes(Step::ConfigUpdate) {
|
||||
@@ -667,14 +614,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(())
|
||||
|
||||
@@ -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"]).check_run()?;
|
||||
ctx.run_type()
|
||||
.execute(sudo)
|
||||
.args(["port", "selfupdate"])
|
||||
.status_checked()?;
|
||||
ctx.run_type()
|
||||
.execute(sudo)
|
||||
.args(["port", "-u", "upgrade", "outdated"])
|
||||
.status_checked()?;
|
||||
.args(&["port", "-u", "upgrade", "outdated"])
|
||||
.check_run()?;
|
||||
if ctx.config().cleanup() {
|
||||
ctx.run_type()
|
||||
.execute(sudo)
|
||||
.args(["port", "-N", "reclaim"])
|
||||
.status_checked()?;
|
||||
.args(&["port", "-N", "reclaim"])
|
||||
.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<()> {
|
||||
@@ -56,21 +52,26 @@ pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> {
|
||||
}
|
||||
|
||||
let mut command = ctx.run_type().execute("softwareupdate");
|
||||
command.args(["--install", "--all"]);
|
||||
command.args(&["--install", "--all"]);
|
||||
|
||||
if should_ask {
|
||||
command.arg("--no-scan");
|
||||
}
|
||||
|
||||
command.status_checked()
|
||||
command.check_run()
|
||||
}
|
||||
|
||||
fn system_update_available() -> Result<bool> {
|
||||
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<()> {
|
||||
@@ -80,14 +81,14 @@ pub fn run_sparkle(ctx: &ExecutionContext) -> Result<()> {
|
||||
|
||||
for application in (fs::read_dir("/Applications")?).flatten() {
|
||||
let probe = Command::new(&sparkle)
|
||||
.args(["--probe", "--application"])
|
||||
.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.args(&["bundle", "--check-immediately", "--application"]);
|
||||
command.arg(application.path());
|
||||
command.status_checked()?;
|
||||
command.spawn()?.wait()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -10,8 +10,6 @@ pub mod freebsd;
|
||||
pub mod linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod macos;
|
||||
#[cfg(target_os = "openbsd")]
|
||||
pub mod openbsd;
|
||||
#[cfg(unix)]
|
||||
pub mod unix;
|
||||
#[cfg(target_os = "windows")]
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
use crate::executor::RunType;
|
||||
use crate::terminal::print_separator;
|
||||
use crate::utils::require_option;
|
||||
use color_eyre::eyre::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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
|
||||
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
|
||||
NAME="Debian GNU/Linux"
|
||||
VERSION_ID="11"
|
||||
VERSION="11 (bullseye)"
|
||||
VERSION_CODENAME=bullseye
|
||||
VERSION_ID="8"
|
||||
VERSION="8 (jessie)"
|
||||
ID=debian
|
||||
HOME_URL="https://www.debian.org/"
|
||||
SUPPORT_URL="https://www.debian.org/support"
|
||||
HOME_URL="http://www.debian.org/"
|
||||
SUPPORT_URL="http://www.debian.org/support"
|
||||
BUG_REPORT_URL="https://bugs.debian.org/"
|
||||
|
||||
@@ -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,27 +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()
|
||||
}
|
||||
|
||||
/// Upgrades `fundle` and `fundle` plugins.
|
||||
///
|
||||
/// `fundle` is a package manager for the Fish shell.
|
||||
///
|
||||
/// See: <https://github.com/danhper/fundle>
|
||||
pub fn run_fundle(ctx: &ExecutionContext) -> Result<()> {
|
||||
let fish = require("fish")?;
|
||||
ctx.base_dirs().home_dir().join(".config/fish/fundle").require()?;
|
||||
|
||||
print_separator("fundle");
|
||||
|
||||
ctx.run_type()
|
||||
.execute(fish)
|
||||
.args(["-c", "fundle self-update && fundle update"])
|
||||
.status_checked()
|
||||
ctx.run_type().execute(&fish).args(["-c", "plug update"]).check_run()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "macos")))]
|
||||
@@ -211,10 +175,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 +196,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 +213,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(())
|
||||
@@ -278,28 +241,28 @@ 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())?;
|
||||
.args(&["--repository", "buo/cask-upgrade"])
|
||||
.check_output()
|
||||
.map(|p| Path::new(p.trim()).exists())?;
|
||||
|
||||
let mut brew_args = vec![];
|
||||
|
||||
if cask_upgrade_exists {
|
||||
brew_args.extend(["cu", "-y"]);
|
||||
brew_args.extend(&["cu", "-y"]);
|
||||
if ctx.config().brew_cask_greedy() {
|
||||
brew_args.push("-a");
|
||||
}
|
||||
} else {
|
||||
brew_args.extend(["upgrade", "--cask"]);
|
||||
brew_args.extend(&["upgrade", "--cask"]);
|
||||
if ctx.config().brew_cask_greedy() {
|
||||
brew_args.push("--greedy");
|
||||
}
|
||||
}
|
||||
|
||||
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 +273,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 +281,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 +297,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 +329,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 +343,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 +354,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 +423,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,20 +454,10 @@ 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)`.
|
||||
///
|
||||
/// See: <https://github.com/thoughtbot/rcm>
|
||||
pub fn run_rcm(ctx: &ExecutionContext) -> Result<()> {
|
||||
let rcup = require("rcup")?;
|
||||
|
||||
print_separator("rcm");
|
||||
ctx.run_type().execute(rcup).arg("-v").status_checked()
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
@@ -27,7 +26,7 @@ pub fn run_chocolatey(ctx: &ExecutionContext) -> Result<()> {
|
||||
args.insert(0, "choco");
|
||||
}
|
||||
|
||||
let mut command = ctx.run_type().execute(cmd);
|
||||
let mut command = ctx.run_type().execute(&cmd);
|
||||
|
||||
command.args(&args);
|
||||
|
||||
@@ -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,41 +55,41 @@ 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<Vec<String>> {
|
||||
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())
|
||||
.map(|x| x.replace(['\u{0}', '\r'], ""))
|
||||
.map(|x| x.replace('\u{0}', "").replace('\r', ""))
|
||||
.collect())
|
||||
}
|
||||
|
||||
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()
|
||||
let topgrade = Command::new(&wsl)
|
||||
.args(&["-d", dist, "bash", "-lc", "which topgrade"])
|
||||
.check_output()
|
||||
.map_err(|_| SkipStep(String::from("Could not find Topgrade installed in WSL")))?;
|
||||
|
||||
let mut command = ctx.run_type().execute(wsl);
|
||||
let mut command = ctx.run_type().execute(&wsl);
|
||||
command
|
||||
.args(["-d", dist, "bash", "-c"])
|
||||
.args(&["-d", dist, "bash", "-c"])
|
||||
.arg(format!("TOPGRADE_PREFIX={} exec {}", dist, topgrade));
|
||||
|
||||
if ctx.config().yes(Step::Wsl) {
|
||||
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<()> {
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
@@ -46,14 +46,14 @@ impl Powershell {
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn has_module(powershell: &Path, command: &str) -> bool {
|
||||
Command::new(powershell)
|
||||
.args([
|
||||
Command::new(&powershell)
|
||||
.args(&[
|
||||
"-NoProfile",
|
||||
"-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)
|
||||
}
|
||||
|
||||
@@ -79,9 +79,8 @@ impl Powershell {
|
||||
println!("Updating modules...");
|
||||
ctx.run_type()
|
||||
.execute(powershell)
|
||||
// This probably doesn't need `shell_words::join`.
|
||||
.args(["-NoProfile", "-Command", &cmd.join(" ")])
|
||||
.status_checked()
|
||||
.check_run()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -100,14 +99,14 @@ impl Powershell {
|
||||
|
||||
let mut command = if let Some(sudo) = ctx.sudo() {
|
||||
let mut command = ctx.run_type().execute(sudo);
|
||||
command.arg(powershell);
|
||||
command.arg(&powershell);
|
||||
command
|
||||
} else {
|
||||
ctx.run_type().execute(powershell)
|
||||
ctx.run_type().execute(&powershell)
|
||||
};
|
||||
|
||||
command
|
||||
.args([
|
||||
.args(&[
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
&format!(
|
||||
@@ -119,6 +118,6 @@ impl Powershell {
|
||||
}
|
||||
),
|
||||
])
|
||||
.status_checked()
|
||||
.check_run()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, &args.join(" "))?;
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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 {
|
||||
@@ -35,10 +29,12 @@ struct Tmux {
|
||||
}
|
||||
|
||||
impl Tmux {
|
||||
fn new(args: Vec<String>) -> Self {
|
||||
fn new(args: &Option<String>) -> Self {
|
||||
Self {
|
||||
tmux: which("tmux").expect("Could not find tmux"),
|
||||
args: if args.is_empty() { None } else { Some(args) },
|
||||
args: args
|
||||
.as_ref()
|
||||
.map(|args| args.split_whitespace().map(String::from).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,130 +46,73 @@ impl Tmux {
|
||||
command
|
||||
}
|
||||
|
||||
fn has_session(&self, session_name: &str) -> Result<bool> {
|
||||
fn has_session(&self, session_name: &str) -> Result<bool, io::Error> {
|
||||
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<bool, io::Error> {
|
||||
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<String> {
|
||||
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<Vec<usize>> {
|
||||
self.build()
|
||||
.args(["list-windows", "-F", "#{window_index}", "-t", session_name])
|
||||
.output_checked_utf8()?
|
||||
.stdout
|
||||
.lines()
|
||||
.map(|l| l.parse())
|
||||
.collect::<Result<Vec<usize>, _>>()
|
||||
.context("Failed to compute tmux windows")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_in_tmux(args: Vec<String>) -> Result<()> {
|
||||
pub fn run_in_tmux(args: &Option<String>) -> ! {
|
||||
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)
|
||||
command.join(" ")
|
||||
};
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -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<Vec<String>> {
|
||||
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<String> = output
|
||||
.stdout
|
||||
let proc: Vec<String> = output_str
|
||||
.lines()
|
||||
// Skip the first line since that contains only status information
|
||||
.skip(1)
|
||||
@@ -36,7 +33,7 @@ pub fn run_toolbx(ctx: &ExecutionContext) -> Result<()> {
|
||||
debug!("Toolboxes to inspect: {:?}", toolboxes);
|
||||
|
||||
let mut topgrade_path = PathBuf::from("/run/host");
|
||||
// Path of the running Topgrade executable
|
||||
// Path of the running topgrade executable
|
||||
// Skip 1 to eliminate the path root, otherwise push overwrites the path
|
||||
topgrade_path.push(std::env::current_exe()?.components().skip(1).collect::<PathBuf>());
|
||||
let topgrade_path = topgrade_path.to_str().unwrap();
|
||||
@@ -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(())
|
||||
|
||||
@@ -18,6 +18,11 @@ if exists(":PlugUpgrade")
|
||||
endif
|
||||
endif
|
||||
|
||||
if exists(":PackerUpdate")
|
||||
echo "Packer"
|
||||
PackerSync
|
||||
endif
|
||||
|
||||
if exists("*dein#update()")
|
||||
echo "dein#update()"
|
||||
call dein#update()
|
||||
@@ -38,18 +43,4 @@ 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
|
||||
PackerSync
|
||||
else
|
||||
quitall
|
||||
endif
|
||||
quitall
|
||||
|
||||
@@ -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, ExecutorOutput, RunType};
|
||||
use crate::terminal::print_separator;
|
||||
use crate::{
|
||||
execution_context::ExecutionContext,
|
||||
utils::{require, PathExt},
|
||||
};
|
||||
use directories::BaseDirs;
|
||||
use std::path::PathBuf;
|
||||
use log::debug;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
process::Command,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
const UPGRADE_VIM: &str = include_str!("upgrade.vim");
|
||||
|
||||
@@ -41,14 +40,19 @@ fn nvimrc(base_dirs: &BaseDirs) -> Result<PathBuf> {
|
||||
.or_else(|_| base_dir.join("nvim/init.lua").require())
|
||||
}
|
||||
|
||||
fn upgrade_script() -> Result<tempfile::NamedTempFile> {
|
||||
fn upgrade(vim: &Path, vimrc: &Path, ctx: &ExecutionContext) -> Result<()> {
|
||||
let mut tempfile = tempfile::NamedTempFile::new()?;
|
||||
tempfile.write_all(UPGRADE_VIM.replace('\r', "").as_bytes())?;
|
||||
debug!("Wrote vim script to {:?}", tempfile.path());
|
||||
Ok(tempfile)
|
||||
}
|
||||
|
||||
fn upgrade(command: &mut Executor, ctx: &ExecutionContext) -> Result<()> {
|
||||
let mut command = ctx.run_type().execute(vim);
|
||||
|
||||
command
|
||||
.args(["-u"])
|
||||
.arg(vimrc)
|
||||
.args(["-U", "NONE", "-V1", "-nNesS"])
|
||||
.arg(tempfile.path());
|
||||
|
||||
if ctx.config().force_vim_plug_update() {
|
||||
command.env("TOPGRADE_FORCE_PLUGUPDATE", "true");
|
||||
}
|
||||
@@ -64,7 +68,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 +89,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,23 +112,15 @@ 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") {
|
||||
return Err(SkipStep(String::from("vim binary might be actually nvim")).into());
|
||||
let output = Command::new(&vim).arg("--version").check_output()?;
|
||||
if !output.starts_with("VIM") {
|
||||
return Err(SkipStep(String::from("vim binary might by actually nvim")).into());
|
||||
}
|
||||
|
||||
let vimrc = vimrc(base_dirs)?;
|
||||
|
||||
print_separator("Vim");
|
||||
upgrade(
|
||||
ctx.run_type()
|
||||
.execute(&vim)
|
||||
.args(["-u"])
|
||||
.arg(vimrc)
|
||||
.args(["-U", "NONE", "-V1", "-nNesS"])
|
||||
.arg(upgrade_script()?.path()),
|
||||
ctx,
|
||||
)
|
||||
upgrade(&vim, &vimrc, ctx)
|
||||
}
|
||||
|
||||
pub fn upgrade_neovim(base_dirs: &BaseDirs, ctx: &ExecutionContext) -> Result<()> {
|
||||
@@ -132,15 +128,7 @@ pub fn upgrade_neovim(base_dirs: &BaseDirs, ctx: &ExecutionContext) -> Result<()
|
||||
let nvimrc = nvimrc(base_dirs)?;
|
||||
|
||||
print_separator("Neovim");
|
||||
upgrade(
|
||||
ctx.run_type()
|
||||
.execute(nvim)
|
||||
.args(["-u"])
|
||||
.arg(nvimrc)
|
||||
.args(["--headless", "-V1", "-nS"])
|
||||
.arg(upgrade_script()?.path()),
|
||||
ctx,
|
||||
)
|
||||
upgrade(&nvim, &nvimrc, ctx)
|
||||
}
|
||||
|
||||
pub fn run_voom(_base_dirs: &BaseDirs, run_type: RunType) -> Result<()> {
|
||||
@@ -148,5 +136,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()
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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<P: AsRef<str>, Q: AsRef<str>>(&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<P: AsRef<str>>(&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<bool> {
|
||||
fn should_retry(&mut self, interrupted: bool, step_name: &str) -> Result<bool, io::Error> {
|
||||
if self.width.is_none() {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -236,33 +223,31 @@ impl Terminal {
|
||||
self.term.set_title("Topgrade - Awaiting user");
|
||||
}
|
||||
|
||||
self.notify_desktop(format!("{} failed", step_name), None);
|
||||
self.notify_desktop(&format!("{} failed", step_name), None);
|
||||
|
||||
let prompt_inner = style(format!("{}Retry? (y)es/(N)o/(s)hell/(q)uit", self.prefix))
|
||||
self.term
|
||||
.write_fmt(format_args!(
|
||||
"\n{}",
|
||||
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();
|
||||
.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 {
|
||||
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<bool> {
|
||||
pub fn should_retry(interrupted: bool, step_name: &str) -> Result<bool, io::Error> {
|
||||
TERMINAL.lock().unwrap().should_retry(interrupted, step_name)
|
||||
}
|
||||
|
||||
@@ -291,11 +276,6 @@ pub fn print_separator<P: AsRef<str>>(message: P) {
|
||||
TERMINAL.lock().unwrap().print_separator(message)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn print_error<P: AsRef<str>, Q: AsRef<str>>(key: Q, message: P) {
|
||||
TERMINAL.lock().unwrap().print_error(key, message)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn print_warning<P: AsRef<str>>(message: P) {
|
||||
TERMINAL.lock().unwrap().print_warning(message)
|
||||
|
||||
93
src/utils.rs
93
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<T: CheckWithCodes> 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<T>(option: Option<T>, cause: String) -> Result<T> {
|
||||
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<String> {
|
||||
use std::ffi;
|
||||
extern crate libc;
|
||||
|
||||
unsafe {
|
||||
let buf_size = libc::sysconf(libc::_SC_HOST_NAME_MAX) as usize;
|
||||
let mut buf = Vec::<u8>::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<String> {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.TH "topgrade" "8"
|
||||
.SH NAME
|
||||
.PP
|
||||
Topgrade \- Upgrade everything
|
||||
topgrade \- upgrade everything
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
topgrade [\fIoptions\f[]]
|
||||
@@ -10,7 +10,7 @@ topgrade [\fIoptions\f[]]
|
||||
.PP
|
||||
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, \fBTopgrade\fR detects which tools you use and runs the appropriate commands to update them.
|
||||
To remedy this, \fBtopgrade\fR detects which tools you use and runs the appropriate commands to update them.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-\-only <only>
|
||||
@@ -75,6 +75,6 @@ A list of steps must be provided as a list of separate arguments, i.e. 'topgrade
|
||||
.SH BUGS
|
||||
For a list of bugs see <\fIhttps://github.com/r-darwish/topgrade/issues\fR>.
|
||||
.SH AUTHOR
|
||||
\fBTopgrade\fR is maintained by Roey Dror (\[aq]r\-darwish\[aq]) and many other contributors.
|
||||
\fBtopgrade\fR is maintained by Roey Dror (\[aq]r\-darwish\[aq]) and many other contributors.
|
||||
You can view the full list at
|
||||
<\fIhttps://github.com/r-darwish/topgrade/graphs/contributors\fR>
|
||||
|
||||
Reference in New Issue
Block a user