From 58773ac10866cc211e8187d402e38518cd8ff173 Mon Sep 17 00:00:00 2001 From: Saravanakumar D Date: Fri, 29 May 2026 16:04:17 -0700 Subject: [PATCH] feat(devops): add interactive WSL CLI installer script Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- install_clis_wsl.sh | 274 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100755 install_clis_wsl.sh diff --git a/install_clis_wsl.sh b/install_clis_wsl.sh new file mode 100755 index 0000000..4c935f2 --- /dev/null +++ b/install_clis_wsl.sh @@ -0,0 +1,274 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +LOG="$HOME/cli-install-wsl.log" +REPORT="$HOME/cli-install-report.md" +BACKUP_DIR="$HOME/cli-install-backups-wsl-$(date +%Y%m%d-%H%M%S)" +mkdir -p "$BACKUP_DIR" +exec > >(tee -a "$LOG") 2>&1 + +echo "=== CLI install script (interactive) ===" +echo "Log: $LOG" +echo + +# Helpers +prompt_yesno() { + local msg="$1" + local ans + while true; do + read -rp "$msg [y/n]: " ans + case "$ans" in + [Yy]*) return 0 ;; + [Nn]*) return 1 ;; + *) echo "Please answer y or n." ;; + esac + done +} + +preview_url() { + local url="$1" + echo + echo "Previewing first 200 lines of $url" + echo "---- BEGIN PREVIEW ----" + curl -fsSL "$url" 2>/dev/null | sed -n '1,200p' + echo "---- END PREVIEW ----" + echo +} + +safe_run_pipe_installer() { + # $1 = url, $2 = shell (bash|sh) + local url="$1"; local shellcmd="${2:-bash}" + echo "About to run installer from: $url" + if ! prompt_yesno "Have you reviewed the preview and want to execute it now?"; then + echo "Skipping $url" + return 1 + fi + # Execute installer (interactive; sudo prompts will show) + curl -fsSL "$url" | $shellcmd +} + +# 0) WSL check +if grep -qi microsoft /proc/version 2>/dev/null || grep -qi microsoft /proc/sys/kernel/osrelease 2>/dev/null; then + echo "Running inside WSL (or WSL2). Proceeding." +else + echo "Warning: This script is intended for WSL/Ubuntu. Continue only if you know what you are doing." + if ! prompt_yesno "Continue anyway?"; then + echo "Aborting." + exit 1 + fi +fi + +# 1) Detect environment +echo "== Environment ==" +uname -a +if command -v lsb_release >/dev/null 2>&1; then lsb_release -a || true; else cat /etc/os-release || true; fi +echo "Shell: $SHELL" +echo "User: $USER" +echo "Home: $HOME" +echo "PATH: $PATH" +echo "Arch: $(uname -m)" +echo + +# 2) Back up shell configs +echo "Backing up shell configs to $BACKUP_DIR" +for f in "$HOME/.bashrc" "$HOME/.profile" "$HOME/.zshrc"; do + if [ -f "$f" ]; then + cp -a "$f" "$BACKUP_DIR/$(basename "$f").bak" || true + echo "Backed up $f" + else + echo "No file: $f" + fi +done +echo + +# 3) Install prerequisites +if prompt_yesno "Install apt prerequisites (curl ca-certificates git unzip gnupg lsb-release build-essential)?"; then + sudo apt-get update + sudo apt-get install -y curl ca-certificates git unzip gnupg lsb-release build-essential apt-transport-https +else + echo "Skipping prerequisites install. Ensure required tools exist." +fi +echo + +# 4) Node check and optional install (Node 20 recommended) +NEED_NODE_INSTALL=0 +if command -v node >/dev/null 2>&1; then + NV="$(node -v 2>/dev/null || true)" + echo "Found node: $NV" + MAJ="$(echo "$NV" | sed -E 's/v([0-9]+).*/\1/')" || MAJ=0 + if [ -z "$MAJ" ] || [ "$MAJ" -lt 18 ]; then + NEED_NODE_INSTALL=1 + echo "Node major version $MAJ is older than 18; Node 20 is recommended." + fi +else + NEED_NODE_INSTALL=1 + echo "Node not found." +fi + +if [ "$NEED_NODE_INSTALL" -eq 1 ]; then + if prompt_yesno "Install Node 20 via NodeSource (recommended) now?"; then + curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - + sudo apt-get install -y nodejs + else + echo "Skipping Node install. Codex npm installer may require Node >=18." + fi +fi +echo "Node version: $(node -v 2>/dev/null || echo 'none')" +echo "npm version: $(npm -v 2>/dev/null || echo 'none')" +echo + +# 5) Ensure npm global prefix writable (user-level) +CUR_PREFIX="$(npm config get prefix 2>/dev/null || echo '')" +echo "Current npm prefix: $CUR_PREFIX" +if [ -z "$CUR_PREFIX" ] || [ ! -w "$CUR_PREFIX" ] || [ "$CUR_PREFIX" = "/usr" ]; then + echo "Configuring user npm global prefix at ~/.npm-global" + mkdir -p "$HOME/.npm-global" + npm config set prefix "$HOME/.npm-global" + if ! grep -q '\.npm-global' "$HOME/.profile" 2>/dev/null; then + printf '\n# npm global bin\nexport PATH="$HOME/.npm-global/bin:$HOME/.local/bin:$PATH"\n' >> "$HOME/.profile" + echo "Appended npm PATH to ~/.profile" + fi + export PATH="$HOME/.npm-global/bin:$HOME/.local/bin:$PATH" +fi +echo "Effective PATH now contains: $HOME/.npm-global/bin (if present)" +echo + +# 6) Claude Code CLI (official) +CLAUDE_URL="https://claude.ai/install.sh" +echo "Claude Code installer (official): $CLAUDE_URL" +preview_url "$CLAUDE_URL" +if prompt_yesno "Install Claude Code CLI now?"; then + safe_run_pipe_installer "$CLAUDE_URL" bash || echo "Claude installer failed or was skipped." +else + echo "Skipped Claude install." +fi +echo + +# 7) OpenAI Codex CLI (official) +echo "OpenAI Codex CLI preferred method: npm i -g @openai/codex" +if prompt_yesno "Attempt npm global install @openai/codex now?"; then + if npm i -g @openai/codex --no-progress; then + echo "Codex npm install succeeded." + else + echo "npm global install failed. You can choose to run the official installer fallback." + if prompt_yesno "Attempt official fallback installer (curl https://chatgpt.com/codex/install.sh | sh)?"; then + preview_url "https://chatgpt.com/codex/install.sh" + safe_run_pipe_installer "https://chatgpt.com/codex/install.sh" sh || echo "Codex fallback failed or skipped." + fi + fi +else + echo "Skipped Codex npm install." +fi +echo + +# 8) Devin CLI (official) +DEVIN_URL="https://cli.devin.ai/install.sh" +echo "Devin CLI official installer: $DEVIN_URL" +preview_url "$DEVIN_URL" +if prompt_yesno "Install Devin CLI now?"; then + safe_run_pipe_installer "$DEVIN_URL" bash || echo "Devin installer failed or was skipped." +else + echo "Skipped Devin install." +fi +echo + +# 9) Antigravity (agy) — manual only +ANTIGRAVITY_PAGE="https://antigravity.google/product/antigravity-cli" +echo "Antigravity CLI official docs page: $ANTIGRAVITY_PAGE" +echo "This script will not auto-run installers from Antigravity. Review the official page and follow their instructions." +if prompt_yesno "Fetch and preview the Antigravity docs page snippet?"; then + echo "Previewing page snippet (first 300 lines):" + curl -fsSL "$ANTIGRAVITY_PAGE" | sed -n '1,300p' || echo "Failed to fetch Antigravity page." + echo + echo "If the page specifies an official installer URL you trust, run it manually (example placeholder):" + echo " curl -fsSL | bash" +fi +echo + +# 10) Reload profile and PATH +echo "Reloading ~/.profile (if present) to pick PATH changes" +if [ -f "$HOME/.profile" ]; then + # shellcheck disable=SC1090 + source "$HOME/.profile" || true +fi +export PATH="$HOME/.npm-global/bin:$HOME/.local/bin:$PATH" +echo "PATH now: $PATH" +echo + +# 11) Verification +echo "=== Verification ===" +declare -A RESULTS +for cmd in claude codex agy devin; do + echo "---- $cmd ----" + if command -v "$cmd" >/dev/null 2>&1; then + p="$(command -v "$cmd")" + out="$($cmd --version 2>&1 || $cmd --help 2>&1 || echo 'no-version-output')" + echo "Found: $p" + echo "Output: $out" + RESULTS["$cmd"]="installed|$out|$p" + else + echo "Not found in PATH." + RESULTS["$cmd"]="not-installed||" + fi + echo +done + +# Also verify in a clean non-login shell +echo "=== Clean shell checks ===" +bash -lc 'command -v claude && claude --version 2>&1 || echo "claude: not-found"' +bash -lc 'command -v codex && codex --version 2>&1 || echo "codex: not-found"' +bash -lc 'command -v devin && (devin --version 2>&1 || devin --help 2>&1) || echo "devin: not-found"' +bash -lc 'command -v agy && agy --version 2>&1 || echo "agy: not-found"' + +# 12) Create report +echo "Writing report to $REPORT" +cat > "$REPORT" </dev/null || grep PRETTY_NAME /etc/os-release | cut -d= -f2-) +User: $USER +Home: $HOME + +Install summary: +EOF + +for key in claude codex devin agy; do + val="${RESULTS[$key]:-not-installed||}" + IFS='|' read -r inst ver path <<< "$val" + if [ "$inst" = "installed" ] || [ "$inst" = "installed_npm" ] || [ "$inst" = "installed_fallback" ]; then + status="yes" + elif [ "$inst" = "not-installed" ]; then + status="no" + else + status="$inst" + fi + { + echo + echo "## $key" + echo "Installed?: $status" + echo "Path: ${path:-}" + echo "Version/Output: ${ver:-}" + } >> "$REPORT" +done + +cat >> "$REPORT" <