ZSH Logo

I have allot of different software tools on my mac that I need to keep up to date (brew, kubectl-krew, tldr, npm, cargo, pipx, gup, helm, mas) all with their own package managers.

I have a zsh script that I run to keep all of my software tools up to date.

Add the following to your .zshrc file or similar, and invoke it with update_everything in your terminal:

function update_everything() {
  local failed=()

  run_step() {
    local label="$1"
    shift
    local cmd=("$@")
    local error_output
    local exit_code
    local tmp_err
    
    # Create temporary file for stderr capture
    tmp_err=$(mktemp)
    
    # Run command, capturing stderr to temp file
    if "${cmd[@]}" 2>"$tmp_err" >/dev/null; then
      echo ">>> [$label] ✅ success"
      rm -f "$tmp_err"
    else
      exit_code=$?
      error_output=$(cat "$tmp_err" 2>/dev/null)
      rm -f "$tmp_err"
      
      echo ">>> [$label] ❌ failed (exit code: $exit_code)"
      echo "    └─ Command: ${cmd[*]}"
      if [[ -n "$error_output" ]]; then
        echo "    └─ Error output:"
        echo "$error_output" | sed 's/^/       │ /'
      else
        echo "    └─ (No error output captured)"
      fi
      failed+=("$label")
    fi
  }

  setopt local_options
  unsetopt monitor

  {
    run_step "brew" bash -c 'brew update && brew upgrade && brew cleanup --prune=all && rm -rf "$(brew --prefix)/var/homebrew/locks"'
  } &

  { run_step "kubectl-krew" bash -c 'kubectl krew update && kubectl krew upgrade'; } &
  { run_step "tldr" tldr --update; } &
  { run_step "npm" npm update -g; } &
  { run_step "cargo" cargo install-update -a; } &
  { run_step "pipx" pipx upgrade-all; } &
  { run_step "gup" gup update; } &
  { run_step "helm" helm repo update; } &
  { 
    run_step "mas" bash -c "
      mapfile -t mas_apps < <(mas outdated | awk '{\$1=\"\"; sub(/^ /,\"\"); sub(/ \\([0-9.]+\\)\$/,\"\"); print}')
  
      relaunch_apps=()
  
      for app in \"\${mas_apps[@]}\"; do
        if pgrep -fx \"\$app\" >/dev/null || pgrep -f \"/Applications/\$app.app\" >/dev/null; then
          osascript -e \"quit app \\\"\$app\\\"\" || true
          relaunch_apps+=(\"\$app\")
        fi
      done
  
      mas upgrade
  
      for app in \"\${relaunch_apps[@]}\"; do
        open -a \"\$app\" || true
      done
    "
  } &

  wait

  echo
  if (( ${#failed[@]} )); then
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo "❌ Summary: ${#failed[@]} step(s) failed"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    for f in "${failed[@]}"; do
      echo "  • $f"
    done
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  else
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo "✅ All updates completed successfully 🎉"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  fi
}

Each utility will be run in a separate background process (run_step), and the script will wait for all of them to complete before displaying the results.

Leave a Reply

Your email address will not be published. Required fields are marked *