Zuletzt aktiv 3 weeks ago

Interactive developer environment bootstrapper for Zsh/Bash with repeatable workstation setup helpers.

Änderung f1d44b32b04ff7032f88594ca5ef73b1feee5bbe

README.md Originalformat

Strict Developer Environment Installer

A robust, idempotent, and interactive bash script to bootstrap your macOS, Linux, or WSL development environment.

Unlike standard setup scripts that rely heavily on package managers like Homebrew or Apt (which can introduce bloat, dependency hell, or outdated packages), this script is designed with a "Strict Official Source" philosophy. It dynamically fetches the absolute latest binaries and pre-compiled releases directly from the official maintainers (e.g., GitHub API, Python.org, Amazon).

✨ Key Features

  • Zero-Brew Dependency: By default, it bypasses Homebrew entirely, fetching tools directly from official APIs and release pages (Homebrew is available as an optional install).
  • Idempotent: Safe to run multiple times. It intelligently checks if a tool is already installed before attempting to download it.
  • Dynamic Versioning: Pings official APIs (like GitHub Releases or go.dev/VERSION) to ensure you are downloading the exact latest version at the time of execution.
  • Auto-Path Management: Intelligently injects paths into a dedicated ~/.pathrc (or your .bashrc/.zshrc) without mangling your config files.
  • Architecture Aware: Automatically detects if you are running on Intel (x86_64) or Apple Silicon/ARM (aarch64/arm64) and downloads the correct binaries.

🚀 Usage

You can launch the interactive installer directly from your terminal with a single command:

bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/af1c64c143a44ffbb0a1632dd6a32af1/raw/HEAD/menu.sh)"

Once the menu loads, simply follow the interactive prompts. You can select multiple tools at once by entering space-separated numbers (e.g., 1 3 8 12).

🛠️ Included Tools

Core Runtimes & Managers

  • Build Tools: GCC, Make, Git, Curl, Unzip, and core libraries.
  • Homebrew: (Optional) Installed officially.
  • NVM (Node Version Manager): Includes interactive prompt to install LTS or Latest Node.js immediately.
  • Python 3: Fetches the latest stable .pkg for Mac or compiles strictly from source for Linux.
  • Go: Fetches the latest tarball and installs system-wide to /usr/local/go.
  • SDKMAN: For managing Java, Kotlin, and Gradle.
  • .NET: Installed via Microsoft's official script.

Cloud & Infrastructure

  • Docker: Downloads the official .dmg (macOS) or runs the official get.docker.com script (Linux) with auto-group assignment.
  • AWS CLI: Fetches the official binaries directly from Amazon.
  • Google Cloud CLI (gcloud): Official Google setup script.
  • Firebase CLI: Standalone binary (no Node.js required natively).

Dev Tools & Security

  • GitHub CLI (gh): Downloaded directly from GitHub Releases to /usr/local/bin.
  • Infisical CLI: Downloaded directly from GitHub Releases for secret management.

AI & Agentic Tools

  • Claude Code CLI: Anthropic's official agent.
  • OpenCode: AI agent orchestration.
  • OpenAI Codex CLI: Required NVM/Node.js to run.

⚠️ Important Notes & Troubleshooting

  • Restart Your Shell: After running the script for the first time, you should restart your terminal or run source ~/.bashrc (or ~/.zshrc) to ensure all new PATH variables (like NVM, Go, or .NET) take effect.
  • Docker Permissions (Linux): The script automatically adds your user to the docker group. You will need to log out and log back in for this to take effect.
  • AWS CLI on Linux: Ensure you install Build Tools (Option 1) first if you are on a fresh Linux install, as the AWS installer requires unzip to extract the payload.

Built for developers who want total control over their toolchain.

menu.sh Originalformat
1#!/usr/bin/env bash
2
3# =======================================================
4# 1. BOOTSTRAPPER: PREFER ZSH, FALLBACK TO BASH
5# =======================================================
6if [ -z "${_PREFER_ZSH_BOOTSTRAPPED:-}" ]; then
7 export _PREFER_ZSH_BOOTSTRAPPED=1
8
9 # Only attempt to re-execute if $0 is an actual file on disk.
10 # This prevents the 'can't open input file' error when running
11 # from memory via `bash -c "$(curl ...)"` or `zsh -c`.
12 if [ -f "$0" ]; then
13 if command -v zsh >/dev/null 2>&1; then
14 exec zsh "$0" "$@"
15 elif command -v bash >/dev/null 2>&1; then
16 exec bash "$0" "$@"
17 else
18 echo "Error: Neither Zsh nor Bash is installed. Exiting."
19 exit 1
20 fi
21 fi
22fi
23
24set -u
25
26# =======================================================
27# 2. CROSS-SHELL NORMALIZATION
28# =======================================================
29if [ -n "${ZSH_VERSION:-}" ]; then
30 # Zsh: Enable Bash-like word splitting for unquoted variables (menu input)
31 setopt shwordsplit 2>/dev/null || true
32 CURRENT_SHELL="zsh"
33 PROFILE_FILE="$HOME/.zshrc"
34elif [ -n "${BASH_VERSION:-}" ]; then
35 # Bash
36 CURRENT_SHELL="bash"
37 PROFILE_FILE="$HOME/.bashrc"
38else
39 # Fallback POSIX
40 CURRENT_SHELL="sh"
41 PROFILE_FILE="$HOME/.profile"
42fi
43
44# =======================================================
45# COLORS & LOGGING
46# =======================================================
47GREEN="\033[0;32m"
48RED="\033[0;31m"
49YELLOW="\033[1;33m"
50BLUE="\033[0;34m"
51NC="\033[0m"
52
53log() { printf "${GREEN}▶ %s${NC}\n" "$*"; }
54warn() { printf "${YELLOW}⚠ %s${NC}\n" "$*"; }
55err() { printf "${RED}✖ %s${NC}\n" "$*"; }
56info() { printf "${BLUE}ℹ %s${NC}\n" "$*"; }
57
58# =======================================================
59# HELPERS & SYSTEM DETECTION
60# =======================================================
61require_cmd() { command -v "$1" >/dev/null 2>&1; }
62
63add_to_path_config() {
64 local label=$1
65 local path_line=$2
66 local target_file="${3:-$PROFILE_FILE}"
67
68 if [[ -f "$target_file" ]]; then
69 if ! grep -q "$label" "$target_file"; then
70 printf "\n# %s\n%s\n" "$label" "$path_line" >> "$target_file"
71 log "Added $label to $target_file"
72 fi
73 else
74 printf "\n# %s\n%s\n" "$label" "$path_line" >> "$PROFILE_FILE"
75 log "Added $label to shell profile ($PROFILE_FILE)."
76 fi
77
78 # Instantly evaluate the path line so the current script session
79 # can immediately use the newly installed tool in subsequent steps.
80 eval "$path_line" 2>/dev/null || true
81}
82
83detect_os_arch() {
84 ARCH=$(uname -m)
85 case "$ARCH" in
86 x86_64|amd64) SYS_ARCH="amd64"; MAC_ARCH="x86_64"; AWS_ARCH="x86_64" ;;
87 aarch64|arm64) SYS_ARCH="arm64"; MAC_ARCH="arm64"; AWS_ARCH="aarch64" ;;
88 *) err "Unsupported architecture: $ARCH"; exit 1 ;;
89 esac
90
91 if [[ "$OSTYPE" == "darwin"* ]]; then
92 OS="macOS"
93 OS_LOWER="darwin"
94 elif grep -qi microsoft /proc/version 2>/dev/null; then
95 OS="WSL"
96 OS_LOWER="linux"
97 elif [[ -f /etc/os-release ]]; then
98 . /etc/os-release
99 [[ "$ID" == "ubuntu" || "$ID_LIKE" == *"ubuntu"* ]] && OS="Ubuntu" || OS="Linux"
100 OS_LOWER="linux"
101 else
102 OS="Unknown"
103 OS_LOWER="unknown"
104 fi
105 log "Detected: $OS ($ARCH) running $CURRENT_SHELL"
106}
107
108detect_os_arch
109
110# =======================================================
111# CORE RUNTIMES & MANAGERS
112# =======================================================
113
114install_build_tools() {
115 log "Installing Build Essentials & Core Dependencies..."
116 case "$OS" in
117 Ubuntu|WSL) sudo apt-get update && sudo apt-get install -y build-essential curl wget git jq unzip libssl-dev zlib1g-dev libffi-dev libsqlite3-dev ;;
118 macOS) xcode-select --install || warn "Xcode tools already installed" ;;
119 *) warn "Manual installation required for $OS." ;;
120 esac
121}
122
123install_brew() {
124 if require_cmd brew; then warn "Homebrew already installed"; return; fi
125 log "Installing Homebrew from Official Source..."
126
127 # Homebrew's installer specifically requires Bash execution, regardless of current shell
128 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
129
130 if [[ "$OS_LOWER" == "linux" ]]; then
131 add_to_path_config "HOMEBREW" 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"'
132 elif [[ "$MAC_ARCH" == "arm64" ]]; then
133 add_to_path_config "HOMEBREW" 'eval "$(/opt/homebrew/bin/brew shellenv)"'
134 else
135 add_to_path_config "HOMEBREW" 'eval "$(/usr/local/bin/brew shellenv)"'
136 fi
137}
138
139install_nvm() {
140 export NVM_DIR="$HOME/.nvm"
141
142 if [ -d "$NVM_DIR" ]; then
143 warn "NVM is already installed."
144 else
145 log "Installing NVM via $CURRENT_SHELL..."
146 curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | $CURRENT_SHELL
147 fi
148
149 [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
150
151 printf "\n${YELLOW}Which version of Node.js would you like to install?${NC}\n"
152 printf " 1) LTS (Long Term Support - Recommended for stability)\n"
153 printf " 2) Latest (Current features - Recommended for testing new APIs)\n"
154 printf " 3) Skip installing Node.js right now\n"
155
156 printf "Select (1/2/3): "
157 read node_choice
158
159 case "$node_choice" in
160 2)
161 log "Installing Latest Node.js..."
162 nvm install node
163 nvm alias default node
164 nvm use node
165
166 log "Disabling npm logs..."
167 npm config set logs-max 0
168 ;;
169 3)
170 log "Skipping Node.js installation."
171 ;;
172 *)
173 log "Installing Latest LTS Node.js..."
174 nvm install --lts
175 nvm alias default 'lts/*'
176 nvm use --lts
177
178 log "Disabling npm logs..."
179 npm config set logs-max 0
180 ;;
181 esac
182}
183
184install_python() {
185 log "Fetching latest stable Python version..."
186
187 LATEST_PY=$(curl -s https://www.python.org/downloads/ | grep -o 'Download Python 3\.[0-9]\{1,2\}\.[0-9]\{1,2\}' | head -1 | grep -o '3\.[0-9]\{1,2\}\.[0-9]\{1,2\}')
188 [[ -z "$LATEST_PY" ]] && LATEST_PY="3.12.3"
189
190 log "Detected Stable Version: $LATEST_PY"
191
192 if [[ "$OS" == "macOS" ]]; then
193 log "Downloading official macOS package..."
194 PKG_URL="https://www.python.org/ftp/python/${LATEST_PY}/python-${LATEST_PY}-macos11.pkg"
195 curl -fsSL -o /tmp/python.pkg "$PKG_URL"
196
197 log "Running installer..."
198 sudo installer -pkg /tmp/python.pkg -target /
199 rm /tmp/python.pkg
200 else
201 log "Installing build dependencies (zlib, ssl, etc)..."
202 sudo apt-get update && sudo apt-get install -y build-essential zlib1g-dev libssl-dev libffi-dev libsqlite3-dev
203
204 SRC_URL="https://www.python.org/ftp/python/${LATEST_PY}/Python-${LATEST_PY}.tgz"
205
206 log "Downloading $SRC_URL..."
207 curl -fSL -o /tmp/Python.tgz "$SRC_URL" || err "Failed to download Python source from $SRC_URL"
208
209 cd /tmp && tar -xzf Python.tgz && cd "Python-${LATEST_PY}"
210
211 log "Configuring build (with ensurepip)..."
212 ./configure --enable-optimizations --with-ensurepip=install
213
214 log "Compiling Python (this may take a few minutes)..."
215 sudo make altinstall
216
217 PY_MINOR=$(echo "$LATEST_PY" | cut -d. -f1,2)
218
219 log "Setting up symlinks..."
220 sudo ln -sf /usr/local/bin/python${PY_MINOR} /usr/local/bin/python3
221
222 if [[ -f "/usr/local/bin/pip${PY_MINOR}" ]]; then
223 sudo ln -sf /usr/local/bin/pip${PY_MINOR} /usr/local/bin/pip3
224 log "Successfully linked pip3!"
225 else
226 err "pip${PY_MINOR} was not generated during the build!"
227 fi
228
229 cd ~ && sudo rm -rf /tmp/Python*
230 fi
231
232 log "Python $LATEST_PY installation sequence finished!"
233}
234
235install_go() {
236 log "Fetching latest Go version from official source..."
237 LATEST_GO=$(curl -sL https://go.dev/VERSION?m=text | head -n 1)
238 TAR_FILE="${LATEST_GO}.${OS_LOWER}-${SYS_ARCH}.tar.gz"
239
240 log "Downloading $TAR_FILE..."
241 curl -fsSL -o /tmp/go.tar.gz "https://go.dev/dl/$TAR_FILE"
242
243 log "Installing to /usr/local/go..."
244 sudo rm -rf /usr/local/go
245 sudo tar -C /usr/local -xzf /tmp/go.tar.gz
246 rm /tmp/go.tar.gz
247 add_to_path_config "GO_BIN" 'export PATH="$PATH:/usr/local/go/bin"'
248}
249
250install_sdkman() {
251 if [ -d "$HOME/.sdkman" ]; then warn "SDKMAN already exists"; return; fi
252
253 # SDKMAN explicitly blocks macOS's default Bash 3.2.
254 # If we are trapped in Bash < 4, force the installer to use Zsh.
255 if [[ "$CURRENT_SHELL" == "bash" && "${BASH_VERSINFO[0]:-0}" -lt 4 ]]; then
256 log "Outdated Bash detected. Installing SDKMAN via Zsh..."
257 curl -s "https://get.sdkman.io" | zsh
258 else
259 log "Installing SDKMAN via $CURRENT_SHELL..."
260 curl -s "https://get.sdkman.io" | $CURRENT_SHELL
261 fi
262}
263
264install_dotnet() {
265 log "Installing .NET from official script..."
266 curl -fsSL https://dot.net/v1/dotnet-install.sh | $CURRENT_SHELL
267 add_to_path_config "DOTNET_TOOLS" 'export PATH="$PATH:$HOME/.dotnet/tools"'
268}
269
270# =======================================================
271# CLOUD & INFRASTRUCTURE
272# =======================================================
273
274install_docker() {
275 if require_cmd docker; then log "Docker exists"; else
276 log "Installing Docker from official source..."
277
278 if [[ "$OS" == "macOS" ]]; then
279 [[ "$SYS_ARCH" == "arm64" ]] && DOCKER_MAC_ARCH="arm64" || DOCKER_MAC_ARCH="amd64"
280 DMG_URL="https://desktop.docker.com/mac/main/${DOCKER_MAC_ARCH}/Docker.dmg"
281 curl -fsSL -o /tmp/Docker.dmg "$DMG_URL"
282 hdiutil attach /tmp/Docker.dmg -nobrowse -mountpoint /Volumes/Docker
283 sudo cp -a /Volumes/Docker/Docker.app /Applications/
284 hdiutil detach /Volumes/Docker
285 rm /tmp/Docker.dmg
286
287 log "Starting Docker Desktop..."
288 open /Applications/Docker.app
289 log "Please complete the setup in the Docker Desktop UI."
290 else
291 curl -fsSL https://get.docker.com | sudo sh
292 fi
293 fi
294
295 if [[ "$OS" != "macOS" ]]; then
296 log "Applying Linux post-install actions..."
297
298 sudo groupadd -f docker
299 sudo usermod -aG docker "$USER"
300
301 if command -v systemctl >/dev/null 2>&1; then
302 sudo systemctl enable --now docker.service
303 sudo systemctl enable --now containerd.service
304 elif [[ "$OS" == "WSL" ]]; then
305 sudo service docker start
306 fi
307
308 if [[ -S /var/run/docker.sock ]]; then
309 log "Fixing ownership of /var/run/docker.sock..."
310 sudo chown root:docker /var/run/docker.sock
311 sudo chmod 660 /var/run/docker.sock
312 fi
313
314 log "Added $USER to the docker group."
315 log "CRITICAL: To apply group changes immediately, run: newgrp docker"
316 fi
317}
318
319install_aws() {
320 if require_cmd aws; then warn "AWS CLI exists"; return; fi
321 log "Installing AWS CLI directly from Amazon..."
322
323 if [[ "$OS" == "macOS" ]]; then
324 curl -fsSL -o /tmp/AWSCLIV2.pkg "https://awscli.amazonaws.com/AWSCLIV2.pkg"
325 sudo installer -pkg /tmp/AWSCLIV2.pkg -target /
326 rm /tmp/AWSCLIV2.pkg
327 else
328 if ! require_cmd unzip; then
329 err "unzip is required. Please install 'Build Tools' (Option 1) first."
330 return 1
331 fi
332 curl -fsSL -o /tmp/awscliv2.zip "https://awscli.amazonaws.com/awscli-exe-linux-${AWS_ARCH}.zip"
333 cd /tmp && unzip -q awscliv2.zip && sudo ./aws/install
334 rm -rf /tmp/awscliv2.zip /tmp/aws
335 fi
336}
337
338install_gcloud() {
339 if require_cmd gcloud; then warn "Google Cloud CLI exists"; return; fi
340 log "Installing Google Cloud CLI..."
341 curl -fsSL https://sdk.cloud.google.com | $CURRENT_SHELL -s -- --disable-prompts
342
343 if [[ "$CURRENT_SHELL" == "zsh" ]]; then
344 add_to_path_config "GCLOUD" '[ -f "$HOME/google-cloud-sdk/path.zsh.inc" ] && source "$HOME/google-cloud-sdk/path.zsh.inc"'
345 else
346 add_to_path_config "GCLOUD" '[ -f "$HOME/google-cloud-sdk/path.bash.inc" ] && source "$HOME/google-cloud-sdk/path.bash.inc"'
347 fi
348}
349
350install_firebase() {
351 if require_cmd firebase; then warn "Firebase CLI exists"; return; fi
352 log "Installing Firebase CLI via NPM to ensure ARM64 compatibility..."
353
354 if ! require_cmd npm; then
355 err "NPM not found. Please install NVM (Option 3) first."
356 return 1
357 fi
358
359 npm install -g firebase-tools
360}
361
362# =======================================================
363# DEV TOOLS & SECURITY
364# =======================================================
365
366install_gh() {
367 if require_cmd gh; then warn "GitHub CLI exists"; return; fi
368 log "Fetching latest GitHub CLI version..."
369 LATEST_GH=$(curl -s https://api.github.com/repos/cli/cli/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
370 TAR_NAME="gh_${LATEST_GH}_${OS_LOWER}_${SYS_ARCH}"
371
372 curl -fsSL -o /tmp/gh.tar.gz "https://github.com/cli/cli/releases/download/v${LATEST_GH}/${TAR_NAME}.tar.gz"
373 tar -xzf /tmp/gh.tar.gz -C /tmp
374 sudo mv "/tmp/${TAR_NAME}/bin/gh" /usr/local/bin/
375 sudo rm -rf "/tmp/${TAR_NAME}" /tmp/gh.tar.gz
376}
377
378install_infisical() {
379 if require_cmd infisical; then warn "Infisical CLI exists"; return; fi
380 log "Installing Infisical CLI via NPM..."
381
382 if ! require_cmd npm; then
383 err "NPM not found. Please install NVM (Option 3) first."
384 return 1
385 fi
386
387 npm install -g @infisical/cli
388}
389
390# =======================================================
391# AI & AGENTIC TOOLS
392# =======================================================
393
394install_claude() { log "Installing Claude Code..."; curl -fsSL https://claude.ai/install.sh | $CURRENT_SHELL; }
395install_opencode() { log "Installing OpenCode..."; curl -fsSL https://opencode.ai/install | $CURRENT_SHELL; }
396
397install_codex() {
398 log "Installing @openai/codex..."
399 if ! require_cmd npm; then err "Node.js/NPM is required. Install NVM (3) first."; return 1; fi
400 npm i -g @openai/codex
401}
402
403# =======================================================
404# MENU LOGIC
405# =======================================================
406show_menu() {
407 clear
408 printf "${BLUE}==================================================${NC}\n"
409 printf "${GREEN} DEVELOPER ENVIRONMENT INSTALLER (Polyglot) ${NC}\n"
410 printf "${BLUE}==================================================${NC}\n"
411 printf "${YELLOW}--- Core Runtimes & Managers ---${NC}\n"
412 printf " 1) Build Tools (GCC/Make)\n"
413 printf " 2) Homebrew (Optional)\n"
414 printf " 3) NVM (Node.js)\n"
415 printf " 4) Python 3 (Official API)\n"
416 printf " 5) Go (Official -> /usr/local)\n"
417 printf " 6) SDKMAN (Java/Kotlin)\n"
418 printf " 7) .NET (Official Script)\n"
419 printf "${YELLOW}--- Cloud & Infrastructure ---${NC}\n"
420 printf " 8) Docker (Official DMG/sh)\n"
421 printf " 9) AWS CLI (Official Bin)\n"
422 printf " 10) Google Cloud CLI (gcloud)\n"
423 printf " 11) Firebase CLI (NPM/ARM64 Safe)\n"
424 printf "${YELLOW}--- Dev Tools & Security ---${NC}\n"
425 printf " 12) GitHub CLI (Official Bin)\n"
426 printf " 13) Infisical (Official Bin)\n"
427 printf "${YELLOW}--- AI & Agentic Tools ---${NC}\n"
428 printf " 14) Claude Code CLI\n"
429 printf " 15) OpenCode (opencode.ai)\n"
430 printf " 16) OpenAI Codex CLI\n"
431 printf "${BLUE}==================================================${NC}\n"
432 printf " 99) Quit & Refresh Shell\n"
433 printf "${BLUE}==================================================${NC}\n"
434}
435
436while true; do
437 show_menu
438
439 # Bulletproof prompt for both Bash and Zsh
440 printf "Select options (space-separated): "
441 read input
442
443 # Thanks to 'setopt shwordsplit' in Zsh, this behaves perfectly across both shells
444 for choice in $input; do
445 case "$choice" in
446 1) install_build_tools ;;
447 2) install_brew ;;
448 3) install_nvm ;;
449 4) install_python ;;
450 5) install_go ;;
451 6) install_sdkman ;;
452 7) install_dotnet ;;
453 8) install_docker ;;
454 9) install_aws ;;
455 10) install_gcloud ;;
456 11) install_firebase ;;
457 12) install_gh ;;
458 13) install_infisical ;;
459 14) install_claude ;;
460 15) install_opencode ;;
461 16) install_codex ;;
462 99)
463 log "Installation complete! Refreshing terminal environment..."
464 exec "$CURRENT_SHELL"
465 ;;
466 *) warn "Option $choice not valid." ;;
467 esac
468 done
469
470 printf "Press Enter to continue..."
471 read dummy
472done