Compare commits

..

16 Commits

Author SHA1 Message Date
MangoPig 1e2515a21a Fedora Fix 2026-05-31 20:48:18 +01:00
MangoPig 5e7031d700 Fedora Setup 2026-05-29 15:57:17 +01:00
MangoPig ad39f07b34 Accept pnpm 2026-05-25 16:41:54 +01:00
MangoPig 689a2dc079 R Script Fix 2026-05-05 11:18:52 +01:00
MangoPig 87fb89a6b7 Azure 2025-12-11 04:55:40 +00:00
MangoPig d41626190d Adding curl too 2025-12-09 03:31:07 +00:00
MangoPig f359bb68eb Add Ninja to base 2025-12-09 03:29:57 +00:00
MangoPig 69f7a5d4d5 Password no interactive 2025-12-06 10:10:29 +00:00
MangoPig b0db2dfaf6 Adding a base setup 2025-12-06 10:01:13 +00:00
MangoPig 7743665917 Cpp 2025-12-06 09:27:49 +00:00
MangoPig 3ba98e21ad Changing Location 2025-12-05 19:49:08 +00:00
MangoPig 28027b31ab Fix 2025-12-05 19:27:06 +00:00
MangoPig 75ffecbfb6 Add clean and stow to setup 2025-12-05 19:25:37 +00:00
MangoPig d3830840c2 Forgot parts of it 2025-12-05 19:10:41 +00:00
MangoPig ece7ac7a0b Old Package Detection 2025-12-05 19:09:46 +00:00
MangoPig cf670488ac Python 2025-12-05 19:01:00 +00:00
37 changed files with 803 additions and 2701 deletions
+2 -2
View File
@@ -16,7 +16,7 @@ alias grep="grep --color=auto"
alias src="source ~/.zshrc && echo '🔄 Reloaded .zshrc'"
# alias cat="bat"
alias cat="bat"
alias fd="fd"
alias tree="eza --icons -T --git-ignore"
@@ -91,4 +91,4 @@ proxy_on() {
proxy_off() {
unset ALL_PROXY HTTP_PROXY HTTPS_PROXY
echo "Proxy OFF (direct)"
}
}
+3
View File
@@ -0,0 +1,3 @@
# .zsh_secrets
export TESTING_SECRET="HELLO THIS IS WORKING"
+31
View File
@@ -0,0 +1,31 @@
# ENV File
export GOGC=500
# CodeGraphContext defaults
export CGC_RUNTIME_DB_TYPE=kuzudb
export KUZUDB_PATH="$HOME/.codegraphcontext/kuzudb"
export PATH=/home/mangopig/.opencode/bin:$PATH
export OPENCHAMBER_HOST=0.0.0.0
export AZURE_COGNITIVE_SERVICES_RESOURCE_NAME="$AZURE_URL"
export VAULT_ADDR="http://192.168.30.53:8200"
export OPENCHAMBER_UI_PASSWORD="$VM_SUDO_PASS"
export AZURE_RESOURCE_NAME="$AZURE_URL"
export PLANNOTATOR_PORT=9999
# OpenCode Secrets
export TAOBAO_API_KEY="sk-BCjuuzYOkaR0L1lBpcqP4N9VWnIh6XoVWghltVjBvk5GQneL"
export AZURE_OPENAI_API_KEY="6ebJTnqeDNv66eoYUmKscjb9bOmR1gE7vRcBnH9zLFnFT1Dy6a6XJQQJ99CBACfhMk5XJ3w3AAAAACOGDFaj"
export FOUNDRY_ANTHROPIC_API_KEY="$AZURE_OPENAI_API_KEY"
export CHROMA_OPENAI_API_KEY="$AZURE_OPENAI_API_KEY"
export OUTLINE_MOKU_TOKEN="ol_api_jZbStf5YaAxWlfT5fE79xgKPKW82cfUn8NaSK9"
export OUTLINE_MOKU_AUTHORIZATION="Bearer $OUTLINE_MOKU_TOKEN"
export DOKPLOY_API_KEY="CkiyeXRRImWOKeVkFAAfdoBjwHmbXoSKXNRWFwnhjKlhHnTkxBfVZDpvmXYAWgbQ"
export N8N_AUTH="725ce955821a3f92c9e85d428cd157a890c6209f254a8d31edca41ddc4b8a324"
export CONTEXT7_API_KEY="ctx7sk-3c4a0f0a-f9c4-4d45-a2a1-765f5dfa33ba"
export GITEA_ACCESS_TOKEN="51bd8f571def772ed54a3e8317189abaa7d5a1e5"
export NETDATA_TOKEN="67e9f974-be37-4bbd-9d1e-4541613fa119"
export OBSIDIAN_API_KEY="360159f3b5b72953b3dd28ebd8e24215117e67478a7333bcc6c0e43a4bfc3720"
+76
View File
@@ -0,0 +1,76 @@
# .zshrc
# Zsh Configuration
export ZSH="$HOME/.oh-my-zsh"
PROMPT="%B%~%b :: "
RPROMPT="%n@%m"
# Plugins
plugins=(git zsh-syntax-highlighting zsh-autosuggestions sudo rclone rust nvm golang conda pyenv)
source $ZSH/oh-my-zsh.sh
# Programming Languages Root
export PROG_DIR="$HOME/.programming"
# Go and GVM (Black Box)
export GVM_ROOT="$PROG_DIR/go"
[[ -s "$GVM_ROOT/scripts/gvm" ]] && source "$GVM_ROOT/scripts/gvm"
# Node and NVM (Lazy Load)
export NVM_DIR="$PROG_DIR/node"
nvm_load() {
echo "💤 Waking up NVM..."
unset -f nvm node npm npx
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
"$@"
}
nvm() { nvm_load "nvm" "$@"; }
node() { nvm_load "node" "$@"; }
npm() { nvm_load "npm" "$@"; }
npx() { nvm_load "npx" "$@"; }
# Rust and Cargo
export RUSTUP_HOME="$PROG_DIR/rust/multirust"
export CARGO_HOME="$PROG_DIR/rust/cargo"
# Python (Pyenv + Miniconda)
export PYENV_ROOT="$PROG_DIR/python/pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
if command -v pyenv 1>/dev/null 2>&1; then
eval "$(pyenv init -)"
fi
if command -v conda >/dev/null 2>&1; then
CONDA_BASE=$(conda info --base)
[ -f "$CONDA_BASE/etc/profile.d/conda.sh" ] && source "$CONDA_BASE/etc/profile.d/conda.sh"
fi
# R and Rig
export RIG_HOME="$PROG_DIR/r"
if [ -d "$RIG_HOME/bin" ]; then
export PATH="$RIG_HOME/bin:$PATH"
fi
# Zoxide
eval "$(zoxide init --cmd cd zsh)"
# Source Aliases & Secrets
[ -f ~/.zsh_aliases ] && source ~/.zsh_aliases
[ -f ~/.zsh_secrets ] && source ~/.zsh_secrets
# Add to PATH
export PATH="$HOME/.local/bin:$CARGO_HOME/bin:$PATH"
export PATH="$GOPATH/bin:$PATH"
# opencode
if ! pgrep -f "openchamber.*7891" > /dev/null; then
openchamber --port 7891 >/dev/null 2>&1
fi
# direnv
eval "$(direnv hook zsh)"
-45
View File
@@ -1,45 +0,0 @@
{
"nvim": {
"owner": "neovim",
"repo": "neovim",
"version": "v0.11.5",
"linux": {
"x86_64": {
"asset": "nvim-linux-x86_64.tar.gz",
"binary": "nvim-linux-x86_64/bin/nvim"
},
"aarch64": {
"asset": "nvim-linux-arm64.tar.gz",
"binary": "nvim-linux-arm64/bin/nvim"
}
},
"macos": {
"x86_64": {
"asset": "nvim-macos-x86_64.tar.gz",
"binary": "nvim-macos-x86_64/bin/nvim"
},
"aarch64": {
"asset": "nvim-macos-arm64.tar.gz",
"binary": "nvim-macos-arm64/bin/nvim"
}
}
},
"eza": {
"owner": "eza-community",
"repo": "eza",
"version": "v0.23.4",
"linux": {
"x86_64": {
"asset": "eza_x86_64-unknown-linux-gnu.tar.gz",
"binary": "eza"
},
"aarch64": {
"asset": "eza_aarch64-unknown-linux-gnu.tar.gz",
"binary": "eza"
}
},
"macos": {
"formula": "eza"
}
}
}
-31
View File
@@ -1,31 +0,0 @@
project_root := justfile_directory()
bin_script_dir := project_root + "/Scripts/bin"
# List the repo-managed binary names, or available releases for one tool.
[default]
list tool='':
if [ -n '{{tool}}' ]; then \
bash '{{bin_script_dir}}/update.sh' --list '{{tool}}'; \
else \
jq -r 'keys[]' '{{project_root}}/Bins/versions.json'; \
fi
# List available releases for a supported binary.
releases tool:
bash '{{bin_script_dir}}/update.sh' --list '{{tool}}'
# Install all pinned repo-managed binaries into ~/.local/bin.
install-all:
bash '{{bin_script_dir}}/install.sh' --all
# Install one pinned repo-managed binary into ~/.local/bin.
install tool:
bash '{{bin_script_dir}}/install.sh' '{{tool}}'
# Update the pinned version for a binary. If no version is given, open an fzf selector when available.
update tool version='':
bash '{{bin_script_dir}}/update.sh' '{{tool}}' '{{version}}'
# Print the currently pinned versions.
show:
cat '{{project_root}}/Bins/versions.json'
-11
View File
@@ -1,11 +0,0 @@
project_root := justfile_directory()
scripts_dir := project_root + "/Scripts"
# Print the active global Git configuration.
[default]
show:
bash '{{scripts_dir}}/git.sh' show
# Set the global Git editor. Defaults to VS Code; pass `nvim` for Neovim.
editor value='code':
bash '{{scripts_dir}}/git.sh' editor '{{value}}'
-29
View File
@@ -1,29 +0,0 @@
project_root := justfile_directory()
scripts_dir := project_root + "/Scripts"
# Install every language toolchain in full-setup order.
all: node go rust python r cpp
# Install Node.js, pnpm, Yarn, and Bun tooling.
node:
bash '{{scripts_dir}}/node.sh'
# Install Go and GVM tooling.
go:
bash '{{scripts_dir}}/go.sh'
# Install the Rust toolchain.
rust:
bash '{{scripts_dir}}/rust.sh'
# Install Python, pyenv, and Miniforge tooling.
python:
bash '{{scripts_dir}}/python.sh'
# Install R tooling.
r:
bash '{{scripts_dir}}/r.sh'
# Install the C and C++ toolchain.
cpp:
bash '{{scripts_dir}}/cpp.sh'
-28
View File
@@ -1,28 +0,0 @@
project_root := justfile_directory()
scripts_dir := project_root + "/Scripts"
# Run the full setup flow.
[default]
all:
bash '{{scripts_dir}}/setup.sh'
# Run the lightweight bootstrap flow: base packages and shell-file stow.
bootstrap:
bash '{{scripts_dir}}/setup.sh' --bootstrap-only
# Install the base system layer only.
base:
bash '{{scripts_dir}}/base.sh'
# Stow shell files into $HOME.
stow:
bash '{{scripts_dir}}/setup.sh' --stow-only
# Remove stowed shell files from $HOME.
clean:
stow --dir='{{project_root}}' -D Zsh --target="$HOME"
# Pull latest changes and rerun setup.
update:
git -C '{{project_root}}' pull origin main
just --justfile '{{project_root}}/Justfile' setup all
-10
View File
@@ -1,10 +0,0 @@
set shell := ["bash", "-cu"]
mod bin "Commands/Bin"
mod git "Commands/Git"
mod lang "Commands/Lang"
mod setup "Commands/Setup"
[default]
help:
just --list --list-submodules
+90 -5
View File
@@ -1,11 +1,96 @@
# Makefile
SCRIPTS_DIR := ./Scripts
REBOOT_MARKER := .setup-reboot-required
# Default target
all: setup
all: stow
# Bootstrap entrypoint for first-run setup.
# This intentionally keeps Make lightweight: base packages, then restow dotfiles.
# Full Setup
setup:
bash $(SCRIPTS_DIR)/setup.sh --bootstrap-only
@set -e; \
rm -f $(REBOOT_MARKER); \
bash ./scripts/base.sh; \
if [ -f $(REBOOT_MARKER) ]; then \
rm -f $(REBOOT_MARKER); \
echo "Package layering finished. Reboot, then rerun make setup."; \
exit 0; \
fi; \
bash ./scripts/node.sh; \
bash ./scripts/go.sh; \
bash ./scripts/rust.sh; \
bash ./scripts/python.sh; \
if [ -f $(REBOOT_MARKER) ]; then \
rm -f $(REBOOT_MARKER); \
echo "Package layering finished. Reboot, then rerun make setup."; \
exit 0; \
fi; \
bash ./scripts/r.sh; \
if [ -f $(REBOOT_MARKER) ]; then \
rm -f $(REBOOT_MARKER); \
echo "Package layering finished. Reboot, then rerun make setup."; \
exit 0; \
fi; \
$(MAKE) clean; \
$(MAKE) stow; \
echo "Full setup completed."
base:
bash ./scripts/base.sh
@echo "Base setup completed."
# Just stow the dotfiles
stow:
stow . --target=$$HOME --ignore=".git" --ignore=".gitignore" --ignore="README.md" --ignore=".zsh_secrets" --ignore=".zsh_secrets.example" --ignore="LICENSE" --ignore="Makefile" --ignore="bin" --ignore="scripts"
@echo "Dotfiles linked."
# Clean old files and links
clean:
stow -D . --target=$$HOME
@echo "Links removed."
# Pull Git Updates
update:
git pull origin main
$(MAKE) setup
# Language Setups
node:
bash ./scripts/node.sh
go:
bash ./scripts/go.sh
rust:
bash ./scripts/rust.sh
python:
bash ./scripts/python.sh
r:
bash ./scripts/r.sh
cpp:
bash ./scripts/cpp.sh
storagebox:
bash ./scripts/storagebox.sh
# Docker Tests
test-ubuntu:
@echo "Ubuntu Test"
docker run -it --rm -e TERM=xterm-256color -v $(PWD):/root/dotfiles ubuntu:latest \
bash -c "export DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
apt-get install -y sudo git make curl && \
cd /root/dotfiles && \
make setup"
test-arch:
@echo "Spawning Arch Container..."
docker run -it --rm -e TERM=xterm-256color -v $(PWD):/root/dotfiles archlinux:latest \
bash -c "pacman -Sy --noconfirm base-devel git make sudo && cd /root/dotfiles && make setup"
test-fedora:
@echo "Spawning Fedora Container..."
docker run -it --rm -e TERM=xterm-256color -v $(PWD):/root/dotfiles fedora:latest \
bash -c "dnf install -y git make sudo curl which passwd procps-ng && cd /root/dotfiles && make setup"
-350
View File
@@ -1,350 +0,0 @@
# Dot-Zsh
Personal cross-platform shell and workstation bootstrap for:
- Arch Linux
- Ubuntu / Debian-family Linux
- Fedora
- macOS
- WSL
The repo is built around a small bootstrap path and a fuller `just`-based workflow.
---
## Layout
```text
.
├── Bins/ # Pinned binary versions (nvim, eza, ...)
├── Commands/ # just modules
├── Scripts/ # install/setup scripts
├── Zsh/ # stowed shell files
├── Justfile # main just entrypoint
└── Makefile # lightweight bootstrap entrypoint
```
### Key folders
#### `Zsh/`
Stowed into `$HOME`.
- `.zshrc`
- `.zshenv`
- `.zsh_aliases`
- `.zsh_prompt`
- `.zsh_completion`
- `.zsh_secrets`
- `.zsh_secrets.example`
#### `Scripts/`
Main install logic.
- `base.sh` — system/base packages and common tools
- `setup.sh` — full setup flow
- `node.sh`, `go.sh`, `rust.sh`, `python.sh`, `r.sh`, `cpp.sh`
- `bin/install.sh` — install pinned repo-managed binaries
- `bin/update.sh` — update pinned binary versions
#### `Commands/`
Modular `just` commands.
- `Commands/Setup/mod.just`
- `Commands/Lang/mod.just`
- `Commands/Bin/mod.just`
- `Commands/Git/mod.just`
#### `Bins/`
`versions.json` is the source of truth for pinned binary versions and per-platform asset mappings.
---
## Core workflow
### Bootstrap only
Use `make setup` when you want the lightweight bootstrap path:
```bash
make setup
```
This currently does:
1. run `Scripts/base.sh`
2. remove old `Zsh` stow links
3. stow `Zsh/` into `$HOME`
It is intentionally small.
### Full setup
Use `just setup all` for the full machine setup:
```bash
just setup all
```
This runs the full flow in `Scripts/setup.sh`:
1. base system setup
2. node / pnpm / yarn / bun
3. go / gvm
4. rust
5. python / pyenv / miniforge
6. r
7. secret-file reconciliation
8. backup of conflicting unmanaged dotfiles
9. stow `Zsh/`
### Other useful commands
```bash
just setup base
just setup bootstrap
just setup stow
just setup clean
just setup update
just git show
just git editor
just git editor nvim
just lang node
just lang go
just lang rust
just lang python
just lang r
just lang cpp
just bin show
just bin list
just bin install-all
just bin install nvim
just bin releases nvim
just bin list nvim # backwards-compatible shorthand for releases
just bin update nvim
just lang all
```
If `fzf` is available, `just bin update <tool>` can use interactive selection.
`just setup all` and `just setup base` now set the default global Git editor to VS Code (`code --wait`).
If you want to switch to Neovim later, run `just git editor nvim`.
---
## Secrets model
This repo now treats shell secrets as repo-managed local dotfiles instead of tracked plaintext config.
### Source of truth
```text
Zsh/.zsh_secrets
```
That file is intended to be stowed to:
```text
$HOME/.zsh_secrets
```
### Important notes
- `.zsh_secrets` is ignored by git
- `.zsh_secrets.example` exists as a template/reference
- setup tries to reconcile an existing `$HOME/.zsh_secrets` safely
- if conflicting unmanaged dotfiles already exist, setup backs them up before stowing
Backup location:
```text
~/.dotzsh-pre-stow-backup/<timestamp>/
```
---
## Binary management
This repo no longer stores shipped binaries in git.
Instead:
- pinned versions live in `Bins/versions.json`
- installs go into `~/.local/bin`
- updates happen through `just bin update <tool>`
Current managed binaries include:
- `nvim`
- `eza`
Platform behavior:
- Linux: install from pinned release assets
- macOS: install from pinned asset when supported, or use Homebrew formula fallback when configured
---
## Shell behavior
### Prompt
Prompt logic lives in:
```text
Zsh/.zsh_prompt
```
It adds:
- git branch
- git dirty/clean indicator
- conda / direnv context
- repo-scoped project markers
- language version hints
- separator rule between prompts
### Completion
Completion logic lives in:
```text
Zsh/.zsh_completion
```
It adds:
- custom first-word command completion
- recently used commands shown first
- used commands visually marked with `*`
- wrapper completion bindings for lazy-loaded tools like `gvm`, `conda`, `go`, `node`, `npm`, and `npx`
### Lazy loading
Several language managers are intentionally lazy-loaded to keep shell startup lighter.
Examples:
- `nvm`
- `gvm`
- `pyenv`
- `conda`
`openchamber` auto-start is restricted to WSL sessions.
---
## Install size
Measured practical installed footprint on Linux is roughly:
- Ubuntu: about `1.96 GB`
- Arch: about `1.84 GB`
- Fedora: about `1.77 GB`
So the setup is best thought of as:
```text
~1.8 GB to ~2.0 GB total
```
### Largest components
Measured Ubuntu component breakdown:
- Rust: about `1.5 GB`
- Python: about `1.1 GB`
- Go: about `635 MB`
- Node: about `235 MB`
- `~/.local`: about `128 MB`
- R wrapper/user area under `~/.programming/r`: very small (`~20 KB` in that measurement)
Important nuance:
- `~/.programming/r` is intentionally small because it mainly holds wrappers and user-library location
- the actual R runtime may still come from the distro package manager or Rig, depending on platform
- so not all R-related disk usage appears under `~/.programming/r`
### Measured paths
Representative measured path sizes from Linux test runs:
```text
Ubuntu
~/.programming ~3.17 GB
~/.local ~133 MB
/usr/local ~13 MB
Arch
~/.programming ~3.17 GB
~/.local ~133 MB
Fedora
~/.programming ~3.17 GB
~/.local ~133 MB
```
Those path totals are larger than the final container image delta because some space is shared/overlapping across package layers and installed tooling.
---
## Notes by platform
### Linux
- full setup has been exercised in container tests on Ubuntu, Arch, and Fedora x86_64
### macOS
- supports Homebrew-based package install flow
- uses `/bin/zsh` for shell-change target
- handles existing dotfiles and secret symlink reconciliation more carefully
### WSL
- WSL-specific logic is used where needed
- `openchamber` is only auto-started in WSL
---
## First-run recommendation
If you are starting clean:
```bash
make setup
just setup all
```
If you only want shell files re-linked:
```bash
just setup clean
just setup stow
```
If you only want to refresh pinned binaries:
```bash
just bin install-all
```
---
## Current philosophy
This repo aims to be:
- owned rather than magical
- reproducible rather than ad hoc
- modular rather than one giant dotfile blob
- explicit about pinned binaries and local secrets
It is not trying to be the smallest possible install. It is trying to be a repeatable personal workstation setup.
-441
View File
@@ -1,441 +0,0 @@
#!/bin/bash
# Path: Scripts/base.sh
set -e
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
RED='\033[1;31m'
NC='\033[0m'
DOTFILES_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REPO_ROOT="$(dirname "$DOTFILES_DIR")"
source "$DOTFILES_DIR/lib/distro.sh"
OH_MY_ZSH_REPO="https://github.com/ohmyzsh/ohmyzsh.git"
OH_MY_ZSH_COMMIT="b26b5002633e865b70e17933536fe4dc99127898"
ZSH_SYNTAX_HIGHLIGHTING_REPO="https://github.com/zsh-users/zsh-syntax-highlighting.git"
ZSH_SYNTAX_HIGHLIGHTING_COMMIT="5eb677bb0fa9a3e60f0eff031dc13926e093df92"
ZSH_AUTOSUGGESTIONS_REPO="https://github.com/zsh-users/zsh-autosuggestions.git"
ZSH_AUTOSUGGESTIONS_COMMIT="85919cd1ffa7d2d5412f6d3fe437ebdbeeec4fc5"
RCLONE_VERSION="v1.74.2"
EARTHLY_VERSION="v0.8.16"
clone_pinned_repo() {
local repo_url="$1"
local destination="$2"
local commit="$3"
local label="$4"
if [ -d "$destination" ]; then
echo -e "${GREEN} LOG: $label already present.${NC}"
return 0
fi
echo -e "${BLUE} LOG:${YELLOW} Installing $label at pinned commit ${commit:0:12}...${NC}"
git clone "$repo_url" "$destination"
git -C "$destination" checkout "$commit"
if [ "$(git -C "$destination" rev-parse HEAD)" != "$commit" ]; then
echo -e "${RED} ERROR: Failed to pin $label to expected commit $commit${NC}"
exit 1
fi
}
install_debian_docker_from_repo() {
local docker_repo_os="$OS"
local docker_codename="${VERSION_CODENAME:-}"
local dpkg_arch
if [[ "$docker_repo_os" != "ubuntu" && "$docker_repo_os" != "debian" ]]; then
docker_repo_os="ubuntu"
fi
if [ -z "$docker_codename" ] && command -v lsb_release >/dev/null 2>&1; then
docker_codename="$(lsb_release -cs)"
fi
if [ -z "$docker_codename" ]; then
echo -e "${RED} ERROR: Unable to determine Debian/Ubuntu codename for Docker repository setup.${NC}"
exit 1
fi
dpkg_arch="$(dpkg --print-architecture)"
sudo apt-get remove -y docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc >/dev/null 2>&1 || true
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL "https://download.docker.com/linux/${docker_repo_os}/gpg" | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=${dpkg_arch} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/${docker_repo_os} ${docker_codename} stable" |
sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
}
download_and_verify_release_asset() {
local asset_url="$1"
local checksum_url="$2"
local asset_name="$3"
local destination_path="$4"
local checksum_file
local expected_checksum
local actual_checksum
checksum_file="$(mktemp)"
curl -fLsS "$asset_url" -o "$destination_path"
curl -fLsS "$checksum_url" -o "$checksum_file"
expected_checksum="$(awk -v asset="$asset_name" '$2 == asset { print $1; exit }' "$checksum_file")"
if [ -z "$expected_checksum" ]; then
rm -f "$checksum_file"
echo -e "${RED} ERROR: Could not find checksum for $asset_name in $checksum_url${NC}"
exit 1
fi
actual_checksum="$(sha256sum "$destination_path" | awk '{print $1}')"
if [ "$actual_checksum" != "$expected_checksum" ]; then
rm -f "$checksum_file"
echo -e "${RED} ERROR: Checksum verification failed for $asset_name${NC}"
exit 1
fi
rm -f "$checksum_file"
}
ensure_git_credential_helper() {
local helper_value="$1"
local existing_helpers=""
existing_helpers=$(git config --global --get-all credential.helper 2>/dev/null || true)
if printf '%s\n' "$existing_helpers" | grep -Fx -- "$helper_value" >/dev/null 2>&1; then
return 0
fi
git config --global --add credential.helper "$helper_value"
}
echo -e "${BLUE} LOG:${YELLOW} Initializing Base System Layer...${NC}"
# Confirm Architecture
ARCH=$(uname -m)
if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ] && [ "$ARCH" != "arm64" ]; then
echo -e "${RED} ERROR: Unsupported architecture: $ARCH${NC}"
exit 1
fi
is_wsl() {
[ -f /proc/version ] && grep -qEi "(Microsoft|WSL)" /proc/version
}
# Package Installation
PACKAGES=(
curl wget git sudo
rsync
zsh tmux
unzip tar gzip
build-essential
openssl
python bison mercurial
ripgrep fd bat fzf jq
btop httpie gnupg
zoxide stow direnv
bind nmap socat tcpdump net-tools
strace gdb hexyl
ninja-build libcurl4-openssl-dev
just
)
FINAL_PACKAGES=()
for pkg in "${PACKAGES[@]}"; do
case "$pkg" in
"build-essential")
if is_arch_family; then
FINAL_PACKAGES+=(base-devel)
elif is_debian_family; then
FINAL_PACKAGES+=(build-essential)
elif is_fedora_family; then
FINAL_PACKAGES+=(gcc gcc-c++ make patch)
elif is_macos; then
:
fi
continue
;;
"python")
if is_arch_family; then
FINAL_PACKAGES+=(python)
elif is_debian_family; then
FINAL_PACKAGES+=(python3 python3-pip python3-venv)
elif is_fedora_family; then
FINAL_PACKAGES+=(python3 python3-pip)
elif is_macos; then
FINAL_PACKAGES+=(python)
fi
continue
;;
"fd")
if is_arch_family; then
FINAL_PACKAGES+=(fd)
elif is_debian_family || is_fedora_family; then
FINAL_PACKAGES+=(fd-find)
fi
continue
;;
"bat")
FINAL_PACKAGES+=(bat)
continue
;;
"openssl")
if is_arch_family; then
FINAL_PACKAGES+=(openssl)
elif is_debian_family; then
FINAL_PACKAGES+=(libssl-dev)
elif is_fedora_family; then
FINAL_PACKAGES+=(openssl openssl-devel)
elif is_macos; then
FINAL_PACKAGES+=(openssl@3)
fi
continue
;;
"bind")
if is_arch_family; then
FINAL_PACKAGES+=(bind)
elif is_debian_family; then
FINAL_PACKAGES+=(dnsutils)
elif is_fedora_family; then
FINAL_PACKAGES+=(bind-utils)
elif is_macos; then
FINAL_PACKAGES+=(bind)
fi
continue
;;
"ninja-build")
if is_arch_family; then
FINAL_PACKAGES+=(ninja)
elif is_debian_family || is_fedora_family; then
FINAL_PACKAGES+=(ninja-build)
elif is_macos; then
FINAL_PACKAGES+=(ninja)
fi
continue
;;
"libcurl4-openssl-dev")
if is_arch_family; then
FINAL_PACKAGES+=(curl)
elif is_debian_family; then
FINAL_PACKAGES+=(libcurl4-openssl-dev)
elif is_fedora_family; then
FINAL_PACKAGES+=(libcurl-devel)
elif is_macos; then
FINAL_PACKAGES+=(curl)
fi
continue
;;
"gnupg")
if is_fedora_family; then
FINAL_PACKAGES+=(gnupg2)
elif is_macos; then
FINAL_PACKAGES+=(gnupg)
else
FINAL_PACKAGES+=(gnupg)
fi
continue
;;
*)
esac
FINAL_PACKAGES+=("$pkg")
done
if is_debian_family; then
FINAL_PACKAGES+=(ca-certificates bsdmainutils pkg-config cmake)
fi
if is_fedora_family; then
FINAL_PACKAGES+=(ca-certificates pkgconf-pkg-config cmake)
FINAL_PACKAGES+=(R-core gcc-gfortran bzip2 bzip2-devel readline-devel sqlite sqlite-devel tk-devel libffi-devel xz xz-devel ncurses-devel zlib-devel findutils llvm)
fi
if is_macos; then
FINAL_PACKAGES=(
curl wget git zsh tmux unzip
python bison mercurial
ripgrep fd bat fzf jq
btop httpie gnupg
zoxide stow direnv
bind nmap socat
hexyl ninja just
)
fi
echo -e "${BLUE} LOG:${YELLOW} Installing: ${NC}${FINAL_PACKAGES[*]}"
install_status=0
install_packages "${FINAL_PACKAGES[@]}" || install_status=$?
if [ "$install_status" -eq 42 ]; then
exit 0
elif [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
if is_debian_family || is_fedora_family; then
echo -e "${BLUE} LOG:${YELLOW} Fixing fd/bat binary names when needed...${NC}"
[ -f /usr/bin/fdfind ] && sudo ln -sf /usr/bin/fdfind /usr/local/bin/fd
[ -f /usr/bin/batcat ] && sudo ln -sf /usr/bin/batcat /usr/local/bin/bat
fi
# Installing pinned repo-managed CLI binaries
bash "$REPO_ROOT/Scripts/bin/install.sh" --all
if ! command -v rclone &> /dev/null; then
echo -e "${BLUE} LOG:${YELLOW} Installing Rclone CLI...${NC}"
if is_macos; then
brew install rclone
else
case "$ARCH" in
x86_64)
RCLONE_ARCH="amd64"
;;
aarch64|arm64)
RCLONE_ARCH="arm64"
;;
esac
TEMP_DIR="$(mktemp -d)"
RCLONE_ZIP="$TEMP_DIR/rclone.zip"
RCLONE_ASSET="rclone-${RCLONE_VERSION}-linux-${RCLONE_ARCH}.zip"
RCLONE_ASSET_URL="https://github.com/rclone/rclone/releases/download/${RCLONE_VERSION}/${RCLONE_ASSET}"
RCLONE_CHECKSUM_URL="https://github.com/rclone/rclone/releases/download/${RCLONE_VERSION}/SHA256SUMS"
download_and_verify_release_asset "$RCLONE_ASSET_URL" "$RCLONE_CHECKSUM_URL" "$RCLONE_ASSET" "$RCLONE_ZIP"
unzip -q "$RCLONE_ZIP" -d "$TEMP_DIR"
install -m 755 "$TEMP_DIR"/rclone-${RCLONE_VERSION}-linux-"${RCLONE_ARCH}"/rclone "$HOME/.local/bin/rclone"
rm -rf "$TEMP_DIR"
fi
fi
if ! command -v earthly &> /dev/null; then
echo -e "${BLUE} LOG:${YELLOW} Installing Earthly CLI...${NC}"
if is_macos; then
brew install earthly
else
case "$ARCH" in
x86_64)
EARTHLY_ARCH="amd64"
;;
aarch64|arm64)
EARTHLY_ARCH="arm64"
;;
esac
EARTHLY_ASSET="earthly-linux-${EARTHLY_ARCH}"
EARTHLY_ASSET_URL="https://github.com/earthly/earthly/releases/download/${EARTHLY_VERSION}/${EARTHLY_ASSET}"
EARTHLY_CHECKSUM_URL="https://github.com/earthly/earthly/releases/download/${EARTHLY_VERSION}/checksum.asc"
download_and_verify_release_asset "$EARTHLY_ASSET_URL" "$EARTHLY_CHECKSUM_URL" "$EARTHLY_ASSET" "$HOME/.local/bin/earthly"
chmod +x "$HOME/.local/bin/earthly"
fi
fi
# Docker Installation
if command -v docker &> /dev/null; then
echo -e "${GREEN} LOG: Docker is already installed.${NC}"
else
if is_wsl; then
echo -e "${RED} LOG: WSL Detected! Skipping Native Docker.${NC}"
echo -e "${RED} >>> Please install Docker Desktop on Windows.${NC}"
elif is_macos; then
echo -e "${YELLOW} NOTE:${NC} Docker is not installed. Please install Docker Desktop for macOS manually."
else
echo -e "${BLUE} LOG:${YELLOW} Installing Native Docker...${NC}"
if is_arch_family; then
sudo pacman -S --noconfirm --needed docker docker-compose
sudo systemctl enable --now docker
elif is_debian_family; then
install_debian_docker_from_repo
elif is_fedora_family; then
if is_atomic_fedora; then
echo -e "${YELLOW} NOTE:${NC} Skipping native Docker auto-install on rpm-ostree systems."
echo -e "${YELLOW} NOTE:${NC} After reboot, install your preferred container runtime separately if needed."
else
sudo dnf install -y moby-engine docker-compose
sudo systemctl enable --now docker
fi
fi
# Add user to group
if command -v getent >/dev/null 2>&1 && getent group docker >/dev/null 2>&1; then
sudo usermod -aG docker $(whoami)
fi
fi
fi
# 4. Zsh & Configuration
if [ ! -d "$HOME/.oh-my-zsh" ]; then
clone_pinned_repo "$OH_MY_ZSH_REPO" "$HOME/.oh-my-zsh" "$OH_MY_ZSH_COMMIT" "Oh My Zsh"
fi
# Plugins
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
mkdir -p "$ZSH_CUSTOM/plugins"
[ ! -d "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" ] && clone_pinned_repo "$ZSH_SYNTAX_HIGHLIGHTING_REPO" "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" "$ZSH_SYNTAX_HIGHLIGHTING_COMMIT" "zsh-syntax-highlighting"
[ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ] && clone_pinned_repo "$ZSH_AUTOSUGGESTIONS_REPO" "$ZSH_CUSTOM/plugins/zsh-autosuggestions" "$ZSH_AUTOSUGGESTIONS_COMMIT" "zsh-autosuggestions"
# 5. Git Credentials (WSL Bridge)
if is_wsl; then
GCM_WIN="/mnt/c/Program Files/Git/mingw64/bin/git-credential-manager.exe"
[ -f "$GCM_WIN" ] && ensure_git_credential_helper "! \"$GCM_WIN\""
else
ensure_git_credential_helper 'cache --timeout=43200'
fi
git config --global core.editor "code --wait"
# 6. Cleanup & Secrets
rm -f "$HOME/.zshrc" "$HOME/.zsh_aliases"
mkdir -p "$REPO_ROOT/Zsh"
[ -e "$REPO_ROOT/Zsh/.zsh_secrets" ] || : > "$REPO_ROOT/Zsh/.zsh_secrets"
# 7. Set Shell
TARGET_SHELL="$(command -v zsh)"
CURRENT_LOGIN_SHELL=""
if is_macos && [ -x /bin/zsh ]; then
TARGET_SHELL="/bin/zsh"
fi
if command -v getent >/dev/null 2>&1; then
CURRENT_LOGIN_SHELL="$(getent passwd "$(whoami)" | cut -d: -f7)"
elif is_macos && command -v dscl >/dev/null 2>&1; then
CURRENT_LOGIN_SHELL="$(dscl . -read "/Users/$(whoami)" UserShell 2>/dev/null | awk '{print $2}')"
fi
if [ "$CURRENT_LOGIN_SHELL" != "$TARGET_SHELL" ]; then
if [ ! -t 0 ]; then
echo -e "${YELLOW} NOTE:${NC} Non-interactive session detected. Skipping login shell change to $TARGET_SHELL."
echo -e "${YELLOW} NOTE:${NC} Run 'chsh -s $TARGET_SHELL' manually later if you want zsh as your login shell."
elif is_macos && command -v chsh >/dev/null 2>&1; then
chsh -s "$TARGET_SHELL"
elif command -v chsh >/dev/null 2>&1; then
sudo chsh -s "$TARGET_SHELL" "$(whoami)"
elif command -v usermod >/dev/null 2>&1; then
sudo usermod -s "$TARGET_SHELL" "$(whoami)"
else
echo -e "${YELLOW} NOTE:${NC} Could not find chsh/usermod. Please change your login shell to $TARGET_SHELL manually."
fi
fi
echo -e "${GREEN} LOG: Base System Setup Complete.${NC}"
-145
View File
@@ -1,145 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
MANIFEST="$REPO_ROOT/Bins/versions.json"
TARGET_DIR="$HOME/.local/bin"
ensure_deps() {
local missing=()
for cmd in jq curl tar; do
if ! command -v "$cmd" >/dev/null 2>&1; then
missing+=("$cmd")
fi
done
if [ "${#missing[@]}" -gt 0 ]; then
printf 'Missing required commands: %s\n' "${missing[*]}" >&2
exit 1
fi
}
detect_platform() {
case "$(uname -s)" in
Linux) printf 'linux\n' ;;
Darwin) printf 'macos\n' ;;
*)
printf 'Unsupported operating system: %s\n' "$(uname -s)" >&2
exit 1
;;
esac
}
detect_arch() {
case "$(uname -m)" in
x86_64) printf 'x86_64\n' ;;
aarch64|arm64) printf 'aarch64\n' ;;
*)
printf 'Unsupported architecture: %s\n' "$(uname -m)" >&2
exit 1
;;
esac
}
install_tool() {
local tool="$1"
local platform arch version owner repo asset binary_rel formula url tmp_dir archive_path extracted_path target_path installed_path formula_prefix
platform="$(detect_platform)"
arch="$(detect_arch)"
if ! jq -e --arg tool "$tool" '.[$tool]' "$MANIFEST" >/dev/null; then
printf 'Unsupported tool: %s\n' "$tool" >&2
exit 1
fi
version="$(jq -r --arg tool "$tool" '.[$tool].version' "$MANIFEST")"
owner="$(jq -r --arg tool "$tool" '.[$tool].owner' "$MANIFEST")"
repo="$(jq -r --arg tool "$tool" '.[$tool].repo' "$MANIFEST")"
formula="$(jq -r --arg tool "$tool" --arg platform "$platform" '.[$tool][$platform].formula // empty' "$MANIFEST")"
if [ -n "$formula" ]; then
if ! command -v brew >/dev/null 2>&1; then
printf 'Homebrew is required to install %s on macOS\n' "$tool" >&2
exit 1
fi
if ! brew list --formula "$formula" >/dev/null 2>&1; then
printf 'Installing %s via Homebrew formula %s\n' "$tool" "$formula"
brew install "$formula"
else
printf '%s already installed via Homebrew\n' "$tool"
fi
formula_prefix="$(brew --prefix "$formula" 2>/dev/null || true)"
if [ -n "$formula_prefix" ] && [ -x "$formula_prefix/bin/$tool" ]; then
installed_path="$formula_prefix/bin/$tool"
else
installed_path="$(command -v "$tool" || true)"
fi
if [ -z "$installed_path" ]; then
printf 'Installed formula %s but %s is not on PATH\n' "$formula" "$tool" >&2
exit 1
fi
mkdir -p "$TARGET_DIR"
target_path="$TARGET_DIR/$tool"
ln -sf "$installed_path" "$target_path"
printf 'Linked %s -> %s\n' "$target_path" "$installed_path"
return 0
fi
asset="$(jq -r --arg tool "$tool" --arg platform "$platform" --arg arch "$arch" '.[$tool][$platform][$arch].asset // empty' "$MANIFEST")"
binary_rel="$(jq -r --arg tool "$tool" --arg platform "$platform" --arg arch "$arch" '.[$tool][$platform][$arch].binary // empty' "$MANIFEST")"
if [ -z "$asset" ] || [ -z "$binary_rel" ]; then
printf 'No %s asset mapping for %s on %s\n' "$platform" "$tool" "$arch" >&2
exit 1
fi
mkdir -p "$TARGET_DIR"
tmp_dir="$(mktemp -d)"
archive_path="$tmp_dir/$asset"
url="https://github.com/$owner/$repo/releases/download/$version/$asset"
printf 'Installing %s %s\n' "$tool" "$version"
curl -fL "$url" -o "$archive_path"
tar -xzf "$archive_path" -C "$tmp_dir"
extracted_path="$tmp_dir/$binary_rel"
if [ ! -f "$extracted_path" ]; then
printf 'Expected binary not found after extraction: %s\n' "$binary_rel" >&2
rm -rf "$tmp_dir"
exit 1
fi
target_path="$TARGET_DIR/$tool"
install -m 755 "$extracted_path" "$target_path"
rm -rf "$tmp_dir"
printf 'Installed %s -> %s\n' "$tool" "$target_path"
}
main() {
ensure_deps
if [ "${1:-}" = "--all" ]; then
while IFS= read -r tool; do
install_tool "$tool"
done < <(jq -r 'keys[]' "$MANIFEST")
exit 0
fi
if [ -z "${1:-}" ]; then
printf 'Usage: %s [--all|tool]\n' "$0" >&2
exit 1
fi
install_tool "$1"
}
main "$@"
-135
View File
@@ -1,135 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
MANIFEST="$REPO_ROOT/Bins/versions.json"
ensure_deps() {
local missing=()
for cmd in jq curl python3; do
if ! command -v "$cmd" >/dev/null 2>&1; then
missing+=("$cmd")
fi
done
if [ "${#missing[@]}" -gt 0 ]; then
printf 'Missing required commands: %s\n' "${missing[*]}" >&2
exit 1
fi
}
github_api() {
local path="$1"
local auth_args=()
if [ -n "${GITHUB_TOKEN:-}" ]; then
auth_args=(-H "Authorization: Bearer $GITHUB_TOKEN")
fi
curl -fsSL \
-H "Accept: application/vnd.github+json" \
"${auth_args[@]}" \
"https://api.github.com${path}"
}
list_versions() {
local tool="$1" owner repo
if ! jq -e --arg tool "$tool" '.[$tool]' "$MANIFEST" >/dev/null; then
printf 'Unsupported tool: %s\n' "$tool" >&2
exit 1
fi
owner="$(jq -r --arg tool "$tool" '.[$tool].owner' "$MANIFEST")"
repo="$(jq -r --arg tool "$tool" '.[$tool].repo' "$MANIFEST")"
github_api "/repos/$owner/$repo/releases?per_page=100" | jq -r '.[].tag_name'
}
select_version() {
local tool="$1"
local versions
versions="$(list_versions "$tool")"
if [ -z "$versions" ]; then
printf 'No releases found for %s\n' "$tool" >&2
exit 1
fi
if command -v fzf >/dev/null 2>&1; then
printf '%s\n' "$versions" | fzf --prompt="Select ${tool} version > " --height=20 --reverse
else
printf '%s\n' "$versions" | sed -n '1,20p' >&2
printf 'fzf is not installed, so pass a version explicitly.\n' >&2
exit 1
fi
}
update_version() {
local tool="$1" version="$2"
local tmp_file
if [ -z "$version" ]; then
printf 'No version selected for %s\n' "$tool" >&2
exit 1
fi
tmp_file="$(mktemp)"
python3 - "$MANIFEST" "$tool" "$version" "$tmp_file" <<'PY'
import json
import pathlib
import sys
manifest_path = pathlib.Path(sys.argv[1])
tool = sys.argv[2]
version = sys.argv[3]
tmp_path = pathlib.Path(sys.argv[4])
data = json.loads(manifest_path.read_text())
if tool not in data:
raise SystemExit(f"Unsupported tool: {tool}")
data[tool]["version"] = version
tmp_path.write_text(json.dumps(data, indent=2) + "\n")
PY
mv "$tmp_file" "$MANIFEST"
printf 'Pinned %s to %s\n' "$tool" "$version"
}
main() {
local mode="update" tool version
ensure_deps
if [ "${1:-}" = "--list" ]; then
mode="list"
shift
fi
tool="${1:-}"
version="${2:-}"
if [ -z "$tool" ]; then
printf 'Usage: %s [--list] tool [version]\n' "$0" >&2
exit 1
fi
case "$mode" in
list)
list_versions "$tool"
;;
update)
if [ -z "$version" ]; then
version="$(select_version "$tool")"
fi
update_version "$tool" "$version"
;;
esac
}
main "$@"
-75
View File
@@ -1,75 +0,0 @@
#!/bin/bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
git.sh show
git.sh editor [code|nvim]
EOF
}
set_editor() {
local editor_name="${1:-code}"
case "$editor_name" in
code)
git config --global core.editor "code --wait"
printf 'Set git core.editor to: %s\n' 'code --wait'
;;
nvim)
git config --global core.editor "nvim"
printf 'Set git core.editor to: %s\n' 'nvim'
;;
*)
printf 'Unknown editor: %s\n' "$editor_name" >&2
usage >&2
exit 1
;;
esac
}
show_config() {
local name email editor
local helpers=()
local helper
name="$(git config --global --get user.name || true)"
email="$(git config --global --get user.email || true)"
editor="$(git config --global --get core.editor || true)"
while IFS= read -r helper; do
[ -n "$helper" ] && helpers+=("$helper")
done < <(git config --global --get-all credential.helper || true)
printf 'Global git config\n'
printf ' user.name %s\n' "${name:-<unset>}"
printf ' user.email %s\n' "${email:-<unset>}"
printf ' core.editor %s\n' "${editor:-<unset>}"
if [ "${#helpers[@]}" -eq 0 ]; then
printf ' credential.helper %s\n' '<unset>'
else
printf ' credential.helper %s\n' "${helpers[0]}"
if [ "${#helpers[@]}" -gt 1 ]; then
local index
for (( index=1; index<${#helpers[@]}; index++ )); do
printf ' %s\n' "${helpers[$index]}"
done
fi
fi
}
case "${1:-show}" in
show)
show_config
;;
editor)
set_editor "${2:-code}"
;;
*)
usage >&2
exit 1
;;
esac
-172
View File
@@ -1,172 +0,0 @@
#!/bin/bash
# Path: Scripts/go.sh
set -e
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
RED='\033[1;31m'
NC='\033[0m'
export GVM_ROOT="$HOME/.programming/go"
BOOTSTRAP_GO="$GVM_ROOT/bootstrap"
GO_BOOTSTRAP_VERSION="1.20.5"
OS_NAME="$(uname -s)"
ARCH="$(uname -m)"
case "$ARCH" in
x86_64)
GO_BOOTSTRAP_ARCH="amd64"
;;
aarch64|arm64)
GO_BOOTSTRAP_ARCH="arm64"
;;
*)
echo -e "${RED} ERROR:${NC} Unsupported architecture for Go bootstrap: $ARCH"
exit 1
;;
esac
case "$OS_NAME" in
Linux)
GO_BOOTSTRAP_OS="linux"
;;
Darwin)
GO_BOOTSTRAP_OS="darwin"
;;
*)
echo -e "${RED} ERROR:${NC} Unsupported operating system for Go bootstrap: $OS_NAME"
exit 1
;;
esac
GO_BOOTSTRAP_FILENAME="go${GO_BOOTSTRAP_VERSION}.${GO_BOOTSTRAP_OS}-${GO_BOOTSTRAP_ARCH}.tar.gz"
GO_BOOTSTRAP_URL="https://go.dev/dl/${GO_BOOTSTRAP_FILENAME}"
GO_RELEASE_METADATA_URL="https://go.dev/dl/?mode=json&include=all"
sha256_file() {
local file_path="$1"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$file_path" | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$file_path" | awk '{print $1}'
else
echo -e "${RED} ERROR:${NC} No SHA256 tool found (need sha256sum or shasum)."
exit 1
fi
}
get_go_release_sha256() {
python3 - "$GO_RELEASE_METADATA_URL" "$GO_BOOTSTRAP_FILENAME" <<'PY'
import json
import sys
import urllib.request
metadata_url = sys.argv[1]
target_filename = sys.argv[2]
with urllib.request.urlopen(metadata_url, timeout=30) as response:
releases = json.load(response)
for release in releases:
for file_info in release.get("files", []):
if file_info.get("filename") == target_filename:
print(file_info.get("sha256", ""))
raise SystemExit(0)
raise SystemExit(1)
PY
}
download_and_verify_go_bootstrap() {
local destination_path="$1"
local expected_sha256=""
local actual_sha256=""
local temp_tarball=""
temp_tarball="$(mktemp "${TMPDIR:-/tmp}/go-bootstrap.XXXXXX")"
cleanup_go_bootstrap_download() {
rm -f "$temp_tarball"
}
trap cleanup_go_bootstrap_download RETURN
echo -e "${BLUE} LOG:${YELLOW} Downloading pinned Bootstrap Go ${GO_BOOTSTRAP_VERSION}...${NC}"
curl -fsSL "$GO_BOOTSTRAP_URL" -o "$temp_tarball"
expected_sha256="$(get_go_release_sha256)"
if [ -z "$expected_sha256" ]; then
echo -e "${RED} ERROR:${NC} Could not find official SHA256 for ${GO_BOOTSTRAP_FILENAME}."
exit 1
fi
actual_sha256="$(sha256_file "$temp_tarball")"
if [ "$actual_sha256" != "$expected_sha256" ]; then
echo -e "${RED} ERROR:${NC} Bootstrap Go checksum mismatch for ${GO_BOOTSTRAP_FILENAME}."
echo -e "${RED} ERROR:${NC} Expected: ${expected_sha256}"
echo -e "${RED} ERROR:${NC} Actual: ${actual_sha256}"
exit 1
fi
tar -C "$destination_path" -xzf "$temp_tarball" --strip-components=1
}
echo -e "${BLUE} LOG:${YELLOW} Setting up Go and GVM in ${GVM_ROOT}...${NC}"
if [ ! -d "$GVM_ROOT/scripts" ]; then
echo -e "${BLUE} LOG:${YELLOW} Cloning GVM...${NC}"
git clone https://github.com/moovweb/gvm.git "$GVM_ROOT"
rm -rf "$GVM_ROOT/.git"
echo -e "${BLUE} LOG:${YELLOW} Configuring GVM scripts...${NC}"
cp "$GVM_ROOT/scripts/gvm-default" "$GVM_ROOT/scripts/gvm"
python3 - "$GVM_ROOT/scripts/gvm" "$GVM_ROOT" <<'PY'
from pathlib import Path
import sys
path = Path(sys.argv[1])
root = sys.argv[2]
text = path.read_text()
path.write_text(text.replace('GVM_ROOT="$HOME/.gvm"', f'GVM_ROOT="{root}"', 1))
PY
echo -e "${GREEN} SUCCESS:${NC} GVM cloned and configured."
else
echo -e "${GREEN} SKIP:${NC} GVM already installed."
fi
if [ ! -d "$BOOTSTRAP_GO" ]; then
mkdir -p "$BOOTSTRAP_GO"
download_and_verify_go_bootstrap "$BOOTSTRAP_GO"
echo -e "${GREEN} SUCCESS:${NC} Bootstrap Go installed."
else
echo -e "${GREEN} SKIP:${NC} Bootstrap Go already exists."
fi
export PATH="$BOOTSTRAP_GO/bin:$PATH"
export GOROOT_BOOTSTRAP="$BOOTSTRAP_GO"
set +e
source "$GVM_ROOT/scripts/gvm"
set -e
if ! command -v gvm &> /dev/null; then
echo -e "${RED} ERROR:${NC} GVM failed to load."
exit 1
fi
TARGET_VER="go1.24.11"
if ! gvm list | grep -q "$TARGET_VER"; then
echo -e "${BLUE} LOG:${YELLOW} Installing ${TARGET_VER}...${NC}"
gvm install "$TARGET_VER" --prefer-binary
fi
gvm use "$TARGET_VER" --default
echo -e "${GREEN} SUCCESS:${NC} Go setup completed."
-220
View File
@@ -1,220 +0,0 @@
#!/bin/bash
# Path: Scripts/r.sh
set -e
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
RED='\033[1;31m'
NC='\033[0m'
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$SCRIPT_DIR/lib/distro.sh"
PROG_DIR="$HOME/.programming"
R_ROOT="$PROG_DIR/r"
R_BIN_DIR="$R_ROOT/bin"
R_LIB_DIR="$R_ROOT/library"
mkdir -p "$R_BIN_DIR" "$R_LIB_DIR"
write_r_wrapper() {
local target_name="$1"
local command_body="$2"
cat > "$R_BIN_DIR/$target_name" <<EOF
#!/bin/bash
set -e
export R_LIBS_USER="$R_LIB_DIR"
$command_body "\$@"
EOF
chmod +x "$R_BIN_DIR/$target_name"
}
resolve_non_wrapper_command() {
local target_name="$1"
local original_path="$PATH"
local filtered_parts=()
local filtered_path=""
local resolved_path=""
local part
IFS=':' read -r -a filtered_parts <<< "$PATH"
local kept_parts=()
for part in "${filtered_parts[@]}"; do
if [ -n "$part" ] && [ "$part" != "$R_BIN_DIR" ]; then
kept_parts+=("$part")
fi
done
filtered_path=$(IFS=:; printf '%s' "${kept_parts[*]}")
resolved_path=$(PATH="$filtered_path" command -v "$target_name" 2>/dev/null || true)
PATH="$original_path"
if [ -z "$resolved_path" ]; then
echo -e "${RED} ERROR:${NC} Could not resolve system command for $target_name outside $R_BIN_DIR"
exit 1
fi
printf '%s\n' "$resolved_path"
}
echo -e "${BLUE} LOG:${YELLOW} Detecting R installation strategy for $OS...${NC}"
if is_arch_family; then
echo -e "${BLUE} LOG:${YELLOW} Arch Linux detected. Using system R (Pacman)...${NC}"
# Install R + Build Tools
install_status=0
install_packages r gcc-fortran curl tar || install_status=$?
if [ "$install_status" -eq 42 ]; then
exit 0
elif [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
echo -e "${GREEN} SUCCESS:${NC} R installed via Pacman."
R_BIN="R"
SYSTEM_R_BIN="$(resolve_non_wrapper_command R)"
SYSTEM_RSCRIPT_BIN="$(resolve_non_wrapper_command Rscript)"
write_r_wrapper "R" "exec \"$SYSTEM_R_BIN\""
write_r_wrapper "Rscript" "exec \"$SYSTEM_RSCRIPT_BIN\""
elif is_debian_family; then
echo -e "${BLUE} LOG:${YELLOW} Installing Debian build dependencies...${NC}"
install_status=0
install_packages gfortran curl tar ca-certificates || install_status=$?
if [ "$install_status" -eq 42 ]; then
exit 0
elif [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
if ! command -v rig >/dev/null 2>&1; then
echo -e "${BLUE} LOG:${YELLOW} Installing Rig for Debian/Ubuntu...${NC}"
DEB_ARCH=$(dpkg --print-architecture)
case "$DEB_ARCH" in
amd64|arm64)
;;
*)
echo -e "${RED} ERROR:${NC} Unsupported Debian architecture for Rig: $DEB_ARCH"
exit 1
;;
esac
LATEST_REL_DATA=$(curl -fsSL https://api.github.com/repos/r-lib/rig/releases/latest)
RIG_URL=$(echo "$LATEST_REL_DATA" | grep -o "https://[^\"]*r-rig_[^\"]*_${DEB_ARCH}\.deb" | head -n 1)
if [ -z "$RIG_URL" ]; then
echo -e "${RED} ERROR:${NC} Could not find Rig .deb download URL for architecture: $DEB_ARCH"
exit 1
fi
TEMP_DEB=$(mktemp --suffix=.deb)
curl -fLsS "$RIG_URL" -o "$TEMP_DEB"
sudo apt-get install -y "$TEMP_DEB"
rm -f "$TEMP_DEB"
fi
TARGET_VER="release"
if ! rig list | grep -q "release"; then
echo -e "${BLUE} LOG:${YELLOW} Installing R (${TARGET_VER})...${NC}"
sudo rig add "$TARGET_VER" --without-cran-mirror
sudo rig default "$TARGET_VER"
fi
R_BIN="R"
SYSTEM_R_BIN="$(resolve_non_wrapper_command R)"
SYSTEM_RSCRIPT_BIN="$(resolve_non_wrapper_command Rscript)"
write_r_wrapper "R" "exec \"$SYSTEM_R_BIN\""
write_r_wrapper "Rscript" "exec \"$SYSTEM_RSCRIPT_BIN\""
elif is_fedora_family; then
echo -e "${BLUE} LOG:${YELLOW} Fedora detected. Using system R...${NC}"
install_status=0
install_packages R-core R-core-devel gcc-gfortran curl tar || install_status=$?
if [ "$install_status" -eq 42 ]; then
exit 0
elif [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
R_BIN="R"
SYSTEM_R_BIN="$(resolve_non_wrapper_command R)"
SYSTEM_RSCRIPT_BIN="$(resolve_non_wrapper_command Rscript)"
write_r_wrapper "R" "exec \"$SYSTEM_R_BIN\""
write_r_wrapper "Rscript" "exec \"$SYSTEM_RSCRIPT_BIN\""
elif is_macos; then
echo -e "${BLUE} LOG:${YELLOW} macOS detected. Using Homebrew R...${NC}"
install_status=0
install_packages r || install_status=$?
if [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
R_BIN="R"
SYSTEM_R_BIN="$(resolve_non_wrapper_command R)"
SYSTEM_RSCRIPT_BIN="$(resolve_non_wrapper_command Rscript)"
write_r_wrapper "R" "exec \"$SYSTEM_R_BIN\""
write_r_wrapper "Rscript" "exec \"$SYSTEM_RSCRIPT_BIN\""
else
echo -e "${RED} ERROR:${NC} Unsupported OS: $OS"
exit 1
fi
export R_LIBS_USER="$R_LIB_DIR"
if compgen -G "$R_LIB_DIR/00LOCK*" >/dev/null 2>&1; then
echo -e "${RED} ERROR:${NC} Detected existing R package lock(s) in $R_LIB_DIR"
find "$R_LIB_DIR" -maxdepth 1 -type d -name '00LOCK*' -print | sed 's/^/ - /'
echo -e "${YELLOW} LOG:${NC} This usually means a previous R package install was interrupted."
echo -e "${YELLOW} LOG:${NC} Remove the stale lock directory/directories above, then rerun the command."
exit 1
fi
echo -e "${BLUE} LOG:${YELLOW} Installing 'renv' package in the user R library...${NC}"
echo -e "${BLUE} LOG:${YELLOW} R user library: $R_LIB_DIR${NC}"
echo -e "${BLUE} LOG:${YELLOW} R executable: $R_BIN_DIR/Rscript${NC}"
"$R_BIN_DIR/Rscript" --vanilla - <<'EOF'
user_lib <- path.expand(Sys.getenv("R_LIBS_USER", unset = "~/.programming/r/library"))
dir.create(user_lib, recursive = TRUE, showWarnings = FALSE)
.libPaths(c(user_lib, .libPaths()))
cat("R user library:", user_lib, "\n")
cat("R library paths:", paste(.libPaths(), collapse = " | "), "\n")
if (requireNamespace("renv", quietly = TRUE)) {
cat("renv is already installed.\n")
quit(save = "no", status = 0)
}
cat("Installing renv from CRAN...\n")
install.packages(
"renv",
lib = user_lib,
repos = "https://cloud.r-project.org",
quiet = FALSE
)
cat("Verifying renv installation...\n")
if (!requireNamespace("renv", quietly = TRUE)) {
stop("renv install finished but the package is still unavailable.")
}
cat("renv installation verified.\n")
EOF
echo -e "${GREEN} SUCCESS:${NC} R setup completed."
-165
View File
@@ -1,165 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
REBOOT_MARKER="$REPO_ROOT/.setup-reboot-required"
REPO_SECRET_FILE="$REPO_ROOT/Zsh/.zsh_secrets"
HOME_SECRET_FILE="$HOME/.zsh_secrets"
BACKUP_ROOT="$HOME/.dotzsh-pre-stow-backup"
handle_reboot_marker() {
if [ -f "$REBOOT_MARKER" ]; then
rm -f "$REBOOT_MARKER"
echo "Package layering finished. Reboot, then rerun the same command."
exit 0
fi
}
sync_repo_managed_secret_file() {
local home_secret_resolved repo_secret_resolved
mkdir -p "$(dirname "$REPO_SECRET_FILE")"
if [ -L "$HOME_SECRET_FILE" ]; then
home_secret_resolved="$(python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$HOME_SECRET_FILE")"
repo_secret_resolved="$(python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$REPO_SECRET_FILE")"
if [ "$home_secret_resolved" = "$repo_secret_resolved" ]; then
return 0
fi
if [ -f "$home_secret_resolved" ]; then
if [ ! -f "$REPO_SECRET_FILE" ] || [ ! -s "$REPO_SECRET_FILE" ]; then
cp "$home_secret_resolved" "$REPO_SECRET_FILE"
rm -f "$HOME_SECRET_FILE"
return 0
fi
if cmp -s "$home_secret_resolved" "$REPO_SECRET_FILE"; then
rm -f "$HOME_SECRET_FILE"
return 0
fi
fi
echo "Conflict: $HOME_SECRET_FILE points to a different secret file than $REPO_SECRET_FILE."
echo "Please merge them manually, then rerun the command."
exit 1
fi
if [ -f "$HOME_SECRET_FILE" ]; then
if [ ! -f "$REPO_SECRET_FILE" ] || [ ! -s "$REPO_SECRET_FILE" ]; then
mv "$HOME_SECRET_FILE" "$REPO_SECRET_FILE"
return 0
fi
if cmp -s "$HOME_SECRET_FILE" "$REPO_SECRET_FILE"; then
rm -f "$HOME_SECRET_FILE"
return 0
fi
echo "Conflict: both $HOME_SECRET_FILE and $REPO_SECRET_FILE exist with different contents."
echo "Please merge them manually, then rerun the command."
exit 1
fi
touch "$REPO_SECRET_FILE"
}
backup_conflicting_home_files() {
local backup_dir backed_up_any=0 source_file filename target_file target_resolved source_resolved
backup_dir="$BACKUP_ROOT/$(date +%Y%m%d-%H%M%S)"
while IFS= read -r source_file; do
filename="$(basename "$source_file")"
target_file="$HOME/$filename"
if [ -L "$target_file" ]; then
target_resolved="$(python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$target_file")"
source_resolved="$(python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$source_file")"
if [ "$target_resolved" = "$source_resolved" ]; then
continue
fi
mkdir -p "$backup_dir"
mv "$target_file" "$backup_dir/$filename"
echo "Backed up existing linked $target_file -> $backup_dir/$filename"
backed_up_any=1
elif [ -e "$target_file" ]; then
mkdir -p "$backup_dir"
mv "$target_file" "$backup_dir/$filename"
echo "Backed up existing $target_file -> $backup_dir/$filename"
backed_up_any=1
fi
done < <(find "$REPO_ROOT/Zsh" -mindepth 1 -maxdepth 1 -type f -name '.*' ! -name '.zsh_secrets')
if [ "$backed_up_any" -eq 1 ]; then
echo "Existing unmanaged dotfiles were backed up before stowing."
fi
}
stow_dotfiles() {
echo "Preparing repo-managed secrets for stow..."
sync_repo_managed_secret_file
echo "Checking for conflicting home dotfiles before stow..."
backup_conflicting_home_files
echo "Cleaning existing Zsh stow links..."
stow --dir="$REPO_ROOT" -D Zsh --target="$HOME" 2>/dev/null || true
echo "Stowing Zsh dotfiles..."
stow --dir="$REPO_ROOT" Zsh --target="$HOME"
echo "Zsh dotfiles stowed."
}
bootstrap_only() {
rm -f "$REBOOT_MARKER"
bash "$SCRIPT_DIR/base.sh"
handle_reboot_marker
stow_dotfiles
echo "Bootstrap setup completed."
}
full_setup() {
rm -f "$REBOOT_MARKER"
bash "$SCRIPT_DIR/base.sh"
handle_reboot_marker
bash "$SCRIPT_DIR/node.sh"
bash "$SCRIPT_DIR/go.sh"
bash "$SCRIPT_DIR/rust.sh"
bash "$SCRIPT_DIR/python.sh"
handle_reboot_marker
bash "$SCRIPT_DIR/r.sh"
handle_reboot_marker
stow_dotfiles
echo "Full setup completed."
}
case "${1:-}" in
--bootstrap-only)
bootstrap_only
;;
--stow-only)
stow_dotfiles
;;
"")
full_setup
;;
*)
echo "Usage: $0 [--bootstrap-only|--stow-only]"
exit 1
;;
esac
-185
View File
@@ -1,185 +0,0 @@
# Completion behavior and wrapper bindings
autoload -Uz add-zsh-hook
if ! whence -w compdef >/dev/null 2>&1; then
autoload -Uz compinit
if [ -n "${ZSH_COMPDUMP:-}" ]; then
mkdir -p "${ZSH_COMPDUMP:h}"
if [ -f "$ZSH_COMPDUMP" ]; then
compinit -C -d "$ZSH_COMPDUMP" >/dev/null 2>&1
else
compinit -i -d "$ZSH_COMPDUMP" >/dev/null 2>&1
fi
else
compinit -i >/dev/null 2>&1
fi
fi
zmodload zsh/complist 2>/dev/null || true
zstyle ':completion:*' menu select
zstyle ':completion:*' verbose yes
zstyle ':completion:*' list-grouped yes
zstyle ':completion:*' use-cache yes
zstyle ':completion:*:descriptions' format '%F{240}%B%d%b%f'
typeset -ga DOTZSH_USED_COMMANDS
typeset -ga DOTZSH_ALL_COMMANDS
typeset -gA DOTZSH_USED_COMMAND_MAP
typeset -gA DOTZSH_ALL_COMMAND_MAP
typeset -g DOTZSH_USED_COMMANDS_INITIALIZED=0
typeset -g DOTZSH_ALL_COMMANDS_INITIALIZED=0
typeset -g DOTZSH_USED_COMMAND_LIMIT=120
_dotzsh_parse_history_line() {
local raw_line="$1"
local parsed_line="$raw_line"
case "$parsed_line" in
': '*';'*)
parsed_line="${parsed_line#*;}"
;;
esac
print -r -- "$parsed_line"
}
_dotzsh_extract_command_name() {
local line="$(_dotzsh_parse_history_line "$1")"
local command_name=""
local -a parts=()
local index=1
[ -n "$line" ] || return 1
[[ "$line" == '#'* ]] && return 1
parts=(${(z)line})
[ "${#parts[@]}" -gt 0 ] || return 1
while [ "$index" -le "${#parts[@]}" ]; do
command_name="${parts[$index]}"
case "$command_name" in
sudo|time|command|builtin|noglob|env)
((index++))
continue
;;
*=*)
((index++))
continue
;;
esac
break
done
[ -n "$command_name" ] || return 1
[[ "$command_name" == _* ]] && return 1
[[ "$command_name" =~ '^[A-Za-z0-9][A-Za-z0-9+._-]*$' ]] || return 1
print -r -- "$command_name"
}
_dotzsh_remember_command() {
local command_name="$1"
[ -n "$command_name" ] || return 0
DOTZSH_USED_COMMANDS=(${DOTZSH_USED_COMMANDS:#$command_name})
DOTZSH_USED_COMMANDS=("$command_name" "${DOTZSH_USED_COMMANDS[@]}")
DOTZSH_USED_COMMAND_MAP[$command_name]=1
}
_dotzsh_initialize_used_commands() {
local history_file="${HISTFILE:-$HOME/.zsh_history}"
local line=""
local command_name=""
[ "$DOTZSH_USED_COMMANDS_INITIALIZED" -eq 0 ] || return 0
DOTZSH_USED_COMMANDS=()
DOTZSH_USED_COMMAND_MAP=()
while IFS= read -r line; do
command_name="$(_dotzsh_extract_command_name "$line")" || continue
[[ -n ${DOTZSH_USED_COMMAND_MAP[$command_name]:-} ]] && continue
DOTZSH_USED_COMMANDS+=("$command_name")
DOTZSH_USED_COMMAND_MAP[$command_name]=1
[ "${#DOTZSH_USED_COMMANDS[@]}" -lt "$DOTZSH_USED_COMMAND_LIMIT" ] || break
done < <(
if [ -r "$history_file" ]; then
tac "$history_file" 2>/dev/null || tail -r "$history_file" 2>/dev/null || cat "$history_file"
else
fc -lnr 1 2>/dev/null
fi
)
DOTZSH_USED_COMMANDS_INITIALIZED=1
}
_dotzsh_initialize_all_commands() {
local command_name=""
[ "$DOTZSH_ALL_COMMANDS_INITIALIZED" -eq 0 ] || return 0
DOTZSH_ALL_COMMANDS=()
DOTZSH_ALL_COMMAND_MAP=()
for command_name in ${(k)aliases} ${(k)builtins} ${(k)commands} ${(k)functions}; do
[ -n "$command_name" ] || continue
[[ "$command_name" == _* ]] && continue
[[ -n ${DOTZSH_ALL_COMMAND_MAP[$command_name]:-} ]] && continue
DOTZSH_ALL_COMMANDS+=("$command_name")
DOTZSH_ALL_COMMAND_MAP[$command_name]=1
done
DOTZSH_ALL_COMMANDS_INITIALIZED=1
}
_dotzsh_record_command() {
local command_name=""
command_name="$(_dotzsh_extract_command_name "$1")" || return 0
_dotzsh_remember_command "$command_name"
}
_dotzsh_command_menu() {
local command_name=""
local -a other_commands=()
local -a filtered_used_commands=()
local -a used_display=()
local current_prefix="$PREFIX"
_dotzsh_initialize_used_commands
_dotzsh_initialize_all_commands
for command_name in "${DOTZSH_USED_COMMANDS[@]}"; do
[[ -n ${DOTZSH_ALL_COMMAND_MAP[$command_name]:-} ]] || continue
[ -z "$current_prefix" ] || [[ "$command_name" == ${~current_prefix}* ]] || continue
filtered_used_commands+=("$command_name")
used_display+=("* $command_name")
done
for command_name in "${DOTZSH_ALL_COMMANDS[@]}"; do
[[ -n ${DOTZSH_USED_COMMAND_MAP[$command_name]:-} ]] && continue
[ -z "$current_prefix" ] || [[ "$command_name" == ${~current_prefix}* ]] || continue
other_commands+=("$command_name")
done
[ "${#filtered_used_commands[@]}" -eq 0 ] || compadd -Q -U -o nosort -d used_display -- "${filtered_used_commands[@]}"
[ "${#other_commands[@]}" -eq 0 ] || compadd -Q -U -o nosort -- "${other_commands[@]}"
[ "${#filtered_used_commands[@]}" -gt 0 ] || [ "${#other_commands[@]}" -gt 0 ]
}
compdef _dotzsh_command_menu -command-
add-zsh-hook preexec _dotzsh_record_command
autoload -Uz _gvm _conda _go _node _npm _npx 2>/dev/null
compdef _gvm gvm
compdef _go go gofmt
compdef _conda conda
compdef _node node
compdef _npm npm
compdef _npx npx
-285
View File
@@ -1,285 +0,0 @@
# Prompt styling and context
autoload -Uz add-zsh-hook vcs_info
zstyle ':vcs_info:*' enable git
zstyle ':vcs_info:git:*' formats ' %b'
typeset -g PROMPT_SHOW_SPACER=0
typeset -gA PROMPT_PROJECT_MATCH_CACHE
typeset -gA PROMPT_VERSION_CACHE
typeset -gA PROMPT_SEPARATOR_CACHE
typeset -g PROMPT_FIRST_RENDER=1
typeset -g PROMPT_PROJECT_ROOT_CACHE_PWD=''
typeset -g PROMPT_PROJECT_ROOT_CACHE=''
prompt_preexec() {
case "$1" in
clear|clear\ *|reset|reset\ *)
PROMPT_SHOW_SPACER=0
;;
*)
PROMPT_SHOW_SPACER=1
;;
esac
}
get_command_version() {
local command_name="$1"
local binary_path=""
local raw_version=""
if [[ -n ${PROMPT_VERSION_CACHE[$command_name]+x} ]]; then
print -r -- "${PROMPT_VERSION_CACHE[$command_name]}"
return 0
fi
binary_path="$(whence -p "$command_name" 2>/dev/null || true)"
if ! [ -n "$binary_path" ] || ! [ -x "$binary_path" ]; then
PROMPT_VERSION_CACHE[$command_name]=""
return 0
fi
case "$command_name" in
python)
raw_version="$($binary_path --version 2>&1 || true)"
raw_version="${raw_version#Python }"
;;
node)
raw_version="$($binary_path -v 2>/dev/null || true)"
raw_version="${raw_version#v}"
;;
go)
raw_version="$($binary_path version 2>/dev/null || true)"
raw_version="${raw_version#go version go}"
raw_version="${raw_version%% *}"
;;
rustc)
raw_version="$($binary_path -V 2>/dev/null || true)"
raw_version="${raw_version#rustc }"
raw_version="${raw_version%% *}"
;;
*)
PROMPT_VERSION_CACHE[$command_name]=""
return 0
;;
esac
[ -n "$raw_version" ] || {
PROMPT_VERSION_CACHE[$command_name]=""
return 0
}
PROMPT_VERSION_CACHE[$command_name]="$raw_version"
print -r -- "$raw_version"
}
get_project_root() {
if [ "$PROMPT_PROJECT_ROOT_CACHE_PWD" = "$PWD" ] && [ -n "$PROMPT_PROJECT_ROOT_CACHE" ]; then
print -r -- "$PROMPT_PROJECT_ROOT_CACHE"
return 0
fi
PROMPT_PROJECT_ROOT_CACHE_PWD="$PWD"
PROMPT_PROJECT_ROOT_CACHE="$(command git rev-parse --show-toplevel 2>/dev/null || print -r -- "$PWD")"
print -r -- "$PROMPT_PROJECT_ROOT_CACHE"
}
project_has_pattern() {
local project_root=""
local cache_key=""
local pattern
local matches=()
project_root="$(get_project_root)"
cache_key="${project_root}::${(j:|:)@}"
if [[ -n ${PROMPT_PROJECT_MATCH_CACHE[$cache_key]+x} ]]; then
[[ ${PROMPT_PROJECT_MATCH_CACHE[$cache_key]} == 1 ]]
return
fi
for pattern in "$@"; do
matches=("${project_root}"/${~pattern}(N))
if [ "${#matches[@]}" -gt 0 ]; then
PROMPT_PROJECT_MATCH_CACHE[$cache_key]=1
return 0
fi
done
PROMPT_PROJECT_MATCH_CACHE[$cache_key]=0
return 1
}
build_git_status_segment() {
local git_status_output=""
local line=""
local staged=0
local unstaged=0
local untracked=0
local status_parts=()
command git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0
git_status_output="$(command git status --porcelain 2>/dev/null || true)"
if [ -z "$git_status_output" ]; then
print -r -- "%F{green}✓ clean%f"
return 0
fi
while IFS= read -r line; do
[ -n "$line" ] || continue
if [ "${line:0:2}" = "??" ]; then
((untracked++))
continue
fi
[ "${line:0:1}" = " " ] || ((staged++))
[ "${line:1:1}" = " " ] || ((unstaged++))
done <<< "$git_status_output"
[ "$staged" -eq 0 ] || status_parts+=("+$staged")
[ "$unstaged" -eq 0 ] || status_parts+=("~$unstaged")
[ "$untracked" -eq 0 ] || status_parts+=("?$untracked")
if [ "${#status_parts[@]}" -eq 0 ]; then
print -r -- "%F{green}✓ clean%f"
else
print -r -- "%F{yellow}${(j: :)status_parts}%f"
fi
}
build_prompt_separator_line() {
local last_status="$1"
local line_width="${COLUMNS:-80}"
local cache_key="${line_width}:${last_status}"
local separator_line=""
local color_prefix=""
local color_suffix=""
local i=0
if [[ -n ${PROMPT_SEPARATOR_CACHE[$cache_key]+x} ]]; then
print -r -- "${PROMPT_SEPARATOR_CACHE[$cache_key]}"
return 0
fi
if [ "$line_width" -lt 1 ]; then
line_width=1
fi
if [ "$last_status" -ne 0 ]; then
color_prefix="%F{red}"
color_suffix="%f"
fi
while [ "$i" -lt "$line_width" ]; do
separator_line+="─"
((i++))
done
PROMPT_SEPARATOR_CACHE[$cache_key]="${color_prefix}${separator_line}${color_suffix}"
print -r -- "${PROMPT_SEPARATOR_CACHE[$cache_key]}"
}
build_prompt() {
local last_status=$?
local git_segment=""
local git_status_segment=""
local env_segment=""
local project_symbols=()
local tool_versions=()
local python_version=""
local node_version=""
local go_version=""
local rust_version=""
local context_line=""
local newline=$'\n'
local prompt_spacer=""
local separator="::"
if [ "$last_status" -ne 0 ]; then
separator="%F{red}::%f"
fi
if [ "$PROMPT_FIRST_RENDER" -eq 1 ]; then
PROMPT_FIRST_RENDER=0
PROMPT="%B%~%b ${separator} "
RPROMPT="%n@%m"
return 0
fi
vcs_info
git_segment="${vcs_info_msg_0_}"
git_status_segment="$(build_git_status_segment)"
if [ -n "${CONDA_DEFAULT_ENV:-}" ]; then
env_segment=" ${CONDA_DEFAULT_ENV}"
elif [ -n "${DIRENV_DIR:-}" ] || [ -n "${DIRENV_FILE:-}" ]; then
env_segment=" direnv"
fi
if project_has_pattern '**/docker-compose*.yml' '**/docker-compose*.yaml' '**/compose*.yml' '**/compose*.yaml'; then
project_symbols+=("%F{blue}%f")
fi
if project_has_pattern '**/Makefile' '**/makefile' '**/GNUmakefile' '**/Justfile' '**/justfile' '**/.justfile'; then
project_symbols+=("%F{yellow}%f")
fi
if project_has_pattern '**/pyproject.toml' '**/requirements.txt' '**/.python-version' '**/setup.py'; then
python_version="$(get_command_version python)"
[ -z "$python_version" ] || tool_versions+=("%F{magenta} ${python_version}%f")
fi
if project_has_pattern '**/package.json' '**/.nvmrc' '**/pnpm-workspace.yaml' '**/yarn.lock' '**/package-lock.json' '**/bun.lock' '**/bun.lockb'; then
node_version="$(get_command_version node)"
[ -z "$node_version" ] || tool_versions+=("%F{green} ${node_version}%f")
fi
if project_has_pattern '**/go.mod' '**/go.work'; then
go_version="$(get_command_version go)"
[ -z "$go_version" ] || tool_versions+=("%F{cyan} ${go_version}%f")
fi
if project_has_pattern '**/Cargo.toml' '**/Cargo.lock' '**/rust-toolchain.toml' '**/rust-toolchain'; then
rust_version="$(get_command_version rustc)"
[ -z "$rust_version" ] || tool_versions+=("%F{yellow} ${rust_version}%f")
fi
if [ -n "$git_segment" ]; then
context_line="%F{cyan}${git_segment}%f"
[ -z "$git_status_segment" ] || context_line+=" ${git_status_segment}"
fi
if [ -n "$env_segment" ]; then
[ -z "$context_line" ] || context_line+=" %F{240}·%f "
context_line+="%F{green}${env_segment}%f"
fi
if [ "${#project_symbols[@]}" -gt 0 ]; then
[ -z "$context_line" ] || context_line+=" %F{240}·%f "
context_line+="${(j: :)project_symbols}"
fi
if [ "${#tool_versions[@]}" -gt 0 ]; then
[ -z "$context_line" ] || context_line+=" %F{240}·%f "
context_line+="${(j: :)tool_versions}"
fi
if [ -n "$context_line" ]; then
context_line+="${newline}"
fi
if [ "$PROMPT_SHOW_SPACER" -eq 1 ]; then
prompt_spacer="$(build_prompt_separator_line "$last_status")${newline}"
fi
PROMPT="${prompt_spacer}${context_line}%B%~%b ${separator} "
RPROMPT="%n@%m"
}
add-zsh-hook preexec prompt_preexec
add-zsh-hook precmd build_prompt
build_prompt
-26
View File
@@ -1,26 +0,0 @@
# ~/.zsh_secrets
#
# Copy this file to ~/.zsh_secrets and fill in real values locally.
# Do not commit real secrets.
# Core local values
export AZURE_URL=""
export VM_SUDO_PASS=""
# API keys and tokens
export TAOBAO_API_KEY=""
export AZURE_OPENAI_API_KEY=""
export OUTLINE_MOKU_TOKEN=""
export DOKPLOY_API_KEY=""
export N8N_AUTH=""
export CONTEXT7_API_KEY=""
export GITEA_ACCESS_TOKEN=""
export NETDATA_TOKEN=""
export OBSIDIAN_API_KEY=""
export GITHUB_TOKEN=""
export OPEN_ROUTER_API_KEY=""
# Optional overrides
# export VAULT_ADDR="http://192.168.30.53:8200"
# export OPENCHAMBER_HOST="0.0.0.0"
# export PLANNOTATOR_PORT="9999"
-33
View File
@@ -1,33 +0,0 @@
# ENV File
# Load machine-local secrets before defining any derived environment variables.
[ -f "$HOME/.zsh_secrets" ] && source "$HOME/.zsh_secrets"
export GOGC=500
export R_ROOT="$HOME/.programming/r"
export R_LIBS_USER="$R_ROOT/library"
# CodeGraphContext defaults
export CGC_RUNTIME_DB_TYPE=kuzudb
export KUZUDB_PATH="$HOME/.codegraphcontext/kuzudb"
if [ -d "$HOME/.local/share/cgc-venv/bin" ]; then
export PATH="$HOME/.local/share/cgc-venv/bin:$PATH"
fi
export PATH="$HOME/.opencode/bin:$PATH"
export OPENCHAMBER_HOST="${OPENCHAMBER_HOST:-0.0.0.0}"
export VAULT_ADDR="${VAULT_ADDR:-http://192.168.30.53:8200}"
export PLANNOTATOR_REMOTE="${PLANNOTATOR_REMOTE:-1}"
export PLANNOTATOR_PORT="${PLANNOTATOR_PORT:-9999}"
# Derived environment variables that may depend on machine-local secrets.
[ -n "${AZURE_URL:-}" ] && export AZURE_COGNITIVE_SERVICES_RESOURCE_NAME="$AZURE_URL"
[ -n "${AZURE_URL:-}" ] && export AZURE_RESOURCE_NAME="$AZURE_URL"
[ -n "${VM_SUDO_PASS:-}" ] && export OPENCHAMBER_UI_PASSWORD="$VM_SUDO_PASS"
[ -n "${AZURE_OPENAI_API_KEY:-}" ] && export FOUNDRY_ANTHROPIC_API_KEY="$AZURE_OPENAI_API_KEY"
[ -n "${AZURE_OPENAI_API_KEY:-}" ] && export CHROMA_OPENAI_API_KEY="$AZURE_OPENAI_API_KEY"
[ -n "${OUTLINE_MOKU_TOKEN:-}" ] && export OUTLINE_MOKU_AUTHORIZATION="Bearer $OUTLINE_MOKU_TOKEN"
# bun completions
[ -s "/home/mangopig/.programming/node/bun/_bun" ] && source "/home/mangopig/.programming/node/bun/_bun"
-256
View File
@@ -1,256 +0,0 @@
# .zshrc
# Zsh Configuration
export ZSH="$HOME/.oh-my-zsh"
export ZSH_CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/oh-my-zsh"
export ZSH_COMPDUMP="${ZSH_CACHE_DIR}/.zcompdump-${HOST}-${ZSH_VERSION}"
mkdir -p "$ZSH_CACHE_DIR"
mkdir -p "$ZSH_CACHE_DIR/completions"
export DOTZSH_GIT_PLUGIN="$ZSH/plugins/git/git.plugin.zsh"
export DOTZSH_SUDO_PLUGIN="$ZSH/plugins/sudo/sudo.plugin.zsh"
export DOTZSH_RCLONE_PLUGIN="$ZSH/plugins/rclone/rclone.plugin.zsh"
export DOTZSH_AUTOSUGGESTIONS_PLUGIN="$ZSH/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh"
export DOTZSH_SYNTAX_HIGHLIGHTING_PLUGIN="$ZSH/custom/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh"
export DOTZSH_HISTORY_SUBSTRING_SEARCH_PLUGIN="$ZSH/plugins/history-substring-search/history-substring-search.plugin.zsh"
# Programming Languages Root
export PROG_DIR="$HOME/.programming"
# History
export HISTFILE="$HOME/.zsh_history"
export HISTSIZE=10000
export SAVEHIST=10000
setopt APPEND_HISTORY
setopt SHARE_HISTORY
setopt EXTENDED_HISTORY
setopt HIST_EXPIRE_DUPS_FIRST
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_IGNORE_SPACE
setopt HIST_FIND_NO_DUPS
setopt HIST_REDUCE_BLANKS
setopt HIST_SAVE_NO_DUPS
typeset -gA DOTZSH_MISSING_TOOL_WARNED
dotzsh_warn_missing_tool_once() {
local tool_key="$1"
local tool_label="$2"
local setup_hint="$3"
if [[ -n "${DOTZSH_MISSING_TOOL_WARNED[$tool_key]:-}" ]]; then
return 0
fi
DOTZSH_MISSING_TOOL_WARNED[$tool_key]=1
print -u2 -- "$tool_label is not installed on this system."
print -u2 -- "Run: $setup_hint"
print -u2 -- "Or: just setup all"
}
# Go and GVM (Black Box)
export GOPATH="$PROG_DIR/go"
export GVM_ROOT="$GOPATH"
gvm_load() {
local requested_command="$1"
unset -f gvm go gofmt
if [[ ! -s "$GVM_ROOT/scripts/gvm" ]]; then
dotzsh_warn_missing_tool_once "go" "Go / GVM" "just lang go"
return 127
fi
source "$GVM_ROOT/scripts/gvm"
if ! command -v "$requested_command" >/dev/null 2>&1; then
dotzsh_warn_missing_tool_once "go" "Go / GVM" "just lang go"
return 127
fi
"$@"
}
gvm() { gvm_load gvm "$@"; }
go() { gvm_load go "$@"; }
gofmt() { gvm_load gofmt "$@"; }
# Node and NVM (Lazy Load)
export NVM_DIR="$PROG_DIR/node"
nvm_load() {
local requested_command="$1"
unset -f nvm node npm npx
if [[ ! -s "$NVM_DIR/nvm.sh" ]]; then
dotzsh_warn_missing_tool_once "node" "Node / NVM" "just lang node"
return 127
fi
. "$NVM_DIR/nvm.sh"
if ! command -v "$requested_command" >/dev/null 2>&1; then
dotzsh_warn_missing_tool_once "node" "Node / NVM" "just lang node"
return 127
fi
"$@"
}
nvm() { nvm_load "nvm" "$@"; }
node() { nvm_load "node" "$@"; }
npm() { nvm_load "npm" "$@"; }
npx() { nvm_load "npx" "$@"; }
# Rust and Cargo
export RUSTUP_HOME="$PROG_DIR/rust/multirust"
export CARGO_HOME="$PROG_DIR/rust/cargo"
# Python (Pyenv + Miniconda)
export PYENV_ROOT="$PROG_DIR/python/pyenv"
export PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH"
export CONDA_ROOT="$PYENV_ROOT/versions/miniforge3-latest"
pyenv_load() {
unset -f pyenv
if ! command -v pyenv >/dev/null 2>&1; then
dotzsh_warn_missing_tool_once "python" "Pyenv / Python" "just lang python"
return 127
fi
eval "$(command pyenv init -)"
if ! command -v pyenv >/dev/null 2>&1; then
dotzsh_warn_missing_tool_once "python" "Pyenv / Python" "just lang python"
return 127
fi
pyenv "$@"
}
pyenv() { pyenv_load "$@"; }
conda_load() {
local requested_command="$1"
unset -f conda mamba
if [[ ! -f "$CONDA_ROOT/etc/profile.d/conda.sh" ]]; then
dotzsh_warn_missing_tool_once "python" "Conda / Mamba" "just lang python"
return 127
fi
source "$CONDA_ROOT/etc/profile.d/conda.sh"
if ! command -v "$requested_command" >/dev/null 2>&1; then
dotzsh_warn_missing_tool_once "python" "Conda / Mamba" "just lang python"
return 127
fi
"$@"
}
conda() { conda_load conda "$@"; }
mamba() { conda_load mamba "$@"; }
# R and Rig
export RIG_HOME="$PROG_DIR/r"
if [ -d "$RIG_HOME/bin" ]; then
export PATH="$RIG_HOME/bin:$PATH"
fi
if [ -n "${WSL_DISTRO_NAME:-}" ] || [ -n "${WSL_INTEROP:-}" ]; then
typeset -gr DOTZSH_IS_WSL=1
elif [ -r /proc/sys/kernel/osrelease ] && grep -qiE '(microsoft|wsl)' /proc/sys/kernel/osrelease 2>/dev/null; then
typeset -gr DOTZSH_IS_WSL=1
else
typeset -gr DOTZSH_IS_WSL=0
fi
# Zoxide
if command -v zoxide >/dev/null 2>&1; then
eval "$(zoxide init --cmd cd zsh)"
fi
# Source Aliases & Secrets
[ -f ~/.zsh_aliases ] && source ~/.zsh_aliases
[ -f ~/.zsh_secrets ] && source ~/.zsh_secrets
# Add to PATH
export PATH="$HOME/.local/bin:$CARGO_HOME/bin:$GOPATH/bin:$PATH"
# opencode
if [ "$DOTZSH_IS_WSL" -eq 1 ] && command -v openchamber >/dev/null 2>&1 && ! pgrep -f "openchamber.*7891" >/dev/null 2>&1; then
openchamber --port 7891 >/dev/null 2>&1 &!
fi
# direnv
if command -v direnv >/dev/null 2>&1; then
eval "$(direnv hook zsh)"
fi
# Completion Styling
[ -f ~/.zsh_completion ] && source ~/.zsh_completion
# Plugin Scripts (lighter than loading full Oh My Zsh)
[ -f "$DOTZSH_GIT_PLUGIN" ] && source "$DOTZSH_GIT_PLUGIN"
[ -f "$DOTZSH_SUDO_PLUGIN" ] && source "$DOTZSH_SUDO_PLUGIN"
[ -f "$DOTZSH_RCLONE_PLUGIN" ] && source "$DOTZSH_RCLONE_PLUGIN"
# Prompt Styling
[ -f ~/.zsh_prompt ] && source ~/.zsh_prompt
# Interactive Enhancements
[ -f "$DOTZSH_HISTORY_SUBSTRING_SEARCH_PLUGIN" ] && source "$DOTZSH_HISTORY_SUBSTRING_SEARCH_PLUGIN"
[ -f "$DOTZSH_AUTOSUGGESTIONS_PLUGIN" ] && source "$DOTZSH_AUTOSUGGESTIONS_PLUGIN"
[ -f "$DOTZSH_SYNTAX_HIGHLIGHTING_PLUGIN" ] && source "$DOTZSH_SYNTAX_HIGHLIGHTING_PLUGIN"
export HISTORY_SUBSTRING_SEARCH_PREFIXED=1
zmodload zsh/terminfo 2>/dev/null || true
dotzsh_bind_if_present() {
local sequence="$1"
local widget="$2"
[ -n "$sequence" ] || return 0
bindkey "$sequence" "$widget"
}
# History keybindings
bindkey '^[[A' history-substring-search-up
bindkey '^[OA' history-substring-search-up
bindkey '^[[B' history-substring-search-down
bindkey '^[OB' history-substring-search-down
# Word movement keybindings
dotzsh_bind_if_present "${terminfo[kLFT5]:-}" backward-word
dotzsh_bind_if_present "${terminfo[kRIT5]:-}" forward-word
bindkey '^[[1;5D' backward-word
bindkey '^[[1;5C' forward-word
bindkey '^[[5D' backward-word
bindkey '^[[5C' forward-word
# bun
export BUN_INSTALL="$PROG_DIR/node/bun"
case ":$PATH:" in
*":$BUN_INSTALL/bin:"*) ;;
*) export PATH="$BUN_INSTALL/bin:$PATH" ;;
esac
# pnpm
export PNPM_HOME="$PROG_DIR/node/pnpm"
case ":$PATH:" in
*":$PNPM_HOME:"*) ;;
*) export PATH="$PNPM_HOME:$PATH" ;;
esac
# pnpm end
# Allow Comments
setopt INTERACTIVE_COMMENTS
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
# Path: scripts/azure.sh
echo "Checking disks..."
lsblk | grep nvme
sudo mkfs.xfs -f /dev/nvme0n1
sudo mkdir -p /mnt/nvme
sudo mount /dev/nvme0n1 /mnt/nvme
sudo chown -R $USER:$USER /mnt/nvme
echo "NVMe mounted at /mnt/nvme"
df -h /mnt/nvme
# rclone sync -P --transfers=16 hetzner-box:models/Qwen3-30B-A3B-Instruct-2507 /mnt/nvme/Qwen3-30B-A3B-Instruct-2507
# rclone sync -P --transfers=16 /mnt/nvme/Qwen3-70B-Instruct-2024-12-04 hetzner-box:models/Qwen3-70B-Instruct-2024-12-04
+269
View File
@@ -0,0 +1,269 @@
#!/bin/bash
# Path: scripts/base.sh
set -e
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
RED='\033[1;31m'
NC='\033[0m'
DOTFILES_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REPO_ROOT="$(dirname "$DOTFILES_DIR")"
source "$DOTFILES_DIR/lib/distro.sh"
echo -e "${BLUE} LOG:${YELLOW} Initializing Base System Layer...${NC}"
# Confirm Architecture
ARCH=$(uname -m)
if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ]; then
echo -e "${RED} ERROR: Unsupported architecture: $ARCH${NC}"
exit 1
fi
# Package Installation
PACKAGES=(
curl wget git sudo
zsh tmux
unzip tar gzip
build-essential
openssl
python bison mercurial
ripgrep fd bat fzf jq
btop httpie gnupg
zoxide stow direnv
bind nmap socat tcpdump net-tools
strace gdb hexyl
ninja-build libcurl4-openssl-dev
just
)
FINAL_PACKAGES=()
for pkg in "${PACKAGES[@]}"; do
case "$pkg" in
"build-essential")
if is_arch_family; then
FINAL_PACKAGES+=(base-devel)
elif is_debian_family; then
FINAL_PACKAGES+=(build-essential)
elif is_fedora_family; then
FINAL_PACKAGES+=(gcc gcc-c++ make patch)
fi
continue
;;
"python")
if is_arch_family; then
FINAL_PACKAGES+=(python)
elif is_debian_family; then
FINAL_PACKAGES+=(python3 python3-pip python3-venv)
elif is_fedora_family; then
FINAL_PACKAGES+=(python3 python3-pip)
fi
continue
;;
"fd")
if is_arch_family; then
FINAL_PACKAGES+=(fd)
elif is_debian_family || is_fedora_family; then
FINAL_PACKAGES+=(fd-find)
fi
continue
;;
"bat")
FINAL_PACKAGES+=(bat)
continue
;;
"openssl")
if is_arch_family; then
FINAL_PACKAGES+=(openssl)
elif is_debian_family; then
FINAL_PACKAGES+=(libssl-dev)
elif is_fedora_family; then
FINAL_PACKAGES+=(openssl openssl-devel)
fi
continue
;;
"bind")
if is_arch_family; then
FINAL_PACKAGES+=(bind)
elif is_debian_family; then
FINAL_PACKAGES+=(dnsutils)
elif is_fedora_family; then
FINAL_PACKAGES+=(bind-utils)
fi
continue
;;
"ninja-build")
if is_arch_family; then
FINAL_PACKAGES+=(ninja)
elif is_debian_family || is_fedora_family; then
FINAL_PACKAGES+=(ninja-build)
fi
continue
;;
"libcurl4-openssl-dev")
if is_arch_family; then
FINAL_PACKAGES+=(curl)
elif is_debian_family; then
FINAL_PACKAGES+=(libcurl4-openssl-dev)
elif is_fedora_family; then
FINAL_PACKAGES+=(libcurl-devel)
fi
continue
;;
"gnupg")
if is_fedora_family; then
FINAL_PACKAGES+=(gnupg2)
else
FINAL_PACKAGES+=(gnupg)
fi
continue
;;
*)
esac
FINAL_PACKAGES+=("$pkg")
done
if is_debian_family; then
FINAL_PACKAGES+=(ca-certificates bsdmainutils pkg-config cmake)
fi
if is_fedora_family; then
FINAL_PACKAGES+=(ca-certificates pkgconf-pkg-config cmake)
FINAL_PACKAGES+=(R-core gcc-gfortran bzip2 bzip2-devel readline-devel sqlite sqlite-devel tk-devel libffi-devel xz xz-devel ncurses-devel zlib-devel findutils llvm)
fi
echo -e "${BLUE} LOG:${YELLOW} Installing: ${NC}${FINAL_PACKAGES[*]}"
install_status=0
install_packages "${FINAL_PACKAGES[@]}" || install_status=$?
if [ "$install_status" -eq 42 ]; then
exit 0
elif [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
if is_debian_family || is_fedora_family; then
echo -e "${BLUE} LOG:${YELLOW} Fixing fd/bat binary names when needed...${NC}"
[ -f /usr/bin/fdfind ] && sudo ln -sf /usr/bin/fdfind /usr/local/bin/fd
[ -f /usr/bin/batcat ] && sudo ln -sf /usr/bin/batcat /usr/local/bin/bat
fi
# Moving Pre-Built bin to .local/bin
mkdir -p "$HOME/.local/bin"
cp -f "$REPO_ROOT/bin/"* "$HOME/.local/bin/"
chmod +x "$HOME/.local/bin/"*
if ! command -v rclone &> /dev/null; then
echo -e "${BLUE} LOG:${YELLOW} Installing Rclone CLI...${NC}"
case "$ARCH" in
x86_64)
RCLONE_ARCH="amd64"
;;
aarch64)
RCLONE_ARCH="arm64"
;;
esac
TEMP_DIR="$(mktemp -d)"
RCLONE_ZIP="$TEMP_DIR/rclone.zip"
curl -fLsS "https://downloads.rclone.org/rclone-current-linux-${RCLONE_ARCH}.zip" -o "$RCLONE_ZIP"
unzip -q "$RCLONE_ZIP" -d "$TEMP_DIR"
install -m 755 "$TEMP_DIR"/rclone-*-linux-"${RCLONE_ARCH}"/rclone "$HOME/.local/bin/rclone"
rm -rf "$TEMP_DIR"
fi
if ! command -v earthly &> /dev/null; then
echo -e "${BLUE} LOG:${YELLOW} Installing Earthly CLI...${NC}"
case "$ARCH" in
x86_64)
EARTHLY_ARCH="amd64"
;;
aarch64)
EARTHLY_ARCH="arm64"
;;
esac
curl -fLsS "https://github.com/earthly/earthly/releases/latest/download/earthly-linux-${EARTHLY_ARCH}" \
-o "$HOME/.local/bin/earthly"
chmod +x "$HOME/.local/bin/earthly"
fi
# Docker Installation
if command -v docker &> /dev/null; then
echo -e "${GREEN} LOG: Docker is already installed.${NC}"
else
if grep -qEi "(Microsoft|WSL)" /proc/version &> /dev/null; then
echo -e "${RED} LOG: WSL Detected! Skipping Native Docker.${NC}"
echo -e "${RED} >>> Please install Docker Desktop on Windows.${NC}"
else
echo -e "${BLUE} LOG:${YELLOW} Installing Native Docker...${NC}"
if is_arch_family; then
sudo pacman -S --noconfirm --needed docker docker-compose
sudo systemctl enable --now docker
elif is_debian_family; then
curl -fsSL https://get.docker.com | sh
elif is_fedora_family; then
if is_atomic_fedora; then
echo -e "${YELLOW} NOTE:${NC} Skipping native Docker auto-install on rpm-ostree systems."
echo -e "${YELLOW} NOTE:${NC} After reboot, install your preferred container runtime separately if needed."
else
sudo dnf install -y moby-engine docker-compose
sudo systemctl enable --now docker
fi
fi
# Add user to group
if getent group docker >/dev/null 2>&1; then
sudo usermod -aG docker $(whoami)
fi
fi
fi
# 4. Zsh & Configuration
if [ ! -d "$HOME/.oh-my-zsh" ]; then
echo -e "${BLUE} LOG:${YELLOW} Installing Oh My Zsh...${NC}"
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
fi
# Plugins
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
mkdir -p "$ZSH_CUSTOM/plugins"
[ ! -d "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" ] && git clone https://github.com/zsh-users/zsh-syntax-highlighting.git "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting"
[ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ] && git clone https://github.com/zsh-users/zsh-autosuggestions "$ZSH_CUSTOM/plugins/zsh-autosuggestions"
# 5. Git Credentials (WSL Bridge)
if grep -qEi "(Microsoft|WSL)" /proc/version &> /dev/null; then
GCM_WIN="/mnt/c/Program Files/Git/mingw64/bin/git-credential-manager.exe"
[ -f "$GCM_WIN" ] && git config --global credential.helper "! \"$GCM_WIN\""
else
git config --global credential.helper 'cache --timeout=43200'
fi
# 6. Cleanup & Secrets
rm -f "$HOME/.zshrc" "$HOME/.zsh_aliases"
touch "$HOME/.zsh_secrets"
# 7. Set Shell
TARGET_SHELL="$(command -v zsh)"
CURRENT_LOGIN_SHELL="$(getent passwd "$(whoami)" | cut -d: -f7)"
if [ "$CURRENT_LOGIN_SHELL" != "$TARGET_SHELL" ]; then
if command -v chsh >/dev/null 2>&1; then
sudo chsh -s "$TARGET_SHELL" "$(whoami)"
elif command -v usermod >/dev/null 2>&1; then
sudo usermod -s "$TARGET_SHELL" "$(whoami)"
else
echo -e "${YELLOW} NOTE:${NC} Could not find chsh/usermod. Please change your login shell to $TARGET_SHELL manually."
fi
fi
echo -e "${GREEN} LOG: Base System Setup Complete.${NC}"
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# Path: Scripts/cpp.sh
# Path: scripts/cpp.sh
set -e
+80
View File
@@ -0,0 +1,80 @@
#!/bin/bash
# Path: scripts/go.sh
set -e
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
RED='\033[1;31m'
NC='\033[0m'
export GVM_ROOT="$HOME/.programming/go"
BOOTSTRAP_GO="$GVM_ROOT/bootstrap"
ARCH="$(uname -m)"
case "$ARCH" in
x86_64)
GO_BOOTSTRAP_ARCH="amd64"
;;
aarch64)
GO_BOOTSTRAP_ARCH="arm64"
;;
*)
echo -e "${RED} ERROR:${NC} Unsupported architecture for Go bootstrap: $ARCH"
exit 1
;;
esac
echo -e "${BLUE} LOG:${YELLOW} Setting up Go and GVM in ${GVM_ROOT}...${NC}"
if [ ! -d "$GVM_ROOT/scripts" ]; then
echo -e "${BLUE} LOG:${YELLOW} Cloning GVM...${NC}"
git clone https://github.com/moovweb/gvm.git "$GVM_ROOT"
rm -rf "$GVM_ROOT/.git"
echo -e "${BLUE} LOG:${YELLOW} Configuring GVM scripts...${NC}"
cp "$GVM_ROOT/scripts/gvm-default" "$GVM_ROOT/scripts/gvm"
sed -i "s|^GVM_ROOT=.*|GVM_ROOT=\"$GVM_ROOT\"|" "$GVM_ROOT/scripts/gvm"
echo -e "${GREEN} SUCCESS:${NC} GVM cloned and configured."
else
echo -e "${GREEN} SKIP:${NC} GVM already installed."
fi
if [ ! -d "$BOOTSTRAP_GO" ]; then
echo -e "${BLUE} LOG:${YELLOW} Downloading Bootstrap Go...${NC}"
mkdir -p "$BOOTSTRAP_GO"
wget -q "https://go.dev/dl/go1.20.5.linux-${GO_BOOTSTRAP_ARCH}.tar.gz" -O /tmp/go-bootstrap.tar.gz
tar -C "$BOOTSTRAP_GO" -xzf /tmp/go-bootstrap.tar.gz --strip-components=1
rm /tmp/go-bootstrap.tar.gz
echo -e "${GREEN} SUCCESS:${NC} Bootstrap Go installed."
else
echo -e "${GREEN} SKIP:${NC} Bootstrap Go already exists."
fi
export PATH="$BOOTSTRAP_GO/bin:$PATH"
export GOROOT_BOOTSTRAP="$BOOTSTRAP_GO"
set +e
source "$GVM_ROOT/scripts/gvm"
set -e
if ! command -v gvm &> /dev/null; then
echo -e "${RED} ERROR:${NC} GVM failed to load."
exit 1
fi
TARGET_VER="go1.24.11"
if ! gvm list | grep -q "$TARGET_VER"; then
echo -e "${BLUE} LOG:${YELLOW} Installing ${TARGET_VER}...${NC}"
gvm install "$TARGET_VER" --prefer-binary
fi
gvm use "$TARGET_VER" --default
echo -e "${GREEN} SUCCESS:${NC} Go setup completed."
@@ -10,11 +10,8 @@ if [ -f /etc/os-release ]; then
. /etc/os-release
OS="${ID}"
OS_LIKE="${ID_LIKE:-}"
elif [ "$(uname -s)" = "Darwin" ]; then
OS="macos"
OS_LIKE="darwin"
else
echo "Unable to detect operating system: /etc/os-release not found and host is not macOS."
echo "Unable to detect operating system: /etc/os-release not found."
return 1 2>/dev/null || exit 1
fi
@@ -30,10 +27,6 @@ is_fedora_family() {
[[ "$OS" == "fedora" || "$OS" == "bazzite" || " $OS_LIKE " == *" fedora "* || " $OS_LIKE " == *" rhel "* ]]
}
is_macos() {
[[ "$OS" == "macos" ]]
}
is_atomic_fedora() {
is_fedora_family && command -v rpm-ostree >/dev/null 2>&1 && [ -f /run/ostree-booted ]
}
@@ -133,22 +126,6 @@ install_packages() {
return 0
fi
if is_macos; then
if ! command -v brew >/dev/null 2>&1; then
echo "Homebrew is required on macOS. Install it from https://brew.sh first."
return 1
fi
local pkg
for pkg in "${packages[@]}"; do
if brew list --formula "$pkg" >/dev/null 2>&1; then
continue
fi
brew install "$pkg"
done
return 0
fi
echo "Unsupported OS: $OS"
return 1
}
+2 -17
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# Path: Scripts/node.sh
# Path: scripts/node.sh
set -e
@@ -11,7 +11,6 @@ NC='\033[0m'
export NVM_DIR="$HOME/.programming/node"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
export BUN_INSTALL="$NVM_DIR/bun"
echo -e "${BLUE} LOG:${YELLOW} Setting up Node.js (NVM) in ${NVM_DIR}...${NC}"
@@ -31,23 +30,9 @@ if command -v nvm &> /dev/null; then
echo -e "${BLUE} LOG:${YELLOW} Enabling Corepack (pnpm/yarn)...${NC}"
corepack enable
echo -e "${BLUE} LOG:${YELLOW} Preparing pnpm and yarn...${NC}"
corepack prepare pnpm@latest --activate
corepack prepare yarn@stable --activate
if [ ! -x "$BUN_INSTALL/bin/bun" ]; then
echo -e "${BLUE} LOG:${YELLOW} Installing Bun to ${BUN_INSTALL}...${NC}"
curl -fsSL https://bun.sh/install | bash
else
echo -e "${BLUE} LOG:${YELLOW} Bun already installed at ${BUN_INSTALL}.${NC}"
fi
export PATH="$BUN_INSTALL/bin:$PATH"
echo -e "${GREEN} LOG: Node setup complete. $(node -v)${NC}"
echo -e "${GREEN} LOG: Package Managers: pnpm $(pnpm -v), yarn $(yarn -v), bun $(bun -v)${NC}"
echo -e "${GREEN} LOG: Package Managers: pnpm $(pnpm -v), yarn $(yarn -v)${NC}"
else
echo -e "${RED} ERROR: NVM failed to load from $NVM_DIR${NC}"
exit 1
+4 -4
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# Path: Scripts/provision.sh
# Path: scripts/provision.sh
set -e
@@ -36,11 +36,11 @@ fi
echo -e "${YELLOW}LOG: Updating system and installing base tools...${NC}"
if is_arch_family; then
pacman -Sy --noconfirm git make curl rsync zsh sudo
pacman -Sy --noconfirm git make curl zsh sudo
SUDO_GROUP="wheel"
elif is_debian_family; then
apt-get update
apt-get install -y git make curl rsync sudo zsh
apt-get install -y git make curl sudo zsh
SUDO_GROUP="sudo"
elif is_fedora_family; then
if is_atomic_fedora; then
@@ -49,7 +49,7 @@ elif is_fedora_family; then
exit 1
fi
dnf install -y git make curl rsync sudo zsh
dnf install -y git make curl sudo zsh
SUDO_GROUP="wheel"
else
echo -e "${RED}Unsupported OS: $OS${NC}"
+1 -4
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# Path: Scripts/python.sh
# Path: scripts/python.sh
set -e
BLUE='\033[1;34m'
@@ -22,9 +22,6 @@ elif is_debian_family; then
elif is_fedora_family; then
echo -e "${BLUE} LOG:${YELLOW} Installing Fedora build dependencies...${NC}"
PYTHON_BUILD_DEPS=(make gcc gcc-c++ patch zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel openssl-devel tk-devel libffi-devel xz xz-devel ncurses-devel findutils git)
elif is_macos; then
echo -e "${BLUE} LOG:${YELLOW} Installing macOS build dependencies...${NC}"
PYTHON_BUILD_DEPS=(openssl@3 readline sqlite xz tcl-tk bzip2 zlib pkg-config git)
else
echo -e "${RED} ERROR:${NC} Unsupported OS: $OS"
exit 1
+103
View File
@@ -0,0 +1,103 @@
#!/bin/bash
# Path: scripts/r.sh
set -e
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
RED='\033[1;31m'
NC='\033[0m'
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$SCRIPT_DIR/lib/distro.sh"
echo -e "${BLUE} LOG:${YELLOW} Detecting R installation strategy for $OS...${NC}"
if is_arch_family; then
echo -e "${BLUE} LOG:${YELLOW} Arch Linux detected. Using system R (Pacman)...${NC}"
# Install R + Build Tools
install_status=0
install_packages r gcc-fortran curl tar || install_status=$?
if [ "$install_status" -eq 42 ]; then
exit 0
elif [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
echo -e "${GREEN} SUCCESS:${NC} R installed via Pacman."
R_BIN="R"
elif is_debian_family; then
echo -e "${BLUE} LOG:${YELLOW} Installing Debian build dependencies...${NC}"
install_status=0
install_packages gfortran curl tar ca-certificates || install_status=$?
if [ "$install_status" -eq 42 ]; then
exit 0
elif [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
if ! command -v rig >/dev/null 2>&1; then
echo -e "${BLUE} LOG:${YELLOW} Installing Rig for Debian/Ubuntu...${NC}"
DEB_ARCH=$(dpkg --print-architecture)
case "$DEB_ARCH" in
amd64|arm64)
;;
*)
echo -e "${RED} ERROR:${NC} Unsupported Debian architecture for Rig: $DEB_ARCH"
exit 1
;;
esac
LATEST_REL_DATA=$(curl -fsSL https://api.github.com/repos/r-lib/rig/releases/latest)
RIG_URL=$(echo "$LATEST_REL_DATA" | grep -o "https://[^\"]*r-rig_[^\"]*_${DEB_ARCH}\.deb" | head -n 1)
if [ -z "$RIG_URL" ]; then
echo -e "${RED} ERROR:${NC} Could not find Rig .deb download URL for architecture: $DEB_ARCH"
exit 1
fi
TEMP_DEB=$(mktemp --suffix=.deb)
curl -fLsS "$RIG_URL" -o "$TEMP_DEB"
sudo apt-get install -y "$TEMP_DEB"
rm -f "$TEMP_DEB"
fi
TARGET_VER="release"
if ! rig list | grep -q "release"; then
echo -e "${BLUE} LOG:${YELLOW} Installing R (${TARGET_VER})...${NC}"
sudo rig add "$TARGET_VER" --without-cran-mirror
sudo rig default "$TARGET_VER"
fi
R_BIN="rig run"
elif is_fedora_family; then
echo -e "${BLUE} LOG:${YELLOW} Fedora detected. Using system R...${NC}"
install_status=0
install_packages R-core gcc-gfortran curl tar || install_status=$?
if [ "$install_status" -eq 42 ]; then
exit 0
elif [ "$install_status" -ne 0 ]; then
exit "$install_status"
fi
R_BIN="R"
else
echo -e "${RED} ERROR:${NC} Unsupported OS: $OS"
exit 1
fi
echo -e "${BLUE} LOG:${YELLOW} Installing 'renv' package in the user R library...${NC}"
$R_BIN -e 'user_lib <- path.expand(Sys.getenv("R_LIBS_USER", unset = "~/R/library")); dir.create(user_lib, recursive = TRUE, showWarnings = FALSE); .libPaths(c(user_lib, .libPaths())); if (!require("renv", quietly=TRUE)) install.packages("renv", lib = user_lib, repos = "https://cloud.r-project.org")'
echo -e "${GREEN} SUCCESS:${NC} R setup completed."
+2 -2
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# Path: Scripts/rust.sh
# Path: scripts/rust.sh
set -e
@@ -38,4 +38,4 @@ if command -v cargo &> /dev/null; then
else
echo -e "${RED} ERROR: Cargo not found in PATH. Check CARGO_HOME.${NC}"
exit 1
fi
fi
+116
View File
@@ -0,0 +1,116 @@
#!/bin/bash
set -e
# Colors
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
RED='\033[1;31m'
NC='\033[0m'
SECRETS_FILE="$HOME/.zsh_secrets"
REMOTE_NAME="hetzner-box"
# Dependency Check
if ! command -v rclone &> /dev/null; then
echo -e "${RED}Error: rclone is not installed.${NC}"
exit 1
fi
# Load Secrets
if [ -f "$SECRETS_FILE" ]; then
source "$SECRETS_FILE"
fi
# Configure Remote (If missing)
if ! rclone listremotes | grep -q "^${REMOTE_NAME}:$"; then
echo -e "${BLUE}Configuration missing. Starting setup...${NC}"
if [ -z "$HETZNER_USER" ]; then
DEFAULT_USER="u513875"
read -p "Enter Username [$DEFAULT_USER]: " INPUT_USER
HETZNER_USER="${INPUT_USER:-$DEFAULT_USER}"
fi
if [ -z "$HETZNER_HOST" ]; then
DEFAULT_HOST="${HETZNER_USER}.your-storagebox.de"
read -p "Enter Host [$DEFAULT_HOST]: " INPUT_HOST
HETZNER_HOST="${INPUT_HOST:-$DEFAULT_HOST}"
fi
if [ -z "$HETZNER_PASS" ]; then
echo -e "${YELLOW}Note: Passwords are obscured in config, but saving to secrets file uses plain text.${NC}"
read -s -p "Enter Password: " HETZNER_PASS
echo ""
fi
# Save to secrets
if ! grep -q "HETZNER_USER" "$SECRETS_FILE" 2>/dev/null; then
echo -e "${BLUE}Saving credentials to $SECRETS_FILE...${NC}"
{
echo ""
echo "# Hetzner Storage Box"
echo "export HETZNER_USER=\"$HETZNER_USER\""
echo "export HETZNER_HOST=\"$HETZNER_HOST\""
echo "export HETZNER_PASS=\"$HETZNER_PASS\""
} >> "$SECRETS_FILE"
fi
OBSCURED_PASS=$(rclone obscure "$HETZNER_PASS")
rclone config create "$REMOTE_NAME" sftp \
host "$HETZNER_HOST" \
user "$HETZNER_USER" \
pass "$OBSCURED_PASS" \
port 23 \
--non-interactive
echo -e "${GREEN}Configuration created!${NC}"
fi
# Interactive Menu
echo -e "${BLUE}--- Hetzner Storage Box Manager ---${NC}"
echo "1) List files (ls)"
echo "2) Upload (Copy LOCAL -> REMOTE)"
echo "3) Download (Copy REMOTE -> LOCAL)"
echo "q) Quit"
read -p "Select option: " OPTION
case $OPTION in
1)
read -p "Enter depth [1]: " INPUT_DEPTH
DEPTH="${INPUT_DEPTH:-1}"
echo -e "${BLUE}Listing files on Hetzner...${NC}"
rclone ls "${REMOTE_NAME}:" --max-depth "$DEPTH"
;;
2)
read -p "Enter local folder/file to upload: " LOCAL_PATH
LOCAL_PATH=${LOCAL_PATH%/}
read -p "Enter remote destination folder (e.g. Backup): " REMOTE_FOLDER
REMOTE_FOLDER=${REMOTE_FOLDER#/}
REMOTE_FOLDER=${REMOTE_FOLDER%/}
echo -e "${YELLOW}Uploading '$LOCAL_PATH' to '${REMOTE_NAME}:$REMOTE_FOLDER'...${NC}"
rclone copy "$LOCAL_PATH" "${REMOTE_NAME}:$REMOTE_FOLDER" -P
;;
3)
echo -e "${BLUE}Contents of Root:${NC}"
rclone lsd "${REMOTE_NAME}:"
read -p "Enter remote path to download (e.g. models/Test): " REMOTE_PATH
REMOTE_PATH=${REMOTE_PATH#/}
read -p "Enter local destination folder: " LOCAL_DEST
mkdir -p "$LOCAL_DEST"
echo -e "${YELLOW}Downloading '${REMOTE_NAME}:$REMOTE_PATH' to '$LOCAL_DEST'...${NC}"
rclone copy "${REMOTE_NAME}:$REMOTE_PATH" "$LOCAL_DEST" -P
;;
q)
exit 0
;;
*)
echo "Invalid option"
;;
esac