Compare commits
42 Commits
1e2515a21a
...
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 | ||
|
|
a93648e946 | ||
|
|
a5b36801c2 | ||
|
|
f1a2d08e14 | ||
|
|
3f753d0dd0 | ||
|
|
d5bcda270d | ||
|
|
e15740115b | ||
|
|
1b8a3a28dc | ||
|
|
19eec9224d | ||
|
|
a9cebf9b8b | ||
|
|
02ba07b207 | ||
|
|
bacf49b39d | ||
|
|
7c03ed12c7 | ||
|
|
db523bf42f | ||
|
|
a2e9cfbb90 | ||
|
|
250e46beaa | ||
|
|
87f27bf6e4 | ||
|
|
26011853ad | ||
|
|
a2d6aab245 |
@@ -1,3 +0,0 @@
|
|||||||
# .zsh_secrets
|
|
||||||
|
|
||||||
export TESTING_SECRET="HELLO THIS IS WORKING"
|
|
||||||
57
.zshrc
57
.zshrc
@@ -1,57 +0,0 @@
|
|||||||
# .zshrc
|
|
||||||
|
|
||||||
# Path to your Oh My Zsh installation.
|
|
||||||
export ZSH="$HOME/.oh-my-zsh"
|
|
||||||
|
|
||||||
PROMPT="%B%~%b :: "
|
|
||||||
RPROMPT="%n@%m"
|
|
||||||
|
|
||||||
# Plugins
|
|
||||||
plugins=(git zsh-syntax-highlighting zsh-autosuggestions)
|
|
||||||
|
|
||||||
source $ZSH/oh-my-zsh.sh
|
|
||||||
|
|
||||||
# Golang and GVM
|
|
||||||
[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"
|
|
||||||
|
|
||||||
# Node and NVM (Lazy Load)
|
|
||||||
export NVM_DIR="$HOME/.nvm"
|
|
||||||
|
|
||||||
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
|
|
||||||
[ -f "$HOME/.cargo/env" ] && source "$HOME/.cargo/env"
|
|
||||||
|
|
||||||
# Zoxide
|
|
||||||
eval "$(zoxide init --cmd cd zsh)"
|
|
||||||
|
|
||||||
# Source Aliases & Secrets
|
|
||||||
[ -f ~/.zsh_aliases ] && source ~/.zsh_aliases
|
|
||||||
[ -f ~/.zsh_secrets ] && source ~/.zsh_secrets
|
|
||||||
|
|
||||||
# Conda (Dynamic Path)
|
|
||||||
# __conda_setup="$('$HOME/Programming/miniconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
|
|
||||||
# if [ $? -eq 0 ]; then
|
|
||||||
# eval "$__conda_setup"
|
|
||||||
# else
|
|
||||||
# if [ -f "$HOME/Programming/miniconda3/etc/profile.d/conda.sh" ]; then
|
|
||||||
# . "$HOME/Programming/miniconda3/etc/profile.d/conda.sh"
|
|
||||||
# else
|
|
||||||
# export PATH="$HOME/Programming/miniconda3/bin:$PATH"
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
# unset __conda_setup
|
|
||||||
|
|
||||||
# Add to PATH
|
|
||||||
export PATH="$HOME/.local/bin:$PATH"
|
|
||||||
export PATH="/usr/local/go/bin:$PATH"
|
|
||||||
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
|
||||||
65
Makefile
65
Makefile
@@ -1,62 +1,11 @@
|
|||||||
# Makefile for Dotfiles
|
# Makefile
|
||||||
|
|
||||||
|
SCRIPTS_DIR := ./Scripts
|
||||||
|
|
||||||
# Default target
|
# 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:
|
setup:
|
||||||
bash ./scripts/base.sh
|
bash $(SCRIPTS_DIR)/setup.sh --bootstrap-only
|
||||||
bash ./scripts/node.sh
|
|
||||||
bash ./scripts/go.sh
|
|
||||||
bash ./scripts/rust.sh
|
|
||||||
bash ./scripts/python.sh
|
|
||||||
@echo "Full 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"
|
|
||||||
@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
|
|
||||||
|
|
||||||
base:
|
|
||||||
bash ./scripts/base.sh
|
|
||||||
|
|
||||||
# Language Setups
|
|
||||||
node:
|
|
||||||
bash ./scripts/node.sh
|
|
||||||
|
|
||||||
go:
|
|
||||||
bash ./scripts/go.sh
|
|
||||||
|
|
||||||
rust:
|
|
||||||
bash ./scripts/rust.sh
|
|
||||||
|
|
||||||
python:
|
|
||||||
bash ./scripts/python.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"
|
|
||||||
|
|||||||
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 "$@"
|
||||||
38
Scripts/cpp.sh
Normal file
38
Scripts/cpp.sh
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Path: Scripts/cpp.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BLUE='\033[1;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
GREEN='\033[1;32m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
source "$SCRIPT_DIR/lib/distro.sh"
|
||||||
|
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Setting up C++ Tooling (LLVM/Clang)...${NC}"
|
||||||
|
|
||||||
|
if is_arch_family; then
|
||||||
|
CPP_PACKAGES=(clang cmake ninja lldb gdb)
|
||||||
|
elif is_debian_family || is_fedora_family; then
|
||||||
|
CPP_PACKAGES=(clang cmake ninja-build lldb gdb)
|
||||||
|
else
|
||||||
|
echo -e "${RED} ERROR:${NC} Unsupported OS: $OS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_status=0
|
||||||
|
install_packages "${CPP_PACKAGES[@]}" || 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} C++ Environment Ready."
|
||||||
|
echo -e " - Compiler: $(clang --version | head -n 1)"
|
||||||
|
echo -e " - Builder: $(cmake --version | head -n 1)"
|
||||||
|
echo -e " - Debugger: $(lldb --version | head -n 1)"
|
||||||
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."
|
||||||
154
Scripts/lib/distro.sh
Normal file
154
Scripts/lib/distro.sh
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Shared distro helpers for install scripts.
|
||||||
|
|
||||||
|
SCRIPT_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="${REPO_ROOT:-$(dirname "$(dirname "$SCRIPT_LIB_DIR")")}"
|
||||||
|
REBOOT_MARKER="${REBOOT_MARKER:-$REPO_ROOT/.setup-reboot-required}"
|
||||||
|
|
||||||
|
if [ -f /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
OS="${ID}"
|
||||||
|
OS_LIKE="${ID_LIKE:-}"
|
||||||
|
elif [ "$(uname -s)" = "Darwin" ]; then
|
||||||
|
OS="macos"
|
||||||
|
OS_LIKE="darwin"
|
||||||
|
else
|
||||||
|
echo "Unable to detect operating system: /etc/os-release not found and host is not macOS."
|
||||||
|
return 1 2>/dev/null || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
is_arch_family() {
|
||||||
|
[[ "$OS" == "arch" || "$OS" == "manjaro" || " $OS_LIKE " == *" arch "* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
is_debian_family() {
|
||||||
|
[[ "$OS" == "ubuntu" || "$OS" == "debian" || " $OS_LIKE " == *" debian "* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
mark_reboot_required() {
|
||||||
|
touch "$REBOOT_MARKER"
|
||||||
|
echo ""
|
||||||
|
echo " REBOOT REQUIRED: Fedora atomic package layering finished."
|
||||||
|
echo " Please reboot, then rerun the same make target."
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
get_atomic_requested_packages() {
|
||||||
|
local python_bin=""
|
||||||
|
local candidate
|
||||||
|
|
||||||
|
for candidate in python3 python /usr/libexec/platform-python; do
|
||||||
|
if command -v "$candidate" >/dev/null 2>&1; then
|
||||||
|
python_bin="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$python_bin" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
rpm-ostree status --json 2>/dev/null | "$python_bin" -c '
|
||||||
|
import json, sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
except Exception:
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
for deployment in data.get("deployments", []):
|
||||||
|
if deployment.get("booted"):
|
||||||
|
for key in ("requested-packages", "requested-local-packages"):
|
||||||
|
for package in deployment.get(key, []):
|
||||||
|
if package:
|
||||||
|
print(package)
|
||||||
|
break
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
install_packages() {
|
||||||
|
local packages=("$@")
|
||||||
|
|
||||||
|
if [ ${#packages[@]} -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if is_arch_family; then
|
||||||
|
sudo pacman -S --noconfirm --needed "${packages[@]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if is_debian_family; then
|
||||||
|
sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||||
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "${packages[@]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if is_fedora_family; then
|
||||||
|
if is_atomic_fedora; then
|
||||||
|
local requested_pkg
|
||||||
|
local missing_packages=()
|
||||||
|
declare -A requested_packages=()
|
||||||
|
local pkg
|
||||||
|
|
||||||
|
while IFS= read -r requested_pkg; do
|
||||||
|
[ -n "$requested_pkg" ] && requested_packages["$requested_pkg"]=1
|
||||||
|
done < <(get_atomic_requested_packages)
|
||||||
|
|
||||||
|
for pkg in "${packages[@]}"; do
|
||||||
|
if rpm -q "$pkg" >/dev/null 2>&1; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${requested_packages[$pkg]:-}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
missing_packages+=("$pkg")
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#missing_packages[@]} -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo rpm-ostree install "${missing_packages[@]}"
|
||||||
|
mark_reboot_required
|
||||||
|
return 42
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo dnf install -y "${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
|
||||||
|
}
|
||||||
54
Scripts/node.sh
Normal file
54
Scripts/node.sh
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Path: Scripts/node.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BLUE='\033[1;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
GREEN='\033[1;32m'
|
||||||
|
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}"
|
||||||
|
|
||||||
|
if [ ! -d "$NVM_DIR" ]; then
|
||||||
|
mkdir -p "$NVM_DIR"
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Installing NVM to custom path...${NC}"
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | PROFILE=/dev/null bash
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||||||
|
|
||||||
|
if command -v nvm &> /dev/null; then
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Installing Node LTS...${NC}"
|
||||||
|
nvm install --lts --no-progress
|
||||||
|
nvm use --lts
|
||||||
|
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Enabling Corepack (pnpm/yarn)...${NC}"
|
||||||
|
|
||||||
|
corepack enable
|
||||||
|
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Preparing pnpm and yarn...${NC}"
|
||||||
|
|
||||||
|
corepack prepare pnpm@latest --activate
|
||||||
|
corepack prepare yarn@stable --activate
|
||||||
|
|
||||||
|
if [ ! -x "$BUN_INSTALL/bin/bun" ]; then
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Installing Bun to ${BUN_INSTALL}...${NC}"
|
||||||
|
curl -fsSL https://bun.sh/install | bash
|
||||||
|
else
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Bun already installed at ${BUN_INSTALL}.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PATH="$BUN_INSTALL/bin:$PATH"
|
||||||
|
|
||||||
|
echo -e "${GREEN} LOG: Node setup complete. $(node -v)${NC}"
|
||||||
|
echo -e "${GREEN} LOG: Package Managers: pnpm $(pnpm -v), yarn $(yarn -v), bun $(bun -v)${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED} ERROR: NVM failed to load from $NVM_DIR${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
55
scripts/provision.sh → Scripts/provision.sh
Executable file → Normal file
55
scripts/provision.sh → Scripts/provision.sh
Executable file → Normal file
@@ -1,7 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Path: Scripts/provision.sh
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Colors
|
|
||||||
GREEN='\033[1;32m'
|
GREEN='\033[1;32m'
|
||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
RED='\033[1;31m'
|
RED='\033[1;31m'
|
||||||
@@ -10,21 +12,16 @@ NC='\033[0m'
|
|||||||
DEFAULT_USER="mangopig"
|
DEFAULT_USER="mangopig"
|
||||||
DEFAULT_UID="1000"
|
DEFAULT_UID="1000"
|
||||||
DEFAULT_GID="1000"
|
DEFAULT_GID="1000"
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
source "$SCRIPT_DIR/lib/distro.sh"
|
||||||
|
|
||||||
# Root Check
|
|
||||||
if [ "$EUID" -ne 0 ]; then
|
if [ "$EUID" -ne 0 ]; then
|
||||||
echo -e "${RED}Please run this script as root.${NC}"
|
echo -e "${RED}Please run this script as root.${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# OS Detection
|
|
||||||
echo -e "${YELLOW}LOG: Detecting OS...${NC}"
|
echo -e "${YELLOW}LOG: Detecting OS...${NC}"
|
||||||
if [ -f /etc/os-release ]; then
|
|
||||||
. /etc/os-release
|
|
||||||
OS=$ID
|
|
||||||
fi
|
|
||||||
|
|
||||||
# IP Address Retrieval
|
|
||||||
echo -e "${YELLOW}LOG: Determining primary IPv4 address...${NC}"
|
echo -e "${YELLOW}LOG: Determining primary IPv4 address...${NC}"
|
||||||
if command -v hostname >/dev/null 2>&1 && hostname -I >/dev/null 2>&1; then
|
if command -v hostname >/dev/null 2>&1 && hostname -I >/dev/null 2>&1; then
|
||||||
IP=$(hostname -I | awk '{print $1}')
|
IP=$(hostname -I | awk '{print $1}')
|
||||||
@@ -36,22 +33,31 @@ else
|
|||||||
IP="127.0.0.1"
|
IP="127.0.0.1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install Prerequisites (Git, Make, Zsh, Sudo)
|
|
||||||
echo -e "${YELLOW}LOG: Updating system and installing base tools...${NC}"
|
echo -e "${YELLOW}LOG: Updating system and installing base tools...${NC}"
|
||||||
|
|
||||||
if [ "$OS" == "arch" ] || [ "$OS" == "manjaro" ]; then
|
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"
|
SUDO_GROUP="wheel"
|
||||||
elif [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
|
elif is_debian_family; then
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y git make curl sudo
|
apt-get install -y git make curl rsync sudo zsh
|
||||||
SUDO_GROUP="sudo"
|
SUDO_GROUP="sudo"
|
||||||
|
elif is_fedora_family; then
|
||||||
|
if is_atomic_fedora; then
|
||||||
|
echo -e "${RED}Provisioning on Fedora atomic systems is not supported by this root script.${NC}"
|
||||||
|
echo -e "${RED}Use the normal user workflow and run make setup after login instead.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnf install -y git make curl rsync sudo zsh
|
||||||
|
SUDO_GROUP="wheel"
|
||||||
else
|
else
|
||||||
echo -e "${RED}Unsupported OS: $OS${NC}"
|
echo -e "${RED}Unsupported OS: $OS${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Interactive Prompts
|
ZSH_PATH="$(command -v zsh)"
|
||||||
|
|
||||||
if [ -t 0 ]; then
|
if [ -t 0 ]; then
|
||||||
echo -e "${GREEN}---------------------------------------${NC}"
|
echo -e "${GREEN}---------------------------------------${NC}"
|
||||||
echo -e "${GREEN} USER PROVISIONING WIZARD ${NC}"
|
echo -e "${GREEN} USER PROVISIONING WIZARD ${NC}"
|
||||||
@@ -67,13 +73,11 @@ if [ -t 0 ]; then
|
|||||||
USER_GID=${INPUT_GID:-$DEFAULT_GID}
|
USER_GID=${INPUT_GID:-$DEFAULT_GID}
|
||||||
else
|
else
|
||||||
echo -e "${YELLOW}LOG: Non-interactive session detected. Using defaults.${NC}"
|
echo -e "${YELLOW}LOG: Non-interactive session detected. Using defaults.${NC}"
|
||||||
# Allow Environment Variables to override defaults in Docker/Headless
|
|
||||||
USERNAME=${USERNAME:-$DEFAULT_USER}
|
USERNAME=${USERNAME:-$DEFAULT_USER}
|
||||||
USER_UID=${USER_UID:-$DEFAULT_UID}
|
USER_UID=${USER_UID:-$DEFAULT_UID}
|
||||||
USER_GID=${USER_GID:-$DEFAULT_GID}
|
USER_GID=${USER_GID:-$DEFAULT_GID}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Group Creation
|
|
||||||
if getent group "$USER_GID" >/dev/null; then
|
if getent group "$USER_GID" >/dev/null; then
|
||||||
echo -e "${YELLOW}LOG: Group with GID $USER_GID already exists. Using it.${NC}"
|
echo -e "${YELLOW}LOG: Group with GID $USER_GID already exists. Using it.${NC}"
|
||||||
else
|
else
|
||||||
@@ -81,31 +85,30 @@ else
|
|||||||
groupadd -g "$USER_GID" "$USERNAME"
|
groupadd -g "$USER_GID" "$USERNAME"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# User Creation
|
|
||||||
if id "$USERNAME" &>/dev/null; then
|
if id "$USERNAME" &>/dev/null; then
|
||||||
echo -e "${YELLOW}LOG: User $USERNAME already exists. Skipping creation.${NC}"
|
echo -e "${YELLOW}LOG: User $USERNAME already exists. Skipping creation.${NC}"
|
||||||
else
|
else
|
||||||
echo -e "${YELLOW}LOG: Creating user $USERNAME...${NC}"
|
echo -e "${YELLOW}LOG: Creating user $USERNAME...${NC}"
|
||||||
|
|
||||||
# Create user with specific UID, GID, Groups, and Shell
|
|
||||||
useradd -m -u "$USER_UID" -g "$USER_GID" -G "$SUDO_GROUP" -s "$ZSH_PATH" "$USERNAME"
|
useradd -m -u "$USER_UID" -g "$USER_GID" -G "$SUDO_GROUP" -s "$ZSH_PATH" "$USERNAME"
|
||||||
|
|
||||||
echo -e "${GREEN}LOG: Setting password for $USERNAME...${NC}"
|
echo -e "${GREEN}LOG: Setting password for $USERNAME...${NC}"
|
||||||
passwd "$USERNAME"
|
|
||||||
|
if [ -t 0 ]; then
|
||||||
|
echo -e "${GREEN}LOG: Setting password for $USERNAME...${NC}"
|
||||||
|
passwd "$USERNAME"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}LOG: Non-interactive: Setting password to '$USERNAME'...${NC}"
|
||||||
|
echo "$USERNAME:$USERNAME" | chpasswd
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Sudo Configuration (Passwordless)
|
|
||||||
echo -e "${YELLOW}LOG: Configuring passwordless sudo...${NC}"
|
echo -e "${YELLOW}LOG: Configuring passwordless sudo...${NC}"
|
||||||
echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/90-$USERNAME"
|
echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/90-$USERNAME"
|
||||||
chmod 0440 "/etc/sudoers.d/90-$USERNAME"
|
chmod 0440 "/etc/sudoers.d/90-$USERNAME"
|
||||||
|
|
||||||
# Arch Specific: Uncomment wheel in sudoers if not already active
|
if is_arch_family || is_fedora_family; then
|
||||||
if [ "$OS" == "arch" ]; then
|
|
||||||
# Ensure the 'wheel' group is actually enabled in the main config if drop-in fails
|
|
||||||
sed -i 's/^# %wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/' /etc/sudoers
|
sed -i 's/^# %wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/' /etc/sudoers
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Cloning Dotfiles
|
|
||||||
echo -e "${YELLOW}LOG: Cloning dotfiles for $USERNAME...${NC}"
|
echo -e "${YELLOW}LOG: Cloning dotfiles for $USERNAME...${NC}"
|
||||||
sudo -u "$USERNAME" git clone https://git.mangopig.tech/MangoPig/Dot-Zsh.git "/home/$USERNAME/Config/Dot-Zsh"
|
sudo -u "$USERNAME" git clone https://git.mangopig.tech/MangoPig/Dot-Zsh.git "/home/$USERNAME/Config/Dot-Zsh"
|
||||||
|
|
||||||
81
Scripts/python.sh
Normal file
81
Scripts/python.sh
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Path: Scripts/python.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"
|
||||||
|
|
||||||
|
# Install Build Dependencies
|
||||||
|
if is_arch_family; then
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Installing Arch build dependencies...${NC}"
|
||||||
|
PYTHON_BUILD_DEPS=(base-devel openssl zlib xz tk libffi bzip2 git)
|
||||||
|
elif is_debian_family; then
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Installing Debian build dependencies...${NC}"
|
||||||
|
PYTHON_BUILD_DEPS=(make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev git)
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_status=0
|
||||||
|
install_packages "${PYTHON_BUILD_DEPS[@]}" || install_status=$?
|
||||||
|
|
||||||
|
if [ "$install_status" -eq 42 ]; then
|
||||||
|
exit 0
|
||||||
|
elif [ "$install_status" -ne 0 ]; then
|
||||||
|
exit "$install_status"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Define the Black Box Location
|
||||||
|
export PYENV_ROOT="$HOME/.programming/python/pyenv"
|
||||||
|
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||||
|
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Setting up Pyenv in ${PYENV_ROOT}...${NC}"
|
||||||
|
|
||||||
|
# Install Pyenv
|
||||||
|
if [ ! -d "$PYENV_ROOT" ]; then
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Cloning Pyenv...${NC}"
|
||||||
|
git clone https://github.com/pyenv/pyenv.git "$PYENV_ROOT"
|
||||||
|
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Compiling Pyenv dynamic bash extension...${NC}"
|
||||||
|
cd "$PYENV_ROOT" && src/configure && make -C src
|
||||||
|
|
||||||
|
echo -e "${GREEN} SUCCESS:${NC} Pyenv installed."
|
||||||
|
else
|
||||||
|
echo -e "${GREEN} SKIP:${NC} Pyenv already installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Initialize Pyenv for this script session
|
||||||
|
eval "$(pyenv init -)"
|
||||||
|
|
||||||
|
# Install Miniforge (The Community Standard)
|
||||||
|
TARGET_VER="miniforge3-latest"
|
||||||
|
|
||||||
|
if ! pyenv versions | grep -q "$TARGET_VER"; then
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Installing ${TARGET_VER}...${NC}"
|
||||||
|
echo -e "${YELLOW} NOTE: This avoids Anaconda licensing issues and uses conda-forge default.${NC}"
|
||||||
|
|
||||||
|
pyenv install "$TARGET_VER"
|
||||||
|
|
||||||
|
echo -e "${GREEN} SUCCESS:${NC} Miniforge installed."
|
||||||
|
else
|
||||||
|
echo -e "${GREEN} SKIP:${NC} Miniforge already installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set Global Default
|
||||||
|
pyenv global "$TARGET_VER"
|
||||||
|
|
||||||
|
echo -e "${GREEN} SUCCESS:${NC} Python setup completed. Default is now Miniforge (Conda)."
|
||||||
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."
|
||||||
41
Scripts/rust.sh
Normal file
41
Scripts/rust.sh
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Path: Scripts/rust.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BLUE='\033[1;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
GREEN='\033[1;32m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
export RUSTUP_HOME="$HOME/.programming/rust/multirust"
|
||||||
|
export CARGO_HOME="$HOME/.programming/rust/cargo"
|
||||||
|
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Setting up Rust (Rustup) in ${CARGO_HOME}...${NC}"
|
||||||
|
|
||||||
|
if [ ! -f "$CARGO_HOME/bin/rustup" ]; then
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Installing Rustup to custom path...${NC}"
|
||||||
|
mkdir -p "$RUSTUP_HOME" "$CARGO_HOME"
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path
|
||||||
|
else
|
||||||
|
echo -e "${GREEN} LOG: Rustup already installed in custom path.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$CARGO_HOME/env" ]; then
|
||||||
|
source "$CARGO_HOME/env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v cargo &> /dev/null; then
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Updating toolchain...${NC}"
|
||||||
|
rustup default stable
|
||||||
|
rustup update
|
||||||
|
|
||||||
|
echo -e "${BLUE} LOG:${YELLOW} Installing rust-src for 'Go to Definition'...${NC}"
|
||||||
|
rustup component add rust-src
|
||||||
|
|
||||||
|
echo -e "${GREEN} LOG: Rust setup complete. $(cargo --version)${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED} ERROR: Cargo not found in PATH. Check CARGO_HOME.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
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 src="source ~/.zshrc && echo '🔄 Reloaded .zshrc'"
|
||||||
|
|
||||||
alias cat="bat"
|
# alias cat="bat"
|
||||||
alias fd="fd"
|
alias fd="fd"
|
||||||
|
|
||||||
alias tree="eza --icons -T --git-ignore"
|
alias tree="eza --icons -T --git-ignore"
|
||||||
@@ -72,3 +72,23 @@ if grep -qEi "(Microsoft|WSL)" /proc/version &> /dev/null; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
alias local-stt="python ~/Application/STT/stt.py"
|
||||||
|
|
||||||
|
# Vault
|
||||||
|
alias vl="vault login -method=userpass username=mangopig"
|
||||||
|
alias vs="vault status"
|
||||||
|
alias vkv="vault kv get"
|
||||||
|
alias vkvl="vault kv list"
|
||||||
|
|
||||||
|
# Proxy
|
||||||
|
proxy_on() {
|
||||||
|
export ALL_PROXY=socks5h://192.168.1.26:1080
|
||||||
|
export HTTP_PROXY=socks5h://192.168.1.26:1080
|
||||||
|
export HTTPS_PROXY=socks5h://192.168.1.26:1080
|
||||||
|
echo "Proxy ON (Mullvad NL)"
|
||||||
|
}
|
||||||
|
proxy_off() {
|
||||||
|
unset ALL_PROXY HTTP_PROXY HTTPS_PROXY
|
||||||
|
echo "Proxy OFF (direct)"
|
||||||
|
}
|
||||||
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
|
||||||
26
Zsh/.zsh_secrets.example
Normal file
26
Zsh/.zsh_secrets.example
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# ~/.zsh_secrets
|
||||||
|
#
|
||||||
|
# Copy this file to ~/.zsh_secrets and fill in real values locally.
|
||||||
|
# Do not commit real secrets.
|
||||||
|
|
||||||
|
# Core local values
|
||||||
|
export AZURE_URL=""
|
||||||
|
export VM_SUDO_PASS=""
|
||||||
|
|
||||||
|
# API keys and tokens
|
||||||
|
export TAOBAO_API_KEY=""
|
||||||
|
export AZURE_OPENAI_API_KEY=""
|
||||||
|
export OUTLINE_MOKU_TOKEN=""
|
||||||
|
export DOKPLOY_API_KEY=""
|
||||||
|
export N8N_AUTH=""
|
||||||
|
export CONTEXT7_API_KEY=""
|
||||||
|
export GITEA_ACCESS_TOKEN=""
|
||||||
|
export NETDATA_TOKEN=""
|
||||||
|
export OBSIDIAN_API_KEY=""
|
||||||
|
export GITHUB_TOKEN=""
|
||||||
|
export OPEN_ROUTER_API_KEY=""
|
||||||
|
|
||||||
|
# Optional overrides
|
||||||
|
# export VAULT_ADDR="http://192.168.30.53:8200"
|
||||||
|
# export OPENCHAMBER_HOST="0.0.0.0"
|
||||||
|
# export PLANNOTATOR_PORT="9999"
|
||||||
29
Zsh/.zshenv
Normal file
29
Zsh/.zshenv
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# ENV File
|
||||||
|
|
||||||
|
# Load machine-local secrets before defining any derived environment variables.
|
||||||
|
[ -f "$HOME/.zsh_secrets" ] && source "$HOME/.zsh_secrets"
|
||||||
|
|
||||||
|
export GOGC=500
|
||||||
|
export R_ROOT="$HOME/.programming/r"
|
||||||
|
export R_LIBS_USER="$R_ROOT/library"
|
||||||
|
|
||||||
|
# CodeGraphContext defaults
|
||||||
|
export CGC_RUNTIME_DB_TYPE=kuzudb
|
||||||
|
export KUZUDB_PATH="$HOME/.codegraphcontext/kuzudb"
|
||||||
|
|
||||||
|
if [ -d "$HOME/.local/share/cgc-venv/bin" ]; then
|
||||||
|
export PATH="$HOME/.local/share/cgc-venv/bin:$PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PATH="$HOME/.opencode/bin:$PATH"
|
||||||
|
export OPENCHAMBER_HOST="${OPENCHAMBER_HOST:-0.0.0.0}"
|
||||||
|
export VAULT_ADDR="${VAULT_ADDR:-http://192.168.30.53:8200}"
|
||||||
|
export PLANNOTATOR_PORT="${PLANNOTATOR_PORT:-9999}"
|
||||||
|
|
||||||
|
# Derived environment variables that may depend on machine-local secrets.
|
||||||
|
[ -n "${AZURE_URL:-}" ] && export AZURE_COGNITIVE_SERVICES_RESOURCE_NAME="$AZURE_URL"
|
||||||
|
[ -n "${AZURE_URL:-}" ] && export AZURE_RESOURCE_NAME="$AZURE_URL"
|
||||||
|
[ -n "${VM_SUDO_PASS:-}" ] && export OPENCHAMBER_UI_PASSWORD="$VM_SUDO_PASS"
|
||||||
|
[ -n "${AZURE_OPENAI_API_KEY:-}" ] && export FOUNDRY_ANTHROPIC_API_KEY="$AZURE_OPENAI_API_KEY"
|
||||||
|
[ -n "${AZURE_OPENAI_API_KEY:-}" ] && export CHROMA_OPENAI_API_KEY="$AZURE_OPENAI_API_KEY"
|
||||||
|
[ -n "${OUTLINE_MOKU_TOKEN:-}" ] && export OUTLINE_MOKU_AUTHORIZATION="Bearer $OUTLINE_MOKU_TOKEN"
|
||||||
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
|
||||||
104
scripts/base.sh
104
scripts/base.sh
@@ -1,104 +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'
|
|
||||||
|
|
||||||
DOTFILES_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
REPO_ROOT="$(dirname "$DOTFILES_DIR")"
|
|
||||||
|
|
||||||
echo -e "${BLUE} LOG:${YELLOW} Initializing Base System Layer...${NC}"
|
|
||||||
|
|
||||||
# OS Detection
|
|
||||||
if [ -f /etc/os-release ]; then
|
|
||||||
. /etc/os-release
|
|
||||||
OS=$ID
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Confirm Architecture
|
|
||||||
ARCH=$(uname -m)
|
|
||||||
if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ]; then
|
|
||||||
echo -e "${RED} ERROR: Unsupported architecture: $ARCH${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2. Package Installation (OS Dependencies)
|
|
||||||
# Note: Removed 'go', 'npm', 'cargo' - these are handled by specific scripts
|
|
||||||
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
|
|
||||||
bind nmap socat tcpdump net-tools strace gdb hexyl
|
|
||||||
rclone
|
|
||||||
# 'eza' is REMOVED from here for Ubuntu logic below
|
|
||||||
)
|
|
||||||
|
|
||||||
if [ "$OS" == "arch" ] || [ "$OS" == "manjaro" ]; then
|
|
||||||
# Arch has eza in the community repo
|
|
||||||
sudo pacman -Sy --noconfirm --needed "${PACKAGES[@]}" base-devel
|
|
||||||
elif [ "$OS" == "ubuntu" ] || [ "$OS" == "debian" ]; then
|
|
||||||
sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
|
||||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "${PACKAGES[@]}" bsdmainutils pkg-config cmake
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Moving Pre-Built bin to /usr/local/bin
|
|
||||||
mkdir -p "$HOME/.local/bin"
|
|
||||||
cp -f "$REPO_ROOT/bin/"* "$HOME/.local/bin/"
|
|
||||||
chmod +x "$HOME/.local/bin/"*
|
|
||||||
|
|
||||||
# 3. 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 [ "$OS" == "arch" ]; then
|
|
||||||
sudo pacman -S --noconfirm --needed docker docker-compose
|
|
||||||
sudo systemctl enable --now docker
|
|
||||||
elif [ "$OS" == "ubuntu" ]; then
|
|
||||||
# (Simplified for brevity - standard Docker install logic)
|
|
||||||
curl -fsSL https://get.docker.com | sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add user to group
|
|
||||||
sudo usermod -aG docker $(whoami)
|
|
||||||
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
|
|
||||||
if [ "$SHELL" != "$(which zsh)" ]; then
|
|
||||||
sudo chsh -s "$(which zsh)" $(whoami)
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${GREEN} LOG: Base System Setup Complete.${NC}"
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
BLUE='\033[1;34m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
GREEN='\033[1;32m'
|
|
||||||
RED='\033[1;31m'
|
|
||||||
NC='\033[0m'
|
|
||||||
|
|
||||||
echo -e "${BLUE} LOG:${YELLOW} Setting up Go (GVM)...${NC}"
|
|
||||||
|
|
||||||
# 1. Install GVM dependencies
|
|
||||||
unalias cd 2>/dev/null || true
|
|
||||||
|
|
||||||
# 2. Install GVM if missing
|
|
||||||
if [ ! -d "$HOME/.gvm" ]; then
|
|
||||||
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 3. Load GVM
|
|
||||||
[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"
|
|
||||||
|
|
||||||
# 4. Install Go
|
|
||||||
# Use a known stable version that exists. 1.24.11 is speculative. 1.24.0 is real.
|
|
||||||
TARGET_GO="go1.24.0"
|
|
||||||
|
|
||||||
if command -v gvm &> /dev/null; then
|
|
||||||
if [[ $(gvm list) != *"$TARGET_GO"* ]]; then
|
|
||||||
echo -e "${BLUE} LOG:${YELLOW} Installing $TARGET_GO...${NC}"
|
|
||||||
|
|
||||||
# Try Binary install first (Fastest/Safest)
|
|
||||||
gvm install "$TARGET_GO" -B || {
|
|
||||||
echo -e "${RED} LOG: Binary failed. Bootstrapping via go1.22.9...${NC}"
|
|
||||||
gvm install go1.22.9 -B
|
|
||||||
gvm use go1.22.9
|
|
||||||
gvm install "$TARGET_GO"
|
|
||||||
}
|
|
||||||
|
|
||||||
gvm use "$TARGET_GO" --default
|
|
||||||
echo -e "${GREEN} LOG: Go setup complete.$(go version)${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN} LOG: $TARGET_GO already installed.${NC}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e "${RED} ERROR: GVM failed to load.${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e # Exit on error
|
|
||||||
BLUE='\033[1;34m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
GREEN='\033[1;32m'
|
|
||||||
NC='\033[0m'
|
|
||||||
|
|
||||||
echo -e "${BLUE} LOG:${YELLOW} Setting up Node.js (NVM)...${NC}"
|
|
||||||
|
|
||||||
export NVM_DIR="$HOME/.nvm"
|
|
||||||
|
|
||||||
# 1. Install NVM
|
|
||||||
if [ ! -d "$NVM_DIR" ]; then
|
|
||||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2. Load NVM
|
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
|
||||||
|
|
||||||
# 3. Install Node
|
|
||||||
if command -v nvm &> /dev/null; then
|
|
||||||
echo -e "${BLUE} LOG:${YELLOW} Installing LTS...${NC}"
|
|
||||||
nvm install --lts
|
|
||||||
nvm use --lts
|
|
||||||
|
|
||||||
echo -e "${BLUE} LOG:${YELLOW} Installing Global Tools...${NC}"
|
|
||||||
# set +e is safer for npm global installs which can be noisy
|
|
||||||
set +e
|
|
||||||
npm install -g pnpm yarn
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo -e "${GREEN} LOG: Node setup complete.$(node -v)${NC}"
|
|
||||||
fi
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Bold Colors
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
BLUE='\033[1;34m'
|
|
||||||
RED='\033[1;31m'
|
|
||||||
GREEN='\033[1;32m'
|
|
||||||
NC='\033[0m'
|
|
||||||
|
|
||||||
# Python Setup
|
|
||||||
echo -e "${BLUE} LOG:${YELLOW} Setting up Python environment...${NC}"
|
|
||||||
|
|
||||||
# MiniConda Installation
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
BLUE='\033[1;34m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
GREEN='\033[1;32m'
|
|
||||||
NC='\033[0m'
|
|
||||||
|
|
||||||
echo -e "${BLUE} LOG:${YELLOW} Setting up Rust (Rustup)...${NC}"
|
|
||||||
|
|
||||||
# 1. Install Rustup
|
|
||||||
if ! command -v rustup &> /dev/null; then
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2. Source Environment
|
|
||||||
[ -f "$HOME/.cargo/env" ] && source "$HOME/.cargo/env"
|
|
||||||
|
|
||||||
# 3. Update to Stable
|
|
||||||
if command -v cargo &> /dev/null; then
|
|
||||||
rustup default stable
|
|
||||||
rustup update
|
|
||||||
echo -e "${GREEN} LOG: Rust setup complete.$(cargo --version)${NC}"
|
|
||||||
fi
|
|
||||||
@@ -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