From 0f71533a8caaa493e610959b71c400ea009bfb4b Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sun, 31 Oct 2021 22:39:25 +0000 Subject: [PATCH] feat(xbar): switch to xbar (BitBar replacement) and re-write my scripts This is a complete rewrite of my old scripts, and a wholesale replacement of BitBar's "brew-services" script with a more feature packed variant. --- bitbar | 1 + bitbar/brew-cask-upgrades.1h.sh | 57 ------- bitbar/brew-services.10m.rb | 119 -------------- bitbar/brew-upgrades.1h.sh | 101 ------------ install.sh | 10 ++ xbar/brew-cask-updates.1h.rb | 209 ++++++++++++++++++++++++ xbar/brew-formula-updates.1h.rb | 270 ++++++++++++++++++++++++++++++++ xbar/brew-services.10m.rb | 252 +++++++++++++++++++++++++++++ 8 files changed, 742 insertions(+), 277 deletions(-) create mode 120000 bitbar delete mode 100755 bitbar/brew-cask-upgrades.1h.sh delete mode 100755 bitbar/brew-services.10m.rb delete mode 100755 bitbar/brew-upgrades.1h.sh create mode 100755 xbar/brew-cask-updates.1h.rb create mode 100755 xbar/brew-formula-updates.1h.rb create mode 100755 xbar/brew-services.10m.rb diff --git a/bitbar b/bitbar new file mode 120000 index 0000000..9d52c84 --- /dev/null +++ b/bitbar @@ -0,0 +1 @@ +./xbar \ No newline at end of file diff --git a/bitbar/brew-cask-upgrades.1h.sh b/bitbar/brew-cask-upgrades.1h.sh deleted file mode 100755 index bcecba4..0000000 --- a/bitbar/brew-cask-upgrades.1h.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# Homebrew Cask Upgrades -# 1.0.0 -# Jim Myhrberg -# jimeh -# Show outdated Homebrew Cask formula -# https://i.imgur.com/VUMVwZM.png -# bash - -shopt -s extglob - -main() { - local count - local formulas - local current_version - local new_version - - formulas=() - current_version=() - new_version=() - - /usr/local/bin/brew update &> /dev/null - - while read -r line; do - if [[ "$line" =~ ^(.+)\ \((.+)\)\ !=\ (.*)$ ]]; then - formulas+=("${BASH_REMATCH[1]}") - current_version+=("${BASH_REMATCH[2]}") - new_version+=("${BASH_REMATCH[3]}") - fi - done < <(/usr/local/bin/brew outdated --cask --verbose) - - count="${#formulas[@]}" - - echo ":tropical_drink:↑${count} | dropdown=false" - echo '---' - if [ "$count" == "1" ]; then - echo "$count outdated cask" - else - echo "$count outdated casks" - fi - - if [ "$count" -gt 0 ]; then - echo 'Upgrade all casks | terminal=true refresh=true' \ - 'bash=/usr/local/bin/brew param1=upgrade param2=--cask' - echo '---' - - echo 'Upgrade:' - for i in "${!formulas[@]}"; do - echo "${formulas[$i]}" - echo "--${current_version[$i]} → ${new_version[$i]} |" \ - "terminal=true refresh=true bash=/usr/local/bin/brew param1=upgrade" \ - "param2=--cask param3=${formulas[$i]}" - done - fi -} - -main "$@" diff --git a/bitbar/brew-services.10m.rb b/bitbar/brew-services.10m.rb deleted file mode 100755 index 01322b2..0000000 --- a/bitbar/brew-services.10m.rb +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env ruby - -# Brew Services -# v1.0 -# Adam Lindberg -# eproxus -# Shows and manages Homebrew services. -# http://i.imgur.com/hVfhHYP.jpg -# ruby, brew, brew-services - -# BitBar Homebrew services plugin -# by Adam Lindbeng (@eproxus) - -#--- User parameters ---------------------------------------------------------- - -BAR_COLORS = true - -#--- Script internals --------------------------------------------------------- - -require 'pathname' - -SCRIPT_PATH = Pathname.new($0).realpath() -BREW = "/usr/local/bin/brew" -BREW_LINK = "http://brew.sh/" -BREW_SERVICES = "/usr/local/Homebrew/Library/Taps/homebrew/homebrew-services/cmd/services.rb" -BREW_SERVICES_LINK = "https://github.com/Homebrew/homebrew-services" - -REFRESH = "---\nRefresh | refresh=true" - -if BAR_COLORS - DARK_MODE=`defaults read -g AppleInterfaceStyle 2> /dev/null`.strip - RESET_COLOR = DARK_MODE == 'Dark' ? "\e[37m" : "\e[30m" -else - RESET_COLOR = "\e[37m" -end - -if !File.exist?(BREW) - puts [ - "Homebrew not installed | color=red", - "---", - "Install Homebrew... | href=#{BREW_LINK}", - REFRESH, - ].join("\n") - exit(1) -end -if !File.exist?(BREW_SERVICES) - puts [ - "Homebrew Services not installed | color=red", - "---", - "Install Homebrew Services... | href=#{BREW_SERVICES_LINK}", - REFRESH, - ].join("\n") - exit(1) -end - -def green(string) - "\e[1m\e[32m#{string}#{RESET_COLOR}" -end - -def service(command, name) - "bash=\"#{BREW}\"" \ - + " param1=services param2=#{command} param3=\"#{name}\"" \ - + " terminal=false refresh=true" -end - -def menu(name, status, user) - if status == "started" - [ - "#{name} | color=#4FFF50", - "--Restart | #{service("restart", name)}", - "--Stop | #{service("stop", name)}", - "-----", - "--State: #{status}", - "--User: #{user}", - ] - else - [ - name, - "--Start | #{service("start", name)}", - "-----", - "--State: #{status}", - ] - end -end - -def plural(count) - count <= 1 ? "#{count} Service" : "#{count} Services" -end - -output = `#{BREW} services list`.split("\n")[1..-1] - -services = output && output.reduce({started: 0, menus: []}) do |acc, service| - name, status, user, _plist = service.split - acc[:started] += 1 if status == "started" - acc[:menus] += menu(name, status, user) - acc -end - -total = (output || []).length -started = services[:started] -menus = services[:menus].join("\n") -all = "" -if total > 0 - all = """ -All ---Start #{plural(total - started)} | #{service("start", "--all")} ---Stop #{plural(started)} | #{service("stop", "--all")} ---Restart #{plural(total)} | #{service("restart", "--all")} -""" -end - -puts """ -#{started != 0 && BAR_COLORS ? green(started) : started}/#{total} ---- -#{menus} ---- -#{all} -#{REFRESH} -""" diff --git a/bitbar/brew-upgrades.1h.sh b/bitbar/brew-upgrades.1h.sh deleted file mode 100755 index ac4226d..0000000 --- a/bitbar/brew-upgrades.1h.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash -# Homebrew Upgrades -# 1.0.0 -# Jim Myhrberg -# jimeh -# Show outdated Homebrew formula -# https://i.imgur.com/bZn3RYs.png -# bash,comm,grep,printf - -shopt -s extglob - -main() { - local count - local pinned - local pinned_list - local formulas - local formulas_list - local current_version - local new_version - local pinned_version - local pkg - - pinned=() - formulas=() - current_version=() - new_version=() - pinned_version=() - - /usr/local/bin/brew update &> /dev/null - - while read -r line; do - pinned+=("$line") - done < <(/usr/local/bin/brew list --pinned) - - while read -r line; do - if [[ "$line" =~ ^(.+)\ \((.+)\)\ \<\ (.*)\ \[pinned\ at\ (.*)]$ ]]; then - formulas+=("${BASH_REMATCH[1]}") - current_version+=("${BASH_REMATCH[2]}") - new_version+=("${BASH_REMATCH[3]}") - pinned_version+=("${BASH_REMATCH[4]}") - elif [[ "$line" =~ ^(.+)\ \((.+)\)\ \<\ (.*)$ ]]; then - formulas+=("${BASH_REMATCH[1]}") - current_version+=("${BASH_REMATCH[2]}") - new_version+=("${BASH_REMATCH[3]}") - pinned_version+=("NONE") - fi - done < <(/usr/local/bin/brew outdated --verbose) - - pinned_list="$(printf '%s\n' "${pinned[@]}" | sort)" - formulas_list="$(printf '%s\n' "${formulas[@]}" | sort)" - - count="$( - comm -13 <(echo "$pinned_list") <(echo "$formulas_list") | - grep -c '[^[:space:]]' - )" - - echo ":beer:↑${count} | dropdown=false" - echo '---' - if [ "${#pinned[@]}" -gt 0 ]; then - echo "$count outdated formula (${#pinned[@]} pinned)" - else - echo "$count outdated formula" - fi - - if [ "$count" -gt 0 ]; then - echo 'Upgrade all formula | terminal=true refresh=true' \ - 'bash=/usr/local/bin/brew param1=upgrade' - echo '---' - - echo 'Upgrade:' - for i in "${!formulas[@]}"; do - pkg="${formulas[$i]}" - - if echo "$pinned_list" | grep "${pkg}" > /dev/null; then - continue - fi - - echo "$pkg" - echo "--${current_version[$i]} → ${new_version[$i]} |" \ - "terminal=true refresh=true bash=/usr/local/bin/brew" \ - "param1=upgrade param2=${pkg}" - done - - if [ "${#pinned[@]}" -gt 0 ]; then - echo '---' - echo 'Pinned:' - for i in "${!formulas[@]}"; do - pkg="${formulas[$i]}" - - if ! echo "$pinned_list" | grep "${pkg}" > /dev/null; then - continue - fi - - echo "$pkg" - echo "--${pinned_version[$i]} → ${new_version[$i]}" - done - fi - fi -} - -main "$@" diff --git a/install.sh b/install.sh index 4c38ada..c8ee691 100755 --- a/install.sh +++ b/install.sh @@ -85,6 +85,13 @@ install_launch_agents() { fi } +install_xbar_scripts() { + mkdir -p "$HOME/Library/Application Support/xbar/plugins" + for file in $ROOT_PATH/xbar/*; do + symlink "$file" "$HOME/Library/Application Support/xbar/plugins/$(basename "$file")" + done +} + install_terminfo() { for file in $ROOT_PATH/terminfo/*.terminfo; do log ok "tic -x" "$file" @@ -254,6 +261,9 @@ case "$1" in launch-agents | launch_agents | agents) install_launch_agents ;; + xbar_scripts | xbar-scripts | xbar) + install_xbar_scripts + ;; terminfo) install_terminfo ;; diff --git a/xbar/brew-cask-updates.1h.rb b/xbar/brew-cask-updates.1h.rb new file mode 100755 index 0000000..887f546 --- /dev/null +++ b/xbar/brew-cask-updates.1h.rb @@ -0,0 +1,209 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Brew Cask Updates +# v2.0.0 +# Jim Myhrberg +# jimeh +# Show outdated Homebrew casks +# https://i.imgur.com/aAD0pqO.png +# ruby +# +# string(VAR_BREW_PATH="/usr/local/bin/brew"): Path to "brew" executable. + +# rubocop:disable Style/IfUnlessModifier + +require 'open3' +require 'json' + +module Xbar + class Printer + attr_reader :nested_level + + SUB_STR = '--' + SEP_STR = '---' + PARAM_SEP = '|' + + def initialize(nested_level = 0) + @nested_level = nested_level + end + + def item(label = nil, **props) + print_item(label, **props) if !label.nil? && !label.empty? + + yield(sub_printer) if block_given? + end + + def separator + print_item(SEP_STR) + end + alias sep separator + + private + + def print_item(text, **props) + output = [text] + unless props.empty? + output << PARAM_SEP + output += props.map { |k, v| "#{k}=\"#{v}\"" } + end + + $stdout.print(SUB_STR * nested_level, output.join(' ')) + $stdout.puts + end + + def sub_printer + @sub_printer || self.class.new(nested_level + 1) + end + end +end + +module Brew + class CommandError < StandardError; end + + class Common + def self.prefix(value = nil) + return @prefix if value.nil? || value == '' + + @prefix = value + end + + private + + def prefix + self.class.prefix + end + + def default_printer + @default_printer ||= ::Xbar::Printer.new + end + + def cmd(*args) + out, err, s = Open3.capture3(*args) + raise CommandError, "#{args.join(' ')}: #{err}" if s.exitstatus != 0 + + out + end + + def brew_path + @brew_path ||= ENV.fetch('VAR_BREW_PATH', '/usr/local/bin/brew') + end + + def brew_check(printer = nil) + printer ||= default_printer + return if File.exist?(brew_path) + + printer.item("#{prefix}↑:warning:", dropdown: false) + printer.sep + printer.item('Homebrew not found', color: 'red') + printer.item("Executable \"#{brew_path}\" does not exist.") + printer.sep + printer.item( + 'Visit https://brew.sh/ for installation instructions', + href: 'https://brew.sh' + ) + + exit 0 + end + + def brew_update + cmd(brew_path, 'update') + rescue CommandError => e + # Continue as if nothing happened when brew update fails, as it likely + # to be due to another update process is already running. + end + end + + class Cask + attr_reader :name, :installed_version, :latest_version + + def initialize(attributes = {}) + @name = attributes['name'] + @installed_version = attributes['installed_versions'] + @latest_version = attributes['current_version'] + end + + alias current_version installed_version + end + + class CaskUpdates < Common + prefix ':tropical_drink:' + + def run + printer = default_printer + + brew_check(printer) + brew_update + + printer.item("#{prefix}↑#{casks.size}", dropdown: false) + printer.sep + printer.item('Brew Cask Updates') + printer.item("#{casks.size} outdated") do |printer| + printer.sep + printer.item(':hourglass: Refresh', refresh: true) + end + + print_casks(printer) + printer.sep + printer.item('Refresh', refresh: true) + end + + private + + def print_casks(printer) + return unless casks.size.positive? + + printer.item( + 'Upgrade all casks', + terminal: true, refresh: true, + shell: brew_path, param1: 'upgrade' + ) + printer.sep + printer.item('Upgrade:') + casks.each do |cask| + printer.item(cask.name) do |printer| + printer.item( + 'Upgrade', + terminal: true, refresh: true, shell: brew_path, + param1: 'upgrade', param2: '--cask', param3: cask.name + ) + printer.item( + "Upgrade (#{cask.current_version} → #{cask.latest_version})", + alternate: true, terminal: true, refresh: true, + shell: brew_path, param1: 'upgrade', param2: '--cask', + param3: cask.name + ) + printer.sep + printer.item("Installed: #{cask.installed_version}") + printer.item("Latest: #{cask.latest_version}") + printer.sep + printer.item('Uninstall') do |printer| + printer.item('Are you sure?') + printer.sep + printer.item( + 'Yes', + terminal: true, refresh: true, + shell: brew_path, param1: 'uninstall', + param2: '--cask', param3: cask.name + ) + end + end + end + end + + def casks + @casks ||= JSON.parse( + cmd(brew_path, 'outdated', '--cask', '--json') + )['casks'].map { |line| Cask.new(line) } + end + end +end + +begin + Brew::CaskUpdates.new.run +rescue StandardError => e + puts "ERROR: #{e.message}:\n\t#{e.backtrace.join("\n\t")}" + exit 1 +end + +# rubocop:enable Style/IfUnlessModifier diff --git a/xbar/brew-formula-updates.1h.rb b/xbar/brew-formula-updates.1h.rb new file mode 100755 index 0000000..efb609f --- /dev/null +++ b/xbar/brew-formula-updates.1h.rb @@ -0,0 +1,270 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Brew Formula Updates +# v2.0.0 +# Jim Myhrberg +# jimeh +# Show outdated Homebrew formulas +# https://i.imgur.com/6PC6OPg.png +# ruby +# +# string(VAR_BREW_PATH="/usr/local/bin/brew"): Path to "brew" executable. + +# rubocop:disable Style/IfUnlessModifier + +require 'open3' +require 'json' + +module Xbar + class Printer + attr_reader :nested_level + + SUB_STR = '--' + SEP_STR = '---' + PARAM_SEP = '|' + + def initialize(nested_level = 0) + @nested_level = nested_level + end + + def item(label = nil, **props) + print_item(label, **props) if !label.nil? && !label.empty? + + yield(sub_printer) if block_given? + end + + def separator + print_item(SEP_STR) + end + alias sep separator + + private + + def print_item(text, **props) + output = [text] + unless props.empty? + output << PARAM_SEP + output += props.map { |k, v| "#{k}=\"#{v}\"" } + end + + $stdout.print(SUB_STR * nested_level, output.join(' ')) + $stdout.puts + end + + def sub_printer + @sub_printer || self.class.new(nested_level + 1) + end + end +end + +module Brew + class CommandError < StandardError; end + + class Common + def self.prefix(value = nil) + return @prefix if value.nil? || value == '' + + @prefix = value + end + + private + + def prefix + self.class.prefix + end + + def default_printer + @default_printer ||= ::Xbar::Printer.new + end + + def cmd(*args) + out, err, s = Open3.capture3(*args) + raise CommandError, "#{args.join(' ')}: #{err}" if s.exitstatus != 0 + + out + end + + def brew_path + @brew_path ||= ENV.fetch('VAR_BREW_PATH', '/usr/local/bin/brew') + end + + def brew_check(printer = nil) + printer ||= default_printer + return if File.exist?(brew_path) + + printer.item("#{prefix}↑:warning:", dropdown: false) + printer.sep + printer.item('Homebrew not found', color: 'red') + printer.item("Executable \"#{brew_path}\" does not exist.") + printer.sep + printer.item( + 'Visit https://brew.sh/ for installation instructions', + href: 'https://brew.sh' + ) + + exit 0 + end + + def brew_update + cmd(brew_path, 'update') + rescue CommandError => e + # Continue as if nothing happened when brew update fails, as it likely + # to be due to another update process is already running. + end + end + + class Formula + attr_reader :name, :installed_versions, :latest_version, + :pinned, :pinned_version + + def initialize(attributes = {}) + @name = attributes['name'] + @installed_versions = attributes['installed_versions'] + @latest_version = attributes['current_version'] + @pinned = attributes['pinned'] + @pinned_version = attributes['pinned_version'] + end + + def current_version + installed_versions.last + end + end + + class FormulaUpdates < Common + prefix ':beer:' + + def run + printer = default_printer + + brew_check(printer) + brew_update + + printer.item("#{prefix}↑#{formulas.size}", dropdown: false) + printer.sep + printer.item('Brew Formula Updates') + pinned_msg = " / #{pinned.size} pinned" if pinned.size.positive? + printer.item( + "#{formulas.size} outdated#{pinned_msg}" + ) do |printer| + printer.sep + printer.item(':hourglass: Refresh', refresh: true) + end + + print_formulas(printer) + print_pinned(printer) + printer.sep + printer.item('Refresh', refresh: true) + end + + private + + def print_formulas(printer) + return unless formulas.size.positive? + + printer.item( + 'Upgrade all formula', + terminal: true, refresh: true, shell: brew_path, param1: 'upgrade' + ) + printer.sep + printer.item('Upgrade:') + formulas.each do |formula| + printer.item(formula.name) do |printer| + printer.item( + 'Upgrade', + terminal: true, refresh: true, + shell: brew_path, param1: 'upgrade', param2: formula.name + ) + printer.item( + "Upgrade (#{formula.current_version} → #{formula.latest_version})", + alternate: true, terminal: true, refresh: true, + shell: brew_path, param1: 'upgrade', param2: formula.name + ) + printer.sep + printer.item("Installed: #{formula.installed_versions.join(', ')}") + printer.item("Latest: #{formula.latest_version}") + printer.sep + printer.item( + 'Pin', + terminal: false, refresh: true, + shell: brew_path, param1: 'pin', param2: formula.name + ) + printer.item( + "Pin (to #{formula.current_version})", + alternate: true, terminal: false, refresh: true, + shell: brew_path, param1: 'pin', param2: formula.name + ) + printer.item('Uninstall') do |printer| + printer.item('Are you sure?') + printer.sep + printer.item( + 'Yes', + terminal: true, refresh: true, + shell: brew_path, param1: 'uninstall', param2: formula.name + ) + end + end + end + end + + def print_pinned(printer) + return unless pinned.size.positive? + + printer.sep + printer.item('Pinned:') + pinned.each do |formula| + printer.item(formula.name) do |printer| + printer.item('Upgrade') + printer.item( + "Upgrade (#{formula.current_version} → #{formula.latest_version})", + alternate: true + ) + printer.sep + printer.item("Pinned: #{formula.pinned_version}") + if formula.installed_versions.size > 1 + printer.item("Installed: #{formula.installed_versions.join(', ')}") + end + printer.item("Latest: #{formula.latest_version}") + printer.sep + printer.item( + 'Unpin', + terminal: false, refresh: true, + shell: brew_path, param1: 'unpin', param2: formula.name + ) + printer.item('Uninstall') do |printer| + printer.item('Are you sure?') + printer.sep + printer.item( + 'Yes', + terminal: true, refresh: true, + shell: brew_path, param1: 'uninstall', param2: formula.name + ) + end + end + end + end + + def formulas + @formulas ||= outdated.reject(&:pinned) + end + + def pinned + @pinned ||= outdated.select(&:pinned) + end + + def outdated + @outdated ||= JSON.parse( + cmd(brew_path, 'outdated', '--formula', '--json') + )['formulae'].map { |line| Formula.new(line) } + end + end +end + +begin + Brew::FormulaUpdates.new.run +rescue StandardError => e + puts "ERROR: #{e.message}:\n\t#{e.backtrace.join("\n\t")}" + exit 1 +end + +# rubocop:enable Style/IfUnlessModifier diff --git a/xbar/brew-services.10m.rb b/xbar/brew-services.10m.rb new file mode 100755 index 0000000..9fbd207 --- /dev/null +++ b/xbar/brew-services.10m.rb @@ -0,0 +1,252 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Brew Services +# v2.0.0 +# Jim Myhrberg +# jimeh +# List and manage Brew Services +# https://i.imgur.com/cAVfsvF.png +# ruby +# +# boolean(VAR_GROUPS=true): List services in started/stopped groups? +# string(VAR_BREW_PATH="/usr/local/bin/brew"): Path to "brew" executable. + +# rubocop:disable Style/IfUnlessModifier + +require 'open3' +require 'json' + +module Xbar + class Printer + attr_reader :nested_level + + SUB_STR = '--' + SEP_STR = '---' + PARAM_SEP = '|' + + def initialize(nested_level = 0) + @nested_level = nested_level + end + + def item(label = nil, **props) + print_item(label, **props) if !label.nil? && !label.empty? + + yield(sub_printer) if block_given? + end + + def separator + print_item(SEP_STR) + end + alias sep separator + + private + + def print_item(text, **props) + output = [text] + unless props.empty? + output << PARAM_SEP + output += props.map { |k, v| "#{k}=\"#{v}\"" } + end + + $stdout.print(SUB_STR * nested_level, output.join(' ')) + $stdout.puts + end + + def sub_printer + @sub_printer || self.class.new(nested_level + 1) + end + end +end + +module Brew + class CommandError < StandardError; end + + class Common + def self.prefix(value = nil) + return @prefix if value.nil? || value == '' + + @prefix = value + end + + private + + def prefix + self.class.prefix + end + + def default_printer + @default_printer ||= ::Xbar::Printer.new + end + + def cmd(*args) + out, err, s = Open3.capture3(*args) + raise CommandError, "#{args.join(' ')}: #{err}" if s.exitstatus != 0 + + out + end + + def brew_path + @brew_path ||= ENV.fetch('VAR_BREW_PATH', '/usr/local/bin/brew') + end + + def brew_check(printer = nil) + printer ||= default_printer + return if File.exist?(brew_path) + + printer.item("#{prefix}↑:warning:", dropdown: false) + printer.sep + printer.item('Homebrew not found', color: 'red') + printer.item("Executable \"#{brew_path}\" does not exist.") + printer.sep + printer.item( + 'Visit https://brew.sh/ for installation instructions', + href: 'https://brew.sh' + ) + + exit 0 + end + end + + class Service + attr_reader :name, :status, :user + + def self.from_line(line) + name, status, user, _plist = line.split + new(name: name, status: status, user: user) + end + + def initialize(name:, status:, user: nil) + @name = name + @status = status + @user = user + end + + def started? + @started ||= @status.downcase == 'started' + end + + def stopped? + !started? + end + end + + class Services < Common + prefix ':bulb:' + + def run + printer = default_printer + + brew_check(printer) + + printer.item( + "#{prefix}#{started_services.size}/#{stopped_services.size}", + dropdown: false + ) + printer.sep + printer.item('Brew Services') + printer.item( + "#{started_services.size} started / " \ + "#{stopped_services.size} stopped" + ) do |printer| + printer.sep + printer.item(':hourglass: Refresh', refresh: true) + end + printer.sep + use_groups? ? print_service_groups(printer) : print_services(printer) + end + + private + + def use_groups? + ENV.fetch('VAR_GROUPS', 'true') == 'true' + end + + def print_service_groups(printer) + printer.item('Started:') + started_services.each do |service| + print_service(printer, service) + end + printer.sep + printer.item('Stopped:') + stopped_services.each do |service| + print_service(printer, service) + end + end + + def print_services(printer) + services.each do |service| + print_service(printer, service) + end + end + + def print_service(printer, service) + label = if service.started? + ":white_check_mark: #{service.name}" + else + ":ballot_box_with_check: #{service.name}" + end + + printer.item(label) do |printer| + if service.started? + printer.item( + 'Stop', + terminal: false, refresh: true, shell: brew_path, + param1: 'services', param2: 'stop', param3: service.name + ) + else + printer.item( + 'Start', + terminal: false, refresh: true, shell: brew_path, + param1: 'services', param2: 'start', param3: service.name + ) + end + + printer.sep + printer.item("State: #{service.status}") + printer.item("User: #{service.user || ''}") + + if service.stopped? + printer.sep + printer.item('Uninstall') do |printer| + printer.item('Are you sure?') + printer.sep + printer.item( + 'Yes', + terminal: true, refresh: true, + shell: brew_path, param1: 'uninstall', + param2: service.name + ) + end + end + end + end + + def started_services + @started_services ||= services.select(&:started?) + end + + def stopped_services + @stopped_services ||= services.reject(&:started?) + end + + def services + @services ||= cmd( + brew_path, 'services', 'list' + ).each_line.each_with_object([]) do |line, memo| + next if line.match(/^Name\s+Status\s+User\s+Plist$/) + + memo.push(Service.from_line(line)) + end + end + end +end + +begin + Brew::Services.new.run +rescue StandardError => e + puts "ERROR: #{e.message}:\n\t#{e.backtrace.join("\n\t")}" + exit 1 +end + +# rubocop:enable Style/IfUnlessModifier