Ultima attività 3 weeks ago

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

Revisione df3696191b14027153d2978b6b959f78bda2eaf7

README.md Raw

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