diff options
Diffstat (limited to 'Library')
49 files changed, 617 insertions, 222 deletions
diff --git a/Library/Homebrew/blacklist.rb b/Library/Homebrew/blacklist.rb index 4312d0bf3..1c79da159 100644 --- a/Library/Homebrew/blacklist.rb +++ b/Library/Homebrew/blacklist.rb @@ -16,7 +16,7 @@ def blacklisted?(name) Homebrew provides pip via: `brew install python`. However you will then have two Pythons installed on your Mac, so alternatively you can install pip via the instructions at: - https://pip.readthedocs.io/en/stable/installing/ + #{Formatter.url("https://pip.readthedocs.io/en/stable/installing/")} EOS when "pil" then <<-EOS.undent Instead of PIL, consider `pip install pillow` or `brew install Homebrew/python/pillow`. @@ -24,7 +24,7 @@ def blacklisted?(name) when "macruby" then <<-EOS.undent MacRuby is not packaged and is on an indefinite development hiatus. You can read more about it at: - https://github.com/MacRuby/MacRuby + #{Formatter.url("https://github.com/MacRuby/MacRuby")} EOS when /(lib)?lzma/ "lzma is now part of the xz formula." @@ -50,7 +50,7 @@ def blacklisted?(name) To install Clojure you should install Leiningen: brew install leiningen and then follow the tutorial: - https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md + #{Formatter.url("https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md")} EOS when "osmium" then <<-EOS.undent The creator of Osmium requests that it not be packaged and that people @@ -65,8 +65,8 @@ def blacklisted?(name) brew install typesafe-activator You can read more about this change at: - https://www.playframework.com/documentation/2.3.x/Migration23 - https://www.playframework.com/documentation/2.3.x/Highlights23 + #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Migration23")} + #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Highlights23")} EOS when "haskell-platform" then <<-EOS.undent We no longer package haskell-platform. Consider installing ghc diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb index 1ba3fb8c2..4ab360995 100644 --- a/Library/Homebrew/brew.rb +++ b/Library/Homebrew/brew.rb @@ -32,20 +32,17 @@ begin empty_argv = ARGV.empty? help_flag_list = %w[-h --help --usage -?] - help_flag = false + help_flag = !ENV["HOMEBREW_HELP"].nil? internal_cmd = true cmd = nil ARGV.dup.each_with_index do |arg, i| break if help_flag && cmd - if help_flag_list.include?(arg) - # Option-style help: Both `--help <cmd>` and `<cmd> --help` are fine. - help_flag = true - elsif arg == "help" && !cmd + if arg == "help" && !cmd # Command-style help: `help <cmd>` is fine, but `<cmd> help` is not. help_flag = true - elsif !cmd + elsif !cmd && !help_flag_list.include?(arg) cmd = ARGV.delete_at(i) end end @@ -75,12 +72,9 @@ begin end # Usage instructions should be displayed if and only if one of: - # - a help flag is passed AND an internal command is matched + # - a help flag is passed AND a command is matched # - a help flag is passed AND there is no command specified # - no arguments are passed - # - # It should never affect external commands so they can handle usage - # arguments themselves. if empty_argv || help_flag require "cmd/help" Homebrew.help cmd, empty_argv: empty_argv @@ -147,8 +141,8 @@ rescue Exception => e Utils::Analytics.report_exception(e) onoe e if internal_cmd && defined?(OS::ISSUES_URL) - $stderr.puts "#{Tty.white}Please report this bug:" - $stderr.puts " #{Tty.em}#{OS::ISSUES_URL}#{Tty.reset}" + $stderr.puts "#{Tty.bold}Please report this bug:#{Tty.reset}" + $stderr.puts " #{Formatter.url(OS::ISSUES_URL)}" end $stderr.puts e.backtrace exit 1 diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index 1ccd8832a..8ffee44b3 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -183,6 +183,15 @@ then set -- "$@" -v fi +for arg in "$@" +do + if [[ $arg = "--help" || $arg = "-h" || $arg = "--usage" || $arg = "-?" ]] + then + export HOMEBREW_HELP="1" + break + fi +done + HOMEBREW_ARG_COUNT="$#" HOMEBREW_COMMAND="$1" shift @@ -272,6 +281,7 @@ setup-analytics report-analytics-screenview-command update-preinstall() { + [[ -z "$HOMEBREW_HELP" ]] || return [[ -z "$HOMEBREW_NO_AUTO_UPDATE" ]] || return [[ -z "$HOMEBREW_UPDATE_PREINSTALL" ]] || return diff --git a/Library/Homebrew/cask/lib/hbc/artifact/moved.rb b/Library/Homebrew/cask/lib/hbc/artifact/moved.rb index 6095887e3..22124005c 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/moved.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/moved.rb @@ -79,13 +79,10 @@ module Hbc load_specification artifact_spec if target.exist? - target_abv = " (#{target.abv})" + "#{printable_target} (#{target.abv})" else - warning = "Missing #{self.class.artifact_english_name}" - warning = "#{Tty.red}#{warning}#{Tty.reset}: " + Formatter.error(printable_target, label: "Missing #{self.class.artifact_english_name}") end - - "#{warning}#{printable_target}#{target_abv}" end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/symlinked.rb b/Library/Homebrew/cask/lib/hbc/artifact/symlinked.rb index 3ab45cccc..46dd42322 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/symlinked.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/symlinked.rb @@ -57,12 +57,17 @@ module Hbc def summarize_artifact(artifact_spec) load_specification artifact_spec - return unless self.class.islink?(target) - - link_description = "#{Tty.red}Broken Link#{Tty.reset}: " unless target.exist? - target_readlink_abv = " (#{target.readlink.abv})" if target.readlink.exist? + if self.class.islink?(target) && target.exist? && target.readlink.exist? + "#{printable_target} -> #{target.readlink} (#{target.readlink.abv})" + else + string = if self.class.islink?(target) + "#{printable_target} -> #{target.readlink}" + else + printable_target + end - "#{link_description}#{printable_target} -> #{target.readlink}#{target_readlink_abv}" + Formatter.error(string, label: "Broken Link") + end end end end diff --git a/Library/Homebrew/cask/lib/hbc/auditor.rb b/Library/Homebrew/cask/lib/hbc/auditor.rb index 6b0c1c476..ee1b50938 100644 --- a/Library/Homebrew/cask/lib/hbc/auditor.rb +++ b/Library/Homebrew/cask/lib/hbc/auditor.rb @@ -1,6 +1,23 @@ module Hbc class Auditor def self.audit(cask, audit_download: false, check_token_conflicts: false) + saved_languages = MacOS.instance_variable_get(:@languages) + + if languages_blocks = cask.instance_variable_get(:@dsl).instance_variable_get(:@language_blocks) + languages_blocks.keys.each do |languages| + ohai "Auditing language: #{languages.map { |lang| "'#{lang}'" }.join(", ")}" + MacOS.instance_variable_set(:@languages, languages) + audit_cask_instance(Hbc.load(cask.sourcefile_path), audit_download, check_token_conflicts) + CLI::Cleanup.run(cask.token) if audit_download + end + else + audit_cask_instance(cask, audit_download, check_token_conflicts) + end + ensure + MacOS.instance_variable_set(:@languages, saved_languages) + end + + def self.audit_cask_instance(cask, audit_download, check_token_conflicts) download = audit_download && Download.new(cask) audit = Audit.new(cask, download: download, check_token_conflicts: check_token_conflicts) diff --git a/Library/Homebrew/cask/lib/hbc/cask.rb b/Library/Homebrew/cask/lib/hbc/cask.rb index 756b05b83..1e2056efc 100644 --- a/Library/Homebrew/cask/lib/hbc/cask.rb +++ b/Library/Homebrew/cask/lib/hbc/cask.rb @@ -11,7 +11,10 @@ module Hbc @token = token @sourcefile_path = sourcefile_path @dsl = dsl || DSL.new(@token) - @dsl.instance_eval(&block) if block_given? + if block_given? + @dsl.instance_eval(&block) + @dsl.language_eval + end end DSL::DSL_METHODS.each do |method_name| diff --git a/Library/Homebrew/cask/lib/hbc/checkable.rb b/Library/Homebrew/cask/lib/hbc/checkable.rb index 42c47ea78..03f052629 100644 --- a/Library/Homebrew/cask/lib/hbc/checkable.rb +++ b/Library/Homebrew/cask/lib/hbc/checkable.rb @@ -28,11 +28,11 @@ module Hbc def result if errors? - "#{Tty.red}failed#{Tty.reset}" + Formatter.error("failed") elsif warnings? - "#{Tty.yellow}warning#{Tty.reset}" + Formatter.warning("warning") else - "#{Tty.green}passed#{Tty.reset}" + Formatter.success("passed") end end @@ -40,11 +40,11 @@ module Hbc summary = ["#{summary_header}: #{result}"] errors.each do |error| - summary << " #{Tty.red}-#{Tty.reset} #{error}" + summary << " #{Formatter.error("-")} #{error}" end warnings.each do |warning| - summary << " #{Tty.yellow}-#{Tty.reset} #{warning}" + summary << " #{Formatter.warning("-")} #{warning}" end summary.join("\n") diff --git a/Library/Homebrew/cask/lib/hbc/cli.rb b/Library/Homebrew/cask/lib/hbc/cli.rb index 3f67e131d..f637ae7af 100644 --- a/Library/Homebrew/cask/lib/hbc/cli.rb +++ b/Library/Homebrew/cask/lib/hbc/cli.rb @@ -179,6 +179,10 @@ module Hbc def self.parser # If you modify these arguments, please update USAGE.md @parser ||= OptionParser.new do |opts| + opts.on("--language STRING") do + # handled in OS::Mac + end + OPTIONS.each do |option, method| opts.on("#{option}" "PATH", Pathname) do |path| Hbc.public_send(method, path) diff --git a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb index 34f000b29..e36999200 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb @@ -122,11 +122,11 @@ module Hbc end def self.notfound_string - "#{Tty.red}Not Found - Unknown Error#{Tty.reset}" + Formatter.error("Not Found - Unknown Error") end def self.error_string(string = "Error") - "#{Tty.red}(#{string})#{Tty.reset}" + Formatter.error("(#{string})") end def self.render_with_none(string) diff --git a/Library/Homebrew/cask/lib/hbc/cli/info.rb b/Library/Homebrew/cask/lib/hbc/cli/info.rb index 7fbdff3eb..0957ba4fd 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/info.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/info.rb @@ -18,16 +18,16 @@ module Hbc def self.info(cask) puts "#{cask.token}: #{cask.version}" - puts formatted_url(cask.homepage) if cask.homepage + puts Formatter.url(cask.homepage) if cask.homepage installation_info(cask) - puts "From: #{formatted_url(github_info(cask))}" if github_info(cask) + puts "From: #{Formatter.url(github_info(cask))}" if github_info(cask) name_info(cask) artifact_info(cask) Installer.print_caveats(cask) end def self.formatted_url(url) - "#{Tty.em}#{url}#{Tty.reset}" + "#{Tty.underline}#{url}#{Tty.reset}" end def self.installation_info(cask) @@ -37,7 +37,7 @@ module Hbc puts versioned_staged_path.to_s .concat(" (") - .concat(versioned_staged_path.exist? ? versioned_staged_path.abv : "#{Tty.red}does not exist#{Tty.reset}") + .concat(versioned_staged_path.exist? ? versioned_staged_path.abv : Formatter.error("does not exist")) .concat(")") end else @@ -47,7 +47,7 @@ module Hbc def self.name_info(cask) ohai cask.name.size > 1 ? "Names" : "Name" - puts cask.name.empty? ? "#{Tty.red}None#{Tty.reset}" : cask.name + puts cask.name.empty? ? Formatter.error("None") : cask.name end def self.github_info(cask) diff --git a/Library/Homebrew/cask/lib/hbc/cli/style.rb b/Library/Homebrew/cask/lib/hbc/cli/style.rb index 66117da8b..bb179fb0c 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/style.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/style.rb @@ -30,7 +30,7 @@ module Hbc begin Homebrew.install_gem_setup_path! "rubocop-cask", RUBOCOP_CASK_VERSION, "rubocop" rescue SystemExit - raise CaskError, $stderr.string.chomp.sub("#{Tty.red}Error#{Tty.reset}: ", "") + raise CaskError, Tty.strip_ansi($stderr.string).chomp.sub(/\AError: /, "") end end end diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index 83c0bf1fb..8e0a7715a 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -1,4 +1,5 @@ require "set" +require "locale" require "hbc/dsl/appcast" require "hbc/dsl/base" @@ -64,6 +65,7 @@ module Hbc :depends_on, :gpg, :homepage, + :language, :license, :name, :sha256, @@ -98,6 +100,43 @@ module Hbc @homepage ||= homepage end + def language(*args, default: false, &block) + if !args.empty? && block_given? + @language_blocks ||= {} + @language_blocks[args] = block + + return unless default + + unless @language_blocks.default.nil? + raise CaskInvalidError.new(token, "Only one default language may be defined") + end + + @language_blocks.default = block + else + language_eval + end + end + + def language_eval + return @language if instance_variable_defined?(:@language) + + if @language_blocks.nil? || @language_blocks.empty? + return @language = nil + end + + MacOS.languages.map(&Locale.method(:parse)).each do |locale| + key = @language_blocks.keys.detect { |strings| + strings.any? { |string| locale.include?(string) } + } + + next if key.nil? + + return @language = @language_blocks[key].call + end + + @language = @language_blocks.default.call + end + def url(*args, &block) url_given = !args.empty? || block_given? return @url unless url_given diff --git a/Library/Homebrew/cask/lib/hbc/dsl/base.rb b/Library/Homebrew/cask/lib/hbc/dsl/base.rb index ccf93dae9..20a3cec61 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/base.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/base.rb @@ -8,7 +8,7 @@ module Hbc @command = command end - def_delegators :@cask, :token, :version, :caskroom_path, :staged_path, :appdir + def_delegators :@cask, :token, :version, :caskroom_path, :staged_path, :appdir, :language def system_command(executable, options = {}) @command.run!(executable, options) diff --git a/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb b/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb index 5efd7d562..9dd9abab1 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb @@ -102,12 +102,12 @@ module Hbc A report has been made to Apple about this app. Their certificate will hopefully be revoked. See the public report at - https://openradar.appspot.com/#{radar_number} + #{Formatter.url("https://openradar.appspot.com/#{radar_number}")} If this report is accurate, please duplicate it at - https://bugreport.apple.com/ + #{Formatter.url("https://bugreport.apple.com/")} If this report is a mistake, please let us know by opening an issue at - https://github.com/caskroom/homebrew-cask/issues/new + #{Formatter.url("https://github.com/caskroom/homebrew-cask/issues/new")} EOS end diff --git a/Library/Homebrew/cask/lib/hbc/installer.rb b/Library/Homebrew/cask/lib/hbc/installer.rb index f0cc0d1a9..4d29acb75 100644 --- a/Library/Homebrew/cask/lib/hbc/installer.rb +++ b/Library/Homebrew/cask/lib/hbc/installer.rb @@ -87,7 +87,7 @@ module Hbc s = if MacOS.version >= :lion && !ENV["HOMEBREW_NO_EMOJI"] (ENV["HOMEBREW_INSTALL_BADGE"] || "\xf0\x9f\x8d\xba") + " " else - "#{Tty.blue}==>#{Tty.reset} #{Tty.white}Success!#{Tty.reset} " + Formatter.headline("Success! ", color: :blue) end s << "#{@cask} was successfully installed!" end diff --git a/Library/Homebrew/cask/lib/hbc/utils.rb b/Library/Homebrew/cask/lib/hbc/utils.rb index 9159544a6..d42d78ef7 100644 --- a/Library/Homebrew/cask/lib/hbc/utils.rb +++ b/Library/Homebrew/cask/lib/hbc/utils.rb @@ -32,12 +32,7 @@ end def odebug(title, *sput) return unless Hbc.respond_to?(:debug) return unless Hbc.debug - - width = Tty.width * 4 - 6 - if $stdout.tty? && title.to_s.length > width - title = title.to_s[0, width - 3] + "..." - end - puts "#{Tty.magenta}==>#{Tty.reset} #{Tty.white}#{title}#{Tty.reset}" + puts Formatter.headline(title, color: :magenta) puts sput unless sput.empty? end @@ -151,12 +146,10 @@ module Hbc def self.error_message_with_suggestions <<-EOS.undent Most likely, this means you have an outdated version of Homebrew-Cask. Please run: + #{UPDATE_CMD} - #{Tty.green}#{UPDATE_CMD} - - #{Tty.reset}If this doesn’t fix the problem, please report this bug: - - #{Tty.em}#{ISSUES_URL}#{Tty.reset} + If this doesn’t fix the problem, please report this bug: + #{Formatter.url(ISSUES_URL)} EOS end diff --git a/Library/Homebrew/cask/spec/locale_spec.rb b/Library/Homebrew/cask/spec/locale_spec.rb new file mode 100644 index 000000000..98a2de913 --- /dev/null +++ b/Library/Homebrew/cask/spec/locale_spec.rb @@ -0,0 +1,72 @@ +require "spec_helper" +require "locale" + +describe Locale do + describe "::parse" do + it "parses a string in the correct format" do + expect(described_class.parse("zh")).to eql(described_class.new("zh", nil, nil)) + expect(described_class.parse("zh-CN")).to eql(described_class.new("zh", "CN", nil)) + expect(described_class.parse("zh-Hans")).to eql(described_class.new("zh", nil, "Hans")) + expect(described_class.parse("zh-CN-Hans")).to eql(described_class.new("zh", "CN", "Hans")) + end + + context "raises a ParserError when given" do + it "an empty string" do + expect{ described_class.parse("") }.to raise_error(Locale::ParserError) + end + + it "a string in a wrong format" do + expect { described_class.parse("zh_CN_Hans") }.to raise_error(Locale::ParserError) + expect { described_class.parse("zhCNHans") }.to raise_error(Locale::ParserError) + expect { described_class.parse("zh-CN_Hans") }.to raise_error(Locale::ParserError) + expect { described_class.parse("zhCN") }.to raise_error(Locale::ParserError) + expect { described_class.parse("zh_Hans") }.to raise_error(Locale::ParserError) + end + end + end + + describe "::new" do + it "raises an ArgumentError when all arguments are nil" do + expect { described_class.new(nil, nil, nil) }.to raise_error(ArgumentError) + end + + it "raises a ParserError when one of the arguments does not match the locale format" do + expect { described_class.new("ZH", nil, nil) }.to raise_error(Locale::ParserError) + expect { described_class.new(nil, "cn", nil) }.to raise_error(Locale::ParserError) + expect { described_class.new(nil, nil, "hans") }.to raise_error(Locale::ParserError) + end + end + + subject { described_class.new("zh", "CN", "Hans") } + + describe "#include?" do + it { is_expected.to include("zh") } + it { is_expected.to include("zh-CN") } + it { is_expected.to include("CN") } + it { is_expected.to include("CN-Hans") } + it { is_expected.to include("Hans") } + it { is_expected.to include("zh-CN-Hans") } + end + + describe "#eql?" do + subject { described_class.new("zh", "CN", "Hans") } + + context "all parts match" do + it { is_expected.to eql("zh-CN-Hans") } + it { is_expected.to eql(subject) } + end + + context "only some parts match" do + it { is_expected.to_not eql("zh") } + it { is_expected.to_not eql("zh-CN") } + it { is_expected.to_not eql("CN") } + it { is_expected.to_not eql("CN-Hans") } + it { is_expected.to_not eql("Hans") } + end + + it "does not raise if 'other' cannot be parsed" do + expect { subject.eql?("zh_CN_Hans") }.not_to raise_error + expect(subject.eql?("zh_CN_Hans")).to be false + end + end +end diff --git a/Library/Homebrew/cask/test/cask/dsl_test.rb b/Library/Homebrew/cask/test/cask/dsl_test.rb index ccf2f1a24..053eae1e1 100644 --- a/Library/Homebrew/cask/test/cask/dsl_test.rb +++ b/Library/Homebrew/cask/test/cask/dsl_test.rb @@ -122,6 +122,56 @@ describe Hbc::DSL do end end + describe "language stanza" do + it "allows multilingual casks" do + cask = lambda do + Hbc::Cask.new("cask-with-apps") do + language "zh" do + sha256 "abc123" + "zh-CN" + end + + language "en-US", default: true do + sha256 "xyz789" + "en-US" + end + + url "https://example.org/#{language}.zip" + end + end + + MacOS.stubs(languages: ["zh"]) + cask.call.language.must_equal "zh-CN" + cask.call.sha256.must_equal "abc123" + cask.call.url.to_s.must_equal "https://example.org/zh-CN.zip" + + MacOS.stubs(languages: ["zh-XX"]) + cask.call.language.must_equal "zh-CN" + cask.call.sha256.must_equal "abc123" + cask.call.url.to_s.must_equal "https://example.org/zh-CN.zip" + + MacOS.stubs(languages: ["en"]) + cask.call.language.must_equal "en-US" + cask.call.sha256.must_equal "xyz789" + cask.call.url.to_s.must_equal "https://example.org/en-US.zip" + + MacOS.stubs(languages: ["xx-XX"]) + cask.call.language.must_equal "en-US" + cask.call.sha256.must_equal "xyz789" + cask.call.url.to_s.must_equal "https://example.org/en-US.zip" + + MacOS.stubs(languages: ["xx-XX", "zh", "en"]) + cask.call.language.must_equal "zh-CN" + cask.call.sha256.must_equal "abc123" + cask.call.url.to_s.must_equal "https://example.org/zh-CN.zip" + + MacOS.stubs(languages: ["xx-XX", "en-US", "zh"]) + cask.call.language.must_equal "en-US" + cask.call.sha256.must_equal "xyz789" + cask.call.url.to_s.must_equal "https://example.org/en-US.zip" + end + end + describe "app stanza" do it "allows you to specify app stanzas" do cask = Hbc::Cask.new("cask-with-apps") do diff --git a/Library/Homebrew/cmd/doctor.rb b/Library/Homebrew/cmd/doctor.rb index df4bba419..cca2dca03 100644 --- a/Library/Homebrew/cmd/doctor.rb +++ b/Library/Homebrew/cmd/doctor.rb @@ -44,9 +44,9 @@ module Homebrew next if out.nil? || out.empty? if first_warning $stderr.puts <<-EOS.undent - #{Tty.white}Please note that these warnings are just used to help the Homebrew maintainers - with debugging if you file an issue. If everything you use Homebrew for is - working fine: please don't worry and just ignore them. Thanks!#{Tty.reset} + #{Tty.bold}Please note that these warnings are just used to help the Homebrew maintainers + with debugging if you file an issue. If everything you use Homebrew for is + working fine: please don't worry and just ignore them. Thanks!#{Tty.reset} EOS end diff --git a/Library/Homebrew/cmd/help.rb b/Library/Homebrew/cmd/help.rb index 793c765ca..982e24965 100644 --- a/Library/Homebrew/cmd/help.rb +++ b/Library/Homebrew/cmd/help.rb @@ -41,6 +41,8 @@ module Homebrew if cmd cmd = HOMEBREW_INTERNAL_COMMAND_ALIASES.fetch(cmd, cmd) path = Commands.path(cmd) + path ||= which("brew-#{cmd}") + path ||= which("brew-#{cmd}.rb") end # Display command-specific (or generic) help in response to `UsageError`. @@ -63,7 +65,7 @@ module Homebrew exit 0 end - # Resume execution in `brew.rb` for external/unknown commands. + # Resume execution in `brew.rb` for unknown commands. return if path.nil? # Display help for internal command (or generic help if undocumented). @@ -79,9 +81,10 @@ module Homebrew else help_lines.map do |line| line.slice(2..-1) - .sub(/^ \* /, "#{Tty.highlight}brew#{Tty.reset} ") - .gsub(/`(.*?)`/, "#{Tty.highlight}\\1#{Tty.reset}") - .gsub(/<(.*?)>/, "#{Tty.em}\\1#{Tty.reset}") + .sub(/^ \* /, "#{Tty.bold}brew#{Tty.reset} ") + .gsub(/`(.*?)`/, "#{Tty.bold}\\1#{Tty.reset}") + .gsub(%r{<([^\s]+?://[^\s]+?)>}) { |url| Formatter.url(url) } + .gsub(/<(.*?)>/, "#{Tty.underline}\\1#{Tty.reset}") .gsub("@hide_from_man_page", "") end.join.strip end diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index 857f1090e..fefabf85f 100644 --- a/Library/Homebrew/cmd/info.rb +++ b/Library/Homebrew/cmd/info.rb @@ -119,7 +119,7 @@ module Homebrew puts "#{f.full_name}: #{specs * ", "}#{" [#{attrs * ", "}]" unless attrs.empty?}" puts f.desc if f.desc - puts "#{Tty.em}#{f.homepage}#{Tty.reset}" if f.homepage + puts Formatter.url(f.homepage) if f.homepage conflicts = f.conflicts.map(&:name).sort! puts "Conflicts with: #{conflicts*", "}" unless conflicts.empty? @@ -135,7 +135,7 @@ module Homebrew end end - puts "From: #{Tty.em}#{github_info(f)}#{Tty.reset}" + puts "From: #{Formatter.url(github_info(f))}" unless f.deps.empty? ohai "Dependencies" diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index 328df07ce..ffb7eeab3 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -201,16 +201,6 @@ module Homebrew puts_columns(taps_search_results) puts "To install one of them, run (for example):\n brew install #{taps_search_results.first}" end - - # If they haven't updated in 48 hours (172800 seconds), that - # might explain the error - master = HOMEBREW_REPOSITORY/".git/refs/heads/master" - return unless master.exist? && (Time.now.to_i - File.mtime(master).to_i) > 172800 - ohai "You haven't updated Homebrew in a while." - puts <<-EOS.undent - A formula for #{e.name} might have been added recently. - Run `brew update` to get the latest Homebrew updates! - EOS end end end diff --git a/Library/Homebrew/cmd/list.rb b/Library/Homebrew/cmd/list.rb index ac13d9c86..01af678a7 100644 --- a/Library/Homebrew/cmd/list.rb +++ b/Library/Homebrew/cmd/list.rb @@ -106,7 +106,11 @@ module Homebrew names = if ARGV.named.empty? Formula.racks else - ARGV.named.map { |n| HOMEBREW_CELLAR+n }.select(&:exist?) + racks = ARGV.named.map { |n| HOMEBREW_CELLAR+n } + racks.select do |rack| + Homebrew.failed = true unless rack.exist? + rack.exist? + end end if ARGV.include? "--pinned" pinned_versions = {} diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index a7077d8db..a76167feb 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -113,6 +113,7 @@ module Homebrew end def install_core_tap_if_necessary + return if ENV["HOMEBREW_UPDATE_TEST"] core_tap = CoreTap.instance return if core_tap.installed? CoreTap.ensure_installed! quiet: false @@ -282,13 +283,13 @@ module Homebrew EOS rescue => e ofail <<-EOS.undent - #{Tty.white}Failed to migrate HOMEBREW_REPOSITORY to #{new_homebrew_repository}! + #{Tty.bold}Failed to migrate HOMEBREW_REPOSITORY to #{new_homebrew_repository}!#{Tty.reset} The error was: #{e} Please try to resolve this error yourself and then run `brew update` again to complete the migration. If you need help please +1 an existing error or comment with your new error in issue: - #{Tty.em}https://github.com/Homebrew/brew/issues/987#{Tty.reset} + #{Formatter.url("https://github.com/Homebrew/brew/issues/987")} EOS $stderr.puts e.backtrace end diff --git a/Library/Homebrew/compat.rb b/Library/Homebrew/compat.rb index 57c018c04..69b45683a 100644 --- a/Library/Homebrew/compat.rb +++ b/Library/Homebrew/compat.rb @@ -16,3 +16,4 @@ require "compat/dependency_collector" require "compat/language/haskell" require "compat/xcode" require "compat/software_spec" +require "compat/utils" diff --git a/Library/Homebrew/compat/utils.rb b/Library/Homebrew/compat/utils.rb index 7de5e85c3..7b529f04e 100644 --- a/Library/Homebrew/compat/utils.rb +++ b/Library/Homebrew/compat/utils.rb @@ -8,3 +8,11 @@ def shell_profile else "~/.bash_profile" end end + +module Tty + module_function + + def white + reset.bold + end +end diff --git a/Library/Homebrew/debrew.rb b/Library/Homebrew/debrew.rb index 1c5047a8c..668a0b4d2 100644 --- a/Library/Homebrew/debrew.rb +++ b/Library/Homebrew/debrew.rb @@ -111,7 +111,7 @@ module Debrew begin puts e.backtrace.first.to_s - puts "#{Tty.red}#{e.class.name}#{Tty.reset}: #{e}" + puts Formatter.error(e, label: e.class.name) loop do Menu.choose do |menu| diff --git a/Library/Homebrew/descriptions.rb b/Library/Homebrew/descriptions.rb index 0ef4316d3..cc690c050 100644 --- a/Library/Homebrew/descriptions.rb +++ b/Library/Homebrew/descriptions.rb @@ -119,12 +119,12 @@ class Descriptions # Take search results -- a hash mapping formula names to descriptions -- and # print them. def print - blank = "#{Tty.yellow}[no description]#{Tty.reset}" + blank = Formatter.warning("[no description]") @descriptions.keys.sort.each do |full_name| short_name = short_names[full_name] printed_name = short_name_counts[short_name] == 1 ? short_name : full_name description = @descriptions[full_name] || blank - puts "#{Tty.white}#{printed_name}:#{Tty.reset} #{description}" + puts "#{Tty.bold}#{printed_name}:#{Tty.reset} #{description}" end end diff --git a/Library/Homebrew/dev-cmd/bottle.rb b/Library/Homebrew/dev-cmd/bottle.rb index a087a5e4c..c6a8503b7 100644 --- a/Library/Homebrew/dev-cmd/bottle.rb +++ b/Library/Homebrew/dev-cmd/bottle.rb @@ -56,7 +56,7 @@ module Homebrew return if @put_filenames.include? filename - puts "#{Tty.red}#{filename}#{Tty.reset}" + puts Formatter.error(filename.to_s) @put_filenames << filename end @@ -72,7 +72,7 @@ module Homebrew if ARGV.verbose? print_filename.call(string, file) unless linked_libraries.empty? linked_libraries.each do |lib| - puts " #{Tty.gray}-->#{Tty.reset} links to #{lib}" + puts " #{Tty.bold}-->#{Tty.reset} links to #{lib}" end end @@ -95,7 +95,7 @@ module Homebrew next unless ARGV.verbose? && !text_matches.empty? print_filename.call(string, file) text_matches.first(MAXIMUM_STRING_MATCHES).each do |match, offset| - puts " #{Tty.gray}-->#{Tty.reset} match '#{match}' at offset #{Tty.em}0x#{offset}#{Tty.reset}" + puts " #{Tty.bold}-->#{Tty.reset} match '#{match}' at offset #{Tty.bold}0x#{offset}#{Tty.reset}" end if text_matches.size > MAXIMUM_STRING_MATCHES diff --git a/Library/Homebrew/dev-cmd/tests.rb b/Library/Homebrew/dev-cmd/tests.rb index 1b6af2850..72f8e0375 100644 --- a/Library/Homebrew/dev-cmd/tests.rb +++ b/Library/Homebrew/dev-cmd/tests.rb @@ -9,6 +9,8 @@ module Homebrew def tests (HOMEBREW_LIBRARY/"Homebrew").cd do + ENV.delete "HOMEBREW_VERBOSE" + ENV.delete "VERBOSE" ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "1" ENV["HOMEBREW_DEVELOPER"] = "1" ENV["TESTOPTS"] = "-v" if ARGV.verbose? diff --git a/Library/Homebrew/dev-cmd/update-test.rb b/Library/Homebrew/dev-cmd/update-test.rb index 7f20104d8..8bb0fa057 100644 --- a/Library/Homebrew/dev-cmd/update-test.rb +++ b/Library/Homebrew/dev-cmd/update-test.rb @@ -18,7 +18,14 @@ module Homebrew module_function def update_test - ENV["HOMEBREW_UPDATE_TO_TAG"] = "1" if ARGV.include?("--to-tag") + ENV["HOMEBREW_UPDATE_TEST"] = "1" + + if ARGV.include?("--to-tag") + ENV["HOMEBREW_UPDATE_TO_TAG"] = "1" + branch = "stable" + else + branch = "master" + end cd HOMEBREW_REPOSITORY start_commit = if commit = ARGV.value("commit") @@ -59,10 +66,10 @@ module Homebrew # run brew update oh1 "Running brew update..." safe_system "brew", "update", "--verbose" - actual_end_commit = Utils.popen_read("git", "rev-parse", "master").chomp + actual_end_commit = Utils.popen_read("git", "rev-parse", branch).chomp if start_commit != end_commit && start_commit == actual_end_commit raise <<-EOS.undent - brew update didn't update master! + brew update didn't update #{branch}! Start commit: #{start_commit} Expected end commit: #{end_commit} Actual end commit: #{actual_end_commit} diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index ca8ade9ff..0db92592c 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -389,10 +389,10 @@ module Homebrew return if HOMEBREW_PREFIX.to_s == "/usr/local" <<-EOS.undent - Your Homebrew is not installed to /usr/local - You can install Homebrew anywhere you want but some bottles (binary - packages) can only be used in /usr/local and some formulae (packages) - may not build correctly unless you install in /usr/local. Sorry! + Your Homebrew's prefix is not /usr/local. + You can install Homebrew anywhere you want but some bottles (binary packages) + can only be used with a /usr/local prefix and some formulae (packages) + may not build correctly with a non-/usr/local prefix. EOS end @@ -473,7 +473,7 @@ module Homebrew If you have trouble downloading packages with Homebrew, then maybe this is the problem? If the following command doesn't work, then try removing your curlrc: - curl https://github.com + curl #{Formatter.url("https://github.com")} EOS end @@ -610,7 +610,7 @@ module Homebrew Setting DYLD_INSERT_LIBRARIES can cause Go builds to fail. Having this set is common if you use this software: - http://asepsis.binaryage.com/ + #{Formatter.url("http://asepsis.binaryage.com/")} EOS end @@ -752,7 +752,7 @@ module Homebrew Without a correctly configured origin, Homebrew won't update properly. You can solve this by adding the Homebrew remote: cd #{HOMEBREW_REPOSITORY} - git remote add origin https://github.com/Homebrew/brew.git + git remote add origin #{Formatter.url("https://github.com/Homebrew/brew.git")} EOS elsif origin !~ %r{Homebrew/brew(\.git)?$} <<-EOS.undent @@ -764,7 +764,7 @@ module Homebrew Unless you have compelling reasons, consider setting the origin remote to point at the main repository, located at: - https://github.com/Homebrew/brew.git + #{Formatter.url("https://github.com/Homebrew/brew.git")} EOS end end @@ -956,8 +956,8 @@ module Homebrew <<-EOS.undent A .pydistutils.cfg file was found in $HOME, which may cause Python builds to fail. See: - https://bugs.python.org/issue6138 - https://bugs.python.org/issue4655 + #{Formatter.url("https://bugs.python.org/issue6138")} + #{Formatter.url("https://bugs.python.org/issue4655")} EOS end diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index 07658c6e7..1b2b24691 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -294,6 +294,17 @@ class FormulaConflictError < RuntimeError end end +class FormulaAmbiguousPythonError < RuntimeError + def initialize(formula) + super <<-EOS.undent + The version of python to use with the virtualenv in the `#{formula.full_name}` formula + cannot be guessed automatically. If the simultaneous use of python and python3 + is intentional, please add `:using => "python"` or `:using => "python3"` to + `virtualenv_install_with_resources` to resolve the ambiguity manually. + EOS + end +end + class BuildError < RuntimeError attr_reader :formula, :env @@ -318,7 +329,7 @@ class BuildError < RuntimeError def dump if !ARGV.verbose? puts - puts "#{Tty.red}READ THIS#{Tty.reset}: #{Tty.em}#{OS::ISSUES_URL}#{Tty.reset}" + puts Formatter.error(Formatter.url(OS::ISSUES_URL), label: "READ THIS") if formula.tap case formula.tap.name when "homebrew/boneyard" @@ -327,7 +338,7 @@ class BuildError < RuntimeError else if issues_url = formula.tap.issues_url puts "If reporting this issue please do so at (not Homebrew/brew):" - puts " #{issues_url}" + puts " #{Formatter.url(issues_url)}" end end end diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb index 063558f19..0ac95bfd9 100644 --- a/Library/Homebrew/extend/os/mac/diagnostic.rb +++ b/Library/Homebrew/extend/os/mac/diagnostic.rb @@ -65,8 +65,8 @@ module Homebrew return unless MacOS::Xcode.installed? && MacOS::Xcode.outdated? message = <<-EOS.undent - Your Xcode (#{MacOS::Xcode.version}) is outdated - Please update to Xcode #{MacOS::Xcode.latest_version}. + Your Xcode (#{MacOS::Xcode.version}) is outdated. + Please update to Xcode #{MacOS::Xcode.latest_version} (or delete it). #{MacOS::Xcode.update_instructions} EOS @@ -171,8 +171,7 @@ module Homebrew end def check_ruby_version - ruby_version = MacOS.version >= "10.9" ? "2.0" : "1.8" - return if RUBY_VERSION[/\d\.\d/] == ruby_version + return if RUBY_VERSION[/\d\.\d/] == "2.0" <<-EOS.undent Ruby version #{RUBY_VERSION} is unsupported on #{MacOS.version}. Homebrew @@ -264,8 +263,8 @@ module Homebrew return if installed_version >= latest_version <<-EOS.undent - Your XQuartz (#{installed_version}) is outdated - Please install XQuartz #{latest_version}: + Your XQuartz (#{installed_version}) is outdated. + Please install XQuartz #{latest_version} (or delete it): https://xquartz.macosforge.org EOS end diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 652f87ae3..09c11318e 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -214,7 +214,7 @@ class FormulaInstaller opoo "#{formula.full_name}: #{old_flag} was deprecated; using #{new_flag} instead!" end - oh1 "Installing #{Tty.green}#{formula.full_name}#{Tty.reset}" if show_header? + oh1 "Installing #{Formatter.identifier(formula.full_name)}" if show_header? if formula.tap && !formula.tap.private? options = [] @@ -426,7 +426,7 @@ class FormulaInstaller if deps.empty? && only_deps? puts "All dependencies for #{formula.full_name} are satisfied." elsif !deps.empty? - oh1 "Installing dependencies for #{formula.full_name}: #{Tty.green}#{deps.map(&:first)*", "}#{Tty.reset}", + oh1 "Installing dependencies for #{formula.full_name}: #{deps.map(&:first).map(&Formatter.method(:identifier)).join(", ")}", truncate: false deps.each { |dep, options| install_dependency(dep, options) } end @@ -476,7 +476,7 @@ class FormulaInstaller fi.verbose = verbose? && !quieter? fi.debug = debug? fi.prelude - oh1 "Installing #{formula.full_name} dependency: #{Tty.green}#{dep.name}#{Tty.reset}" + oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}" fi.install fi.finish rescue Exception diff --git a/Library/Homebrew/language/python.rb b/Library/Homebrew/language/python.rb index 95259c841..b28b1ca60 100644 --- a/Library/Homebrew/language/python.rb +++ b/Library/Homebrew/language/python.rb @@ -139,11 +139,33 @@ module Language venv end + # Returns true if a formula option for the specified python is currently + # active or if the specified python is required by the formula. Valid + # inputs are "python", "python3", :python, and :python3. Note that + # "with-python", "without-python", "with-python3", and "without-python3" + # formula options are handled correctly even if not associated with any + # corresponding depends_on statement. + # @api private + def needs_python?(python) + return true if build.with?(python) + (requirements.to_a | deps).any? { |r| r.name == python && r.required? } + end + # Helper method for the common case of installing a Python application. # Creates a virtualenv in `libexec`, installs all `resource`s defined - # on the formula, and then installs the formula. - def virtualenv_install_with_resources - venv = virtualenv_create(libexec) + # on the formula, and then installs the formula. An options hash may be + # passed (e.g., :using => "python3") to override the default, guessed + # formula preference for python or python3, or to resolve an ambiguous + # case where it's not clear whether python or python3 should be the + # default guess. + def virtualenv_install_with_resources(options = {}) + python = options[:using] + if python.nil? + wanted = %w[python python3].select { |py| needs_python?(py) } + raise FormulaAmbiguousPythonError, self if wanted.size > 1 + python = wanted.first || "python" + end + venv = virtualenv_create(libexec, python) venv.pip_install resources venv.pip_install_and_link buildpath venv diff --git a/Library/Homebrew/locale.rb b/Library/Homebrew/locale.rb new file mode 100644 index 000000000..1aff33dae --- /dev/null +++ b/Library/Homebrew/locale.rb @@ -0,0 +1,68 @@ +class Locale + class ParserError < ::RuntimeError + end + + LANGUAGE_REGEX = /(?:[a-z]{2})/ + REGION_REGEX = /(?:[A-Z]{2})/ + SCRIPT_REGEX = /(?:[A-Z][a-z]{3})/ + + LOCALE_REGEX = /^(#{LANGUAGE_REGEX})?(?:(?:^|-)(#{REGION_REGEX}))?(?:(?:^|-)(#{SCRIPT_REGEX}))?$/ + + def self.parse(string) + language, region, script = string.to_s.scan(LOCALE_REGEX)[0] + + if language.nil? && region.nil? && script.nil? + raise ParserError, "'#{string}' cannot be parsed to a #{self.class}" + end + + new(language, region, script) + end + + attr_reader :language, :region, :script + + def initialize(language, region, script) + if language.nil? && region.nil? && script.nil? + raise ArgumentError, "#{self.class} cannot be empty" + end + + { + language: language, + region: region, + script: script, + }.each do |key, value| + next if value.nil? + + regex = self.class.const_get("#{key.upcase}_REGEX") + raise ParserError, "'#{value}' does not match #{regex}" unless value =~ regex + instance_variable_set(:"@#{key}", value) + end + + self + end + + def include?(other) + other = self.class.parse(other) unless other.is_a?(self.class) + + [:language, :region, :script].all? { |var| + if other.public_send(var).nil? + true + else + public_send(var) == other.public_send(var) + end + } + end + + def eql?(other) + other = self.class.parse(other) unless other.is_a?(self.class) + [:language, :region, :script].all? { |var| + public_send(var) == other.public_send(var) + } + rescue ParserError + false + end + alias == eql? + + def to_s + [@language, @region, @script].compact.join("-") + end +end diff --git a/Library/Homebrew/manpages/brew.1.md.erb b/Library/Homebrew/manpages/brew.1.md.erb index bc2140b40..7412d0a69 100644 --- a/Library/Homebrew/manpages/brew.1.md.erb +++ b/Library/Homebrew/manpages/brew.1.md.erb @@ -54,7 +54,21 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note <%= developer_commands.join("\n") %> -## EXTERNAL COMMANDS +## OFFICIAL EXTERNAL COMMANDS + + * `bundle`: + Bundler for non-Ruby dependencies from Homebrew: + <https://github.com/Homebrew/homebrew-bundle> + + * `cask`: + Install macOS applications distributed as binaries: + <https://github.com/caskroom/homebrew-cask> + + * `services`: + Integrates Homebrew formulae with macOS's `launchctl` manager: + <https://github.com/Homebrew/homebrew-services> + +## CUSTOM EXTERNAL COMMANDS Homebrew, like `git`(1), supports external commands. These are executable scripts that reside somewhere in the `PATH`, named `brew-`<cmdname> or @@ -251,7 +265,7 @@ If your proxy requires authentication: Homebrew Documentation: <https://github.com/Homebrew/brew/blob/master/docs/> -`git`(1), `git-log`(1) +`brew-cask`(1), `git`(1), `git-log`(1) ## AUTHORS diff --git a/Library/Homebrew/migrator.rb b/Library/Homebrew/migrator.rb index 4f8cba1ce..fe480cd3b 100644 --- a/Library/Homebrew/migrator.rb +++ b/Library/Homebrew/migrator.rb @@ -153,7 +153,7 @@ class Migrator end begin - oh1 "Migrating #{Tty.green}#{oldname}#{Tty.white} to #{Tty.green}#{newname}#{Tty.reset}" + oh1 "Migrating #{Formatter.identifier(oldname)} to #{Formatter.identifier(newname)}" lock unlink_oldname move_to_new_directory @@ -200,7 +200,7 @@ class Migrator end def unlink_oldname - oh1 "Unlinking #{Tty.green}#{oldname}#{Tty.reset}" + oh1 "Unlinking #{Formatter.identifier(oldname)}" old_cellar.subdirs.each do |d| keg = Keg.new(d) keg.unlink @@ -208,7 +208,7 @@ class Migrator end def link_newname - oh1 "Linking #{Tty.green}#{newname}#{Tty.reset}" + oh1 "Linking #{Formatter.identifier(newname)}" new_keg = Keg.new(new_linked_keg_record) # If old_keg wasn't linked then we just optlink a keg. diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 0b0147825..b2f0515a0 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -41,8 +41,24 @@ module OS version.to_sym end + def languages + return @languages unless @languages.nil? + + @languages = Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").scan(/[^ \n"(),]+/) + + if ENV["HOMEBREW_LANGUAGES"] + @languages = ENV["HOMEBREW_LANGUAGES"].split(",") + @languages + end + + if ARGV.value("language") + @languages = ARGV.value("language").split(",") + @languages + end + + @languages = @languages.uniq + end + def language - @language ||= Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").delete(" \n\"()").sub(/,.*/, "") + languages.first end def active_developer_dir diff --git a/Library/Homebrew/requirements.rb b/Library/Homebrew/requirements.rb index 274ba5c9b..87b315cb7 100644 --- a/Library/Homebrew/requirements.rb +++ b/Library/Homebrew/requirements.rb @@ -44,7 +44,7 @@ class XcodeRequirement < Requirement EOS else message + <<-EOS.undent - Xcode can be installed from https://developer.apple.com/xcode/downloads/ + Xcode can be installed from #{Formatter.url("https://developer.apple.com/xcode/downloads/")} EOS end end diff --git a/Library/Homebrew/test/test_diagnostic.rb b/Library/Homebrew/test/test_diagnostic.rb index 37f56e961..b9b995f0f 100644 --- a/Library/Homebrew/test/test_diagnostic.rb +++ b/Library/Homebrew/test/test_diagnostic.rb @@ -85,7 +85,7 @@ class DiagnosticChecksTest < Homebrew::TestCase def test_check_homebrew_prefix # the integration tests are run in a special prefix - assert_match "Your Homebrew is not installed to /usr/local", + assert_match "Your Homebrew's prefix is not /usr/local.", @checks.check_homebrew_prefix end diff --git a/Library/Homebrew/test/test_os_mac_language.rb b/Library/Homebrew/test/test_os_mac_language.rb index 2cdd50917..709913000 100644 --- a/Library/Homebrew/test/test_os_mac_language.rb +++ b/Library/Homebrew/test/test_os_mac_language.rb @@ -2,7 +2,15 @@ require "testing_env" require "os/mac" class OSMacLanguageTests < Homebrew::TestCase + LANGUAGE_REGEX = /\A[a-z]{2}(-[A-Z]{2})?(-[A-Z][a-z]{3})?\Z/ + + def test_languages_format + OS::Mac.languages.each do |language| + assert_match LANGUAGE_REGEX, language + end + end + def test_language_format - assert_match(/\A[a-z]{2}(-[A-Z]{2})?\Z/, OS::Mac.language) + assert_match LANGUAGE_REGEX, OS::Mac.language end end diff --git a/Library/Homebrew/test/test_utils.rb b/Library/Homebrew/test/test_utils.rb index 422cd6229..cd5a379ab 100644 --- a/Library/Homebrew/test/test_utils.rb +++ b/Library/Homebrew/test/test_utils.rb @@ -5,8 +5,11 @@ require "utils/shell" class TtyTests < Homebrew::TestCase def test_strip_ansi - assert_equal "hello", - Tty.strip_ansi("\033\[36;7mhello\033\[0m") + assert_equal "hello", Tty.strip_ansi("\033\[36;7mhello\033\[0m") + end + + def test_width + assert_kind_of Integer, Tty.width end def test_truncate @@ -21,15 +24,26 @@ class TtyTests < Homebrew::TestCase def test_no_tty_formatting $stdout.stubs(:tty?).returns false - assert_nil Tty.blue - assert_nil Tty.white - assert_nil Tty.red - assert_nil Tty.green - assert_nil Tty.gray - assert_nil Tty.yellow - assert_nil Tty.reset - assert_nil Tty.em - assert_nil Tty.highlight + assert_equal "", Tty.to_s + assert_equal "", Tty.red.to_s + assert_equal "", Tty.green.to_s + assert_equal "", Tty.yellow.to_s + assert_equal "", Tty.blue.to_s + assert_equal "", Tty.magenta.to_s + assert_equal "", Tty.cyan.to_s + assert_equal "", Tty.default.to_s + end + + def test_formatting + $stdout.stubs(:tty?).returns(true) + assert_equal "", Tty.to_s + assert_equal "\033[31m", Tty.red.to_s + assert_equal "\033[32m", Tty.green.to_s + assert_equal "\033[33m", Tty.yellow.to_s + assert_equal "\033[34m", Tty.blue.to_s + assert_equal "\033[35m", Tty.magenta.to_s + assert_equal "\033[36m", Tty.cyan.to_s + assert_equal "\033[39m", Tty.default.to_s end end diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index 7c68437ca..04d157262 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -1,6 +1,7 @@ require "pathname" require "emoji" require "exceptions" +require "utils/formatter" require "utils/hash" require "utils/json" require "utils/inreplace" @@ -10,85 +11,11 @@ require "utils/git" require "utils/analytics" require "utils/github" require "utils/curl" - -class Tty - class << self - def strip_ansi(string) - string.gsub(/\033\[\d+(;\d+)*m/, "") - end - - def blue - bold 34 - end - - def white - bold 39 - end - - def magenta - bold 35 - end - - def red - underline 31 - end - - def yellow - underline 33 - end - - def reset - escape 0 - end - - def em - underline 39 - end - - def green - bold 32 - end - - def gray - bold 30 - end - - def highlight - bold 39 - end - - def width - `/usr/bin/tput cols`.strip.to_i - end - - def truncate(str) - w = width - w > 10 ? str.to_s[0, w - 4] : str - end - - private - - def color(n) - escape "0;#{n}" - end - - def bold(n) - escape "1;#{n}" - end - - def underline(n) - escape "4;#{n}" - end - - def escape(n) - "\033[#{n}m" if $stdout.tty? - end - end -end +require "utils/tty" def ohai(title, *sput) title = Tty.truncate(title) if $stdout.tty? && !ARGV.verbose? - puts "#{Tty.blue}==>#{Tty.white} #{title}#{Tty.reset}" + puts Formatter.headline(title, color: :blue) puts sput end @@ -96,16 +23,16 @@ def oh1(title, options = {}) if $stdout.tty? && !ARGV.verbose? && options.fetch(:truncate, :auto) == :auto title = Tty.truncate(title) end - puts "#{Tty.green}==>#{Tty.white} #{title}#{Tty.reset}" + puts Formatter.headline(title, color: :green) end # Print a warning (do this rarely) -def opoo(warning) - $stderr.puts "#{Tty.yellow}Warning#{Tty.reset}: #{warning}" +def opoo(message) + $stderr.puts Formatter.warning(message, label: "Warning") end -def onoe(error) - $stderr.puts "#{Tty.red}Error#{Tty.reset}: #{error}" +def onoe(message) + $stderr.puts Formatter.error(message, label: "Error") end def ofail(error) @@ -171,9 +98,9 @@ def pretty_installed(f) if !$stdout.tty? f.to_s elsif Emoji.enabled? - "#{Tty.highlight}#{f} #{Tty.green}#{Emoji.tick}#{Tty.reset}" + "#{Tty.bold}#{f} #{Formatter.success(Emoji.tick)}#{Tty.reset}" else - "#{Tty.highlight}#{Tty.green}#{f} (installed)#{Tty.reset}" + Formatter.success("#{Tty.bold}#{f} (installed)#{Tty.reset}") end end @@ -181,9 +108,9 @@ def pretty_uninstalled(f) if !$stdout.tty? f.to_s elsif Emoji.enabled? - "#{f} #{Tty.red}#{Emoji.cross}#{Tty.reset}" + "#{Tty.bold}#{f} #{Formatter.error(Emoji.cross)}#{Tty.reset}" else - "#{Tty.red}#{f} (uninstalled)#{Tty.reset}" + Formatter.error("#{Tty.bold}#{f} (uninstalled)#{Tty.reset}") end end diff --git a/Library/Homebrew/utils/formatter.rb b/Library/Homebrew/utils/formatter.rb new file mode 100644 index 000000000..cb4f041f4 --- /dev/null +++ b/Library/Homebrew/utils/formatter.rb @@ -0,0 +1,52 @@ +require "utils/tty" + +module Formatter + module_function + + def arrow(string, color: nil) + prefix("==>", string, color) + end + + def headline(string, color: nil) + arrow("#{Tty.bold}#{string}#{Tty.reset}", color: color) + end + + def identifier(string) + "#{Tty.green}#{string}#{Tty.reset}" + end + + def success(string, label: nil) + label(label, string, :green) + end + + def warning(string, label: nil) + label(label, string, :yellow) + end + + def error(string, label: nil) + label(label, string, :red) + end + + def url(string) + "#{Tty.underline}#{string}#{Tty.no_underline}" + end + + def label(label, string, color) + label = "#{label}:" unless label.nil? + prefix(label, string, color) + end + private_class_method :label + + def prefix(prefix, string, color) + if prefix.nil? && color.nil? + string + elsif prefix.nil? + "#{Tty.send(color)}#{string}#{Tty.reset}" + elsif color.nil? + "#{prefix} #{string}" + else + "#{Tty.send(color)}#{prefix}#{Tty.reset} #{string}" + end + end + private_class_method :prefix +end diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index b7ec538f9..5611f9aad 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -14,7 +14,7 @@ module GitHub super <<-EOS.undent GitHub API Error: #{error} Try again in #{pretty_ratelimit_reset(reset)}, or create a personal access token: - #{Tty.em}https://github.com/settings/tokens/new?scopes=&description=Homebrew#{Tty.reset} + #{Formatter.url("https://github.com/settings/tokens/new?scopes=&description=Homebrew")} and then set the token as: export HOMEBREW_GITHUB_API_TOKEN="your_new_token" EOS end @@ -30,7 +30,7 @@ module GitHub if ENV["HOMEBREW_GITHUB_API_TOKEN"] message << <<-EOS.undent HOMEBREW_GITHUB_API_TOKEN may be invalid or expired; check: - #{Tty.em}https://github.com/settings/tokens#{Tty.reset} + #{Formatter.url("https://github.com/settings/tokens")} EOS else message << <<-EOS.undent @@ -38,7 +38,7 @@ module GitHub Clear them with: printf "protocol=https\\nhost=github.com\\n" | git credential-osxkeychain erase Or create a personal access token: - #{Tty.em}https://github.com/settings/tokens/new?scopes=&description=Homebrew#{Tty.reset} + #{Formatter.url("https://github.com/settings/tokens/new?scopes=&description=Homebrew")} and then set the token as: export HOMEBREW_GITHUB_API_TOKEN="your_new_token" EOS end @@ -106,14 +106,14 @@ module GitHub onoe <<-EOS.undent Your macOS keychain GitHub credentials do not have sufficient scope! Scopes they have: #{credentials_scopes} - Create a personal access token: https://github.com/settings/tokens + Create a personal access token: #{Formatter.url("https://github.com/settings/tokens")} and then set HOMEBREW_GITHUB_API_TOKEN as the authentication method instead. EOS when :environment onoe <<-EOS.undent Your HOMEBREW_GITHUB_API_TOKEN does not have sufficient scope! Scopes it has: #{credentials_scopes} - Create a new personal access token: https://github.com/settings/tokens + Create a new personal access token: #{Formatter.url("https://github.com/settings/tokens")} and then set the new HOMEBREW_GITHUB_API_TOKEN as the authentication method instead. EOS end diff --git a/Library/Homebrew/utils/tty.rb b/Library/Homebrew/utils/tty.rb new file mode 100644 index 000000000..505165dc5 --- /dev/null +++ b/Library/Homebrew/utils/tty.rb @@ -0,0 +1,64 @@ +module Tty + module_function + + def strip_ansi(string) + string.gsub(/\033\[\d+(;\d+)*m/, "") + end + + def width + `/usr/bin/tput cols`.strip.to_i + end + + def truncate(string) + (w = width).zero? ? string.to_s : string.to_s[0, w - 4] + end + + COLOR_CODES = { + red: 31, + green: 32, + yellow: 33, + blue: 34, + magenta: 35, + cyan: 36, + default: 39, + }.freeze + + STYLE_CODES = { + reset: 0, + bold: 1, + italic: 3, + underline: 4, + strikethrough: 9, + no_underline: 24, + }.freeze + + CODES = COLOR_CODES.merge(STYLE_CODES).freeze + + def append_to_escape_sequence(code) + @escape_sequence ||= [] + @escape_sequence << code + self + end + + def current_escape_sequence + return "" if @escape_sequence.nil? + "\033[#{@escape_sequence.join(";")}m" + end + + def reset_escape_sequence! + @escape_sequence = nil + end + + CODES.each do |name, code| + define_singleton_method(name) do + append_to_escape_sequence(code) + end + end + + def to_s + return "" unless $stdout.tty? + current_escape_sequence + ensure + reset_escape_sequence! + end +end |
