livecheck: progress bar for JSON output

Co-authored-by: Sam Ford <1584702+samford@users.noreply.github.com>
Co-authored-by: Dustin Rodrigues <dust.rod@gmail.com>
This commit is contained in:
nandahkrishna 2020-09-03 00:41:16 +05:30
parent e7ab97a818
commit cf293d0ef5
No known key found for this signature in database
GPG Key ID: 067E5FCD58ADF3AA
35 changed files with 1301 additions and 39 deletions

1
.gitignore vendored
View File

@ -130,7 +130,6 @@
**/vendor/bundle/ruby/*/gems/rubocop-0*/ **/vendor/bundle/ruby/*/gems/rubocop-0*/
**/vendor/bundle/ruby/*/gems/rubocop-ast-*/ **/vendor/bundle/ruby/*/gems/rubocop-ast-*/
**/vendor/bundle/ruby/*/gems/ruby-prof-*/ **/vendor/bundle/ruby/*/gems/ruby-prof-*/
**/vendor/bundle/ruby/*/gems/ruby-progressbar-*/
**/vendor/bundle/ruby/*/gems/simplecov-*/ **/vendor/bundle/ruby/*/gems/simplecov-*/
**/vendor/bundle/ruby/*/gems/simplecov-html-*/ **/vendor/bundle/ruby/*/gems/simplecov-html-*/
**/vendor/bundle/ruby/*/gems/sorbet-*/ **/vendor/bundle/ruby/*/gems/sorbet-*/

View File

@ -26,15 +26,17 @@ module Homebrew
switch "--full-name", switch "--full-name",
description: "Print formulae with fully-qualified names." description: "Print formulae with fully-qualified names."
flag "--tap=", flag "--tap=",
description: "Check the formulae within the given tap, specified as <user>`/`<repo>." description: "Check formulae within the given tap, specified as <user>`/`<repo>."
switch "--installed",
description: "Check formulae that are currently installed."
switch "--json",
description: "Output informations in JSON format."
switch "--all", switch "--all",
description: "Check all available formulae." description: "Check all available formulae."
switch "--installed",
description: "Check formulae that are currently installed."
switch "--newer-only", switch "--newer-only",
description: "Show the latest version only if it's newer than the formula." description: "Show the latest version only if it's newer than the formula."
switch "--json",
description: "Output information in JSON format."
switch "-q", "--quiet",
description: "Suppress warnings, don't print a progress bar for JSON output."
conflicts "--debug", "--json" conflicts "--debug", "--json"
conflicts "--tap=", "--all", "--installed" conflicts "--tap=", "--all", "--installed"
end end

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "livecheck/strategy" require "livecheck/strategy"
require "ruby-progressbar"
module Homebrew module Homebrew
# The `Livecheck` module consists of methods used by the `brew livecheck` # The `Livecheck` module consists of methods used by the `brew livecheck`
@ -66,6 +67,24 @@ module Homebrew
@livecheck_strategy_names.freeze @livecheck_strategy_names.freeze
has_a_newer_upstream_version = false has_a_newer_upstream_version = false
if args.json? && !args.quiet? && $stderr.tty?
total_formulae = if formulae_to_check == Formula
formulae_to_check.count
else
formulae_to_check.length
end
$stderr.puts Formatter.headline("Running checks", color: :blue)
progress = ProgressBar.create(
total: total_formulae,
progress_mark: "#",
remainder_mark: ".",
format: " %t: [%B] %c/%C ",
output: $stderr,
)
end
formulae_checked = formulae_to_check.sort.map.with_index do |formula, i| formulae_checked = formulae_to_check.sort.map.with_index do |formula, i|
if args.debug? && i.positive? if args.debug? && i.positive?
puts <<~EOS puts <<~EOS
@ -78,7 +97,7 @@ module Homebrew
skip_result = skip_conditions(formula, args: args) skip_result = skip_conditions(formula, args: args)
next skip_result if skip_result != false next skip_result if skip_result != false
formula.head.downloader.shutup! if formula.head? formula.head&.downloader&.shutup!
current = if formula.head? current = if formula.head?
formula.any_installed_version.version.commit formula.any_installed_version.version.commit
@ -136,6 +155,7 @@ module Homebrew
has_a_newer_upstream_version ||= true has_a_newer_upstream_version ||= true
if args.json? if args.json?
progress&.increment
info.except!(:meta) unless args.verbose? info.except!(:meta) unless args.verbose?
next info next info
end end
@ -146,6 +166,7 @@ module Homebrew
Homebrew.failed = true Homebrew.failed = true
if args.json? if args.json?
progress&.increment
status_hash(formula, "error", [e.to_s], args: args) status_hash(formula, "error", [e.to_s], args: args)
elsif !args.quiet? elsif !args.quiet?
onoe "#{Tty.blue}#{formula_name(formula, args: args)}#{Tty.reset}: #{e}" onoe "#{Tty.blue}#{formula_name(formula, args: args)}#{Tty.reset}: #{e}"
@ -157,7 +178,14 @@ module Homebrew
puts "No newer upstream versions." puts "No newer upstream versions."
end end
puts JSON.generate(formulae_checked.compact) if args.json? return unless args.json?
if progress
progress.finish
$stderr.print "#{Tty.up}#{Tty.erase_line}" * 2
end
puts JSON.generate(formulae_checked.compact)
end end
# Returns the fully-qualified name of a formula if the full_name argument is # Returns the fully-qualified name of a formula if the full_name argument is

View File

@ -53,6 +53,15 @@ module Tty
no_underline: 24, no_underline: 24,
}.freeze }.freeze
SPECIAL_CODES = {
up: "1A",
down: "1B",
right: "1C",
left: "1D",
erase_line: "K",
erase_char: "P",
}.freeze
CODES = COLOR_CODES.merge(STYLE_CODES).freeze CODES = COLOR_CODES.merge(STYLE_CODES).freeze
def append_to_escape_sequence(code) def append_to_escape_sequence(code)
@ -77,6 +86,16 @@ module Tty
end end
end end
SPECIAL_CODES.each do |name, code|
define_singleton_method(name) do
if $stdout.tty?
"\033[#{code}"
else
""
end
end
end
def to_s def to_s
return "" unless color? return "" unless color?

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'ruby-progressbar/output'
require 'ruby-progressbar/outputs/tty'
require 'ruby-progressbar/outputs/non_tty'
require 'ruby-progressbar/timer'
require 'ruby-progressbar/progress'
require 'ruby-progressbar/throttle'
require 'ruby-progressbar/calculators/length'
require 'ruby-progressbar/calculators/running_average'
require 'ruby-progressbar/components'
require 'ruby-progressbar/format'
require 'ruby-progressbar/base'
require 'ruby-progressbar/refinements' if Module.
private_instance_methods.
include?(:using)
class ProgressBar
def self.create(*args)
ProgressBar::Base.new(*args)
end
end

View File

@ -0,0 +1,183 @@
require 'forwardable'
class ProgressBar
class Base
extend Forwardable
def_delegators :output,
:clear,
:log,
:refresh
def_delegators :progressable,
:progress,
:total
def initialize(options = {})
self.autostart = options.fetch(:autostart, true)
self.autofinish = options.fetch(:autofinish, true)
self.finished = false
self.timer = Timer.new(options)
self.progressable = Progress.new(options)
options = options.merge(:timer => timer,
:progress => progressable)
self.title_comp = Components::Title.new(options)
self.bar = Components::Bar.new(options)
self.percentage = Components::Percentage.new(options)
self.rate = Components::Rate.new(options)
self.time = Components::Time.new(options)
self.output = Output.detect(options.merge(:bar => self))
@format = Format::String.new(output.resolve_format(options[:format]))
start :at => options[:starting_at] if autostart
end
def start(options = {})
timer.start
update_progress(:start, options)
end
def finish
return if finished?
output.with_refresh do
self.finished = true
progressable.finish
timer.stop
end
end
def pause
output.with_refresh { timer.pause } unless paused?
end
def stop
output.with_refresh { timer.stop } unless stopped?
end
def resume
output.with_refresh { timer.resume } if stopped?
end
def reset
output.with_refresh do
self.finished = false
progressable.reset
timer.reset
end
end
def stopped?
timer.stopped? || finished?
end
alias paused? stopped?
def finished?
finished || (autofinish && progressable.finished?)
end
def started?
timer.started?
end
def decrement
update_progress(:decrement)
end
def increment
update_progress(:increment)
end
def progress=(new_progress)
update_progress(:progress=, new_progress)
end
def total=(new_total)
update_progress(:total=, new_total)
end
def progress_mark=(mark)
output.refresh_with_format_change { bar.progress_mark = mark }
end
def remainder_mark=(mark)
output.refresh_with_format_change { bar.remainder_mark = mark }
end
def title
title_comp.title
end
def title=(title)
output.refresh_with_format_change { title_comp.title = title }
end
def to_s(new_format = nil)
self.format = new_format if new_format
Format::Formatter.process(@format, output.length, self)
end
# rubocop:disable Metrics/AbcSize, Metrics/LineLength
def to_h
{
'output_stream' => output.__send__(:stream),
'length' => output.length,
'title' => title_comp.title,
'progress_mark' => bar.progress_mark,
'remainder_mark' => bar.remainder_mark,
'progress' => progressable.progress,
'total' => progressable.total,
'percentage' => progressable.percentage_completed_with_precision.to_f,
'elapsed_time_in_seconds' => time.__send__(:timer).elapsed_seconds,
'estimated_time_remaining_in_seconds' => time.__send__(:estimated_seconds_remaining),
'base_rate_of_change' => rate.__send__(:base_rate),
'scaled_rate_of_change' => rate.__send__(:scaled_rate),
'unknown_progress_animation_steps' => bar.upa_steps,
'throttle_rate' => output.__send__(:throttle).rate,
'started?' => started?,
'stopped?' => stopped?,
'finished?' => finished?
}
end
# rubocop:enable Metrics/AbcSize, Metrics/LineLength
def inspect
"#<ProgressBar:#{progress}/#{total || 'unknown'}>"
end
def format=(other)
output.refresh_with_format_change do
@format = Format::String.new(other || output.default_format)
end
end
alias format format=
protected
attr_accessor :output,
:timer,
:progressable,
:title_comp,
:bar,
:percentage,
:rate,
:time,
:autostart,
:autofinish,
:finished
def update_progress(*args)
output.with_refresh do
progressable.__send__(*args)
timer.stop if finished?
end
end
end
end

View File

@ -0,0 +1,100 @@
class ProgressBar
module Calculators
class Length
attr_reader :length_override
attr_accessor :current_length,
:output
def initialize(options = {})
self.length_override = options[:length]
self.output = options[:output]
self.current_length = nil
end
def length
current_length || reset_length
end
def length_changed?
previous_length = current_length
self.current_length = calculate_length
previous_length != current_length
end
def calculate_length
length_override || terminal_width || 80
end
def reset_length
self.current_length = calculate_length
end
def length_override=(other)
@length_override ||= ENV['RUBY_PROGRESS_BAR_LENGTH'] || other
@length_override = @length_override.to_i if @length_override
end
private
# This code was copied and modified from Rake, available under MIT-LICENSE
# Copyright (c) 2003, 2004 Jim Weirich
# rubocop:disable Style/RescueStandardError
def terminal_width
return 80 unless unix?
result = dynamic_width
(result < 20) ? 80 : result
rescue
80
end
# rubocop:enable Style/RescueStandardError
# rubocop:disable Lint/DuplicateMethods
begin
require 'io/console'
def dynamic_width
if output && output.tty? && output.respond_to?(:winsize)
dynamic_width_via_output_stream_object
elsif IO.console
dynamic_width_via_io_object
else
dynamic_width_via_system_calls
end
end
rescue LoadError
def dynamic_width
dynamic_width_via_system_calls
end
end
# rubocop:enable Lint/DuplicateMethods
def dynamic_width_via_output_stream_object
_rows, columns = output.winsize
columns
end
def dynamic_width_via_io_object
_rows, columns = IO.console.winsize
columns
end
def dynamic_width_via_system_calls
dynamic_width_stty.nonzero? || dynamic_width_tput
end
def dynamic_width_stty
`stty size 2>/dev/null`.split[1].to_i
end
def dynamic_width_tput
`tput cols 2>/dev/null`.to_i
end
def unix?
RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
end
end
end
end

View File

@ -0,0 +1,9 @@
class ProgressBar
module Calculators
class RunningAverage
def self.calculate(current_average, new_value_to_average, smoothing_factor)
new_value_to_average * (1.0 - smoothing_factor) + current_average * smoothing_factor
end
end
end
end

View File

@ -0,0 +1,5 @@
require 'ruby-progressbar/components/bar'
require 'ruby-progressbar/components/percentage'
require 'ruby-progressbar/components/rate'
require 'ruby-progressbar/components/time'
require 'ruby-progressbar/components/title'

View File

@ -0,0 +1,102 @@
###
# UPA = Unknown Progress Animation
#
class ProgressBar
module Components
class Bar
DEFAULT_PROGRESS_MARK = '='.freeze
DEFAULT_REMAINDER_MARK = ' '.freeze
DEFAULT_UPA_STEPS = ['=---', '-=--', '--=-', '---='].freeze
attr_accessor :progress_mark,
:remainder_mark,
:length,
:progress,
:upa_steps
def initialize(options = {})
self.upa_steps = options[:unknown_progress_animation_steps] || DEFAULT_UPA_STEPS
self.progress_mark = options[:progress_mark] || DEFAULT_PROGRESS_MARK
self.remainder_mark = options[:remainder_mark] || DEFAULT_REMAINDER_MARK
self.progress = options[:progress]
self.length = options[:length]
end
def to_s(options = { :format => :standard })
if progress.unknown?
unknown_string
elsif options[:format] == :standard
"#{standard_complete_string}#{incomplete_string}"
elsif options[:format] == :integrated_percentage
"#{integrated_percentage_complete_string}#{incomplete_string}"
end
end
private
def integrated_percentage_complete_string
return standard_complete_string if completed_length < 5
" #{progress.percentage_completed} ".to_s.center(completed_length, progress_mark)
end
def standard_complete_string
progress_mark * completed_length
end
def incomplete_string
remainder_mark * (length - completed_length)
end
def bar(length)
self.length = length
standard_complete_string
end
def complete_bar(length)
self.length = length
to_s(:format => :standard)
end
def complete_bar_with_percentage(length)
self.length = length
to_s(:format => :integrated_percentage)
end
def unknown_string
unknown_frame_string = unknown_progress_frame * ((length / upa_steps.size) + 2)
unknown_frame_string[0, length]
end
def incomplete_space(length)
self.length = length
if progress.unknown?
unknown_string
else
incomplete_string
end
end
def bar_with_percentage(length)
self.length = length
integrated_percentage_complete_string
end
def completed_length
(length * progress.percentage_completed / 100).floor
end
def unknown_progress_frame
current_animation_step = progress.progress % upa_steps.size
upa_steps[current_animation_step]
end
end
end
end

View File

@ -0,0 +1,29 @@
class ProgressBar
module Components
class Percentage
attr_accessor :progress
def initialize(options = {})
self.progress = options[:progress]
end
private
def percentage
progress.percentage_completed
end
def justified_percentage
progress.percentage_completed.to_s.rjust(3)
end
def percentage_with_precision
progress.percentage_completed_with_precision
end
def justified_percentage_with_precision
progress.percentage_completed_with_precision.to_s.rjust(6)
end
end
end
end

View File

@ -0,0 +1,43 @@
class ProgressBar
module Components
class Rate
attr_accessor :rate_scale,
:started_at,
:stopped_at,
:timer,
:progress
def initialize(options = {})
self.rate_scale = options[:rate_scale] || lambda { |x| x }
self.started_at = nil
self.stopped_at = nil
self.timer = options[:timer]
self.progress = options[:progress]
end
private
def rate_of_change(format_string = '%i')
return 0 unless elapsed_seconds > 0
format_string % scaled_rate
end
def rate_of_change_with_precision
rate_of_change('%.2f')
end
def scaled_rate
rate_scale.call(base_rate)
end
def base_rate
progress.absolute / elapsed_seconds
end
def elapsed_seconds
timer.elapsed_whole_seconds.to_f
end
end
end
end

View File

@ -0,0 +1,107 @@
###
# OOB = 'Out of Bounds'
#
class ProgressBar
module Components
class Time
TIME_FORMAT = '%02d:%02d:%02d'.freeze
OOB_TIME_FORMATS = [:unknown, :friendly, nil].freeze
OOB_LIMIT_IN_HOURS = 99
OOB_UNKNOWN_TIME_TEXT = '??:??:??'.freeze
OOB_FRIENDLY_TIME_TEXT = '> 4 Days'.freeze
NO_TIME_ELAPSED_TEXT = '--:--:--'.freeze
ESTIMATED_LABEL = ' ETA'.freeze
ELAPSED_LABEL = 'Time'.freeze
def initialize(options = {})
self.out_of_bounds_time_format = options[:out_of_bounds_time_format]
self.timer = options[:timer]
self.progress = options[:progress]
end
def estimated_with_label
"#{ESTIMATED_LABEL}: #{estimated}"
end
def elapsed_with_label
"#{ELAPSED_LABEL}: #{elapsed}"
end
protected
def estimated_with_no_oob
self.out_of_bounds_time_format = nil
estimated_with_elapsed_fallback
end
def estimated_with_unknown_oob
self.out_of_bounds_time_format = :unknown
estimated_with_elapsed_fallback
end
def estimated_with_friendly_oob
self.out_of_bounds_time_format = :friendly
estimated_with_elapsed_fallback
end
attr_reader :out_of_bounds_time_format
attr_accessor :timer,
:progress
def out_of_bounds_time_format=(format)
unless OOB_TIME_FORMATS.include? format
fail 'Invalid Out Of Bounds time format. Valid formats are ' +
OOB_TIME_FORMATS.inspect
end
@out_of_bounds_time_format = format
end
private
def estimated
memo_estimated_seconds_remaining = estimated_seconds_remaining
return OOB_UNKNOWN_TIME_TEXT unless memo_estimated_seconds_remaining
hours, minutes, seconds = timer.divide_seconds(memo_estimated_seconds_remaining)
if hours > OOB_LIMIT_IN_HOURS && out_of_bounds_time_format
out_of_bounds_time
else
TIME_FORMAT % [hours, minutes, seconds]
end
end
def elapsed
return NO_TIME_ELAPSED_TEXT unless timer.started?
hours, minutes, seconds = timer.divide_seconds(timer.elapsed_whole_seconds)
TIME_FORMAT % [hours, minutes, seconds]
end
def estimated_with_elapsed_fallback
progress.finished? ? elapsed_with_label : estimated_with_label
end
def estimated_seconds_remaining
return if progress.unknown? || progress.none? || timer.stopped?
(timer.elapsed_seconds * (progress.total / progress.running_average - 1)).round
end
def out_of_bounds_time
case out_of_bounds_time_format
when :unknown
OOB_UNKNOWN_TIME_TEXT
when :friendly
OOB_FRIENDLY_TIME_TEXT
end
end
end
end
end

View File

@ -0,0 +1,13 @@
class ProgressBar
module Components
class Title
DEFAULT_TITLE = 'Progress'.freeze
attr_accessor :title
def initialize(options = {})
self.title = options[:title] || DEFAULT_TITLE
end
end
end
end

View File

@ -0,0 +1,4 @@
class ProgressBar
class InvalidProgressError < RuntimeError
end
end

View File

@ -0,0 +1,3 @@
require 'ruby-progressbar/format/molecule'
require 'ruby-progressbar/format/formatter'
require 'ruby-progressbar/format/string'

View File

@ -0,0 +1,27 @@
class ProgressBar
module Format
class Formatter
def self.process(format_string, max_length, bar)
processed_string = format_string.dup
format_string.non_bar_molecules.each do |molecule|
processed_string.gsub!(molecule.full_key, molecule.lookup_value(bar, nil))
end
processed_string.gsub!(/%%/, '%')
bar_length = max_length -
processed_string.displayable_length +
format_string.bar_molecule_placeholder_length
bar_length = (bar_length < 0) ? 0 : bar_length
format_string.bar_molecules.each do |molecule|
processed_string.gsub!(molecule.full_key,
molecule.lookup_value(bar, bar_length))
end
processed_string
end
end
end
end

View File

@ -0,0 +1,60 @@
class ProgressBar
module Format
class Molecule
MOLECULES = {
:t => [:title_comp, :title],
:T => [:title_comp, :title],
:c => [:progressable, :progress],
:C => [:progressable, :total],
:u => [:progressable, :total_with_unknown_indicator],
:p => [:percentage, :percentage],
:P => [:percentage, :percentage_with_precision],
:j => [:percentage, :justified_percentage],
:J => [:percentage, :justified_percentage_with_precision],
:a => [:time, :elapsed_with_label],
:e => [:time, :estimated_with_unknown_oob],
:E => [:time, :estimated_with_friendly_oob],
:f => [:time, :estimated_with_no_oob],
:B => [:bar, :complete_bar],
:b => [:bar, :bar],
:W => [:bar, :complete_bar_with_percentage],
:w => [:bar, :bar_with_percentage],
:i => [:bar, :incomplete_space],
:r => [:rate, :rate_of_change],
:R => [:rate, :rate_of_change_with_precision]
}.freeze
BAR_MOLECULES = %w{W w B b i}.freeze
attr_accessor :key,
:method_name
def initialize(letter)
self.key = letter
self.method_name = MOLECULES.fetch(key.to_sym)
end
def bar_molecule?
BAR_MOLECULES.include? key
end
def non_bar_molecule?
!bar_molecule?
end
def full_key
"%#{key}"
end
def lookup_value(environment, length = 0)
component = environment.__send__(method_name[0])
if bar_molecule?
component.__send__(method_name[1], length).to_s
else
component.__send__(method_name[1]).to_s
end
end
end
end
end

View File

@ -0,0 +1,36 @@
class ProgressBar
module Format
class String < ::String
MOLECULE_PATTERN = /%[a-zA-Z]/.freeze
ANSI_SGR_PATTERN = /\e\[[\d;]+m/.freeze
def displayable_length
gsub(ANSI_SGR_PATTERN, '').length
end
def bar_molecule_placeholder_length
@bar_molecule_placeholder_length ||= bar_molecules.size * 2
end
def non_bar_molecules
@non_bar_molecules ||= molecules.select(&:non_bar_molecule?)
end
def bar_molecules
@bar_molecules ||= molecules.select(&:bar_molecule?)
end
def molecules
@molecules ||= begin
molecules = []
scan(MOLECULE_PATTERN) do |match|
molecules << Molecule.new(match[1, 1])
end
molecules
end
end
end
end
end

View File

@ -0,0 +1,68 @@
class ProgressBar
class Output
DEFAULT_OUTPUT_STREAM = $stdout
attr_accessor :stream
def initialize(options = {})
self.bar = options[:bar]
self.stream = options[:output] || DEFAULT_OUTPUT_STREAM
self.throttle = Throttle.new(options)
self.length_calculator = Calculators::Length.new(
:length => options[:length],
:output => stream
)
end
def self.detect(options = {})
if options[:output].is_a?(Class) && options[:output] <= ProgressBar::Output
options[:output].new(options)
elsif (options[:output] || DEFAULT_OUTPUT_STREAM).tty?
Outputs::Tty.new(options)
else
Outputs::NonTty.new(options)
end
end
def log(string)
clear
stream.puts string
refresh(:force => true) unless bar.stopped?
end
def clear_string
' ' * length_calculator.length
end
def length
length_calculator.length
end
def with_refresh
yield
refresh
end
def refresh(options = {})
throttle.choke(:force_update_if => (bar.stopped? || options[:force])) do
clear if length_calculator.length_changed?
print_and_flush
end
end
protected
attr_accessor :length_calculator,
:throttle,
:bar
private
def print_and_flush
stream.print bar_update_string + eol
stream.flush
end
end
end

View File

@ -0,0 +1,47 @@
require 'ruby-progressbar/output'
class ProgressBar
module Outputs
class NonTty < Output
DEFAULT_FORMAT_STRING = '%t: |%b|'.freeze
def clear
self.last_update_length = 0
stream.print "\n"
end
def last_update_length
@last_update_length ||= 0
end
def bar_update_string
formatted_string = bar.to_s
formatted_string = formatted_string[0...-1] unless bar.finished?
output_string = formatted_string[last_update_length..-1]
self.last_update_length = formatted_string.length
output_string.to_s
end
def default_format
DEFAULT_FORMAT_STRING
end
def resolve_format(*)
default_format
end
def refresh_with_format_change(*); end
def eol
bar.stopped? ? "\n" : ''
end
protected
attr_writer :last_update_length
end
end
end

View File

@ -0,0 +1,33 @@
require 'ruby-progressbar/output'
class ProgressBar
module Outputs
class Null < Output
alias refresh_with_format_change with_refresh
def clear; end
def log(_string); end
def refresh(*); end
def clear_string
''
end
def bar_update_string
''
end
def default_format
''
end
def resolve_format(_format)
''
end
def eol
''
end
end
end
end

View File

@ -0,0 +1,32 @@
require 'ruby-progressbar/output'
class ProgressBar
module Outputs
class Tty < Output
DEFAULT_FORMAT_STRING = '%t: |%B|'.freeze
alias refresh_with_format_change with_refresh
def clear
stream.print clear_string
stream.print "\r"
end
def bar_update_string
bar.to_s
end
def default_format
DEFAULT_FORMAT_STRING
end
def resolve_format(other_format)
other_format || default_format
end
def eol
bar.stopped? ? "\n" : "\r"
end
end
end
end

View File

@ -0,0 +1,118 @@
require 'ruby-progressbar/errors/invalid_progress_error'
class ProgressBar
class Progress
DEFAULT_TOTAL = 100
DEFAULT_BEGINNING_POSITION = 0
DEFAULT_SMOOTHING = 0.1
attr_reader :total,
:progress
attr_accessor :starting_position,
:running_average,
:smoothing
def initialize(options = {})
self.total = options.fetch(:total, DEFAULT_TOTAL)
self.smoothing = options[:smoothing] || DEFAULT_SMOOTHING
start :at => DEFAULT_BEGINNING_POSITION
end
def start(options = {})
self.running_average = 0
self.progress = \
self.starting_position = options[:at] || progress
end
def finish
self.progress = total unless unknown?
end
def finished?
@progress == @total
end
def increment
if progress == total
warn "WARNING: Your progress bar is currently at #{progress} out of #{total} " \
"and cannot be incremented. In v2.0.0 this will become a " \
"ProgressBar::InvalidProgressError."
end
self.progress += 1 unless progress == total
end
def decrement
if progress == 0
warn "WARNING: Your progress bar is currently at #{progress} out of #{total} " \
"and cannot be decremented. In v2.0.0 this will become a " \
"ProgressBar::InvalidProgressError."
end
self.progress -= 1 unless progress == 0
end
def reset
start :at => starting_position
end
def progress=(new_progress)
if total && new_progress > total
fail ProgressBar::InvalidProgressError,
"You can't set the item's current value to be greater than the total."
end
@progress = new_progress
self.running_average = Calculators::RunningAverage.calculate(running_average,
absolute,
smoothing)
end
def total=(new_total)
unless progress.nil? || new_total.nil? || new_total >= progress
fail ProgressBar::InvalidProgressError,
"You can't set the item's total value to less than the current progress."
end
@total = new_total
end
def percentage_completed
return 0 if total.nil?
return 100 if total == 0
# progress / total * 100
#
# Doing this way so we can avoid converting each
# number to a float and then back to an integer.
#
(progress * 100 / total).to_i
end
def none?
running_average.zero? || progress.zero?
end
def unknown?
progress.nil? || total.nil?
end
def total_with_unknown_indicator
total || '??'
end
def percentage_completed_with_precision
return 100.0 if total == 0
return 0.0 if total.nil?
'%5.2f' % [(progress * 100 / total.to_f * 100).floor / 100.0]
end
def absolute
progress - starting_position
end
end
end

View File

@ -0,0 +1 @@
require 'ruby-progressbar/refinements/enumerator'

View File

@ -0,0 +1,23 @@
class ProgressBar
module Refinements
module Enumerator
refine ::Enumerator do
def with_progressbar(options = {}, &block)
chain = ::Enumerator.new do |yielder|
progress_bar = ProgressBar.create(options.merge(:starting_at => 0, :total => size))
each do |*args|
yielder.yield(*args).tap do
progress_bar.increment
end
end
end
return chain unless block_given?
chain.each(&block)
end
end
end
end
end

View File

@ -0,0 +1,25 @@
class ProgressBar
class Throttle
attr_accessor :rate,
:started_at,
:stopped_at,
:timer
def initialize(options = {})
self.rate = options[:throttle_rate] || 0.01
self.started_at = nil
self.stopped_at = nil
self.timer = options.fetch(:throttle_timer, Timer.new)
end
def choke(options = {})
return unless !timer.started? ||
options.fetch(:force_update_if, false) ||
timer.elapsed_seconds >= rate
timer.restart
yield
end
end
end

View File

@ -0,0 +1,32 @@
# rubocop:disable Style/InlineComment
class ProgressBar
class Time
TIME_MOCKING_LIBRARY_METHODS = [
:__simple_stub__now, # ActiveSupport
:now_without_mock_time, # Timecop
:now_without_delorean, # Delorean
:now # Unmocked
].freeze
def initialize(time = ::Time)
self.time = time
end
def now
time.__send__(unmocked_time_method)
end
def unmocked_time_method
@unmocked_time_method ||= begin
TIME_MOCKING_LIBRARY_METHODS.find do |method|
time.respond_to? method
end
end
end
protected
attr_accessor :time
end
end
# rubocop:enable Style/InlineComment

View File

@ -0,0 +1,72 @@
require 'ruby-progressbar/time'
class ProgressBar
class Timer
attr_accessor :started_at,
:stopped_at
def initialize(options = {})
self.time = options[:time] || ::ProgressBar::Time.new
end
def start
self.started_at = stopped? ? time.now - (stopped_at - started_at) : time.now
self.stopped_at = nil
end
def stop
return unless started?
self.stopped_at = time.now
end
def pause
stop
end
def resume
start
end
def started?
started_at
end
def stopped?
stopped_at
end
def reset
self.started_at = nil
self.stopped_at = nil
end
def reset?
!started_at
end
def restart
reset
start
end
def elapsed_seconds
((stopped_at || time.now) - started_at)
end
def elapsed_whole_seconds
elapsed_seconds.floor
end
def divide_seconds(seconds)
hours, seconds = seconds.divmod(3600)
minutes, seconds = seconds.divmod(60)
[hours, minutes, seconds]
end
protected
attr_accessor :time
end
end

View File

@ -0,0 +1,3 @@
class ProgressBar
VERSION = '1.10.1'.freeze
end

View File

@ -356,7 +356,18 @@ _brew_livecheck() {
local cur="${COMP_WORDS[COMP_CWORD]}" local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in case "$cur" in
-*) -*)
__brewcomp "--verbose --quiet --debug --full-name --tap --installed --json --all --newer-only --help" __brewcomp "
--full-name
--tap
--all
--installed
--newer-only
--json
--quiet
--debug
--verbose
--help
"
return return
;; ;;
esac esac

View File

@ -448,15 +448,15 @@ __fish_brew_complete_arg 'list ls;
__fish_brew_complete_cmd 'livecheck' "Check for newer versions of formulae from upstream" __fish_brew_complete_cmd 'livecheck' "Check for newer versions of formulae from upstream"
__fish_brew_complete_arg 'livecheck' -a '(__fish_brew_suggest_formulae_all)' __fish_brew_complete_arg 'livecheck' -a '(__fish_brew_suggest_formulae_all)'
__fish_brew_complete_arg 'livecheck' -s v -l verbose -d "Make some output more verbose"
__fish_brew_complete_arg 'livecheck' -s q -l quiet -d "Suppress any warnings"
__fish_brew_complete_arg 'livecheck' -s d -l debug -d "Display any debugging information"
__fish_brew_complete_arg 'livecheck' -l full-name -d "Print formulae with fully-qualified name" __fish_brew_complete_arg 'livecheck' -l full-name -d "Print formulae with fully-qualified name"
__fish_brew_complete_arg 'livecheck' -l tap -d "Check the formulae within the given tap, specified as user/repo" __fish_brew_complete_arg 'livecheck' -l tap -d "Check the formulae within the given tap, specified as user/repo"
__fish_brew_complete_arg 'livecheck' -l installed -d "Check formulae that are currently installed"
__fish_brew_complete_arg 'livecheck' -l json -d "Output information in JSON format"
__fish_brew_complete_arg 'livecheck' -l all -d "Check all available formulae" __fish_brew_complete_arg 'livecheck' -l all -d "Check all available formulae"
__fish_brew_complete_arg 'livecheck' -l newer-only -d "Show the latest version only if it is newer than the formula" __fish_brew_complete_arg 'livecheck' -l installed -d "Check formulae that are currently installed"
__fish_brew_complete_arg 'livecheck' -l newer-only -d "Show the latest version only if it's newer than the formula"
__fish_brew_complete_arg 'livecheck' -l json -d "Output information in JSON format"
__fish_brew_complete_arg 'livecheck' -s q -l quiet -d "Suppress warnings, don't print a progress bar for JSON output"
__fish_brew_complete_arg 'livecheck' -s d -l debug -d "Display any debugging information"
__fish_brew_complete_arg 'livecheck' -s v -l verbose -d "Make some output more verbose"
__fish_brew_complete_arg 'livecheck' -s h -l help -d "Show the help message" __fish_brew_complete_arg 'livecheck' -s h -l help -d "Show the help message"

View File

@ -508,15 +508,15 @@ _brew_list() {
# [--installed] [--json] [--all] [--newer-only] formulae # [--installed] [--json] [--all] [--newer-only] formulae
_brew_livecheck() { _brew_livecheck() {
_arguments \ _arguments \
'(--verbose,-v)'{--verbose,-v}'[Make some output more verbose]' \
'(--quiet,-q)'{--quiet,-q}'[Suppress any warnings]' \
'(--debug,-d)'{--debug,-d}'[Display any debugging information]' \
'--full-name[Print formulae with fully-qualified name]' \ '--full-name[Print formulae with fully-qualified name]' \
'--tap[Check the formulae within the given tap, specified as user/repo]' \ '--tap[Check the formulae within the given tap, specified as user/repo]' \
'--installed[Check formulae that are currently installed]' \
'--json[Output information in JSON format]' \
'--all[Check all available formulae]' \ '--all[Check all available formulae]' \
'--installed[Check formulae that are currently installed]' \
'--newer-only[Show the latest version only if it is newer than the formula]' \ '--newer-only[Show the latest version only if it is newer than the formula]' \
'--json[Output information in JSON format]' \
'(--quiet,-q)'{--quiet,-q}'[Suppress warnings, do not print a progress bar for JSON output]' \
'(--debug,-d)'{--debug,-d}'[Display any debugging information]' \
'(--verbose,-v)'{--verbose,-v}'[Make some output more verbose]' \
'(--help,-h)'{--help,-h}'[Show the help message]' \ '(--help,-h)'{--help,-h}'[Show the help message]' \
'*:: :__brew_formulae' '*:: :__brew_formulae'
} }
@ -590,7 +590,7 @@ _brew_pull() {
'--resolve[allow user to resolve patches that fail to apply]' \ '--resolve[allow user to resolve patches that fail to apply]' \
'--branch-okay[do not warn on pulling branches other than master]' \ '--branch-okay[do not warn on pulling branches other than master]' \
'--no-pbcopy[do not copy anything to the system]' \ '--no-pbcopy[do not copy anything to the system]' \
'--no-publish[do no publish bottles to Bintray]' \ '--no-publish[do not publish bottles to Bintray]' \
'*:patch source: ' '*:patch source: '
} }

View File

@ -1034,15 +1034,17 @@ or `~/.brew_livecheck_watchlist`.
* `--full-name`: * `--full-name`:
Print formulae with fully-qualified names. Print formulae with fully-qualified names.
* `--tap`: * `--tap`:
Check the formulae within the given tap, specified as *`user`*`/`*`repo`*. Check formulae within the given tap, specified as *`user`*`/`*`repo`*.
* `--installed`:
Check formulae that are currently installed.
* `--json`:
Output informations in JSON format.
* `--all`: * `--all`:
Check all available formulae. Check all available formulae.
* `--installed`:
Check formulae that are currently installed.
* `--newer-only`: * `--newer-only`:
Show the latest version only if it's newer than the formula. Show the latest version only if it's newer than the formula.
* `--json`:
Output information in JSON format.
* `-q`, `--quiet`:
Suppress warnings, don't print a progress bar for JSON output.
### `man` [*`options`*] ### `man` [*`options`*]

View File

@ -1428,24 +1428,28 @@ Print formulae with fully\-qualified names\.
. .
.TP .TP
\fB\-\-tap\fR \fB\-\-tap\fR
Check the formulae within the given tap, specified as \fIuser\fR\fB/\fR\fIrepo\fR\. Check formulae within the given tap, specified as \fIuser\fR\fB/\fR\fIrepo\fR\.
.
.TP
\fB\-\-installed\fR
Check formulae that are currently installed\.
.
.TP
\fB\-\-json\fR
Output informations in JSON format\.
. .
.TP .TP
\fB\-\-all\fR \fB\-\-all\fR
Check all available formulae\. Check all available formulae\.
. .
.TP .TP
\fB\-\-installed\fR
Check formulae that are currently installed\.
.
.TP
\fB\-\-newer\-only\fR \fB\-\-newer\-only\fR
Show the latest version only if it\'s newer than the formula\. Show the latest version only if it\'s newer than the formula\.
. .
.TP
\fB\-\-json\fR
Output information in JSON format\.
.
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Suppress warnings, don\'t print a progress bar for JSON output\.
.
.SS "\fBman\fR [\fIoptions\fR]" .SS "\fBman\fR [\fIoptions\fR]"
Generate Homebrew\'s manpages\. Generate Homebrew\'s manpages\.
. .