mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-05 21:28:48 +00:00
update zsh install
This commit is contained in:
commit
e58c54ad04
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -6,7 +6,7 @@
|
||||||
## Type of Change
|
## Type of Change
|
||||||
- [ ] New feature
|
- [ ] New feature
|
||||||
- [ ] Bug fix
|
- [ ] Bug fix
|
||||||
- [ ] Documentation Update (Due to be added!)
|
- [ ] Documentation Update
|
||||||
- [ ] Refactoring
|
- [ ] Refactoring
|
||||||
- [ ] Hotfix
|
- [ ] Hotfix
|
||||||
- [ ] Security patch
|
- [ ] Security patch
|
||||||
|
|
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
|
@ -8,3 +8,8 @@ updates:
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
ignore:
|
||||||
|
- dependency-name: "actions/stale"
|
||||||
|
versions: '>= 9'
|
||||||
|
- dependency-name: "actions/setup-python"
|
||||||
|
versions: '> 4'
|
22
.github/workflows/github-pages.yml
vendored
Normal file
22
.github/workflows/github-pages.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
name: GitHub Pages Deploy
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published, prereleased]
|
||||||
|
workflow_dispatch:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: ${{ github.ref }}
|
||||||
|
path: .cache
|
||||||
|
- run: pip install mkdocs-material
|
||||||
|
- run: pip install pillow cairosvg
|
||||||
|
- run: mkdocs gh-deploy --force
|
45
.github/workflows/issue-slash-commands.yaml
vendored
Normal file
45
.github/workflows/issue-slash-commands.yaml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
name: Close issue on /close
|
||||||
|
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created, edited]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
closeIssueOnClose:
|
||||||
|
# Skip this job if the comment was created/edited on a PR
|
||||||
|
if: ${{ !github.event.issue.pull_request }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: none
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check for /close comment
|
||||||
|
id: check_comment
|
||||||
|
run: |
|
||||||
|
if [[ "${{ contains(github.event.comment.body, '/close') }}" == "true" ]]; then
|
||||||
|
echo "comment=true" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "comment=false" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check if the user is allowed
|
||||||
|
id: check_user
|
||||||
|
if: env.comment == 'true'
|
||||||
|
run: |
|
||||||
|
ALLOWED_USERS=("ChrisTitusTech" "afonsofrancof" "Marterich" "MyDrift-user" "Real-MullaC")
|
||||||
|
if [[ " ${ALLOWED_USERS[@]} " =~ " ${{ github.event.comment.user.login }} " ]]; then
|
||||||
|
echo "user=true" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "user=false" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Close issue if conditions are met
|
||||||
|
if: env.comment == 'true' && env.user == 'true'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||||
|
run: |
|
||||||
|
echo Closing the issue...
|
||||||
|
gh issue close $ISSUE_NUMBER --repo ${{ github.repository }}
|
95
.github/workflows/linutil.yml
vendored
95
.github/workflows/linutil.yml
vendored
|
@ -1,40 +1,55 @@
|
||||||
name: LinUtil Release
|
name: LinUtil Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: ["main"]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linutil_build:
|
linutil_build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Cache Cargo registry
|
- name: Cache Cargo registry
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cargo/registry
|
path: ~/.cargo/registry
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||||
restore-keys: ${{ runner.os }}-cargo-registry-
|
restore-keys: ${{ runner.os }}-cargo-registry-
|
||||||
- name: Cache Cargo index
|
- name: Cache Cargo index
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cargo/git
|
path: ~/.cargo/git
|
||||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
||||||
restore-keys: ${{ runner.os }}-cargo-index-
|
restore-keys: ${{ runner.os }}-cargo-index-
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Build
|
with:
|
||||||
run: cargo build --target-dir=build --release --verbose
|
targets: x86_64-unknown-linux-musl
|
||||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
- name: Install cross-rs for cross-compilation
|
||||||
with:
|
run: cargo install cross
|
||||||
commit_message: Commit Linutil
|
- name: Build x86_64 binary
|
||||||
file_pattern: 'build/release/linutil'
|
run: cargo build --target-dir=build --release --verbose --target=x86_64-unknown-linux-musl
|
||||||
if: success()
|
- name: Build aarch64 binary
|
||||||
|
run: cross build --target-dir=build --release --verbose --target=aarch64-unknown-linux-musl
|
||||||
|
- name: Move binaries to build directory
|
||||||
|
run: |
|
||||||
|
mv build/x86_64-unknown-linux-musl/release/linutil build/linutil
|
||||||
|
mv build/aarch64-unknown-linux-musl/release/linutil build/linutil-aarch64
|
||||||
|
- name: Pull latest changes
|
||||||
|
run: |
|
||||||
|
git config --global user.email "github-actions@github.com"
|
||||||
|
git config --global user.name "GitHub Actions"
|
||||||
|
git pull origin main
|
||||||
|
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
|
with:
|
||||||
|
commit_message: Commit Linutil
|
||||||
|
file_pattern: "build/linutil build/linutil-aarch64"
|
||||||
|
if: success()
|
9
.github/workflows/pre-release.yaml
vendored
9
.github/workflows/pre-release.yaml
vendored
|
@ -1,8 +1,8 @@
|
||||||
name: Pre-Release LinUtil
|
name: Pre-Release LinUtil
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # Grant write permissions to contents
|
contents: write # Grant write permissions to contents
|
||||||
packages: write # Grant write permissions to packages
|
packages: write # Grant write permissions to packages
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: # Manual trigger added
|
workflow_dispatch: # Manual trigger added
|
||||||
|
@ -30,9 +30,10 @@ jobs:
|
||||||
body: "![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/${{ env.version }}/linutil)"
|
body: "![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/${{ env.version }}/linutil)"
|
||||||
append_body: false
|
append_body: false
|
||||||
files: |
|
files: |
|
||||||
./build/release/linutil
|
./build/linutil
|
||||||
|
./build/linutil-aarch64
|
||||||
prerelease: true
|
prerelease: true
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
env:
|
env:
|
||||||
version: ${{ env.version }}
|
version: ${{ env.version }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
60
.github/workflows/rust.yml
vendored
60
.github/workflows/rust.yml
vendored
|
@ -2,53 +2,39 @@ name: Rust Checks
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: ["main"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cargo_check:
|
|
||||||
name: Cargo Check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Cache Cargo registry
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/registry
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: ${{ runner.os }}-cargo-registry-
|
|
||||||
|
|
||||||
- name: Cache Cargo index
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/git
|
|
||||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: ${{ runner.os }}-cargo-index-
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: cargo check
|
|
||||||
|
|
||||||
lints:
|
lints:
|
||||||
name: Lints
|
name: Lints
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
- name: Run cargo fmt
|
- name: Cache Cargo registry
|
||||||
run: cargo fmt --all --check
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/registry
|
||||||
|
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: ${{ runner.os }}-cargo-registry-
|
||||||
|
|
||||||
- name: Run cargo clippy
|
- name: Cache Cargo index
|
||||||
run: cargo clippy
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/git
|
||||||
|
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: ${{ runner.os }}-cargo-index-
|
||||||
|
|
||||||
|
- name: Run cargo clippy
|
||||||
|
run: cargo clippy
|
||||||
|
|
||||||
|
- name: Run cargo fmt
|
||||||
|
run: cargo fmt --all --check
|
||||||
|
|
95
Cargo.lock
generated
95
Cargo.lock
generated
|
@ -163,9 +163,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.9"
|
version = "4.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -173,9 +173,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.9"
|
version = "4.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -185,9 +185,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.8"
|
version = "4.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -280,6 +280,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
|
@ -319,6 +325,25 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
|
||||||
|
dependencies = [
|
||||||
|
"include_dir_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir_macros"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ioctl-rs"
|
name = "ioctl-rs"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -525,6 +550,34 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||||
|
dependencies = [
|
||||||
|
"fuchsia-cprng",
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
"rdrand",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ratatui"
|
name = "ratatui"
|
||||||
version = "0.27.0"
|
version = "0.27.0"
|
||||||
|
@ -546,6 +599,15 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -555,6 +617,15 @@ dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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 = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
@ -722,6 +793,16 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempdir"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
"remove_dir_all",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termios"
|
name = "termios"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -759,9 +840,11 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"ego-tree",
|
"ego-tree",
|
||||||
|
"include_dir",
|
||||||
"oneshot",
|
"oneshot",
|
||||||
"portable-pty",
|
"portable-pty",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"tempdir",
|
||||||
"tui-term",
|
"tui-term",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,16 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.33"
|
chrono = "0.4.33"
|
||||||
clap = { version = "4.5.9", features = ["derive"] }
|
clap = { version = "4.5.11", features = ["derive"] }
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
ego-tree = "0.6.2"
|
ego-tree = "0.6.2"
|
||||||
oneshot = "0.1.8"
|
oneshot = "0.1.8"
|
||||||
portable-pty = "0.8.1"
|
portable-pty = "0.8.1"
|
||||||
ratatui = "0.27.0"
|
ratatui = "0.27.0"
|
||||||
tui-term = "0.1.12"
|
tui-term = "0.1.12"
|
||||||
|
include_dir = "0.7.4"
|
||||||
|
tempdir = "0.3.7"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "linutil"
|
name = "linutil"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Chris Titus
|
||||||
|
|
||||||
|
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.
|
14
README.md
14
README.md
|
@ -1,21 +1,29 @@
|
||||||
# Chris Titus Tech's Linux Utility
|
# Chris Titus Tech's Linux Utility
|
||||||
|
|
||||||
[![Version](https://img.shields.io/github/v/release/ChrisTitusTech/linutil?color=%230567ff&label=Latest%20Release&style=for-the-badge)](https://github.com/ChrisTitusTech/linutil/releases/latest)
|
[![Version](https://img.shields.io/github/v/release/ChrisTitusTech/linutil?color=%230567ff&label=Latest%20Release&style=for-the-badge)](https://github.com/ChrisTitusTech/linutil/releases/latest)
|
||||||
![GitHub Downloads (specific asset, all releases)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/linutil?label=Total%20Downloads&style=for-the-badge)
|
![GitHub Downloads (specific asset, all releases)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/linutil?label=Total%20Downloads&style=for-the-badge)
|
||||||
|
|
||||||
![Preview](docs/assets/preview.png)
|
![Preview](docs/assets/preview.png)
|
||||||
|
|
||||||
|
A distro-agnostic* toolbox which helps with everyday Linux tasks. It can help you set up applications and your system for specific use cases! Written with Rust 🦀
|
||||||
|
|
||||||
|
\* — The project is in active development, so there could be some issues. Please consider [submitting feedback](https://github.com/ChrisTitusTech/linutil/issues).
|
||||||
|
|
||||||
## 💡 Usage
|
## 💡 Usage
|
||||||
|
|
||||||
Open your terminal and paste this command
|
Open your terminal and paste this command:
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://christitus.com/linux | sh
|
curl -fsSL https://christitus.com/linux | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🎓 Documentation
|
||||||
|
### [LinUtil Official Documentation](https://christitustech.github.io/linutil/)
|
||||||
|
|
||||||
## 💖 Support
|
## 💖 Support
|
||||||
- To morally and mentally support the project, make sure to leave a ⭐️!
|
To morally and mentally support the project, make sure to leave a ⭐️!
|
||||||
|
|
||||||
## 🏅 Thanks to all Contributors
|
## 🏅 Thanks to all Contributors
|
||||||
Thanks a lot for spending your time helping Winutil grow. Thanks a lot! Keep rocking 🍻.
|
Thanks a lot for spending your time helping Linutil grow. Keep rocking 🍻.
|
||||||
|
|
||||||
[![Contributors](https://contrib.rocks/image?repo=ChrisTitusTech/linutil)](https://github.com/ChrisTitusTech/linutil/graphs/contributors)
|
[![Contributors](https://contrib.rocks/image?repo=ChrisTitusTech/linutil)](https://github.com/ChrisTitusTech/linutil/graphs/contributors)
|
||||||
|
|
||||||
|
|
BIN
build/aarch64-unknown-linux-musl/release/linutil
Executable file
BIN
build/aarch64-unknown-linux-musl/release/linutil
Executable file
Binary file not shown.
BIN
build/linutil
Executable file
BIN
build/linutil
Executable file
Binary file not shown.
BIN
build/linutil-aarch64
Executable file
BIN
build/linutil-aarch64
Executable file
Binary file not shown.
Binary file not shown.
BIN
build/x86_64-unknown-linux-musl/release/linutil
Executable file
BIN
build/x86_64-unknown-linux-musl/release/linutil
Executable file
Binary file not shown.
|
@ -1,11 +1,6 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
checkEnv() {
|
. ./common-script.sh
|
||||||
checkCommandRequirements 'curl groups sudo'
|
|
||||||
checkPackageManager 'apt-get dnf pacman zypper'
|
|
||||||
checkSuperUser
|
|
||||||
checkDistro
|
|
||||||
}
|
|
||||||
|
|
||||||
setupAlacritty() {
|
setupAlacritty() {
|
||||||
echo "Install Alacritty if not already installed..."
|
echo "Install Alacritty if not already installed..."
|
27
src/commands/applications-setup/dwmtitus-setup.sh
Normal file
27
src/commands/applications-setup/dwmtitus-setup.sh
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/sh -e
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
|
makeDWM(){
|
||||||
|
cd $HOME && git clone https://github.com/ChrisTitusTech/dwm-titus.git # CD to Home directory to install dwm-titus
|
||||||
|
# This path can be changed (e.g. to linux-toolbox directory)
|
||||||
|
cd dwm-titus/ # Hardcoded path, maybe not the best.
|
||||||
|
sudo ./setup.sh # Run setup
|
||||||
|
sudo make clean install # Run make clean install
|
||||||
|
}
|
||||||
|
|
||||||
|
setupDWM() {
|
||||||
|
echo "Installing DWM-Titus if not already installed"
|
||||||
|
case "$PACKAGER" in # Install pre-Requisites
|
||||||
|
pacman)
|
||||||
|
sudo "$PACKAGER" -S --noconfirm --needed base-devel libx11 libxinerama libxft imlib2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
sudo "$PACKAGER" install -y build-essential libx11-dev libxinerama-dev libxft-dev libimlib2-dev
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEnv
|
||||||
|
setupDWM
|
||||||
|
makeDWM
|
|
@ -1,11 +1,6 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
checkEnv() {
|
. ./common-script.sh
|
||||||
checkCommandRequirements 'curl groups sudo'
|
|
||||||
checkPackageManager 'apt-get dnf pacman zypper'
|
|
||||||
checkSuperUser
|
|
||||||
checkDistro
|
|
||||||
}
|
|
||||||
|
|
||||||
setupKitty() {
|
setupKitty() {
|
||||||
echo "Install Kitty if not already installed..."
|
echo "Install Kitty if not already installed..."
|
|
@ -1,11 +1,6 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
checkEnv() {
|
. ./common-script.sh
|
||||||
checkCommandRequirements 'curl groups sudo'
|
|
||||||
checkPackageManager 'apt-get dnf pacman zypper'
|
|
||||||
checkSuperUser
|
|
||||||
checkDistro
|
|
||||||
}
|
|
||||||
|
|
||||||
setupRofi() {
|
setupRofi() {
|
||||||
echo "Install Rofi if not already installed..."
|
echo "Install Rofi if not already installed..."
|
51
src/commands/applications-setup/zsh-setup.sh
Normal file
51
src/commands/applications-setup/zsh-setup.sh
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
|
# Function to install zsh
|
||||||
|
install_zsh() {
|
||||||
|
echo "Install ZSH if not already installed..."
|
||||||
|
if ! command_exists zsh; then
|
||||||
|
case "$PACKAGER" in
|
||||||
|
pacman)
|
||||||
|
sudo "$PACKAGER" -S --noconfirm zsh
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
sudo "$PACKAGER" install -y zsh
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
echo "ZSH is already installed."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to setup zsh configuration
|
||||||
|
setup_zsh_config() {
|
||||||
|
CONFIG_DIR="$HOME/.config/zsh"
|
||||||
|
ZSHRC_FILE="$CONFIG_DIR/.zshrc"
|
||||||
|
|
||||||
|
if [ ! -d "$CONFIG_DIR" ]; then
|
||||||
|
mkdir -p "$CONFIG_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Write the configuration to .zshrc
|
||||||
|
cat <<EOL >"$ZSHRC_FILE"
|
||||||
|
HISTFILE=~/.config/zsh/.histfile
|
||||||
|
HISTSIZE=5000
|
||||||
|
SAVEHIST=100000
|
||||||
|
setopt autocd extendedglob
|
||||||
|
unsetopt beep
|
||||||
|
bindkey -v
|
||||||
|
|
||||||
|
# Configure the prompt with embedded Solarized color codes
|
||||||
|
PROMPT='%F{32}%n%f%F{166}@%f%F{64}%m:%F{166}%~%f%F{15}$%f '
|
||||||
|
RPROMPT='%F{15}(%F{166}%D{%H:%M}%F{15})%f'
|
||||||
|
EOL
|
||||||
|
|
||||||
|
# Ensure /etc/zsh/zshenv sets ZDOTDIR to the user's config directory
|
||||||
|
echo 'export ZDOTDIR="$HOME/.config/zsh"' | sudo tee -a /etc/zsh/zshenv
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEnv
|
||||||
|
setup_zsh_config
|
||||||
|
install_zsh
|
|
@ -78,7 +78,8 @@ checkDistro() {
|
||||||
|
|
||||||
checkEnv() {
|
checkEnv() {
|
||||||
checkCommandRequirements 'curl groups sudo'
|
checkCommandRequirements 'curl groups sudo'
|
||||||
checkPackageManager 'apt-get dnf pacman zypper'
|
checkPackageManager 'apt-get nala dnf pacman zypper yum xbps-install nix-env'
|
||||||
|
checkCurrentDirectoryWritable
|
||||||
checkSuperUser
|
checkSuperUser
|
||||||
checkDistro
|
checkDistro
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Function to install zsh
|
|
||||||
install_zsh() {
|
|
||||||
if command -v apt-get >/dev/null 2>&1; then
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y zsh
|
|
||||||
elif command -v yum >/dev/null 2>&1; then
|
|
||||||
sudo yum install -y zsh
|
|
||||||
elif command -v dnf >/dev/null 2>&1; then
|
|
||||||
sudo dnf install -y zsh
|
|
||||||
elif command -v pacman >/dev/null 2>&1; then
|
|
||||||
sudo pacman -Syu zsh
|
|
||||||
elif command -v zypper >/dev/null 2>&1; then
|
|
||||||
sudo zypper install -y zsh
|
|
||||||
else
|
|
||||||
echo "No compatible package manager found. Please install zsh manually." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to setup zsh configuration
|
|
||||||
setup_zsh_config() {
|
|
||||||
CONFIG_DIR="$HOME/.config/zsh"
|
|
||||||
ZSHRC_FILE="$CONFIG_DIR/.zshrc"
|
|
||||||
|
|
||||||
# Create config directory if it doesn't exist
|
|
||||||
mkdir -p "$CONFIG_DIR"
|
|
||||||
|
|
||||||
# Write the configuration to .zshrc
|
|
||||||
cat <<EOL >"$ZSHRC_FILE"
|
|
||||||
# Lines configured by zsh-newuser-install
|
|
||||||
HISTFILE=~/.config/zsh/.histfile
|
|
||||||
HISTSIZE=5000
|
|
||||||
SAVEHIST=100000
|
|
||||||
setopt autocd extendedglob
|
|
||||||
unsetopt beep
|
|
||||||
bindkey -v
|
|
||||||
# End of lines configured by zsh-newuser-install
|
|
||||||
|
|
||||||
# Configure the prompt with embedded Solarized color codes
|
|
||||||
PROMPT='%F{32}%n%f%F{166}@%f%F{64}%m:%F{166}%~%f%F{15}$%f '
|
|
||||||
RPROMPT='%F{15}(%F{166}%D{%H:%M}%F{15})%f'
|
|
||||||
EOL
|
|
||||||
|
|
||||||
# Ensure /etc/zsh/zshenv sets ZDOTDIR to the user's config directory
|
|
||||||
echo 'export ZDOTDIR="$HOME/.config/zsh"' | sudo tee -a /etc/zsh/zshenv
|
|
||||||
}
|
|
||||||
|
|
||||||
# Execute the installation and setup
|
|
||||||
install_zsh
|
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "zsh installed successfully."
|
|
||||||
setup_zsh_config
|
|
||||||
else
|
|
||||||
echo "zsh installation failed."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
39
src/commands/security/firewall-baselines.sh
Normal file
39
src/commands/security/firewall-baselines.sh
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
installPkg() {
|
||||||
|
echo "Install UFW if not already installed..."
|
||||||
|
if ! command_exists ufw; then
|
||||||
|
case ${PACKAGER} in
|
||||||
|
pacman)
|
||||||
|
sudo "${PACKAGER}" -S --noconfirm ufw
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
sudo "${PACKAGER}" install -y ufw
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
echo "UFW is already installed."
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}Using Chris Titus Recommended Firewall Rules${RC}"
|
||||||
|
sudo ufw limit 22/tcp
|
||||||
|
echo "Limiting port 22/tcp (UFW)"
|
||||||
|
|
||||||
|
sudo ufw allow 80/tcp
|
||||||
|
echo "Allowing port 80/tcp (UFW)"
|
||||||
|
|
||||||
|
sudo ufw allow 443/tcp
|
||||||
|
echo "Allowing port 443/tcp (UFW)"
|
||||||
|
|
||||||
|
sudo ufw default deny incoming
|
||||||
|
echo "Denying Incoming Packets by Default(UFW)"
|
||||||
|
|
||||||
|
sudo ufw default allow outgoing
|
||||||
|
echo "Allowing Outcoming Packets by Default(UFW)"
|
||||||
|
|
||||||
|
sudo ufw enable
|
||||||
|
echo -e "${GREEN}Enabled Firewall with Baselines!${RC}"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEnv
|
||||||
|
installPkg
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
||||||
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
||||||
|
|
||||||
|
@ -22,13 +24,6 @@ fi
|
||||||
|
|
||||||
cd "$LINUXTOOLBOXDIR/linutil" || exit
|
cd "$LINUXTOOLBOXDIR/linutil" || exit
|
||||||
|
|
||||||
checkEnv() {
|
|
||||||
checkCommandRequirements 'curl groups sudo'
|
|
||||||
checkPackageHandler 'apt yum dnf pacman zypper'
|
|
||||||
checkCurrentDirectoryWritable
|
|
||||||
checkSuperUser
|
|
||||||
}
|
|
||||||
|
|
||||||
installDepend() {
|
installDepend() {
|
||||||
## Check for dependencies.
|
## Check for dependencies.
|
||||||
DEPENDENCIES='tar tree multitail tldr trash-cli unzip cmake make jq'
|
DEPENDENCIES='tar tree multitail tldr trash-cli unzip cmake make jq'
|
||||||
|
@ -60,12 +55,12 @@ installDepend() {
|
||||||
fi
|
fi
|
||||||
"$AUR_HELPER" --noconfirm -S "$DEPENDENCIES"
|
"$AUR_HELPER" --noconfirm -S "$DEPENDENCIES"
|
||||||
;;
|
;;
|
||||||
apt)
|
apt-get|nala)
|
||||||
COMPILEDEPS='build-essential'
|
COMPILEDEPS='build-essential'
|
||||||
sudo "$PACKAGER" update
|
sudo "$PACKAGER" update
|
||||||
sudo dpkg --add-architecture i386
|
sudo dpkg --add-architecture i386
|
||||||
sudo "$PACKAGER" update
|
sudo "$PACKAGER" update
|
||||||
sudo "$PACKAGER" install -y "$DEPENDENCIES" $COMPILEDEPS
|
sudo "$PACKAGER" install -y $DEPENDENCIES $COMPILEDEPS
|
||||||
;;
|
;;
|
||||||
dnf)
|
dnf)
|
||||||
COMPILEDEPS='@development-tools'
|
COMPILEDEPS='@development-tools'
|
||||||
|
@ -81,7 +76,7 @@ installDepend() {
|
||||||
sudo "$PACKAGER" --non-interactive install libgcc_s1-gcc7-32bit glibc-devel-32bit
|
sudo "$PACKAGER" --non-interactive install libgcc_s1-gcc7-32bit glibc-devel-32bit
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
sudo "$PACKAGER" install -y "$DEPENDENCIES"
|
sudo "$PACKAGER" install -y $DEPENDENCIES # Fixed bug where no packages found on debian-based
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
checkEnv() {
|
. ./common-script.sh
|
||||||
checkCommandRequirements 'curl groups sudo'
|
|
||||||
checkPackageManager 'apt-get yum dnf pacman zypper'
|
|
||||||
checkCurrentDirectoryWritable
|
|
||||||
checkSuperUser
|
|
||||||
}
|
|
||||||
|
|
||||||
installDepend() {
|
installDepend() {
|
||||||
## Check for dependencies.
|
## Check for dependencies.
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
||||||
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
||||||
|
|
||||||
if [ ! -d "$LINUXTOOLBOXDIR" ]; then
|
if [ ! -d "$LINUXTOOLBOXDIR" ]; then
|
||||||
echo -e "${YELLOW}Creating linuxtoolbox directory: $LINUXTOOLBOXDIR${RC}"
|
printf "${YELLOW}Creating linuxtoolbox directory: %s${RC}\n" "$LINUXTOOLBOXDIR"
|
||||||
mkdir -p "$LINUXTOOLBOXDIR"
|
mkdir -p "$LINUXTOOLBOXDIR"
|
||||||
echo -e "${GREEN}linuxtoolbox directory created: $LINUXTOOLBOXDIR${RC}"
|
printf "${GREEN}linuxtoolbox directory created: %s${RC}\n" "$LINUXTOOLBOXDIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$LINUXTOOLBOXDIR" || exit
|
cd "$LINUXTOOLBOXDIR" || exit
|
||||||
|
|
||||||
install_theme_tools() {
|
install_theme_tools() {
|
||||||
echo -e "${YELLOW}Installing theme tools (qt6ct and kvantum)...${RC}"
|
printf "${YELLOW}Installing theme tools (qt6ct and kvantum)...${RC}\n"
|
||||||
case $PACKAGER in
|
case $PACKAGER in
|
||||||
apt-get)
|
apt-get)
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
@ -31,14 +33,14 @@ install_theme_tools() {
|
||||||
sudo pacman --noconfirm -S qt6ct kvantum
|
sudo pacman --noconfirm -S qt6ct kvantum
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo -e "${RED}Unsupported package manager. Please install qt6ct and kvantum manually.${RC}"
|
printf "${RED}Unsupported package manager. Please install qt6ct and kvantum manually.${RC}\n"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_qt6ct() {
|
configure_qt6ct() {
|
||||||
echo -e "${YELLOW}Configuring qt6ct...${RC}"
|
printf "${YELLOW}Configuring qt6ct...${RC}\n"
|
||||||
mkdir -p "$HOME/.config/qt6ct"
|
mkdir -p "$HOME/.config/qt6ct"
|
||||||
cat <<EOF > "$HOME/.config/qt6ct/qt6ct.conf"
|
cat <<EOF > "$HOME/.config/qt6ct/qt6ct.conf"
|
||||||
[Appearance]
|
[Appearance]
|
||||||
|
@ -46,20 +48,29 @@ style=kvantum
|
||||||
color_scheme=default
|
color_scheme=default
|
||||||
icon_theme=breeze
|
icon_theme=breeze
|
||||||
EOF
|
EOF
|
||||||
echo -e "${GREEN}qt6ct configured successfully.${RC}"
|
printf "${GREEN}qt6ct configured successfully.${RC}\n"
|
||||||
|
|
||||||
|
# Add QT_QPA_PLATFORMTHEME to /etc/environment
|
||||||
|
if ! grep -q "QT_QPA_PLATFORMTHEME=qt6ct" /etc/environment; then
|
||||||
|
printf "${YELLOW}Adding QT_QPA_PLATFORMTHEME to /etc/environment...${RC}\n"
|
||||||
|
echo "QT_QPA_PLATFORMTHEME=qt6ct" | sudo tee -a /etc/environment > /dev/null
|
||||||
|
printf "${GREEN}QT_QPA_PLATFORMTHEME added to /etc/environment.${RC}\n"
|
||||||
|
else
|
||||||
|
printf "${GREEN}QT_QPA_PLATFORMTHEME already set in /etc/environment.${RC}\n"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_kvantum() {
|
configure_kvantum() {
|
||||||
echo -e "${YELLOW}Configuring Kvantum...${RC}"
|
printf "${YELLOW}Configuring Kvantum...${RC}\n"
|
||||||
mkdir -p "$HOME/.config/Kvantum"
|
mkdir -p "$HOME/.config/Kvantum"
|
||||||
cat <<EOF > "$HOME/.config/Kvantum/kvantum.kvconfig"
|
cat <<EOF > "$HOME/.config/Kvantum/kvantum.kvconfig"
|
||||||
[General]
|
[General]
|
||||||
theme=Breeze
|
theme=Breeze
|
||||||
EOF
|
EOF
|
||||||
echo -e "${GREEN}Kvantum configured successfully.${RC}"
|
printf "${GREEN}Kvantum configured successfully.${RC}\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEnv
|
checkEnv
|
||||||
install_theme_tools
|
install_theme_tools
|
||||||
configure_qt6ct
|
configure_qt6ct
|
||||||
configure_kvantum
|
configure_kvantum
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
checkEnv() {
|
. ./common-script.sh
|
||||||
checkCommandRequirements 'curl groups sudo'
|
|
||||||
checkPackageManager 'apt-get nala dnf pacman zypper yum xbps-install'
|
|
||||||
checkSuperUser
|
|
||||||
checkDistro
|
|
||||||
}
|
|
||||||
|
|
||||||
fastUpdate() {
|
fastUpdate() {
|
||||||
case ${PACKAGER} in
|
case ${PACKAGER} in
|
||||||
|
@ -83,7 +78,8 @@ updateSystem() {
|
||||||
sudo "${PACKAGER}" upgrade -y
|
sudo "${PACKAGER}" upgrade -y
|
||||||
;;
|
;;
|
||||||
pacman)
|
pacman)
|
||||||
sudo "${PACKAGER}" -Syu --noconfirm
|
sudo "${PACKAGER}" -Sy --noconfirm --needed archlinux-keyring
|
||||||
|
sudo "${PACKAGER}" -Su --noconfirm
|
||||||
;;
|
;;
|
||||||
zypper)
|
zypper)
|
||||||
sudo ${PACKAGER} ref
|
sudo ${PACKAGER} ref
|
||||||
|
@ -99,6 +95,13 @@ updateSystem() {
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFlatpaks() {
|
||||||
|
if command_exists flatpak; then
|
||||||
|
flatpak update -y
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
checkEnv
|
checkEnv
|
||||||
fastUpdate
|
fastUpdate
|
||||||
updateSystem
|
updateSystem
|
||||||
|
updateFlatpaks
|
||||||
|
|
5
src/commands/test/lib.sh
Executable file
5
src/commands/test/lib.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
say_hello () {
|
||||||
|
echo Hi
|
||||||
|
}
|
6
src/commands/test/main.sh
Executable file
6
src/commands/test/main.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# The current working directory will always be inside "commands"
|
||||||
|
. test/lib.sh
|
||||||
|
|
||||||
|
say_hello
|
377
src/list.rs
377
src/list.rs
|
@ -1,4 +1,4 @@
|
||||||
use crate::{float::floating_window, theme::*};
|
use crate::{float::floating_window, running_command::Command, state::AppState};
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
|
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
|
||||||
use ego_tree::{tree, NodeId};
|
use ego_tree::{tree, NodeId};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
|
@ -9,18 +9,10 @@ use ratatui::{
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! with_common_script {
|
#[derive(Clone)]
|
||||||
($command:expr) => {
|
|
||||||
concat!(
|
|
||||||
include_str!("commands/common-script.sh"),
|
|
||||||
include_str!($command)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ListNode {
|
struct ListNode {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
command: &'static str,
|
command: Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a data structure that has everything necessary to draw and manage a menu of commands
|
/// This is a data structure that has everything necessary to draw and manage a menu of commands
|
||||||
|
@ -37,6 +29,10 @@ pub struct CustomList {
|
||||||
/// This stores the preview windows state. If it is None, it will not be displayed.
|
/// This stores the preview windows state. If it is None, it will not be displayed.
|
||||||
/// If it is Some, we show it with the content of the selected item
|
/// If it is Some, we show it with the content of the selected item
|
||||||
preview_window_state: Option<PreviewWindowState>,
|
preview_window_state: Option<PreviewWindowState>,
|
||||||
|
// This stores the current search query
|
||||||
|
filter_query: String,
|
||||||
|
// This stores the filtered tree
|
||||||
|
filtered_items: Vec<ListNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This struct stores the preview window state
|
/// This struct stores the preview window state
|
||||||
|
@ -60,66 +56,72 @@ impl CustomList {
|
||||||
// case the tree! macro expands to `ego-tree::tree` data structure
|
// case the tree! macro expands to `ego-tree::tree` data structure
|
||||||
let tree = tree!(ListNode {
|
let tree = tree!(ListNode {
|
||||||
name: "root",
|
name: "root",
|
||||||
command: ""
|
command: Command::None,
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
|
||||||
name: "Full System Update",
|
|
||||||
command: with_common_script!("commands/system-update.sh"),
|
|
||||||
},
|
|
||||||
ListNode {
|
|
||||||
name: "Setup Bash Prompt",
|
|
||||||
command: "bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""
|
|
||||||
},
|
|
||||||
ListNode {
|
|
||||||
name: "Setup Neovim",
|
|
||||||
command: "bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""
|
|
||||||
},
|
|
||||||
ListNode {
|
|
||||||
name: "Setup Zsh and Prompt",
|
|
||||||
command: with_common_script!("commands/install-zsh.sh"),
|
|
||||||
},
|
|
||||||
// ListNode {
|
|
||||||
// name: "Just ls, nothing special, trust me",
|
|
||||||
// command: include_str!("commands/special_ls.sh"),
|
|
||||||
// },
|
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "System Setup",
|
name: "System Setup",
|
||||||
command: ""
|
command: Command::None,
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Build Prerequisites",
|
name: "Build Prerequisites",
|
||||||
command: with_common_script!("commands/system-setup/1-compile-setup.sh"),
|
command: Command::LocalFile("system-setup/1-compile-setup.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Gaming Dependencies",
|
name: "Gaming Dependencies",
|
||||||
command: with_common_script!("commands/system-setup/2-gaming-setup.sh"),
|
command: Command::LocalFile("system-setup/2-gaming-setup.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Global Theme",
|
name: "Global Theme",
|
||||||
command: with_common_script!("commands/system-setup/3-global-theme.sh"),
|
command: Command::LocalFile("system-setup/3-global-theme.sh"),
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Security",
|
||||||
|
command: Command::None
|
||||||
|
} => {
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Recursion?",
|
name: "Firewall Baselines (CTT)",
|
||||||
command: "cargo run"
|
command: Command::LocalFile("security/firewall-baselines.sh"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Titus Dotfiles",
|
name: "Applications Setup",
|
||||||
command: ""
|
command: Command::None
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Alacritty Setup",
|
name: "Alacritty",
|
||||||
command: with_common_script!("commands/dotfiles/alacritty-setup.sh"),
|
command: Command::LocalFile("applications-setup/alacritty-setup.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Kitty Setup",
|
name: "Bash Prompt",
|
||||||
command: with_common_script!("commands/dotfiles/kitty-setup.sh"),
|
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Rofi Setup",
|
name: "DWM-Titus",
|
||||||
command: with_common_script!("commands/dotfiles/rofi-setup.sh"),
|
command: Command::LocalFile("applications-setup/dwmtitus-setup.sh")
|
||||||
},
|
},
|
||||||
}
|
ListNode {
|
||||||
|
name: "Kitty",
|
||||||
|
command: Command::LocalFile("applications-setup/kitty-setup.sh")
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Neovim",
|
||||||
|
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""),
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Rofi",
|
||||||
|
command: Command::LocalFile("applications-setup/rofi-setup.sh"),
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "ZSH Prompt",
|
||||||
|
command: Command::LocalFile("applications-setup/zsh-setup.sh"),
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Full System Update",
|
||||||
|
command: Command::LocalFile("system-update.sh"),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
// We don't get a reference, but rather an id, because references are siginficantly more
|
// We don't get a reference, but rather an id, because references are siginficantly more
|
||||||
// paintfull to manage
|
// paintfull to manage
|
||||||
|
@ -130,46 +132,61 @@ impl CustomList {
|
||||||
list_state: ListState::default().with_selected(Some(0)),
|
list_state: ListState::default().with_selected(Some(0)),
|
||||||
// By default the PreviewWindowState is set to None, so it is not being shown
|
// By default the PreviewWindowState is set to None, so it is not being shown
|
||||||
preview_window_state: None,
|
preview_window_state: None,
|
||||||
|
filter_query: String::new(),
|
||||||
|
filtered_items: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw our custom widget to the frame
|
/// Draw our custom widget to the frame
|
||||||
pub fn draw(&mut self, frame: &mut Frame, area: Rect) {
|
pub fn draw(&mut self, frame: &mut Frame, area: Rect, state: &AppState) {
|
||||||
// Get the last element in the `visit_stack` vec
|
let item_list: Vec<Line> = if self.filter_query.is_empty() {
|
||||||
let theme = get_theme();
|
let mut items: Vec<Line> = vec![];
|
||||||
let curr = self
|
// If we are not at the root of our filesystem tree, we need to add `..` path, to be able
|
||||||
.inner_tree
|
// to go up the tree
|
||||||
.get(*self.visit_stack.last().unwrap())
|
// icons:
|
||||||
.unwrap();
|
if !self.at_root() {
|
||||||
let mut items = vec![];
|
|
||||||
|
|
||||||
// If we are not at the root of our filesystem tree, we need to add `..` path, to be able
|
|
||||||
// to go up the tree
|
|
||||||
// icons:
|
|
||||||
if !self.at_root() {
|
|
||||||
items.push(Line::from(format!("{} ..", theme.dir_icon)).style(theme.dir_color));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through all the children
|
|
||||||
for node in curr.children() {
|
|
||||||
// The difference between a "directory" and a "command" is simple: if it has children,
|
|
||||||
// it's a directory and will be handled as such
|
|
||||||
if node.has_children() {
|
|
||||||
items.push(
|
items.push(
|
||||||
Line::from(format!("{} {}", theme.dir_icon, node.value().name))
|
Line::from(format!("{} ..", state.theme.dir_icon))
|
||||||
.style(theme.dir_color),
|
.style(state.theme.dir_color),
|
||||||
);
|
|
||||||
} else {
|
|
||||||
items.push(
|
|
||||||
Line::from(format!("{} {}", theme.cmd_icon, node.value().name))
|
|
||||||
.style(theme.cmd_color),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Get the last element in the `visit_stack` vec
|
||||||
|
let curr = self
|
||||||
|
.inner_tree
|
||||||
|
.get(*self.visit_stack.last().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Iterate through all the children
|
||||||
|
for node in curr.children() {
|
||||||
|
// The difference between a "directory" and a "command" is simple: if it has children,
|
||||||
|
// it's a directory and will be handled as such
|
||||||
|
if node.has_children() {
|
||||||
|
items.push(
|
||||||
|
Line::from(format!("{} {}", state.theme.dir_icon, node.value().name))
|
||||||
|
.style(state.theme.dir_color),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
items.push(
|
||||||
|
Line::from(format!("{} {}", state.theme.cmd_icon, node.value().name))
|
||||||
|
.style(state.theme.cmd_color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items
|
||||||
|
} else {
|
||||||
|
self.filtered_items
|
||||||
|
.iter()
|
||||||
|
.map(|node| {
|
||||||
|
Line::from(format!("{} {}", state.theme.cmd_icon, node.name))
|
||||||
|
.style(state.theme.cmd_color)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
// create the normal list widget containing only item in our "working directory" / tree
|
// create the normal list widget containing only item in our "working directory" / tree
|
||||||
// node
|
// node
|
||||||
let list = List::new(items)
|
let list = List::new(item_list)
|
||||||
.highlight_style(Style::default().reversed())
|
.highlight_style(Style::default().reversed())
|
||||||
.block(Block::default().borders(Borders::ALL).title(format!(
|
.block(Block::default().borders(Borders::ALL).title(format!(
|
||||||
"Linux Toolbox - {}",
|
"Linux Toolbox - {}",
|
||||||
|
@ -208,8 +225,39 @@ impl CustomList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn filter(&mut self, query: String) {
|
||||||
|
self.filter_query.clone_from(&query);
|
||||||
|
self.filtered_items.clear();
|
||||||
|
|
||||||
|
let query_lower = query.to_lowercase();
|
||||||
|
|
||||||
|
let mut stack = vec![self.inner_tree.root().id()];
|
||||||
|
|
||||||
|
while let Some(node_id) = stack.pop() {
|
||||||
|
let node = self.inner_tree.get(node_id).unwrap();
|
||||||
|
|
||||||
|
if node.value().name.to_lowercase().contains(&query_lower) && !node.has_children() {
|
||||||
|
self.filtered_items.push(node.value().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in node.children() {
|
||||||
|
stack.push(child.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.filtered_items.sort_by(|a, b| a.name.cmp(b.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the selection to the first item
|
||||||
|
pub fn reset_selection(&mut self) {
|
||||||
|
if !self.filtered_items.is_empty() {
|
||||||
|
self.list_state.select(Some(0));
|
||||||
|
} else {
|
||||||
|
self.list_state.select(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle key events, we are only interested in `Press` and `Repeat` events
|
/// Handle key events, we are only interested in `Press` and `Repeat` events
|
||||||
pub fn handle_key(&mut self, event: KeyEvent) -> Option<&'static str> {
|
pub fn handle_key(&mut self, event: KeyEvent, state: &AppState) -> Option<Command> {
|
||||||
if event.kind == KeyEventKind::Release {
|
if event.kind == KeyEventKind::Release {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -223,7 +271,7 @@ impl CustomList {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.try_scroll_down();
|
self.list_state.select_next();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
KeyCode::Char('k') | KeyCode::Up => {
|
KeyCode::Char('k') | KeyCode::Up => {
|
||||||
|
@ -234,19 +282,26 @@ impl CustomList {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.try_scroll_up();
|
self.list_state.select_previous();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
// The 'p' key toggles the preview on and off
|
// The 'p' key toggles the preview on and off
|
||||||
KeyCode::Char('p') => {
|
KeyCode::Char('p') => {
|
||||||
self.toggle_preview_window();
|
self.toggle_preview_window(state);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
KeyCode::Enter => self.handle_enter(),
|
|
||||||
|
KeyCode::Enter => {
|
||||||
|
if self.preview_window_state.is_none() {
|
||||||
|
self.handle_enter()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn toggle_preview_window(&mut self) {
|
fn toggle_preview_window(&mut self, state: &AppState) {
|
||||||
// If the preview window is active, disable it
|
// If the preview window is active, disable it
|
||||||
if self.preview_window_state.is_some() {
|
if self.preview_window_state.is_some() {
|
||||||
self.preview_window_state = None;
|
self.preview_window_state = None;
|
||||||
|
@ -255,46 +310,29 @@ impl CustomList {
|
||||||
|
|
||||||
// Get the selected command
|
// Get the selected command
|
||||||
if let Some(selected_command) = self.get_selected_command() {
|
if let Some(selected_command) = self.get_selected_command() {
|
||||||
// If command is a folder, we don't display a preview
|
let lines = match selected_command {
|
||||||
if selected_command.is_empty() {
|
Command::Raw(cmd) => {
|
||||||
return;
|
// Reconstruct the line breaks and file formatting after the
|
||||||
}
|
// 'include_str!()' call in the node
|
||||||
|
cmd.lines().map(|line| line.to_string()).collect()
|
||||||
// Reconstruct the line breaks and file formatting after the
|
}
|
||||||
// 'include_str!()' call in the node
|
Command::LocalFile(file_path) => {
|
||||||
let lines: Vec<String> = selected_command
|
let mut full_path = state.temp_path.clone();
|
||||||
.lines()
|
full_path.push(file_path);
|
||||||
.map(|line| line.to_string())
|
let file_contents = std::fs::read_to_string(&full_path)
|
||||||
.collect();
|
.map_err(|_| format!("File not found: {:?}", &full_path))
|
||||||
|
.unwrap();
|
||||||
|
file_contents.lines().map(|line| line.to_string()).collect()
|
||||||
|
}
|
||||||
|
// If command is a folder, we don't display a preview
|
||||||
|
Command::None => return,
|
||||||
|
};
|
||||||
|
|
||||||
// Show the preview window with the text lines
|
// Show the preview window with the text lines
|
||||||
self.preview_window_state = Some(PreviewWindowState::new(lines));
|
self.preview_window_state = Some(PreviewWindowState::new(lines));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn try_scroll_up(&mut self) {
|
|
||||||
self.list_state
|
|
||||||
.select(Some(self.list_state.selected().unwrap().saturating_sub(1)));
|
|
||||||
}
|
|
||||||
fn try_scroll_down(&mut self) {
|
|
||||||
let curr = self
|
|
||||||
.inner_tree
|
|
||||||
.get(*self.visit_stack.last().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let count = curr.children().count();
|
|
||||||
|
|
||||||
let curr_selection = self.list_state.selected().unwrap();
|
|
||||||
if self.at_root() {
|
|
||||||
self.list_state
|
|
||||||
.select(Some((curr_selection + 1).min(count - 1)));
|
|
||||||
} else {
|
|
||||||
// When we are not at the root, we have to account for 1 more "virtual" node, `..`. So
|
|
||||||
// the count is 1 bigger (select is 0 based, because it's an index)
|
|
||||||
self.list_state
|
|
||||||
.select(Some((curr_selection + 1).min(count)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Scroll the preview window down
|
/// Scroll the preview window down
|
||||||
fn scroll_preview_window_down(&mut self) {
|
fn scroll_preview_window_down(&mut self) {
|
||||||
|
@ -314,31 +352,44 @@ impl CustomList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method return the currently selected command, or None if no command is selected.
|
/// This method returns the currently selected command, or None if no command is selected.
|
||||||
/// It was extracted from the 'handle_enter()'
|
/// It was extracted from the 'handle_enter()'
|
||||||
///
|
///
|
||||||
/// This could probably be integrated into the 'handle_enter()' method as to avoid code
|
/// This could probably be integrated into the 'handle_enter()' method to avoid code
|
||||||
/// duplication, but I don't want to make too major changes to the codebase.
|
/// duplication, but I don't want to make too major changes to the codebase.
|
||||||
fn get_selected_command(&self) -> Option<&'static str> {
|
fn get_selected_command(&self) -> Option<Command> {
|
||||||
let curr = self
|
let selected_index = self.list_state.selected().unwrap_or(0);
|
||||||
.inner_tree
|
println!("Selected Index: {}", selected_index);
|
||||||
.get(*self.visit_stack.last().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
let selected = self.list_state.selected().unwrap();
|
|
||||||
|
|
||||||
// If we are not at the root and the first item is selected, it's the `..` item
|
if self.filter_query.is_empty() {
|
||||||
if !self.at_root() && selected == 0 {
|
// No filter query, use the regular tree navigation
|
||||||
return None;
|
let curr = self
|
||||||
}
|
.inner_tree
|
||||||
|
.get(*self.visit_stack.last().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for (mut idx, node) in curr.children().enumerate() {
|
if !self.at_root() && selected_index == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut actual_index = selected_index;
|
||||||
if !self.at_root() {
|
if !self.at_root() {
|
||||||
idx += 1;
|
actual_index -= 1; // Adjust for the ".." item if not at root
|
||||||
}
|
}
|
||||||
if idx == selected {
|
|
||||||
return Some(node.value().command);
|
for (idx, node) in curr.children().enumerate() {
|
||||||
|
if idx == actual_index {
|
||||||
|
return Some(node.value().command.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Filter query is active, use the filtered items
|
||||||
|
if let Some(filtered_node) = self.filtered_items.get(selected_index) {
|
||||||
|
println!("Filtered Node Name: {}", filtered_node.name);
|
||||||
|
return Some(filtered_node.command.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,39 +397,47 @@ impl CustomList {
|
||||||
/// - Run a command, if it is the currently selected item,
|
/// - Run a command, if it is the currently selected item,
|
||||||
/// - Go up a directory
|
/// - Go up a directory
|
||||||
/// - Go down into a directory
|
/// - Go down into a directory
|
||||||
|
///
|
||||||
/// Returns `Some(command)` when command is selected, othervise we returns `None`
|
/// Returns `Some(command)` when command is selected, othervise we returns `None`
|
||||||
fn handle_enter(&mut self) -> Option<&'static str> {
|
fn handle_enter(&mut self) -> Option<Command> {
|
||||||
// Get the current node (current directory)
|
let selected_index = self.list_state.selected().unwrap_or(0);
|
||||||
let curr = self
|
|
||||||
.inner_tree
|
|
||||||
.get(*self.visit_stack.last().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
let selected = self.list_state.selected().unwrap();
|
|
||||||
|
|
||||||
// if we are not at the root, and the first element is selected,
|
if self.filter_query.is_empty() {
|
||||||
// we can be sure it's '..', so we go up the directory
|
// No filter query, use the regular tree navigation
|
||||||
if !self.at_root() && selected == 0 {
|
let curr = self
|
||||||
self.visit_stack.pop();
|
.inner_tree
|
||||||
self.list_state.select(Some(0));
|
.get(*self.visit_stack.last().unwrap())
|
||||||
return None;
|
.unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
for (mut idx, node) in curr.children().enumerate() {
|
if !self.at_root() && selected_index == 0 {
|
||||||
// at this point, we know that we are not on the .. item, and our indexes of the items never had ..
|
self.visit_stack.pop();
|
||||||
// item. so to balance it out, in case the selection index contains .., se add 1 to our node index
|
self.list_state.select(Some(0));
|
||||||
if !self.at_root() {
|
return None;
|
||||||
idx += 1;
|
|
||||||
}
|
}
|
||||||
if idx == selected {
|
|
||||||
if node.has_children() {
|
let mut actual_index = selected_index;
|
||||||
self.visit_stack.push(node.id());
|
if !self.at_root() {
|
||||||
self.list_state.select(Some(0));
|
actual_index -= 1; // Adjust for the ".." item if not at root
|
||||||
return None;
|
}
|
||||||
} else {
|
|
||||||
return Some(node.value().command);
|
for (idx, node) in curr.children().enumerate() {
|
||||||
|
if idx == actual_index {
|
||||||
|
if node.has_children() {
|
||||||
|
self.visit_stack.push(node.id());
|
||||||
|
self.list_state.select(Some(0));
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
return Some(node.value().command.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Filter query is active, use the filtered items
|
||||||
|
if let Some(filtered_node) = self.filtered_items.get(selected_index) {
|
||||||
|
return Some(filtered_node.command.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
105
src/main.rs
105
src/main.rs
|
@ -1,6 +1,7 @@
|
||||||
mod float;
|
mod float;
|
||||||
mod list;
|
mod list;
|
||||||
mod running_command;
|
mod running_command;
|
||||||
|
pub mod state;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -16,13 +17,20 @@ use crossterm::{
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
ExecutableCommand,
|
ExecutableCommand,
|
||||||
};
|
};
|
||||||
|
use include_dir::include_dir;
|
||||||
use list::CustomList;
|
use list::CustomList;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::{Backend, CrosstermBackend},
|
backend::{Backend, CrosstermBackend},
|
||||||
|
layout::{Constraint, Direction, Layout},
|
||||||
|
style::{Color, Style},
|
||||||
|
text::Span,
|
||||||
|
widgets::{Block, Borders, Paragraph},
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
use running_command::RunningCommand;
|
use running_command::RunningCommand;
|
||||||
use theme::set_theme;
|
use state::AppState;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
use theme::THEMES;
|
||||||
|
|
||||||
/// This is a binary :), Chris, change this to update the documentation on -h
|
/// This is a binary :), Chris, change this to update the documentation on -h
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
@ -34,16 +42,29 @@ struct Args {
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
if args.compat {
|
|
||||||
set_theme(0);
|
let theme = if args.compat {
|
||||||
}
|
THEMES[0].clone()
|
||||||
|
} else {
|
||||||
|
THEMES[1].clone()
|
||||||
|
};
|
||||||
|
let commands_dir = include_dir!("src/commands");
|
||||||
|
let temp_dir: TempDir = TempDir::new("linutil_scripts").unwrap();
|
||||||
|
commands_dir
|
||||||
|
.extract(temp_dir.path())
|
||||||
|
.expect("Failed to extract the saved directory");
|
||||||
|
|
||||||
|
let state = AppState {
|
||||||
|
theme,
|
||||||
|
temp_path: temp_dir.path().to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
stdout().execute(EnterAlternateScreen)?;
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
||||||
terminal.clear()?;
|
terminal.clear()?;
|
||||||
|
|
||||||
run(&mut terminal)?;
|
run(&mut terminal, &state)?;
|
||||||
|
|
||||||
// restore terminal
|
// restore terminal
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
|
@ -55,17 +76,51 @@ fn main() -> std::io::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run<B: Backend>(terminal: &mut Terminal<B>) -> io::Result<()> {
|
fn run<B: Backend>(terminal: &mut Terminal<B>, state: &AppState) -> io::Result<()> {
|
||||||
let mut command_opt: Option<RunningCommand> = None;
|
let mut command_opt: Option<RunningCommand> = None;
|
||||||
|
|
||||||
let mut custom_list = CustomList::new();
|
let mut custom_list = CustomList::new();
|
||||||
|
let mut search_input = String::new();
|
||||||
|
let mut in_search_mode = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Always redraw
|
// Always redraw
|
||||||
terminal
|
terminal
|
||||||
.draw(|frame| {
|
.draw(|frame| {
|
||||||
custom_list.draw(frame, frame.size());
|
//Split the terminal into 2 vertical chunks
|
||||||
|
//One for the search bar and one for the command list
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
|
||||||
|
.split(frame.size());
|
||||||
|
|
||||||
|
//Set the search bar text (If empty use the placeholder)
|
||||||
|
let display_text = if search_input.is_empty() {
|
||||||
|
if in_search_mode {
|
||||||
|
Span::raw("")
|
||||||
|
} else {
|
||||||
|
Span::raw("Press / to search")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Span::raw(&search_input)
|
||||||
|
};
|
||||||
|
|
||||||
|
//Create the search bar widget
|
||||||
|
let mut search_bar = Paragraph::new(display_text)
|
||||||
|
.block(Block::default().borders(Borders::ALL).title("Search"))
|
||||||
|
.style(Style::default().fg(Color::DarkGray));
|
||||||
|
|
||||||
|
//Change the color if in search mode
|
||||||
|
if in_search_mode {
|
||||||
|
search_bar = search_bar.clone().style(Style::default().fg(Color::Blue));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Render the search bar (First chunk of the screen)
|
||||||
|
frame.render_widget(search_bar, chunks[0]);
|
||||||
|
//Render the command list (Second chunk of the screen)
|
||||||
|
custom_list.draw(frame, chunks[1], state);
|
||||||
|
|
||||||
if let Some(ref mut command) = &mut command_opt {
|
if let Some(ref mut command) = &mut command_opt {
|
||||||
command.draw(frame);
|
command.draw(frame, state);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -90,8 +145,36 @@ fn run<B: Backend>(terminal: &mut Terminal<B>) -> io::Result<()> {
|
||||||
if key.code == KeyCode::Char('q') {
|
if key.code == KeyCode::Char('q') {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if let Some(cmd) = custom_list.handle_key(key) {
|
//Activate search mode if the forward slash key gets pressed
|
||||||
command_opt = Some(RunningCommand::new(cmd));
|
if key.code == KeyCode::Char('/') {
|
||||||
|
// Enter search mode
|
||||||
|
in_search_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Insert user input into the search bar
|
||||||
|
if in_search_mode {
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Char(c) => {
|
||||||
|
search_input.push(c);
|
||||||
|
custom_list.filter(search_input.clone());
|
||||||
|
}
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
search_input.pop();
|
||||||
|
custom_list.filter(search_input.clone());
|
||||||
|
}
|
||||||
|
KeyCode::Esc => {
|
||||||
|
search_input = String::new();
|
||||||
|
custom_list.filter(search_input.clone());
|
||||||
|
in_search_mode = false
|
||||||
|
}
|
||||||
|
KeyCode::Enter => {
|
||||||
|
in_search_mode = false;
|
||||||
|
custom_list.reset_selection();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else if let Some(cmd) = custom_list.handle_key(key, state) {
|
||||||
|
command_opt = Some(RunningCommand::new(cmd, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use portable_pty::{
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::Size,
|
layout::Size,
|
||||||
style::{Color, Style, Stylize},
|
style::{Style, Stylize},
|
||||||
text::{Line, Span},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders},
|
widgets::{Block, Borders},
|
||||||
Frame,
|
Frame,
|
||||||
|
@ -21,7 +21,14 @@ use tui_term::{
|
||||||
widget::PseudoTerminal,
|
widget::PseudoTerminal,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{float::floating_window, theme::get_theme};
|
use crate::{float::floating_window, state::AppState};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Command {
|
||||||
|
Raw(&'static str),
|
||||||
|
LocalFile(&'static str),
|
||||||
|
None, // Directory
|
||||||
|
}
|
||||||
|
|
||||||
/// This is a struct for storing everything connected to a running command
|
/// This is a struct for storing everything connected to a running command
|
||||||
// Create a new instance on every new command you want to run
|
// Create a new instance on every new command you want to run
|
||||||
|
@ -50,14 +57,22 @@ pub struct RunningCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunningCommand {
|
impl RunningCommand {
|
||||||
pub fn new(command: &str) -> Self {
|
pub fn new(command: Command, state: &AppState) -> Self {
|
||||||
let pty_system = NativePtySystem::default();
|
let pty_system = NativePtySystem::default();
|
||||||
let mut cmd = CommandBuilder::new("sh");
|
|
||||||
cmd.arg("-c");
|
|
||||||
cmd.arg(command);
|
|
||||||
|
|
||||||
let cwd = std::env::current_dir().unwrap();
|
let mut cmd = CommandBuilder::new("sh");
|
||||||
cmd.cwd(cwd);
|
match command {
|
||||||
|
Command::Raw(prompt) => {
|
||||||
|
cmd.arg("-c");
|
||||||
|
cmd.arg(prompt);
|
||||||
|
}
|
||||||
|
Command::LocalFile(file) => {
|
||||||
|
cmd.arg(file);
|
||||||
|
}
|
||||||
|
Command::None => panic!("Command::None was treated as a command"),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.cwd(&state.temp_path);
|
||||||
|
|
||||||
let pair = pty_system
|
let pair = pty_system
|
||||||
.openpty(PtySize {
|
.openpty(PtySize {
|
||||||
|
@ -153,59 +168,57 @@ impl RunningCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self, frame: &mut Frame) {
|
pub fn draw(&mut self, frame: &mut Frame, state: &AppState) {
|
||||||
{
|
// Funny name
|
||||||
let theme = get_theme();
|
let floater = floating_window(frame.size());
|
||||||
// Funny name
|
|
||||||
let floater = floating_window(frame.size());
|
|
||||||
|
|
||||||
let inner_size = Size {
|
let inner_size = Size {
|
||||||
width: floater.width - 2, // Because we add a `Block` with a border
|
width: floater.width - 2, // Because we add a `Block` with a border
|
||||||
height: floater.height - 2,
|
height: floater.height - 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
// When the command is running
|
// When the command is running
|
||||||
let term_border = if !self.is_finished() {
|
let term_border = if !self.is_finished() {
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.title_top(Line::from("Running the command....").centered())
|
.title_top(Line::from("Running the command....").centered())
|
||||||
.title_style(Style::default().reversed())
|
.title_style(Style::default().reversed())
|
||||||
.title_bottom(Line::from("Press Ctrl-C to KILL the command"))
|
.title_bottom(Line::from("Press Ctrl-C to KILL the command"))
|
||||||
} else {
|
} else {
|
||||||
// This portion is just for pretty colors.
|
// This portion is just for pretty colors.
|
||||||
// You can use multiple `Span`s with different styles each, to construct a line,
|
// You can use multiple `Span`s with different styles each, to construct a line,
|
||||||
// which can be used as a list item, or in this case a `Block` title
|
// which can be used as a list item, or in this case a `Block` title
|
||||||
|
|
||||||
let mut title_line = if self.get_exit_status().success() {
|
let mut title_line = if self.get_exit_status().success() {
|
||||||
Line::from(
|
Line::from(
|
||||||
Span::default()
|
|
||||||
.content("SUCCESS!")
|
|
||||||
.style(Style::default().fg(theme.success_color).reversed()),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Line::from(
|
|
||||||
Span::default()
|
|
||||||
.content("FAILED!")
|
|
||||||
.style(Style::default().fg(theme.fail_color).reversed()),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
title_line.push_span(
|
|
||||||
Span::default()
|
Span::default()
|
||||||
.content(" press <ENTER> to close this window ")
|
.content("SUCCESS!")
|
||||||
.style(Style::default()),
|
.style(Style::default().fg(state.theme.success_color).reversed()),
|
||||||
);
|
)
|
||||||
|
} else {
|
||||||
Block::default()
|
Line::from(
|
||||||
.borders(Borders::ALL)
|
Span::default()
|
||||||
.title_top(title_line.centered())
|
.content("FAILED!")
|
||||||
|
.style(Style::default().fg(state.theme.fail_color).reversed()),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let screen = self.screen(inner_size); // when the terminal is changing a lot, there
|
|
||||||
// will be 1 frame of lag on resizing
|
title_line.push_span(
|
||||||
let pseudo_term = PseudoTerminal::new(&screen).block(term_border);
|
Span::default()
|
||||||
frame.render_widget(pseudo_term, floater);
|
.content(" press <ENTER> to close this window ")
|
||||||
}
|
.style(Style::default()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title_top(title_line.centered())
|
||||||
|
};
|
||||||
|
let screen = self.screen(inner_size); // when the terminal is changing a lot, there
|
||||||
|
// will be 1 frame of lag on resizing
|
||||||
|
let pseudo_term = PseudoTerminal::new(&screen).block(term_border);
|
||||||
|
frame.render_widget(pseudo_term, floater);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send SIGHUB signal, *not* SIGKILL or SIGTERM, to the child process
|
/// Send SIGHUB signal, *not* SIGKILL or SIGTERM, to the child process
|
||||||
pub fn kill_child(&mut self) {
|
pub fn kill_child(&mut self) {
|
||||||
if !self.is_finished() {
|
if !self.is_finished() {
|
||||||
|
|
9
src/state.rs
Normal file
9
src/state.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::theme::Theme;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub struct AppState {
|
||||||
|
/// Selected theme
|
||||||
|
pub theme: Theme,
|
||||||
|
/// Path to the root of the unpacked files in /tmp
|
||||||
|
pub temp_path: PathBuf,
|
||||||
|
}
|
11
src/theme.rs
11
src/theme.rs
|
@ -1,7 +1,6 @@
|
||||||
use ratatui::style::Color;
|
use ratatui::style::Color;
|
||||||
|
|
||||||
pub static mut THEME_IDX: usize = 1;
|
#[derive(Clone)]
|
||||||
|
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub dir_color: Color,
|
pub dir_color: Color,
|
||||||
pub cmd_color: Color,
|
pub cmd_color: Color,
|
||||||
|
@ -29,11 +28,3 @@ pub const THEMES: [Theme; 2] = [
|
||||||
success_color: Color::Rgb(5, 255, 55),
|
success_color: Color::Rgb(5, 255, 55),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn get_theme() -> &'static Theme {
|
|
||||||
&THEMES[unsafe { THEME_IDX }]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_theme(idx: usize) {
|
|
||||||
unsafe { THEME_IDX = idx };
|
|
||||||
}
|
|
||||||
|
|
20
start.sh
20
start.sh
|
@ -3,8 +3,6 @@
|
||||||
rc='\033[0m'
|
rc='\033[0m'
|
||||||
red='\033[0;31m'
|
red='\033[0;31m'
|
||||||
|
|
||||||
binary_url="https://github.com/ChrisTitusTech/linutil/releases/latest/download/linutil"
|
|
||||||
|
|
||||||
check() {
|
check() {
|
||||||
exit_code=$1
|
exit_code=$1
|
||||||
message=$2
|
message=$2
|
||||||
|
@ -18,10 +16,26 @@ check() {
|
||||||
unset message
|
unset message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findArch() {
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64|amd64) arch="x86_64" ;;
|
||||||
|
aarch64|arm64) arch="aarch64" ;;
|
||||||
|
*) check 1 "Unsupported architecture"
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
getUrl() {
|
||||||
|
case "${arch}" in
|
||||||
|
x86_64) echo "https://github.com/ChrisTitusTech/linutil/releases/latest/download/linutil";;
|
||||||
|
*) echo "https://github.com/ChrisTitusTech/linutil/releases/latest/download/linutil-${arch}";;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
findArch
|
||||||
temp_file=$(mktemp)
|
temp_file=$(mktemp)
|
||||||
check $? "Creating the temporary file"
|
check $? "Creating the temporary file"
|
||||||
|
|
||||||
curl -fsL "$binary_url" -o "$temp_file"
|
curl -fsL "$(getUrl)" -o "$temp_file"
|
||||||
check $? "Downloading linutil"
|
check $? "Downloading linutil"
|
||||||
|
|
||||||
chmod +x "$temp_file"
|
chmod +x "$temp_file"
|
||||||
|
|
17
startdev.sh
17
startdev.sh
|
@ -24,6 +24,7 @@ redirect_to_latest_pre_release() {
|
||||||
echo "Using latest Full Release"
|
echo "Using latest Full Release"
|
||||||
url="https://github.com/ChrisTitusTech/linutil/releases/latest/download/linutil"
|
url="https://github.com/ChrisTitusTech/linutil/releases/latest/download/linutil"
|
||||||
fi
|
fi
|
||||||
|
addArch
|
||||||
echo "Using URL: $url" # Log the URL being used
|
echo "Using URL: $url" # Log the URL being used
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +38,22 @@ check() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addArch() {
|
||||||
|
case "${arch}" in
|
||||||
|
x86_64);;
|
||||||
|
*) url="${url}-${arch}";;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
findArch() {
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64|amd64) arch="x86_64" ;;
|
||||||
|
aarch64|arm64) arch="aarch64" ;;
|
||||||
|
*) check 1 "Unsupported architecture"
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
findArch
|
||||||
redirect_to_latest_pre_release
|
redirect_to_latest_pre_release
|
||||||
|
|
||||||
TMPFILE=$(mktemp)
|
TMPFILE=$(mktemp)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user