commit b2beaf5bdcd46b762f8cd127091ceb0eabdb929f
parent 10d636c2d1bcb4398f73920763a6bf26b4593ea8
Author: Alex Balgavy <alex@balgavy.eu>
Date: Mon, 11 Jan 2021 12:06:19 +0100
updatemaster: begin Ruby rewrite
Diffstat:
M | scripts/updatemaster | | | 388 | ++++++++++++++++++++++--------------------------------------------------------- |
1 file changed, 108 insertions(+), 280 deletions(-)
diff --git a/scripts/updatemaster b/scripts/updatemaster
@@ -1,280 +1,108 @@
-#!/usr/bin/env bash
-# vim: foldmethod=marker foldlevel=0:
-
-# Function/variable definitions {{{
-casks=0
-formulas=0
-select_casks=0
-
-TICK="\e[1;32m\u2714\e[0m"
-CROSS="\e[1;31m\u2718\e[0m"
-
-echo_succ() {
- echo -e "[ $TICK ] \e[1;32m$1\e[0m"
-}
-echo_err() {
- echo -e "[ $CROSS ] \e[1;31m$1\e[0m"
-}
-echo_warn() {
- echo -e "[\e[1;34m!!!\e[0m] \e[1;34m$1\e[0m"
-}
-echo_progress() {
- echo -e "[\e[1;36m...\e[0m] \e[1;36m$1\e[0m"
-}
-hr() {
- cols=$(tput cols)
- printf '#%.0s' $(seq 1 "$cols")
-}
-clean_up() {
- kill $cache_sudo_pid &>/dev/null
- exit ${1:-0}
-}
-
-cache_sudo() {
- while :; do
- sudo -n true
- sleep 60
- kill -0 "$$" || clean_up
- done 2>/dev/null
-}
-# }}}
-
-# Parse options/arguments {{{
-while getopts "hfcns" opt; do
- case "$opt" in
- c)
- casks=1
- ;;
- f)
- formulas=1
- ;;
- s)
- select_casks=1
- ;;
- n)
- maintenance=1
- ;;
- h|*)
- echo "Update your Homebrew, Mac App Store, NPM, and PIP packages in one place."
- echo "Usage: updatemaster [-cfsnh]"
- echo "Options:"
- echo "-c: Upgrade all casks, without prompting."
- echo "-f: Upgrade all formulas, without prompting."
- echo "-s: Select which casks to upgrade."
- echo "-n: Don't upgrade anything"
- echo "-h: Show this help text."
- echo
- echo "Options can be combined. For example:"
- echo " updatemaster -fs"
- echo "Would update all formulas, and ask you to select which casks to upgrade."
- clean_up
- ;;
- esac
-done
-# }}}
-
-# Set up notification method {{{
-if command -v terminal-notifier &> /dev/null; then
- notify() { terminal-notifier -group updatemaster -sender updatemaster -title "$1" -message "$2" -activate com.googlecode.iterm2; }
-else
- echo_warn "terminal-notifier not found, defaulting to bell. Install terminal-notifier to get the most out of this script."
- notify() { echo -e '\a'; }
-fi
-# }}}
-
-echo_progress "### STARTING UPDATE SCRIPT ###\n"
-if [ -z "$maintenance" ]; then
- echo "Please enter the administrator password (required for installation/upgrading)."
- sudo -v
- [ $? -eq 0 ] || exit
- cache_sudo &
- cache_sudo_pid=$!
- trap clean_up SIGINT SIGKILL SIGQUIT SIGTERM
-fi
-
-# Homebrew {{{
-echo_progress "Homebrew update/upgrade."
-if command -v brew &> /dev/null; then
- brew update;
- brew update > /dev/null;
- outdated="$(brew outdated)"
- outdated_c="$(brew outdated --cask --greedy)"
- if [ "${#outdated_c}" -eq 0 ]; then
- echo_succ "No casks to update."
- else
- notify "Cask: Finished reading updates" "Click to view outdated casks."
- echo -e "Casks to update:"
- brew outdated --cask --greedy
-
- if [ "$select_casks" -eq 1 ]; then
- read -rp "Casks to upgrade (empty string cancels upgrade of casks): " -a CASKS;
- fi
- fi
-
- # Programs {{{
- if [[ "$maintenance" == 1 ]] || { [[ "$formulas" == 0 ]] && [[ "${#outdated}" != 0 ]]; }; then
- brew outdated
- echo_succ "No programs will be updated."
- elif [ "${#outdated}" -eq 0 ]; then
- echo -e "No programs to update."
- else
- echo -e "To update:\n"
- brew outdated;
- notify "Brew: Finished reading updates" "Click to view outdated packages."
- if [ "$formulas" -eq 1 ]; then
- brew upgrade
- fi
- fi
- # }}}
-
- # Casks {{{
- if [ "${#outdated_c}" -eq 0 ]; then
- echo_succ "No casks to update."
- elif [ "$casks" -eq 0 ] && [ "$select_casks" -eq 0 ]; then
- echo_succ "No casks will be updated."
- else
-
- if [ "$casks" -eq 1 ] && [ "$select_casks" -eq 0 ]; then
- brew cask upgrade --greedy
- elif [ "$select_casks" -eq 1 ]; then
- for i in ${CASKS[@]}; do
- if ! brew cask upgrade "$i"; then echo -e "Could not upgrade cask $i."; fi
- done
- else
- if [ "$maintenance" -eq 0 ]; then
- read -rsn 1 -p "Upgrade all? [Y(es)/n(o)/s(elect)] " CONF
-
- case $CONF in
- [Ss] | [Ss]elect )
- echo
- read -rp "Which casks would you like to upgrade? " -a CASKS;
- for i in "${CASKS[@]}"; do
- if ! brew cask upgrade "$i"; then echo -e "Could not upgrade cask $i."; fi
- done
- ;;
- [Yy] | [Yy]es )
- echo -e "\nContinuing with cask upgrade."
- brew cask upgrade --greedy;
- ;;
- * )
- echo -e "\nCancelled cask upgrade."
- ;;
- esac
- fi
- fi
- fi
- # }}}
-
- # Final steps & cleanup {{{
- brew cleanup -s --prune-prefix;
- brew cleanup;
- brew doctor;
- brew missing;
-
- echo "Homebrew cache disk usage:"
- du -skh "$(brew --cache)"
- echo "Remove with:"
- # shellcheck disable=SC2016
- echo ' rm -rf $(brew --cache)'
-
- echo "Backing up Brew installation..."
- brew bundle dump --force --file=~/.dotfiles/Brewfile;
-
- echo_succ "Backup complete."
- notify "Brewed." "All packages have been upgraded.";
- # }}}
-else
- echo_err "brew not found."
- echo_err "Cannot upgrade Homebrew packages. Please do so manually."
-fi
-# }}}
-
-hr
-
-# Mac App Store (MAS) {{{
-echo_progress "MAS update/upgrade."
-if command -v mas &> /dev/null; then
- echo -e "MAS outdated:"
- outdated=$(mas outdated)
-
- if [ "${#outdated}" -eq 0 ]; then
- echo_succ "Everything is up to date."
- else
- mas outdated
- read -rsn 1 -p "Continue? [Y/n]" CONF
-
- case $CONF in
- [Yy] )
- echo_succ -e "Continuing with mas upgrade."
- mas upgrade;
- ;;
-
- * )
- echo -e "Cancelled mas upgrade."
- ;;
- esac
- notify "MAS Updated." "All apps at newest version."
- fi
-else
- echo_err -e "mas not found. Install mas to update App Store apps on the command line."
- echo_err -e "Cannot update Mac App Store apps. Please do so manually."
-fi
-# }}}
-
-hr
-
-# Global pip packages {{{
-echo_progress "Pip global upgrade"
-if pip &> /dev/null; then
- val="$PIP_REQUIRE_VIRTUALENV"
- PIP_REQUIRE_VIRTUALENV=""
- pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U
- PIP_REQUIRE_VIRTUALENV="$val"
- echo_succ "Pip packages upgraded."
-else
- echo_err "Pip not found."
-fi
-# }}}
-
-hr
-
-# Global NPM packages {{{
-echo_progress "NPM global update/upgrade."
-if command -v npm &> /dev/null; then
- outdated=$(npm outdated)
- if [ "${#outdated}" -eq 0 ]; then
- echo_succ "Everything is up to date."
- else
- npm update -g
- npm install -g npm
- notify "NPM Update Finished" "See Terminal for info."
- fi
- npm cache verify
- echo_succ "NPM update complete."
-else
- echo_err "npm not found."
- echo_err "Cannot update NPM packages. Please do so manually."
-fi
-# }}}
-
-hr
-
-# Atom editor packages {{{
-if command -v apm &> /dev/null; then
- echo_progress "Atom package upgrades"
- if apm outdated &> /dev/null; then
- apm upgrade --no-confirm;
- echo_succ "All packages upgraded."
- else
- echo_err "Cannot upgrade Atom packages. Please do so manually."
- fi
- notify "APM Upgrade Complete" "See Terminal for info."
-else
- echo_err "Atom not installed (or APM not functioning properly)."
-fi
-# }}}
-
-hr
-
-echo_succ "All systems checked."
-notify "All systems checked." "Everything you indicated should be upgraded."
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+# TODO: check caffeinate termination
+
+def check_deps(deps)
+ deps.each do |dep|
+ unless system("command -v #{dep} >/dev/null 2>&1")
+ warn "error: command #{dep} not found"
+ exit 1
+ end
+ end
+end
+
+def stay_awake
+ system `caffeinate -d`
+end
+
+# The Homebrew package manager for macOS
+class Homebrew
+ def initialize
+ check_deps ['brew']
+ puts '-- HOMEBREW --'
+ 2.times { system 'brew update' }
+ @outdated_casks = `brew outdated --cask --greedy --verbose`.lines.map(&:chomp)
+ @cask_names = @outdated_casks.map { |c| c.split.first }
+ @outdated_formulae = `brew outdated --formula`
+ end
+
+ def prompt_user
+ puts "\nOutdated casks:"
+ @outdated_casks.each_with_index { |c, i| puts "#{i} #{c}" }
+ print 'Numbers of casks to upgrade (space-separated): '
+ begin
+ @to_upgrade = gets.chomp.split.map { |casknum| @cask_names[Integer(casknum)] }
+ rescue ArgumentError
+ warn 'Only numbers allowed'
+ exit 1
+ end
+
+ puts "Please enter the administrator password to install cask upgrades."
+ system 'sudo -v'
+ @sudo_cache_pid = spawn "while :; do sudo -n true; sleep 60; done 2>/dev/null"
+ end
+
+ def do_upgrade
+ puts "\nUpgrading all formulae.\nTo upgrade:"
+ puts @outdated_formulae
+ system 'brew upgrade --formula'
+
+ puts "\nUpgrading casks:"
+ puts @to_upgrade
+
+ @to_upgrade.each { |cask| system "brew upgrade --cask --greedy --verbose #{cask}" }
+ puts 'Upgrade complete.'
+ end
+
+ def cleanup
+ Process.kill "TERM", @sudo_cache_pid
+ system 'brew cleanup -s --prune-prefix'
+ system 'brew cleanup'
+ system 'brew doctor'
+ system 'brew missing'
+
+ puts "Homebrew cache disk usage: #{`du -skh "$(brew --cache)" | cut -f1`}"
+ puts "Remove with:\trm -rf $(brew --cache)"
+ system 'brew bundle dump --force --file=~/.dotfiles/Brewfile;'
+ end
+end
+
+# Pipx for Python
+class Pipx
+ def initialize
+ check_deps ['pipx']
+ puts '-- PIPX --'
+ end
+
+ def do_upgrade
+ system 'pipx upgrade-all'
+ end
+end
+
+begin
+ caffeine_thread = Thread.new { stay_awake }
+ package_systems = [Homebrew, Pipx]
+
+ # Do all the prompting up front
+ systems_with_prompt = package_systems.select { |s| s.method_defined? 'prompt_user' }
+ unless systems_with_prompt.empty?
+ system_instances = systems_with_prompt.map(&:new)
+ system_instances.each(&:prompt_user)
+ system_instances.each do |pksys|
+ pksys.do_upgrade
+ pksys.cleanup if pksys.class.method_defined? 'cleanup'
+ end
+ package_systems -= systems_with_prompt
+ end
+
+ # Complete remaining package systems
+ package_systems.each do |pksys_class|
+ pksys = pksys_class.new
+ pksys.prompt_user if pksys_class.method_defined? 'prompt_user'
+ pksys.do_upgrade
+ pksys.cleanup if pksys.class.method_defined? 'cleanup'
+ end
+rescue SystemExit, Interrupt
+ caffeine_thread.join
+ exit 0
+end