dotfiles

My personal shell configs and stuff
git clone git://git.alex.balgavy.eu/dotfiles.git
Log | Files | Refs | Submodules | README | LICENSE

commit b2beaf5bdcd46b762f8cd127091ceb0eabdb929f
parent 10d636c2d1bcb4398f73920763a6bf26b4593ea8
Author: Alex Balgavy <alex@balgavy.eu>
Date:   Mon, 11 Jan 2021 12:06:19 +0100

updatemaster: begin Ruby rewrite

Diffstat:
Mscripts/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