Última atividade 3 weeks ago

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

README.md Bruto

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 Bruto
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=$(
188 curl -fsSL --compressed https://www.python.org/downloads/ |
189 sed -nE 's/.*Download Python ([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' |
190 head -1
191 )
192
193 if [[ -z "$LATEST_PY" ]]; then
194 err "Could not detect the latest stable Python version from python.org."
195 return 1
196 fi
197
198 log "Detected Stable Version: $LATEST_PY"
199
200 if [[ "$OS" == "macOS" ]]; then
201 log "Downloading official macOS package..."
202 PKG_URL="https://www.python.org/ftp/python/${LATEST_PY}/python-${LATEST_PY}-macos11.pkg"
203 curl -fsSL --compressed -o /tmp/python.pkg "$PKG_URL" || {
204 err "Failed to download Python macOS package from $PKG_URL"
205 return 1
206 }
207
208 log "Running installer..."
209 sudo installer -pkg /tmp/python.pkg -target / || {
210 err "Python macOS installer failed."
211 rm -f /tmp/python.pkg
212 return 1
213 }
214 rm /tmp/python.pkg
215 else
216 log "Installing build dependencies (zlib, ssl, etc)..."
217 sudo apt-get update && sudo apt-get install -y build-essential zlib1g-dev libssl-dev libffi-dev libsqlite3-dev || {
218 err "Failed to install Python build dependencies."
219 return 1
220 }
221
222 SRC_URL="https://www.python.org/ftp/python/${LATEST_PY}/Python-${LATEST_PY}.tgz"
223
224 log "Downloading $SRC_URL..."
225 curl -fSL --compressed -o /tmp/Python.tgz "$SRC_URL" || {
226 err "Failed to download Python source from $SRC_URL"
227 return 1
228 }
229
230 cd /tmp && tar -xzf Python.tgz && cd "Python-${LATEST_PY}" || {
231 err "Failed to extract Python source archive."
232 return 1
233 }
234
235 log "Configuring build (with ensurepip)..."
236 ./configure --enable-optimizations --with-ensurepip=install || {
237 err "Python configure step failed."
238 return 1
239 }
240
241 log "Compiling Python (this may take a few minutes)..."
242 sudo make altinstall || {
243 err "Python build/install step failed."
244 return 1
245 }
246
247 PY_MINOR=$(echo "$LATEST_PY" | cut -d. -f1,2)
248
249 log "Setting up symlinks..."
250 sudo ln -sf /usr/local/bin/python${PY_MINOR} /usr/local/bin/python3
251
252 if [[ -f "/usr/local/bin/pip${PY_MINOR}" ]]; then
253 sudo ln -sf /usr/local/bin/pip${PY_MINOR} /usr/local/bin/pip3
254 log "Successfully linked pip3!"
255 else
256 err "pip${PY_MINOR} was not generated during the build!"
257 fi
258
259 cd ~ && sudo rm -rf /tmp/Python*
260 fi
261
262 log "Python $LATEST_PY installation sequence finished!"
263}
264
265install_go() {
266 log "Fetching latest Go version from official source..."
267 LATEST_GO=$(curl -sL https://go.dev/VERSION?m=text | head -n 1)
268 TAR_FILE="${LATEST_GO}.${OS_LOWER}-${SYS_ARCH}.tar.gz"
269
270 log "Downloading $TAR_FILE..."
271 curl -fsSL -o /tmp/go.tar.gz "https://go.dev/dl/$TAR_FILE"
272
273 log "Installing to /usr/local/go..."
274 sudo rm -rf /usr/local/go
275 sudo tar -C /usr/local -xzf /tmp/go.tar.gz
276 rm /tmp/go.tar.gz
277 add_to_path_config "GO_BIN" 'export PATH="$PATH:/usr/local/go/bin"'
278}
279
280install_sdkman() {
281 if [ -d "$HOME/.sdkman" ]; then warn "SDKMAN already exists"; return; fi
282
283 # SDKMAN explicitly blocks macOS's default Bash 3.2.
284 # If we are trapped in Bash < 4, force the installer to use Zsh.
285 if [[ "$CURRENT_SHELL" == "bash" && "${BASH_VERSINFO[0]:-0}" -lt 4 ]]; then
286 log "Outdated Bash detected. Installing SDKMAN via Zsh..."
287 curl -s "https://get.sdkman.io" | zsh
288 else
289 log "Installing SDKMAN via $CURRENT_SHELL..."
290 curl -s "https://get.sdkman.io" | $CURRENT_SHELL
291 fi
292}
293
294install_dotnet() {
295 log "Installing .NET from official script..."
296 curl -fsSL https://dot.net/v1/dotnet-install.sh | $CURRENT_SHELL
297 add_to_path_config "DOTNET_TOOLS" 'export PATH="$PATH:$HOME/.dotnet/tools"'
298}
299
300install_android() {
301 log "Installing Android SDK command-line tools..."
302
303 if ! require_cmd unzip; then
304 err "unzip is required. Please install 'Build Tools' (Option 1) first."
305 return 1
306 fi
307
308 if [[ "$OS_LOWER" == "linux" && "$SYS_ARCH" != "amd64" ]]; then
309 err "Google's Android command-line tools installer supports Linux x86_64 only."
310 return 1
311 fi
312
313 ANDROID_HOME="$HOME/Android/Sdk"
314 ANDROID_SDK_ROOT="$ANDROID_HOME"
315 export ANDROID_HOME ANDROID_SDK_ROOT
316
317 mkdir -p "$ANDROID_HOME/cmdline-tools"
318
319 if [[ "$OS" == "macOS" ]]; then
320 TOOLS_OS="mac"
321 elif [[ "$OS_LOWER" == "linux" ]]; then
322 TOOLS_OS="linux"
323 else
324 err "Android SDK installation is not supported for $OS."
325 return 1
326 fi
327
328 log "Finding latest Android command-line tools from Google..."
329 TOOLS_URL=$(curl -fsSL https://developer.android.com/studio \
330 | grep -o "https://dl.google.com/android/repository/commandlinetools-${TOOLS_OS}-[0-9]*_latest.zip" \
331 | head -1)
332
333 if [[ -z "$TOOLS_URL" ]]; then
334 err "Could not find the Android command-line tools download URL."
335 return 1
336 fi
337
338 log "Downloading Android command-line tools..."
339 rm -rf /tmp/android-cmdline-tools /tmp/android-cmdline-tools.zip
340 mkdir -p /tmp/android-cmdline-tools
341 curl -fsSL -o /tmp/android-cmdline-tools.zip "$TOOLS_URL"
342 unzip -q /tmp/android-cmdline-tools.zip -d /tmp/android-cmdline-tools
343
344 rm -rf "$ANDROID_HOME/cmdline-tools/latest"
345 mkdir -p "$ANDROID_HOME/cmdline-tools/latest"
346 mv /tmp/android-cmdline-tools/cmdline-tools/* "$ANDROID_HOME/cmdline-tools/latest/"
347 rm -rf /tmp/android-cmdline-tools /tmp/android-cmdline-tools.zip
348
349 SDKMANAGER="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager"
350 if [[ ! -x "$SDKMANAGER" ]]; then
351 err "sdkmanager was not installed correctly."
352 return 1
353 fi
354
355 log "Accepting Android SDK licenses..."
356 yes | "$SDKMANAGER" --sdk_root="$ANDROID_HOME" --licenses >/dev/null || true
357
358 log "Installing Android SDK packages..."
359 "$SDKMANAGER" --sdk_root="$ANDROID_HOME" \
360 "cmdline-tools;latest" \
361 "platform-tools" \
362 "emulator" \
363 "platforms;android-36" \
364 "build-tools;36.0.0"
365
366 add_to_path_config "ANDROID_SDK" 'export ANDROID_HOME="$HOME/Android/Sdk"
367export ANDROID_SDK_ROOT="$ANDROID_HOME"
368export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator"'
369
370 log "Android SDK installation sequence finished!"
371}
372
373# =======================================================
374# CLOUD & INFRASTRUCTURE
375# =======================================================
376
377install_docker() {
378 log "Checking Docker status..."
379
380 # 1. Install if missing
381 if ! require_cmd docker; then
382 log "Installing Docker from official source..."
383 if [[ "$OS" == "macOS" ]]; then
384 [[ "$SYS_ARCH" == "arm64" ]] && DOCKER_MAC_ARCH="arm64" || DOCKER_MAC_ARCH="amd64"
385 DMG_URL="https://desktop.docker.com/mac/main/${DOCKER_MAC_ARCH}/Docker.dmg"
386 curl -fsSL -o /tmp/Docker.dmg "$DMG_URL"
387 hdiutil attach /tmp/Docker.dmg -nobrowse -mountpoint /Volumes/Docker
388 sudo cp -a /Volumes/Docker/Docker.app /Applications/
389 hdiutil detach /Volumes/Docker
390 rm /tmp/Docker.dmg
391
392 log "Starting Docker Desktop..."
393 open /Applications/Docker.app
394 log "Please complete the setup in the Docker Desktop UI."
395 else
396 curl -fsSL https://get.docker.com | sudo sh
397 fi
398 else
399 warn "Docker is already installed. Enforcing permissions..."
400 fi
401
402 # 2. Aggressively enforce Linux permissions
403 if [[ "$OS" != "macOS" ]]; then
404 log "Applying strict Linux post-install actions..."
405
406 # Ensure docker group exists and user is added
407 sudo groupadd -f docker
408 sudo usermod -aG docker "$USER"
409
410 # Ensure services are enabled and running
411 if command -v systemctl >/dev/null 2>&1; then
412 sudo systemctl enable --now docker.service
413 sudo systemctl enable --now docker.socket containerd.service 2>/dev/null || true
414 elif [[ "$OS" == "WSL" ]]; then
415 sudo service docker start
416 fi
417
418 # Give the system a second to generate the socket file
419 sleep 2
420
421 # Lock in ownership and permissions on the socket
422 if [[ -S /var/run/docker.sock ]]; then
423 log "Securing /var/run/docker.sock..."
424 sudo chown root:docker /var/run/docker.sock
425 sudo chmod 660 /var/run/docker.sock
426 else
427 warn "Docker socket not found. The service may have failed to start."
428 fi
429
430 log "Added $USER to the docker group and secured the socket."
431 fi
432}
433
434install_aws() {
435 if require_cmd aws; then warn "AWS CLI exists"; return; fi
436 log "Installing AWS CLI directly from Amazon..."
437
438 if [[ "$OS" == "macOS" ]]; then
439 curl -fsSL -o /tmp/AWSCLIV2.pkg "https://awscli.amazonaws.com/AWSCLIV2.pkg"
440 sudo installer -pkg /tmp/AWSCLIV2.pkg -target /
441 rm /tmp/AWSCLIV2.pkg
442 else
443 if ! require_cmd unzip; then
444 err "unzip is required. Please install 'Build Tools' (Option 1) first."
445 return 1
446 fi
447 curl -fsSL -o /tmp/awscliv2.zip "https://awscli.amazonaws.com/awscli-exe-linux-${AWS_ARCH}.zip"
448 cd /tmp && unzip -q awscliv2.zip && sudo ./aws/install
449 rm -rf /tmp/awscliv2.zip /tmp/aws
450 fi
451}
452
453install_gcloud() {
454 if require_cmd gcloud; then warn "Google Cloud CLI exists"; return; fi
455 log "Installing Google Cloud CLI..."
456 curl -fsSL https://sdk.cloud.google.com | $CURRENT_SHELL -s -- --disable-prompts
457
458 if [[ "$CURRENT_SHELL" == "zsh" ]]; then
459 add_to_path_config "GCLOUD" '[ -f "$HOME/google-cloud-sdk/path.zsh.inc" ] && source "$HOME/google-cloud-sdk/path.zsh.inc"'
460 else
461 add_to_path_config "GCLOUD" '[ -f "$HOME/google-cloud-sdk/path.bash.inc" ] && source "$HOME/google-cloud-sdk/path.bash.inc"'
462 fi
463}
464
465install_firebase() {
466 if require_cmd firebase; then warn "Firebase CLI exists"; return; fi
467 log "Installing Firebase CLI via NPM to ensure ARM64 compatibility..."
468
469 if ! require_cmd npm; then
470 err "NPM not found. Please install NVM (Option 3) first."
471 return 1
472 fi
473
474 npm install -g firebase-tools
475}
476
477# =======================================================
478# DEV TOOLS & SECURITY
479# =======================================================
480
481install_gh() {
482 if require_cmd gh; then warn "GitHub CLI exists"; return; fi
483 log "Fetching latest GitHub CLI version..."
484 LATEST_GH=$(curl -s https://api.github.com/repos/cli/cli/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
485 TAR_NAME="gh_${LATEST_GH}_${OS_LOWER}_${SYS_ARCH}"
486
487 curl -fsSL -o /tmp/gh.tar.gz "https://github.com/cli/cli/releases/download/v${LATEST_GH}/${TAR_NAME}.tar.gz"
488 tar -xzf /tmp/gh.tar.gz -C /tmp
489 sudo mv "/tmp/${TAR_NAME}/bin/gh" /usr/local/bin/
490 sudo rm -rf "/tmp/${TAR_NAME}" /tmp/gh.tar.gz
491}
492
493install_infisical() {
494 if require_cmd infisical; then warn "Infisical CLI exists"; return; fi
495 log "Installing Infisical CLI via NPM..."
496
497 if ! require_cmd npm; then
498 err "NPM not found. Please install NVM (Option 3) first."
499 return 1
500 fi
501
502 npm install -g @infisical/cli
503}
504
505# =======================================================
506# AI & AGENTIC TOOLS
507# =======================================================
508
509install_claude() { log "Installing Claude Code..."; curl -fsSL https://claude.ai/install.sh | $CURRENT_SHELL; }
510install_opencode() { log "Installing OpenCode..."; curl -fsSL https://opencode.ai/install | $CURRENT_SHELL; }
511
512install_codex() {
513 log "Installing @openai/codex..."
514 if ! require_cmd npm; then err "Node.js/NPM is required. Install NVM (3) first."; return 1; fi
515 npm i -g @openai/codex
516}
517
518# =======================================================
519# MENU LOGIC
520# =======================================================
521show_menu() {
522 clear
523 printf "${BLUE}==================================================${NC}\n"
524 printf "${GREEN} DEVELOPER ENVIRONMENT INSTALLER (Polyglot) ${NC}\n"
525 printf "${BLUE}==================================================${NC}\n"
526 printf "${YELLOW}--- Core Runtimes & Managers ---${NC}\n"
527 printf " 1) Build Tools (GCC/Make)\n"
528 printf " 2) Homebrew (Optional)\n"
529 printf " 3) NVM (Node.js)\n"
530 printf " 4) Python 3 (Official API)\n"
531 printf " 5) Go (Official -> /usr/local)\n"
532 printf " 6) SDKMAN (Java/Kotlin)\n"
533 printf " 7) .NET (Official Script)\n"
534 printf " 8) Android SDK (CLI + Platform Tools)\n"
535 printf "${YELLOW}--- Cloud & Infrastructure ---${NC}\n"
536 printf " 9) Docker (Official DMG/sh)\n"
537 printf " 10) AWS CLI (Official Bin)\n"
538 printf " 11) Google Cloud CLI (gcloud)\n"
539 printf " 12) Firebase CLI (NPM/ARM64 Safe)\n"
540 printf "${YELLOW}--- Dev Tools & Security ---${NC}\n"
541 printf " 13) GitHub CLI (Official Bin)\n"
542 printf " 14) Infisical (Official Bin)\n"
543 printf "${YELLOW}--- AI & Agentic Tools ---${NC}\n"
544 printf " 15) Claude Code CLI\n"
545 printf " 16) OpenCode (opencode.ai)\n"
546 printf " 17) OpenAI Codex CLI\n"
547 printf "${BLUE}==================================================${NC}\n"
548 printf " 99) Quit & Refresh Shell\n"
549 printf "${BLUE}==================================================${NC}\n"
550}
551
552while true; do
553 show_menu
554
555 # Bulletproof prompt for both Bash and Zsh
556 printf "Select options (space-separated): "
557 read input
558
559 # Thanks to 'setopt shwordsplit' in Zsh, this behaves perfectly across both shells
560 for choice in $input; do
561 case "$choice" in
562 1) install_build_tools ;;
563 2) install_brew ;;
564 3) install_nvm ;;
565 4) install_python ;;
566 5) install_go ;;
567 6) install_sdkman ;;
568 7) install_dotnet ;;
569 8) install_android ;;
570 9) install_docker ;;
571 10) install_aws ;;
572 11) install_gcloud ;;
573 12) install_firebase ;;
574 13) install_gh ;;
575 14) install_infisical ;;
576 15) install_claude ;;
577 16) install_opencode ;;
578 17) install_codex ;;
579 99)
580 log "Installation complete! Refreshing terminal environment..."
581
582 # If on Linux, forcefully inherit the new docker group without needing a logout
583 if [[ "$OS" != "macOS" ]] && command -v sg >/dev/null 2>&1; then
584 exec sg docker -c "exec ${SHELL:-zsh}"
585 else
586 exec "${SHELL:-zsh}"
587 fi
588 ;;
589 *) warn "Option $choice not valid." ;;
590 esac
591 done
592
593 printf "Press Enter to continue..."
594 read dummy
595done
596