Compare commits

...

24 Commits

Author SHA1 Message Date
SteveLauC
8e580457a5 chore: release v12.0.2 (#518) 2023-07-25 14:22:14 +08:00
SteveLauC
5350658dab fix: WSL detection (#508)
* fix: wSL detection
2023-07-25 14:02:13 +08:00
SteveLauC
1ec0ac50a5 fix: fix Linux and DragonFlyBSD yes option (#513) 2023-07-25 08:37:03 +08:00
SteveLauC
635bfce198 feat: extra arguments for Home Manager (#507)
* feat: extra arguments for Home Manager
2023-07-24 13:07:55 +08:00
6543
1307d2d7e8 feat: better error message on wrong os-release file (#511)
* enhancement: better error message when os-release parsing fails
2023-07-24 08:27:13 +08:00
SteveLauC
d21141fefe chore: release v12.0.1 (#510) 2023-07-23 20:06:31 +08:00
SteveLauC
0ec0e5a9dd chore: bump ci toolchain and MSRV (#506)
* chore: bump ci toolchain and MSRV

* fix clippy on macOS
2023-07-19 10:54:34 +08:00
SteveLauC
9415d7c61f fix(oh-my-zsh): fix remote oh-my-zsh issue (#496)
* fix(oh-my-zsh): fix remote oh-my-zsh issue
2023-07-18 13:59:55 +08:00
SteveLauC
42188af02b CI: release to PyPI (#500) 2023-07-18 08:11:36 +08:00
signed-log
e9581bcf15 feat: add assume-yes to more Linux managers (#501)
* Add assume-yes options to most Linux managers

Add `assume-yes` to :
- SUSE (Micro) - TW (`zypper`)
- PCLinux OS (`apt`)
- Solus (`eopkg`)
- `pacdef`
- Clear Linux (`swupd`)
2023-07-17 15:47:13 +08:00
SteveLauC
6afe4f51c6 test: unit test for Solus (#504) 2023-07-17 13:31:46 +08:00
signed-log
f623746d6c Fix clippy warning about non_minimal_cfg (#505)
Fix clippy::non_minimal_cfg warning
2023-07-17 13:30:55 +08:00
signed-log
1ce4d66e74 Ass assume-yes to DragonflyBSD (#502)
Add assume-yes to DragonflyBSD
2023-07-17 11:40:00 +08:00
har7an
3735d5c537 steps/toolbx: Don't self-update and don't send notifications (#503)
steps/toolbx: Don't send notification

after finishing execution in the toolbx step, and don't perform another
self-update (because the application will already have done that).
2023-07-17 09:08:44 +08:00
SteveLauC
f3b1d2dfb3 Merge pull request from GHSA-f2wx-xjfw-xjv6
chore: bump tempfile to ~3.6
2023-07-15 09:24:08 +08:00
Steve Lau
7f7d2633cd chore: bump tempfile to ~3.6 2023-07-15 09:17:47 +08:00
Harsh Shandilya
afd95e3d5c fix(generic): add alternate binary name for spicetify (#486) 2023-07-14 16:14:06 +00:00
SteveLauC
8f72545894 docs(config): document 4 missing sections in example config file (#485) 2023-07-14 16:13:44 +00:00
SteveLauC
d0d447deac fix: fix wrong path in oh-my-bash (#478) 2023-07-14 16:13:28 +00:00
SteveLauC
53a8683788 ci: separate code-coverage and test-config-creation (#488) 2023-07-14 16:12:53 +00:00
Janek
81491a8d03 docs: apply corrections in config.example.toml (#492)
* Fix Issues in config.example.toml

* Update config.example.toml
2023-07-14 16:12:32 +00:00
Janek
83504754ac docs: add Karma commit messages to CONTRIBUTING.md (#493)
Add Karma commit messages to CONTRIBUTING.md
2023-07-14 16:11:59 +00:00
Marcelo Duarte Trevisani
2068c2c169 Update only base conda env (#495) 2023-07-14 16:11:18 +00:00
SteveLauC
dbac121a90 refactor(config): move sudo_command to section misc (#484) 2023-07-01 13:58:39 +00:00
23 changed files with 465 additions and 134 deletions

View File

@@ -7,7 +7,7 @@ on:
name: CI name: CI
env: env:
RUST_VER: '1.68.0' RUST_VER: '1.71.0'
CROSS_VER: '0.2.5' CROSS_VER: '0.2.5'
CARGO_NET_RETRY: 3 CARGO_NET_RETRY: 3

View File

@@ -57,10 +57,3 @@ jobs:
# token: ${{ secrets.CODECOV_TOKEN }} # token: ${{ secrets.CODECOV_TOKEN }}
files: ./lcov.info files: ./lcov.info
fail_ci_if_error: true fail_ci_if_error: true
- name: Test creation of config file
run: |
CONFIG_PATH=~/.config/topgrade.toml;
if [ -f "$CONFIG_PATH" ]; then rm $CONFIG_PATH; fi
cargo build;
./target/debug/topgrade --dry-run --only system;
stat $CONFIG_PATH;

View File

@@ -4,7 +4,7 @@ on:
# types: # types:
# - completed # - completed
release: release:
types: [published, edited] types: [published]
name: Publish to crates.io on release name: Publish to crates.io on release

View File

@@ -0,0 +1,21 @@
name: Test Configuration File Creation
on:
pull_request:
env:
CARGO_TERM_COLOR: always
jobs:
TestConfig:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
CONFIG_PATH=~/.config/topgrade.toml;
if [ -f "$CONFIG_PATH" ]; then rm $CONFIG_PATH; fi
cargo build;
./target/debug/topgrade --dry-run --only system;
stat $CONFIG_PATH;

99
.github/workflows/update_pypi.yml vendored Normal file
View File

@@ -0,0 +1,99 @@
name: Update PyPi
on:
release:
types: [published]
permissions:
contents: read
jobs:
linux:
runs-on: ubuntu-latest
strategy:
matrix:
target: [x86_64, x86, aarch64]
steps:
- uses: actions/checkout@v3
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist
sccache: 'true'
manylinux: auto
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
windows:
runs-on: windows-latest
strategy:
matrix:
target: [x64, x86]
steps:
- uses: actions/checkout@v3
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
macos:
runs-on: macos-latest
strategy:
matrix:
target: [x86_64, aarch64]
steps:
- uses: actions/checkout@v3
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
sdist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
- name: Upload sdist
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
release:
name: Release
runs-on: ubuntu-latest
if: "startsWith(github.ref, 'refs/tags/')"
needs: [linux, windows, macos, sdist]
steps:
- uses: actions/download-artifact@v3
with:
name: wheels
- name: Publish to PyPI
uses: PyO3/maturin-action@v1
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
with:
command: upload
args: --skip-existing *

View File

@@ -1,16 +1,19 @@
## Contributing to `topgrade` ## Contributing to `topgrade`
Thank you for your interest in contributing to `topgrade`! We welcome and encourage Thank you for your interest in contributing to `topgrade`!
contributions of all kinds, such as: We welcome and encourage contributions of all kinds, such as:
1. Issue reports or feature requests 1. Issue reports or feature requests
2. Documentation improvements 2. Documentation improvements
3. Code (PR or PR Review) 3. Code (PR or PR Review)
Please follow the [Karma Runner guidelines](http://karma-runner.github.io/6.2/dev/git-commit-msg.html)
for commit messages.
## Adding a new `step` ## Adding a new `step`
In `topgrade`'s term, package manager is called `step`. To add a new `step` to In `topgrade`'s term, package manager is called `step`.
`topgrade`: To add a new `step` to `topgrade`:
1. Add a new variant to 1. Add a new variant to
[`enum Step`](https://github.com/topgrade-rs/topgrade/blob/cb7adc8ced8a77addf2cb051d18bba9f202ab866/src/config.rs#L100) [`enum Step`](https://github.com/topgrade-rs/topgrade/blob/cb7adc8ced8a77addf2cb051d18bba9f202ab866/src/config.rs#L100)

120
Cargo.lock generated
View File

@@ -17,17 +17,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.20" version = "0.7.20"
@@ -406,6 +395,28 @@ dependencies = [
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
[[package]]
name = "const-random"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e"
dependencies = [
"const-random-macro",
"proc-macro-hack",
]
[[package]]
name = "const-random-macro"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb"
dependencies = [
"getrandom",
"once_cell",
"proc-macro-hack",
"tiny-keccak",
]
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.4" version = "0.8.4"
@@ -439,6 +450,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@@ -513,9 +530,12 @@ dependencies = [
[[package]] [[package]]
name = "dlv-list" name = "dlv-list"
version = "0.3.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" checksum = "d529fd73d344663edfd598ccb3f344e46034db51ebd103518eae34338248ad73"
dependencies = [
"const-random",
]
[[package]] [[package]]
name = "either" name = "either"
@@ -624,7 +644,7 @@ checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall 0.2.16",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
@@ -814,9 +834,12 @@ name = "hashbrown"
version = "0.12.3" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash", [[package]]
] name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
[[package]] [[package]]
name = "heck" name = "heck"
@@ -980,7 +1003,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown 0.12.3",
] ]
[[package]] [[package]]
@@ -1291,12 +1314,12 @@ checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
[[package]] [[package]]
name = "ordered-multimap" name = "ordered-multimap"
version = "0.4.3" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
dependencies = [ dependencies = [
"dlv-list", "dlv-list",
"hashbrown", "hashbrown 0.13.2",
] ]
[[package]] [[package]]
@@ -1420,6 +1443,12 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.63" version = "1.0.63"
@@ -1495,6 +1524,15 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.4.3" version = "0.4.3"
@@ -1502,7 +1540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"redox_syscall", "redox_syscall 0.2.16",
"thiserror", "thiserror",
] ]
@@ -1541,15 +1579,6 @@ version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.18" version = "0.11.18"
@@ -1612,9 +1641,9 @@ checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
[[package]] [[package]]
name = "rust-ini" name = "rust-ini"
version = "0.18.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"ordered-multimap", "ordered-multimap",
@@ -1947,16 +1976,16 @@ dependencies = [
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.2.0" version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
dependencies = [ dependencies = [
"autocfg",
"cfg-if", "cfg-if",
"libc", "fastrand",
"rand", "redox_syscall 0.3.5",
"redox_syscall", "rustix",
"remove_dir_all", "windows-sys 0.48.0",
"winapi",
] ]
[[package]] [[package]]
@@ -2042,6 +2071,15 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.6.0" version = "1.6.0"
@@ -2126,7 +2164,7 @@ dependencies = [
[[package]] [[package]]
name = "topgrade" name = "topgrade"
version = "12.0.0" version = "12.0.2"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"chrono", "chrono",

View File

@@ -5,7 +5,7 @@ categories = ["os"]
keywords = ["upgrade", "update"] keywords = ["upgrade", "update"]
license = "GPL-3.0" license = "GPL-3.0"
repository = "https://github.com/topgrade-rs/topgrade" repository = "https://github.com/topgrade-rs/topgrade"
version = "12.0.0" version = "12.0.2"
authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"] authors = ["Roey Darwish Dror <roey.ghost@gmail.com>", "Thomas Schönauer <t.schoenauer@hgs-wt.at>"]
exclude = ["doc/screenshot.gif"] exclude = ["doc/screenshot.gif"]
edition = "2021" edition = "2021"
@@ -37,7 +37,7 @@ chrono = "~0.4"
glob = "~0.3" glob = "~0.3"
strum = { version = "~0.24", features = ["derive"] } strum = { version = "~0.24", features = ["derive"] }
thiserror = "~1.0" thiserror = "~1.0"
tempfile = "~3.2" tempfile = "~3.6"
cfg-if = "~1.0" cfg-if = "~1.0"
tokio = { version = "~1.18", features = ["process", "rt-multi-thread"] } tokio = { version = "~1.18", features = ["process", "rt-multi-thread"] }
futures = "~0.3" futures = "~0.3"
@@ -63,7 +63,7 @@ depends = "$auto,git"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
libc = "~0.2" libc = "~0.2"
nix = "~0.24" nix = "~0.24"
rust-ini = "~0.18" rust-ini = "~0.19"
self_update_crate = { version = "~0.30", default-features = false, optional = true, package = "self_update", features = ["archive-tar", "compression-flate2", "rustls"] } self_update_crate = { version = "~0.30", default-features = false, optional = true, package = "self_update", features = ["archive-tar", "compression-flate2", "rustls"] }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]

View File

@@ -34,11 +34,13 @@ To remedy this, **Topgrade** detects which tools you use and runs the appropriat
- Void Linux: [XBPS](https://voidlinux.org/packages/?arch=x86_64&q=topgrade) - Void Linux: [XBPS](https://voidlinux.org/packages/?arch=x86_64&q=topgrade)
- macOS: [Homebrew](https://formulae.brew.sh/formula/topgrade) or [MacPorts](https://ports.macports.org/port/topgrade/) - macOS: [Homebrew](https://formulae.brew.sh/formula/topgrade) or [MacPorts](https://ports.macports.org/port/topgrade/)
- Windows: [Scoop](https://github.com/ScoopInstaller/Main/blob/master/bucket/topgrade.json) - Windows: [Scoop](https://github.com/ScoopInstaller/Main/blob/master/bucket/topgrade.json)
- PyPi: [pip](https://pypi.org/project/topgrade/)
Other systems users can either use `cargo install` or the compiled binaries from the release page. Other systems users can either use `cargo install` or the compiled binaries from the release page.
The compiled binaries contain a self-upgrading feature. The compiled binaries contain a self-upgrading feature.
Topgrade requires Rust 1.60 or above. > Currently, Topgrade requires Rust 1.65 or above. In general, Topgrade tracks
> the latest stable toolchain.
## Usage ## Usage

View File

@@ -1,6 +1,6 @@
# Include any additional configuration file(s) # Include any additional configuration file(s)
# [include] sections are processed in the order you write them # [include] sections are processed in the order you write them
# Files in $CONFIG_DIR/topgrade/topgrade.d/ are automatically included at the beginning of this file # Files in $CONFIG_DIR/topgrade.d/ are automatically included before this file
[include] [include]
#paths = ["/etc/topgrade.toml"] #paths = ["/etc/topgrade.toml"]
@@ -23,8 +23,8 @@
# Sudo command to be used # Sudo command to be used
#sudo_command = "sudo" #sudo_command = "sudo"
# Run `sudo -v` to cache credentials at the start of the run; this avoids a # Run `sudo -v` to cache credentials at the start of the run
# blocking password prompt in the middle of a possibly-unattended run. # This avoids a blocking password prompt in the middle of an unattended run
#pre_sudo = false #pre_sudo = false
# Run inside tmux # Run inside tmux
@@ -33,7 +33,7 @@
# List of remote machines with Topgrade installed on them # List of remote machines with Topgrade installed on them
#remote_topgrades = ["toothless", "pi", "parnas"] #remote_topgrades = ["toothless", "pi", "parnas"]
# Arguments to pass SSH when upgrading remote systems # Arguments to pass to SSH when upgrading remote systems
#ssh_arguments = "-o ConnectTimeout=2" #ssh_arguments = "-o ConnectTimeout=2"
# Path to Topgrade executable on remote machines # Path to Topgrade executable on remote machines
@@ -57,32 +57,31 @@
# Whether to self update (this is ignored if the binary has been built without self update support, available also via setting the environment variable TOPGRADE_NO_SELF_UPGRADE) # Whether to self update (this is ignored if the binary has been built without self update support, available also via setting the environment variable TOPGRADE_NO_SELF_UPGRADE)
#no_self_update = true #no_self_update = true
[git] # Extra Home Manager arguments
#max_concurrency = 5 #home_manager_arguments = ["--flake", "file"]
# Additional git repositories to pull
#repos = [
# "~/src/*/",
# "~/.config/something"
#]
# Don't pull the predefined git repos
#pull_predefined = false
# Arguments to pass Git when pulling Repositories
#arguments = "--rebase --autostash"
[composer]
#self_update = true
# Commands to run before anything # Commands to run before anything
[pre_commands] [pre_commands]
#"Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak" #"Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak"
# Commands to run after anything
[post_commands]
#"Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak"
# Custom commands # Custom commands
[commands] [commands]
#"Python Environment" = "~/dev/.env/bin/pip install -i https://pypi.python.org/simple -U --upgrade-strategy eager jupyter" #"Python Environment" = "~/dev/.env/bin/pip install -i https://pypi.python.org/simple -U --upgrade-strategy eager jupyter"
#"Custom command using interactive shell (unix)" = "-i vim_upgrade" #"Custom command using interactive shell (unix)" = "-i vim_upgrade"
[python]
#enable_pip_review = true ###disabled by default
#enable_pip_review_local = true ###disabled by default
#enable_pipupgrade = true ###disabled by default
#pipupgrade_arguments = "-y -u --pip-path pip" ###disabled by default
[composer]
#self_update = true
[brew] [brew]
#greedy_cask = true #greedy_cask = true
#autoremove = true #autoremove = true
@@ -109,11 +108,19 @@
#rpm_ostree = false #rpm_ostree = false
#nix_arguments = "--flake" #nix_arguments = "--flake"
[python] [git]
#enable_pip_review = true ###disabled by default #max_concurrency = 5
#enable_pip_review_local = true ###disabled by default # Additional git repositories to pull
#enable_pipupgrade = true ###disabled by default #repos = [
#pipupgrade_arguments = "-y -u --pip-path pip" ###disabled by default # "~/src/*/",
# "~/.config/something"
#]
# Don't pull the predefined git repos
#pull_predefined = false
# Arguments to pass Git when pulling Repositories
#arguments = "--rebase --autostash"
[windows] [windows]
# Manually select Windows updates # Manually select Windows updates
@@ -131,10 +138,28 @@
# Use sudo if the NPM directory isn't owned by the current user # Use sudo if the NPM directory isn't owned by the current user
#use_sudo = true #use_sudo = true
[yarn]
# Run `yarn global upgrade` with `sudo`
#use_sudo = true
[vim]
# For `vim-plug`, execute `PlugUpdate!` instead of `PlugUpdate`
#force_plug_update = true
[firmware] [firmware]
# Offer to update firmware; if false just check for and display available updates # Offer to update firmware; if false just check for and display available updates
#upgrade = true #upgrade = true
[vagrant]
# Vagrant directories
#directories = []
# power on vagrant boxes if needed
#power_on = true
# Always suspend vagrant boxes instead of powering off
#always_suspend = true
[flatpak] [flatpak]
# Use sudo for updating the system-wide installation # Use sudo for updating the system-wide installation
#use_sudo = true #use_sudo = true

16
pyproject.toml Normal file
View File

@@ -0,0 +1,16 @@
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"
[project]
name = "topgrade"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
[tool.maturin]
bindings = "bin"

View File

@@ -351,6 +351,9 @@ pub struct Linux {
#[merge(strategy = crate::utils::merge_strategies::string_append_opt)] #[merge(strategy = crate::utils::merge_strategies::string_append_opt)]
emerge_update_flags: Option<String>, emerge_update_flags: Option<String>,
#[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)]
home_manager_arguments: Option<Vec<String>>,
} }
#[derive(Deserialize, Default, Debug, Merge)] #[derive(Deserialize, Default, Debug, Merge)]
@@ -370,6 +373,8 @@ pub struct Vim {
pub struct Misc { pub struct Misc {
pre_sudo: Option<bool>, pre_sudo: Option<bool>,
sudo_command: Option<SudoKind>,
#[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)] #[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)]
git_repos: Option<Vec<String>>, git_repos: Option<Vec<String>>,
@@ -440,8 +445,6 @@ pub struct ConfigFile {
#[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)] #[merge(strategy = crate::utils::merge_strategies::inner_merge_opt)]
misc: Option<Misc>, misc: Option<Misc>,
sudo_command: Option<SudoKind>,
#[merge(strategy = crate::utils::merge_strategies::commands_merge_opt)] #[merge(strategy = crate::utils::merge_strategies::commands_merge_opt)]
pre_commands: Option<Commands>, pre_commands: Option<Commands>,
@@ -1275,6 +1278,14 @@ impl Config {
.and_then(|linux| linux.nix_arguments.as_deref()) .and_then(|linux| linux.nix_arguments.as_deref())
} }
/// Extra Home Manager arguments
pub fn home_manager(&self) -> Option<&Vec<String>> {
self.config_file
.linux
.as_ref()
.and_then(|misc| misc.home_manager_arguments.as_ref())
}
/// Distrobox use root /// Distrobox use root
pub fn distrobox_root(&self) -> bool { pub fn distrobox_root(&self) -> bool {
self.config_file self.config_file
@@ -1389,7 +1400,7 @@ impl Config {
} }
pub fn sudo_command(&self) -> Option<SudoKind> { pub fn sudo_command(&self) -> Option<SudoKind> {
self.config_file.sudo_command self.config_file.misc.as_ref().and_then(|misc| misc.sudo_command)
} }
/// If `true`, `sudo` should be called after `pre_commands` in order to elevate at the /// If `true`, `sudo` should be called after `pre_commands` in order to elevate at the

View File

@@ -14,6 +14,10 @@ pub enum TopgradeError {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
UnknownLinuxDistribution, UnknownLinuxDistribution,
#[error("File \"/etc/os-release\" does not exist or is empty")]
#[cfg(target_os = "linux")]
EmptyOSReleaseFile,
#[error("Failed getting the system package manager")] #[error("Failed getting the system package manager")]
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
FailedGettingPackageManager, FailedGettingPackageManager,

View File

@@ -5,6 +5,7 @@ use crate::sudo::Sudo;
use crate::utils::{require_option, REQUIRE_SUDO}; use crate::utils::{require_option, REQUIRE_SUDO};
use crate::{config::Config, executor::Executor}; use crate::{config::Config, executor::Executor};
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use std::env::var;
use std::path::Path; use std::path::Path;
use std::sync::Mutex; use std::sync::Mutex;
@@ -17,16 +18,20 @@ pub struct ExecutionContext<'a> {
/// This is used in `./steps/remote/ssh.rs`, where we want to run `topgrade` in a new /// This is used in `./steps/remote/ssh.rs`, where we want to run `topgrade` in a new
/// tmux window for each remote. /// tmux window for each remote.
tmux_session: Mutex<Option<String>>, tmux_session: Mutex<Option<String>>,
/// True if topgrade is running under ssh.
under_ssh: bool,
} }
impl<'a> ExecutionContext<'a> { impl<'a> ExecutionContext<'a> {
pub fn new(run_type: RunType, sudo: Option<Sudo>, git: &'a Git, config: &'a Config) -> Self { pub fn new(run_type: RunType, sudo: Option<Sudo>, git: &'a Git, config: &'a Config) -> Self {
let under_ssh = var("SSH_CLIENT").is_ok() || var("SSH_TTY").is_ok();
Self { Self {
run_type, run_type,
sudo, sudo,
git, git,
config, config,
tmux_session: Mutex::new(None), tmux_session: Mutex::new(None),
under_ssh,
} }
} }
@@ -51,6 +56,10 @@ impl<'a> ExecutionContext<'a> {
self.config self.config
} }
pub fn under_ssh(&self) -> bool {
self.under_ssh
}
pub fn set_tmux_session(&self, session_name: String) { pub fn set_tmux_session(&self, session_name: String) {
self.tmux_session.lock().unwrap().replace(session_name); self.tmux_session.lock().unwrap().replace(session_name);
} }

View File

@@ -1,4 +1,4 @@
#[cfg(any(windows))] #[cfg(windows)]
use std::env; use std::env;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View File

@@ -339,7 +339,7 @@ pub fn run_conda_update(ctx: &ExecutionContext) -> Result<()> {
print_separator("Conda"); print_separator("Conda");
let mut command = ctx.run_type().execute(conda); let mut command = ctx.run_type().execute(conda);
command.args(["update", "--all"]); command.args(["update", "--all", "-n", "base"]);
if ctx.config().yes(Step::Conda) { if ctx.config().yes(Step::Conda) {
command.arg("--yes"); command.arg("--yes");
} }
@@ -360,7 +360,7 @@ pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> {
print_separator("Mamba"); print_separator("Mamba");
let mut command = ctx.run_type().execute(mamba); let mut command = ctx.run_type().execute(mamba);
command.args(["update", "--all"]); command.args(["update", "--all", "-n", "base"]);
if ctx.config().yes(Step::Mamba) { if ctx.config().yes(Step::Mamba) {
command.arg("--yes"); command.arg("--yes");
} }
@@ -720,7 +720,8 @@ pub fn bin_update(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn spicetify_upgrade(ctx: &ExecutionContext) -> Result<()> { pub fn spicetify_upgrade(ctx: &ExecutionContext) -> Result<()> {
let spicetify = require("spicetify")?; // As of 04-07-2023 NixOS packages Spicetify with the `spicetify-cli` binary name
let spicetify = require("spicetify").or(require("spicetify-cli"))?;
print_separator("Spicetify"); print_separator("Spicetify");
ctx.run_type().execute(spicetify).arg("upgrade").status_checked() ctx.run_type().execute(spicetify).arg("upgrade").status_checked()

View File

@@ -8,9 +8,12 @@ use std::process::Command;
pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> { pub fn upgrade_packages(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
print_separator("DragonFly BSD Packages"); print_separator("DragonFly BSD Packages");
ctx.execute(sudo) let mut cmd = ctx.execute(sudo);
.args(["/usr/local/sbin/pkg", "upgrade"]) cmd.args(["/usr/local/sbin/pkg", "upgrade"]);
.status_checked() if ctx.config().yes(Step::System) {
cmd.arg("-y");
}
cmd.status_checked()
} }
pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> { pub fn audit_packages(ctx: &ExecutionContext) -> Result<()> {

View File

@@ -114,10 +114,14 @@ impl Distribution {
if PathBuf::from(OS_RELEASE_PATH).exists() { if PathBuf::from(OS_RELEASE_PATH).exists() {
let os_release = Ini::load_from_file(OS_RELEASE_PATH)?; let os_release = Ini::load_from_file(OS_RELEASE_PATH)?;
if os_release.general_section().is_empty() {
return Err(TopgradeError::EmptyOSReleaseFile.into());
}
return Self::parse_os_release(&os_release); return Self::parse_os_release(&os_release);
} }
Err(TopgradeError::UnknownLinuxDistribution.into()) Err(TopgradeError::EmptyOSReleaseFile.into())
} }
pub fn upgrade(self, ctx: &ExecutionContext) -> Result<()> { pub fn upgrade(self, ctx: &ExecutionContext) -> Result<()> {
@@ -248,15 +252,18 @@ fn upgrade_suse(ctx: &ExecutionContext) -> Result<()> {
.args(["zypper", "refresh"]) .args(["zypper", "refresh"])
.status_checked()?; .status_checked()?;
ctx.run_type() let mut cmd = ctx.run_type().execute(sudo);
.execute(sudo) cmd.arg("zypper");
.arg("zypper") cmd.arg(if ctx.config().suse_dup() {
.arg(if ctx.config().suse_dup() { "dist-upgrade"
"dist-upgrade" } else {
} else { "update"
"update" });
}) if ctx.config().yes(Step::System) {
.status_checked()?; cmd.arg("-y");
}
cmd.status_checked()?;
Ok(()) Ok(())
} }
@@ -268,21 +275,26 @@ fn upgrade_opensuse_tumbleweed(ctx: &ExecutionContext) -> Result<()> {
.args(["zypper", "refresh"]) .args(["zypper", "refresh"])
.status_checked()?; .status_checked()?;
ctx.run_type() let mut cmd = ctx.run_type().execute(sudo);
.execute(sudo) cmd.args(["zypper", "dist-upgrade"]);
.arg("zypper") if ctx.config().yes(Step::System) {
.arg("dist-upgrade") cmd.arg("-y");
.status_checked()?; }
cmd.status_checked()?;
Ok(()) Ok(())
} }
fn upgrade_suse_micro(ctx: &ExecutionContext) -> Result<()> { fn upgrade_suse_micro(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() let mut cmd = ctx.run_type().execute(sudo);
.execute(sudo) cmd.arg("transactional-update");
.args(["transactional-update", "dup"]) if ctx.config().yes(Step::System) {
.status_checked()?; cmd.arg("-n");
}
cmd.arg("dup").status_checked()?;
Ok(()) Ok(())
} }
@@ -321,11 +333,13 @@ fn upgrade_pclinuxos(ctx: &ExecutionContext) -> Result<()> {
command_update.status_checked()?; command_update.status_checked()?;
ctx.run_type() let mut cmd = ctx.run_type().execute(sudo);
.execute(sudo) cmd.arg(&which("apt-get").unwrap());
.arg(&which("apt-get").unwrap()) cmd.arg("dist-upgrade");
.arg("dist-upgrade") if ctx.config().yes(Step::System) {
.status_checked()?; cmd.arg("-y");
}
cmd.status_checked()?;
Ok(()) Ok(())
} }
@@ -497,10 +511,12 @@ pub fn run_deb_get(ctx: &ExecutionContext) -> Result<()> {
fn upgrade_solus(ctx: &ExecutionContext) -> Result<()> { fn upgrade_solus(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() let mut cmd = ctx.run_type().execute(sudo);
.execute(sudo) cmd.arg("eopkg");
.args(["eopkg", "upgrade"]) if ctx.config().yes(Step::System) {
.status_checked()?; cmd.arg("-y");
}
cmd.arg("upgrade").status_checked()?;
Ok(()) Ok(())
} }
@@ -539,10 +555,12 @@ pub fn run_pacdef(ctx: &ExecutionContext) -> Result<()> {
let new_version = string.contains("version: 1"); let new_version = string.contains("version: 1");
if new_version { if new_version {
ctx.run_type() let mut cmd = ctx.run_type().execute(&pacdef);
.execute(&pacdef) cmd.args(["package", "sync"]);
.args(["package", "sync"]) if ctx.config().yes(Step::System) {
.status_checked()?; cmd.arg("--noconfirm");
}
cmd.status_checked()?;
println!(); println!();
ctx.run_type() ctx.run_type()
@@ -550,7 +568,13 @@ pub fn run_pacdef(ctx: &ExecutionContext) -> Result<()> {
.args(["package", "review"]) .args(["package", "review"])
.status_checked()?; .status_checked()?;
} else { } else {
ctx.run_type().execute(&pacdef).arg("sync").status_checked()?; let mut cmd = ctx.run_type().execute(&pacdef);
cmd.arg("sync");
if ctx.config().yes(Step::System) {
cmd.arg("--noconfirm");
}
cmd.status_checked()?;
println!(); println!();
ctx.run_type().execute(&pacdef).arg("review").status_checked()?; ctx.run_type().execute(&pacdef).arg("review").status_checked()?;
@@ -596,10 +620,12 @@ pub fn run_packer_nu(ctx: &ExecutionContext) -> Result<()> {
fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> { fn upgrade_clearlinux(ctx: &ExecutionContext) -> Result<()> {
let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?; let sudo = require_option(ctx.sudo().as_ref(), REQUIRE_SUDO.to_string())?;
ctx.run_type() let mut cmd = ctx.run_type().execute(sudo);
.execute(sudo) cmd.args(["swupd", "update"]);
.args(["swupd", "update"]) if ctx.config().yes(Step::System) {
.status_checked()?; cmd.arg("--assume=yes");
}
cmd.status_checked()?;
Ok(()) Ok(())
} }
@@ -1014,4 +1040,9 @@ mod tests {
fn test_vanilla() { fn test_vanilla() {
test_template(include_str!("os_release/vanilla"), Distribution::Vanilla); test_template(include_str!("os_release/vanilla"), Distribution::Vanilla);
} }
#[test]
fn test_solus() {
test_template(include_str!("os_release/solus"), Distribution::Solus);
}
} }

View File

@@ -0,0 +1,11 @@
NAME="Solus"
VERSION="4.4"
ID="solus"
VERSION_CODENAME=harmony
VERSION_ID="4.4"
PRETTY_NAME="Solus 4.4 Harmony"
ANSI_COLOR="1;34"
HOME_URL="https://getsol.us"
SUPPORT_URL="https://help.getsol.us/docs/user/contributing/getting-involved"
BUG_REPORT_URL="https://dev.getsol.us/"
LOGO="distributor-logo-solus"

View File

@@ -155,7 +155,7 @@ pub fn run_oh_my_bash(ctx: &ExecutionContext) -> Result<()> {
print_separator("oh-my-bash"); print_separator("oh-my-bash");
let mut update_script = oh_my_bash; let mut update_script = oh_my_bash;
update_script.push_str("tools/upgrade.sh"); update_script.push_str("/tools/upgrade.sh");
ctx.run_type().execute("bash").arg(update_script).status_checked() ctx.run_type().execute("bash").arg(update_script).status_checked()
} }
@@ -383,7 +383,7 @@ pub fn run_nix(ctx: &ExecutionContext) -> Result<()> {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
if let Ok(..) = require("darwin-rebuild") { if require("darwin-rebuild").is_ok() {
return Err(SkipStep(String::from( return Err(SkipStep(String::from(
"Nix-darwin on macOS must be upgraded via darwin-rebuild switch", "Nix-darwin on macOS must be upgraded via darwin-rebuild switch",
)) ))
@@ -442,7 +442,15 @@ pub fn run_home_manager(ctx: &ExecutionContext) -> Result<()> {
let home_manager = require("home-manager")?; let home_manager = require("home-manager")?;
print_separator("home-manager"); print_separator("home-manager");
ctx.run_type().execute(home_manager).arg("switch").status_checked()
let mut cmd = ctx.run_type().execute(home_manager);
cmd.arg("switch");
if let Some(extra_args) = ctx.config().home_manager() {
cmd.args(extra_args);
}
cmd.status_checked()
} }
pub fn run_tldr(ctx: &ExecutionContext) -> Result<()> { pub fn run_tldr(ctx: &ExecutionContext) -> Result<()> {

View File

@@ -9,7 +9,7 @@ use tracing::debug;
use crate::command::CommandExt; use crate::command::CommandExt;
use crate::execution_context::ExecutionContext; use crate::execution_context::ExecutionContext;
use crate::terminal::{print_separator, print_warning}; use crate::terminal::{print_separator, print_warning};
use crate::utils::require; use crate::utils::{require, which};
use crate::{error::SkipStep, steps::git::Repositories}; use crate::{error::SkipStep, steps::git::Repositories};
use crate::{powershell, Step}; use crate::{powershell, Step};
@@ -69,6 +69,10 @@ pub fn run_scoop(ctx: &ExecutionContext) -> Result<()> {
} }
pub fn update_wsl(ctx: &ExecutionContext) -> Result<()> { pub fn update_wsl(ctx: &ExecutionContext) -> Result<()> {
if !is_wsl_installed()? {
return Err(SkipStep("WSL not installed".to_string()).into());
}
let wsl = require("wsl")?; let wsl = require("wsl")?;
print_separator("Update WSL"); print_separator("Update WSL");
@@ -87,6 +91,30 @@ pub fn update_wsl(ctx: &ExecutionContext) -> Result<()> {
Ok(()) Ok(())
} }
/// Detect if WSL is installed or not.
///
/// For WSL, we cannot simply check if command `wsl` is installed as on newer
/// versions of Windows (since windows 10 version 2004), this commmand is
/// installed by default.
///
/// If the command is installed and the user hasn't installed any Linux distros
/// on it, command `wsl -l` would print a help message and exit with failure, we
/// use this to check whether WSL is install or not.
fn is_wsl_installed() -> Result<bool> {
if let Some(wsl) = which("wsl") {
// Don't use `output_checked` as an execution failure log is not wanted
#[allow(clippy::disallowed_methods)]
let output = Command::new(wsl).arg("-l").output()?;
let status = output.status;
if status.success() {
return Ok(true);
}
}
Ok(false)
}
fn get_wsl_distributions(wsl: &Path) -> Result<Vec<String>> { 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"]).output_checked_utf8()?.stdout;
Ok(output Ok(output
@@ -115,6 +143,10 @@ fn upgrade_wsl_distribution(wsl: &Path, dist: &str, ctx: &ExecutionContext) -> R
} }
pub fn run_wsl_topgrade(ctx: &ExecutionContext) -> Result<()> { pub fn run_wsl_topgrade(ctx: &ExecutionContext) -> Result<()> {
if !is_wsl_installed()? {
return Err(SkipStep("WSL not installed".to_string()).into());
}
let wsl = require("wsl")?; let wsl = require("wsl")?;
let wsl_distributions = get_wsl_distributions(&wsl)?; let wsl_distributions = get_wsl_distributions(&wsl)?;
let mut ran = false; let mut ran = false;

View File

@@ -52,6 +52,8 @@ pub fn run_toolbx(ctx: &ExecutionContext) -> Result<()> {
topgrade_path, topgrade_path,
"--only", "--only",
"system", "system",
"--no-self-update",
"--skip-notify",
]; ];
if ctx.config().yes(Step::Toolbx) { if ctx.config().yes(Step::Toolbx) {
args.push("--yes"); args.push("--yes");

View File

@@ -165,6 +165,28 @@ pub fn run_zim(ctx: &ExecutionContext) -> Result<()> {
pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> { pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> {
require("zsh")?; require("zsh")?;
// When updating `oh-my-zsh` on a remote machine through topgrade, the
// following processes will be created:
//
// SSH -> ZSH -> ZSH ($SHELL) -> topgrade -> ZSH
//
// The first ZSH process, won't source zshrc (as it is a login shell),
// and thus it won't have the ZSH environment variable, as a result, the
// children processes won't get it either, so we source the zshrc and set
// the ZSH variable for topgrade here.
if ctx.under_ssh() {
let zshrc_path = zshrc().require()?;
let output = Command::new("zsh")
.args([
"-c",
// ` > /dev/null` is used in case the user's zshrc will have some stdout output.
format!("source {} > /dev/null && echo $ZSH", zshrc_path.display()).as_str(),
])
.output_checked_utf8()?;
env::set_var("ZSH", output.stdout.trim());
}
let oh_my_zsh = env::var("ZSH") let oh_my_zsh = env::var("ZSH")
.map(PathBuf::from) .map(PathBuf::from)
// default to `~/.oh-my-zsh` // default to `~/.oh-my-zsh`