Compare commits
24 Commits
a93648e946
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d79cb9a62b | ||
|
|
a596a2d527 | ||
|
|
3299e2e949 | ||
|
|
1c37bfeede | ||
|
|
cbfc097d60 | ||
|
|
dd8c952b1d | ||
|
|
3a3073e389 | ||
|
|
2429eb3603 | ||
|
|
88817aa7d2 | ||
|
|
0e0a320de9 | ||
|
|
cc1dc202e1 | ||
|
|
52a41fae61 | ||
|
|
671c948153 | ||
|
|
7996568696 | ||
|
|
f2be49aaac | ||
|
|
348ef2a0e7 | ||
|
|
5b3f5e7698 | ||
|
|
bec96e8456 | ||
|
|
84100a85d1 | ||
|
|
b41dcbac2a | ||
|
|
12b752892b | ||
|
|
52054493cc | ||
|
|
58531bf579 | ||
|
|
5cefa4019b |
76
.zshrc
76
.zshrc
@@ -1,76 +0,0 @@
|
||||
# .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
Bins/versions.json
Normal file
45
Bins/versions.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Commands/Bin/mod.just
Normal file
22
Commands/Bin/mod.just
Normal file
@@ -0,0 +1,22 @@
|
||||
project_root := justfile_directory()
|
||||
bin_script_dir := project_root + "/Scripts/bin"
|
||||
|
||||
# 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}}'
|
||||
|
||||
# List available releases for a supported binary.
|
||||
list tool:
|
||||
bash '{{bin_script_dir}}/update.sh' --list '{{tool}}'
|
||||
|
||||
# Print the currently pinned versions.
|
||||
show:
|
||||
cat '{{project_root}}/Bins/versions.json'
|
||||
20
Commands/Lang/mod.just
Normal file
20
Commands/Lang/mod.just
Normal file
@@ -0,0 +1,20 @@
|
||||
project_root := justfile_directory()
|
||||
scripts_dir := project_root + "/Scripts"
|
||||
|
||||
node:
|
||||
bash '{{scripts_dir}}/node.sh'
|
||||
|
||||
go:
|
||||
bash '{{scripts_dir}}/go.sh'
|
||||
|
||||
rust:
|
||||
bash '{{scripts_dir}}/rust.sh'
|
||||
|
||||
python:
|
||||
bash '{{scripts_dir}}/python.sh'
|
||||
|
||||
r:
|
||||
bash '{{scripts_dir}}/r.sh'
|
||||
|
||||
cpp:
|
||||
bash '{{scripts_dir}}/cpp.sh'
|
||||
23
Commands/Setup/mod.just
Normal file
23
Commands/Setup/mod.just
Normal file
@@ -0,0 +1,23 @@
|
||||
project_root := justfile_directory()
|
||||
scripts_dir := project_root + "/Scripts"
|
||||
|
||||
# Run the full setup flow.
|
||||
all:
|
||||
bash '{{scripts_dir}}/setup.sh'
|
||||
|
||||
# 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
|
||||
59
Justfile
Normal file
59
Justfile
Normal file
@@ -0,0 +1,59 @@
|
||||
set shell := ["bash", "-cu"]
|
||||
|
||||
project_root := justfile_directory()
|
||||
scripts_dir := project_root + "/Scripts"
|
||||
bin_script_dir := scripts_dir + "/bin"
|
||||
|
||||
help:
|
||||
@printf '%s\n' \
|
||||
'Dot-Zsh commands:' \
|
||||
'' \
|
||||
' just setup all # full setup flow' \
|
||||
' just setup base # base layer only' \
|
||||
' just setup stow # stow Zsh files into $HOME' \
|
||||
' just setup clean # remove stowed Zsh files from $HOME' \
|
||||
' just setup update # git pull + full setup' \
|
||||
'' \
|
||||
' just lang node # install Node tooling' \
|
||||
' just lang go # install Go tooling' \
|
||||
' just lang rust # install Rust tooling' \
|
||||
' just lang python # install Python tooling' \
|
||||
' just lang r # install R tooling' \
|
||||
' just lang cpp # install C/C++ tooling' \
|
||||
'' \
|
||||
' just bin install-all # install all pinned binaries' \
|
||||
' just bin install nvim # install one pinned binary' \
|
||||
' just bin update nvim # update pinned version for a binary' \
|
||||
' just bin list nvim # list available releases' \
|
||||
' just bin show # print current pinned versions'
|
||||
|
||||
setup action='all':
|
||||
case '{{action}}' in \
|
||||
all) bash '{{scripts_dir}}/setup.sh' ;; \
|
||||
base) bash '{{scripts_dir}}/base.sh' ;; \
|
||||
stow) bash '{{scripts_dir}}/setup.sh' --stow-only ;; \
|
||||
clean) stow --dir='{{project_root}}' -D Zsh --target="$HOME" ;; \
|
||||
update) git -C '{{project_root}}' pull origin main && just --justfile '{{project_root}}/Justfile' setup all ;; \
|
||||
*) printf 'Unknown setup action: %s\n' '{{action}}' >&2; exit 1 ;; \
|
||||
esac
|
||||
|
||||
lang tool:
|
||||
case '{{tool}}' in \
|
||||
node) bash '{{scripts_dir}}/node.sh' ;; \
|
||||
go) bash '{{scripts_dir}}/go.sh' ;; \
|
||||
rust) bash '{{scripts_dir}}/rust.sh' ;; \
|
||||
python) bash '{{scripts_dir}}/python.sh' ;; \
|
||||
r) bash '{{scripts_dir}}/r.sh' ;; \
|
||||
cpp) bash '{{scripts_dir}}/cpp.sh' ;; \
|
||||
*) printf 'Unknown language target: %s\n' '{{tool}}' >&2; exit 1 ;; \
|
||||
esac
|
||||
|
||||
bin action='show' tool='' version='':
|
||||
case '{{action}}' in \
|
||||
install-all) bash '{{bin_script_dir}}/install.sh' --all ;; \
|
||||
install) [ -n '{{tool}}' ] || { printf 'bin install requires a tool name\n' >&2; exit 1; }; bash '{{bin_script_dir}}/install.sh' '{{tool}}' ;; \
|
||||
update) [ -n '{{tool}}' ] || { printf 'bin update requires a tool name\n' >&2; exit 1; }; bash '{{bin_script_dir}}/update.sh' '{{tool}}' '{{version}}' ;; \
|
||||
list) [ -n '{{tool}}' ] || { printf 'bin list requires a tool name\n' >&2; exit 1; }; bash '{{bin_script_dir}}/update.sh' --list '{{tool}}' ;; \
|
||||
show) cat '{{project_root}}/Bins/versions.json' ;; \
|
||||
*) printf 'Unknown bin action: %s\n' '{{action}}' >&2; exit 1 ;; \
|
||||
esac
|
||||
95
Makefile
95
Makefile
@@ -1,96 +1,11 @@
|
||||
# Makefile
|
||||
|
||||
REBOOT_MARKER := .setup-reboot-required
|
||||
SCRIPTS_DIR := ./Scripts
|
||||
|
||||
# Default target
|
||||
all: stow
|
||||
all: setup
|
||||
|
||||
# Full Setup
|
||||
# Bootstrap entrypoint for first-run setup.
|
||||
# This intentionally keeps Make lightweight: base packages, then restow dotfiles.
|
||||
setup:
|
||||
@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"
|
||||
bash $(SCRIPTS_DIR)/setup.sh --bootstrap-only
|
||||
|
||||
337
README.md
337
README.md
@@ -0,0 +1,337 @@
|
||||
# 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`
|
||||
|
||||
#### `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 stow
|
||||
just setup clean
|
||||
just setup update
|
||||
|
||||
just lang node
|
||||
just lang go
|
||||
just lang rust
|
||||
just lang python
|
||||
just lang r
|
||||
just lang cpp
|
||||
|
||||
just bin show
|
||||
just bin install-all
|
||||
just bin install nvim
|
||||
just bin list nvim
|
||||
just bin update nvim
|
||||
```
|
||||
|
||||
If `fzf` is available, `just bin update <tool>` can use interactive selection.
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
439
Scripts/base.sh
Normal file
439
Scripts/base.sh
Normal file
@@ -0,0 +1,439 @@
|
||||
#!/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
|
||||
|
||||
# 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
Scripts/bin/install.sh
Normal file
145
Scripts/bin/install.sh
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/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
Scripts/bin/update.sh
Normal file
135
Scripts/bin/update.sh
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/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 "$@"
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Path: scripts/cpp.sh
|
||||
# Path: Scripts/cpp.sh
|
||||
|
||||
set -e
|
||||
|
||||
172
Scripts/go.sh
Normal file
172
Scripts/go.sh
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/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."
|
||||
@@ -10,8 +10,11 @@ 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."
|
||||
echo "Unable to detect operating system: /etc/os-release not found and host is not macOS."
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
@@ -27,6 +30,10 @@ 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 ]
|
||||
}
|
||||
@@ -126,6 +133,22 @@ 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
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Path: scripts/node.sh
|
||||
# Path: Scripts/node.sh
|
||||
|
||||
set -e
|
||||
|
||||
@@ -11,6 +11,7 @@ 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,8 +32,22 @@ if command -v nvm &> /dev/null; then
|
||||
|
||||
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)${NC}"
|
||||
echo -e "${GREEN} LOG: Package Managers: pnpm $(pnpm -v), yarn $(yarn -v), bun $(bun -v)${NC}"
|
||||
else
|
||||
echo -e "${RED} ERROR: NVM failed to load from $NVM_DIR${NC}"
|
||||
exit 1
|
||||
8
scripts/provision.sh → Scripts/provision.sh
Executable file → Normal file
8
scripts/provision.sh → Scripts/provision.sh
Executable file → Normal 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 zsh sudo
|
||||
pacman -Sy --noconfirm git make curl rsync zsh sudo
|
||||
SUDO_GROUP="wheel"
|
||||
elif is_debian_family; then
|
||||
apt-get update
|
||||
apt-get install -y git make curl sudo zsh
|
||||
apt-get install -y git make curl rsync 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 sudo zsh
|
||||
dnf install -y git make curl rsync sudo zsh
|
||||
SUDO_GROUP="wheel"
|
||||
else
|
||||
echo -e "${RED}Unsupported OS: $OS${NC}"
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Path: scripts/python.sh
|
||||
# Path: Scripts/python.sh
|
||||
set -e
|
||||
|
||||
BLUE='\033[1;34m'
|
||||
@@ -22,6 +22,9 @@ 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
|
||||
220
Scripts/r.sh
Normal file
220
Scripts/r.sh
Normal file
@@ -0,0 +1,220 @@
|
||||
#!/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."
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Path: scripts/rust.sh
|
||||
# Path: Scripts/rust.sh
|
||||
|
||||
set -e
|
||||
|
||||
165
Scripts/setup.sh
Normal file
165
Scripts/setup.sh
Normal file
@@ -0,0 +1,165 @@
|
||||
#!/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
|
||||
@@ -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"
|
||||
185
Zsh/.zsh_completion
Normal file
185
Zsh/.zsh_completion
Normal file
@@ -0,0 +1,185 @@
|
||||
# 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
Zsh/.zsh_prompt
Normal file
285
Zsh/.zsh_prompt
Normal file
@@ -0,0 +1,285 @@
|
||||
# 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
|
||||
@@ -17,6 +17,8 @@ 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"
|
||||
@@ -4,11 +4,17 @@
|
||||
[ -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}"
|
||||
235
Zsh/.zshrc
Normal file
235
Zsh/.zshrc
Normal file
@@ -0,0 +1,235 @@
|
||||
# .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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/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
scripts/base.sh
269
scripts/base.sh
@@ -1,269 +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"
|
||||
|
||||
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,80 +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"
|
||||
|
||||
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."
|
||||
103
scripts/r.sh
103
scripts/r.sh
@@ -1,103 +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"
|
||||
|
||||
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."
|
||||
@@ -1,116 +0,0 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user