diff --git a/Library/Homebrew/cask/lib/hbc/exceptions.rb b/Library/Homebrew/cask/lib/hbc/exceptions.rb index 2626a467a..392e34f98 100644 --- a/Library/Homebrew/cask/lib/hbc/exceptions.rb +++ b/Library/Homebrew/cask/lib/hbc/exceptions.rb @@ -38,7 +38,7 @@ module Hbc class CaskAlreadyCreatedError < AbstractCaskErrorWithToken def to_s - %Q(Cask '#{token}' already exists. Run #{Formatter.identifier("brew cask cat #{token}")} to edit it.) + %Q(Cask '#{token}' already exists. Run #{Formatter.identifier("brew cask edit #{token}")} to edit it.) end end diff --git a/Library/Homebrew/cli_parser.rb b/Library/Homebrew/cli_parser.rb index dd467f3fc..06d5e4a80 100644 --- a/Library/Homebrew/cli_parser.rb +++ b/Library/Homebrew/cli_parser.rb @@ -1,5 +1,6 @@ require "optparse" require "ostruct" +require "set" module Homebrew module CLI @@ -13,6 +14,8 @@ module Homebrew @parsed_args = OpenStruct.new # undefine tap to allow --tap argument @parsed_args.instance_eval { undef tap } + @constraints = [] + @conflicts = [] instance_eval(&block) end @@ -34,7 +37,7 @@ module Homebrew end end - def flag(name, description: nil) + def flag(name, description: nil, required_for: nil, depends_on: nil) if name.end_with? "=" required = OptionParser::REQUIRED_ARGUMENT name.chomp! "=" @@ -45,10 +48,16 @@ module Homebrew @parser.on(name, description, required) do |option_value| @parsed_args[option_to_name(name)] = option_value end + + set_constraints(name, required_for: required_for, depends_on: depends_on) + end + + def conflicts(*options) + @conflicts << options.map { |option| option_to_name(option) } end def option_to_name(name) - name.sub(/\A--?/, "").tr("-", "_") + name.sub(/\A--?/, "").tr("-", "_").delete("=") end def option_to_description(*names) @@ -57,6 +66,7 @@ module Homebrew def parse(cmdline_args = ARGV) @parser.parse(cmdline_args) + check_constraint_violations @parsed_args end @@ -82,6 +92,91 @@ module Homebrew else name end end + + def option_passed?(name) + @parsed_args.respond_to?(name) || @parsed_args.respond_to?("#{name}?") + end + + def set_constraints(name, depends_on:, required_for:) + secondary = option_to_name(name) + unless required_for.nil? + primary = option_to_name(required_for) + @constraints << [primary, secondary, :mandatory] + end + + return if depends_on.nil? + primary = option_to_name(depends_on) + @constraints << [primary, secondary, :optional] + end + + def check_constraints + @constraints.each do |primary, secondary, constraint_type| + primary_passed = option_passed?(primary) + secondary_passed = option_passed?(secondary) + if :mandatory.equal?(constraint_type) && primary_passed && !secondary_passed + raise OptionConstraintError.new(primary, secondary) + end + if secondary_passed && !primary_passed + raise OptionConstraintError.new(primary, secondary, missing: true) + end + end + end + + def check_conflicts + @conflicts.each do |mutually_exclusive_options_group| + violations = mutually_exclusive_options_group.select do |option| + option_passed? option + end + raise OptionConflictError, violations if violations.length > 1 + end + end + + def check_invalid_constraints + @conflicts.each do |mutually_exclusive_options_group| + @constraints.each do |p, s| + next unless Set[p, s].subset?(Set[*mutually_exclusive_options_group]) + raise InvalidConstraintError.new(p, s) + end + end + end + + def check_constraint_violations + check_invalid_constraints + check_conflicts + check_constraints + end + end + + class OptionConstraintError < RuntimeError + def initialize(arg1, arg2, missing: false) + if !missing + message = <<~EOS + `#{arg1}` and `#{arg2}` should be passed together + EOS + else + message = <<~EOS + `#{arg2}` cannot be passed without `#{arg1}` + EOS + end + super message + end + end + + class OptionConflictError < RuntimeError + def initialize(args) + args_list = args.join("` and `") + super <<~EOS + `#{args_list}` are mutually exclusive + EOS + end + end + + class InvalidConstraintError < RuntimeError + def initialize(arg1, arg2) + super <<~EOS + `#{arg1}` and `#{arg2}` cannot be mutually exclusive and mutually dependent simultaneously + EOS + end end end end diff --git a/Library/Homebrew/cmd/vendor-install.sh b/Library/Homebrew/cmd/vendor-install.sh index 909ff936a..913f2302a 100644 --- a/Library/Homebrew/cmd/vendor-install.sh +++ b/Library/Homebrew/cmd/vendor-install.sh @@ -13,22 +13,17 @@ if [[ -n "$HOMEBREW_MACOS" ]] then if [[ "$HOMEBREW_PROCESSOR" = "Intel" ]] then - ruby_URL="https://homebrew.bintray.com/bottles-portable-ruby/portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz" - ruby_URL2="https://github.com/Homebrew/homebrew-portable-ruby/releases/download/2.3.3/portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz" - ruby_SHA="34ce9e4c9c1be28db564d744165aa29291426f8a3d2ef806ba4f0b9175aedb2b" + ruby_URL="https://homebrew.bintray.com/bottles-portable-ruby/portable-ruby-2.3.3_1.leopard_64.bottle.tar.gz" + ruby_URL2="https://github.com/Homebrew/homebrew-portable-ruby/releases/download/2.3.3_1/portable-ruby-2.3.3_1.leopard_64.bottle.tar.gz" + ruby_SHA="a7f8ebcae0a3d88b3f1d9fd1ff77330b64a52a4fb5cbf25e3e02bec0211cbe23" fi elif [[ -n "$HOMEBREW_LINUX" ]] then case "$HOMEBREW_PROCESSOR" in - armv7l) - ruby_URL="https://homebrew.bintray.com/bottles-portable-ruby/portable-ruby-2.3.3.armv7l_linux.bottle.1.tar.gz" - ruby_URL2="https://github.com/Homebrew/homebrew-portable-ruby/releases/download/2.3.3/portable-ruby-2.3.3.armv7l_linux.bottle.1.tar.gz" - ruby_SHA="d26affe6f6ac299557a9044b311b4066b554874fc828ebc323d2705d3f4a8249" - ;; x86_64) - ruby_URL="https://homebrew.bintray.com/bottles-portable-ruby/portable-ruby-2.3.3.x86_64_linux.bottle.1.tar.gz" - ruby_URL2="https://github.com/Homebrew/homebrew-portable-ruby/releases/download/2.3.3/portable-ruby-2.3.3.x86_64_linux.bottle.1.tar.gz" - ruby_SHA="33643b1ca6f860d6df01686636326785763e5e81cf0cef37d8a7ab96a6ca1fa1" + ruby_URL="https://homebrew.bintray.com/bottles-portable-ruby/portable-ruby-2.3.3_1.x86_64_linux.bottle.tar.gz" + ruby_URL2="https://github.com/Homebrew/homebrew-portable-ruby/releases/download/2.3.3_1/portable-ruby-2.3.3.x86_64_linux.bottle.tar.gz" + ruby_SHA="1615136f44b2b8c0106f28ef1cae95f3abb7c083a7e42300fd2a3de021601f7b" ;; esac fi diff --git a/Library/Homebrew/compilers.rb b/Library/Homebrew/compilers.rb index 36e50a177..2f80fafe2 100644 --- a/Library/Homebrew/compilers.rb +++ b/Library/Homebrew/compilers.rb @@ -1,7 +1,7 @@ # @private module CompilerConstants - GNU_GCC_VERSIONS = %w[4.3 4.4 4.5 4.6 4.7 4.8 4.9 5 6 7].freeze - GNU_GCC_REGEXP = /^gcc-(4\.[3-9]|[5-7])$/ + GNU_GCC_VERSIONS = %w[4.3 4.4 4.5 4.6 4.7 4.8 4.9 5 6 7 8].freeze + GNU_GCC_REGEXP = /^gcc-(4\.[3-9]|[5-8])$/ COMPILER_SYMBOL_MAP = { "gcc-4.0" => :gcc_4_0, "gcc-4.2" => :gcc_4_2, diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index b5c1dccdd..66a72fe2a 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -42,16 +42,304 @@ #: the preexisting formula already uses. require "formula" +require "cli_parser" module Homebrew module_function + def bump_formula_pr + @args = Homebrew::CLI::Parser.parse do + switch "--devel" + switch "-n", "--dry-run" + switch "--write" + switch "--audit" + switch "--strict" + switch "--no-browse" + switch :quiet + switch :force + switch :verbose + switch :debug + + flag "--url=" + flag "--revision=" + flag "--tag=", required_for: "--revision=" + flag "--sha256=", depends_on: "--url=" + flag "--mirror=" + flag "--version=" + flag "--message=" + + conflicts "--url", "--tag" + end + + # As this command is simplifying user run commands then let's just use a + # user path, too. + ENV["PATH"] = ENV["HOMEBREW_PATH"] + + # Use the user's browser, too. + ENV["BROWSER"] = ENV["HOMEBREW_BROWSER"] + + # Setup GitHub environment variables + %w[GITHUB_USER GITHUB_PASSWORD GITHUB_TOKEN].each do |env| + homebrew_env = ENV["HOMEBREW_#{env}"] + next unless homebrew_env + next if homebrew_env.empty? + ENV[env] = homebrew_env + end + + gh_api_errors = [GitHub::AuthenticationFailedError, GitHub::HTTPNotFoundError, + GitHub::RateLimitExceededError, GitHub::Error, JSON::ParserError].freeze + + formula = ARGV.formulae.first + + if formula + check_for_duplicate_pull_requests(formula) + checked_for_duplicates = true + end + + new_url = @args.url + if new_url && !formula + # Split the new URL on / and find any formulae that have the same URL + # except for the last component, but don't try to match any more than the + # first five components since sometimes the last component isn't the only + # one to change. + new_url_split = new_url.split("/") + maximum_url_components_to_match = 5 + components_to_match = [new_url_split.count - 1, maximum_url_components_to_match].min + base_url = new_url_split.first(components_to_match).join("/") + base_url = /#{Regexp.escape(base_url)}/ + is_devel = @args.devel? + guesses = [] + Formula.each do |f| + if is_devel && f.devel && f.devel.url && f.devel.url.match(base_url) + guesses << f + elsif f.stable&.url && f.stable.url.match(base_url) + guesses << f + end + end + if guesses.count == 1 + formula = guesses.shift + elsif guesses.count > 1 + odie "Couldn't guess formula for sure: could be one of these:\n#{guesses}" + end + end + odie "No formula found!" unless formula + + check_for_duplicate_pull_requests(formula) unless checked_for_duplicates + + requested_spec, formula_spec = if @args.devel? + devel_message = " (devel)" + [:devel, formula.devel] + else + [:stable, formula.stable] + end + odie "#{formula}: no #{requested_spec} specification found!" unless formula_spec + + hash_type, old_hash = if (checksum = formula_spec.checksum) + [checksum.hash_type, checksum.hexdigest] + end + + new_hash = @args[hash_type] + new_tag = @args.tag + new_revision = @args.revision + new_mirror = @args.mirror + forced_version = @args.version + new_url_hash = if new_url && new_hash + true + elsif new_tag && new_revision + false + elsif !hash_type + odie "#{formula}: no --tag=/--revision= arguments specified!" + elsif !new_url + odie "#{formula}: no --url= argument specified!" + else + new_mirror = case new_url + when requested_spec != :devel && %r{.*ftp.gnu.org/gnu.*} + new_url.sub "ftp.gnu.org/gnu", "ftpmirror.gnu.org" + when %r{.*mirrors.ocf.berkeley.edu/debian.*} + new_url.sub "mirrors.ocf.berkeley.edu/debian", "mirrorservice.org/sites/ftp.debian.org/debian" + end + resource = Resource.new { @url = new_url } + resource.download_strategy = DownloadStrategyDetector.detect_from_url(new_url) + resource.owner = Resource.new(formula.name) + resource.version = forced_version if forced_version + odie "No --version= argument specified!" unless resource.version + resource_path = resource.fetch + tar_file_extensions = %w[.tar .tb2 .tbz .tbz2 .tgz .tlz .txz .tZ] + if tar_file_extensions.any? { |extension| new_url.include? extension } + gnu_tar_gtar_path = HOMEBREW_PREFIX/"opt/gnu-tar/bin/gtar" + gnu_tar_gtar = gnu_tar_gtar_path if gnu_tar_gtar_path.executable? + tar = which("gtar") || gnu_tar_gtar || which("tar") + if Utils.popen_read(tar, "-tf", resource_path) =~ %r{/.*\.} + new_hash = resource_path.sha256 + else + odie "#{resource_path} is not a valid tar file!" + end + else + new_hash = resource_path.sha256 + end + end + + if @args.dry_run? + ohai "brew update" + else + safe_system "brew", "update" + end + + old_formula_version = formula_version(formula, requested_spec) + + replacement_pairs = [] + if requested_spec == :stable && formula.revision.nonzero? + replacement_pairs << [/^ revision \d+\n(\n( head "))?/m, "\\2"] + end + + replacement_pairs += formula_spec.mirrors.map do |mirror| + [/ +mirror \"#{Regexp.escape(mirror)}\"\n/m, ""] + end + + replacement_pairs += if new_url_hash + [ + [/#{Regexp.escape(formula_spec.url)}/, new_url], + [old_hash, new_hash], + ] + else + [ + [formula_spec.specs[:tag], new_tag], + [formula_spec.specs[:revision], new_revision], + ] + end + + backup_file = File.read(formula.path) unless @args.dry_run? + + if new_mirror + replacement_pairs << [/^( +)(url \"#{Regexp.escape(new_url)}\"\n)/m, "\\1\\2\\1mirror \"#{new_mirror}\"\n"] + end + + if forced_version && forced_version != "0" + if requested_spec == :stable + if File.read(formula.path).include?("version \"#{old_formula_version}\"") + replacement_pairs << [old_formula_version.to_s, forced_version] + elsif new_mirror + replacement_pairs << [/^( +)(mirror \"#{new_mirror}\"\n)/m, "\\1\\2\\1version \"#{forced_version}\"\n"] + else + replacement_pairs << [/^( +)(url \"#{new_url}\"\n)/m, "\\1\\2\\1version \"#{forced_version}\"\n"] + end + elsif requested_spec == :devel + replacement_pairs << [/( devel do.+?version \")#{old_formula_version}(\"\n.+?end\n)/m, "\\1#{forced_version}\\2"] + end + elsif forced_version && forced_version == "0" + if requested_spec == :stable + replacement_pairs << [/^ version \"[\w\.\-\+]+\"\n/m, ""] + elsif requested_spec == :devel + replacement_pairs << [/( devel do.+?)^ +version \"[^\n]+\"\n(.+?end\n)/m, "\\1\\2"] + end + end + new_contents = inreplace_pairs(formula.path, replacement_pairs) + + new_formula_version = formula_version(formula, requested_spec, new_contents) + + if new_formula_version < old_formula_version + formula.path.atomic_write(backup_file) unless @args.dry_run? + odie <<~EOS + You probably need to bump this formula manually since changing the + version from #{old_formula_version} to #{new_formula_version} would be a downgrade. + EOS + elsif new_formula_version == old_formula_version + formula.path.atomic_write(backup_file) unless @args.dry_run? + odie <<~EOS + You probably need to bump this formula manually since the new version + and old version are both #{new_formula_version}. + EOS + end + + if @args.dry_run? + if @args.strict? + ohai "brew audit --strict #{formula.path.basename}" + elsif @args.audit? + ohai "brew audit #{formula.path.basename}" + end + else + failed_audit = false + if @args.strict? + system HOMEBREW_BREW_FILE, "audit", "--strict", formula.path + failed_audit = !$CHILD_STATUS.success? + elsif @args.audit? + system HOMEBREW_BREW_FILE, "audit", formula.path + failed_audit = !$CHILD_STATUS.success? + end + if failed_audit + formula.path.atomic_write(backup_file) + odie "brew audit failed!" + end + end + + formula.path.parent.cd do + branch = "#{formula.name}-#{new_formula_version}" + git_dir = Utils.popen_read("git rev-parse --git-dir").chomp + shallow = !git_dir.empty? && File.exist?("#{git_dir}/shallow") + + if @args.dry_run? + ohai "fork repository with GitHub API" + ohai "git fetch --unshallow origin" if shallow + ohai "git checkout --no-track -b #{branch} origin/master" + ohai "git commit --no-edit --verbose --message='#{formula.name} #{new_formula_version}#{devel_message}' -- #{formula.path}" + ohai "git push --set-upstream $HUB_REMOTE #{branch}:#{branch}" + ohai "create pull request with GitHub API" + ohai "git checkout -" + else + + begin + response = GitHub.create_fork(formula.tap.full_name) + # GitHub API responds immediately but fork takes a few seconds to be ready. + sleep 3 + rescue *gh_api_errors => e + formula.path.atomic_write(backup_file) unless @args.dry_run? + odie "Unable to fork: #{e.message}!" + end + + remote_url = response.fetch("clone_url") + username = response.fetch("owner").fetch("login") + + safe_system "git", "fetch", "--unshallow", "origin" if shallow + safe_system "git", "checkout", "--no-track", "-b", branch, "origin/master" + safe_system "git", "commit", "--no-edit", "--verbose", + "--message=#{formula.name} #{new_formula_version}#{devel_message}", + "--", formula.path + safe_system "git", "push", "--set-upstream", remote_url, "#{branch}:#{branch}" + safe_system "git", "checkout", "--quiet", "-" + pr_message = <<~EOS + Created with `brew bump-formula-pr`. + EOS + user_message = @args.message + if user_message + pr_message += "\n" + <<~EOS + --- + + #{user_message} + EOS + end + pr_title = "#{formula.name} #{new_formula_version}#{devel_message}" + + begin + url = GitHub.create_pull_request(formula.tap.full_name, pr_title, + "#{username}:#{branch}", "master", pr_message)["html_url"] + if @args.no_browse? + puts url + else + exec_browser url + end + rescue *gh_api_errors => e + odie "Unable to open pull request: #{e.message}!" + end + end + end + end + def inreplace_pairs(path, replacement_pairs) - if ARGV.dry_run? + if @args.dry_run? contents = path.open("r") { |f| Formulary.ensure_utf8_encoding(f).read } contents.extend(StringInreplaceExtension) replacement_pairs.each do |old, new| - unless ARGV.flag?("--quiet") + unless Homebrew.args.quiet? ohai "replace #{old.inspect} with #{new.inspect}" end contents.gsub!(old, new) @@ -59,12 +347,12 @@ module Homebrew unless contents.errors.empty? raise Utils::InreplaceError, path => contents.errors end - path.atomic_write(contents) if ARGV.include?("--write") + path.atomic_write(contents) if @args.write? contents else Utils::Inreplace.inreplace(path) do |s| replacement_pairs.each do |old, new| - unless ARGV.flag?("--quiet") + unless Homebrew.args.quiet? ohai "replace #{old.inspect} with #{new.inspect}" end s.gsub!(old, new) @@ -103,279 +391,15 @@ module Homebrew #{pull_requests.map { |pr| "#{pr["title"]} #{pr["html_url"]}" }.join("\n")} EOS error_message = "Duplicate PRs should not be opened. Use --force to override this error." - if ARGV.force? && !ARGV.flag?("--quiet") + if Homebrew.args.force? && !Homebrew.args.quiet? opoo duplicates_message - elsif !ARGV.force? && ARGV.flag?("--quiet") + elsif !Homebrew.args.force? && Homebrew.args.quiet? odie error_message - elsif !ARGV.force? + elsif !Homebrew.args.force? odie <<~EOS #{duplicates_message.chomp} #{error_message} EOS end end - - def bump_formula_pr - # As this command is simplifying user run commands then let's just use a - # user path, too. - ENV["PATH"] = ENV["HOMEBREW_PATH"] - - # Use the user's browser, too. - ENV["BROWSER"] = ENV["HOMEBREW_BROWSER"] - - # Setup GitHub environment variables - %w[GITHUB_USER GITHUB_PASSWORD GITHUB_TOKEN].each do |env| - homebrew_env = ENV["HOMEBREW_#{env}"] - next unless homebrew_env - next if homebrew_env.empty? - ENV[env] = homebrew_env - end - - gh_api_errors = [GitHub::AuthenticationFailedError, GitHub::HTTPNotFoundError, - GitHub::RateLimitExceededError, GitHub::Error, JSON::ParserError].freeze - - formula = ARGV.formulae.first - - if formula - check_for_duplicate_pull_requests(formula) - checked_for_duplicates = true - end - - new_url = ARGV.value("url") - if new_url && !formula - # Split the new URL on / and find any formulae that have the same URL - # except for the last component, but don't try to match any more than the - # first five components since sometimes the last component isn't the only - # one to change. - new_url_split = new_url.split("/") - maximum_url_components_to_match = 5 - components_to_match = [new_url_split.count - 1, maximum_url_components_to_match].min - base_url = new_url_split.first(components_to_match).join("/") - base_url = /#{Regexp.escape(base_url)}/ - is_devel = ARGV.include?("--devel") - guesses = [] - Formula.each do |f| - if is_devel && f.devel && f.devel.url && f.devel.url.match(base_url) - guesses << f - elsif f.stable&.url && f.stable.url.match(base_url) - guesses << f - end - end - if guesses.count == 1 - formula = guesses.shift - elsif guesses.count > 1 - odie "Couldn't guess formula for sure: could be one of these:\n#{guesses}" - end - end - odie "No formula found!" unless formula - - check_for_duplicate_pull_requests(formula) unless checked_for_duplicates - - requested_spec, formula_spec = if ARGV.include?("--devel") - devel_message = " (devel)" - [:devel, formula.devel] - else - [:stable, formula.stable] - end - odie "#{formula}: no #{requested_spec} specification found!" unless formula_spec - - hash_type, old_hash = if (checksum = formula_spec.checksum) - [checksum.hash_type.to_s, checksum.hexdigest] - end - - new_hash = ARGV.value(hash_type) - new_tag = ARGV.value("tag") - new_revision = ARGV.value("revision") - new_mirror = ARGV.value("mirror") - forced_version = ARGV.value("version") - new_url_hash = if new_url && new_hash - true - elsif new_tag && new_revision - false - elsif !hash_type - odie "#{formula}: no --tag=/--revision= arguments specified!" - elsif !new_url - odie "#{formula}: no --url= argument specified!" - else - new_mirror = case new_url - when requested_spec != :devel && %r{.*ftp.gnu.org/gnu.*} - new_url.sub "ftp.gnu.org/gnu", "ftpmirror.gnu.org" - when %r{.*mirrors.ocf.berkeley.edu/debian.*} - new_url.sub "mirrors.ocf.berkeley.edu/debian", "mirrorservice.org/sites/ftp.debian.org/debian" - end - resource = Resource.new { @url = new_url } - resource.download_strategy = DownloadStrategyDetector.detect_from_url(new_url) - resource.owner = Resource.new(formula.name) - resource.version = forced_version if forced_version - odie "No --version= argument specified!" unless resource.version - resource_path = resource.fetch - tar_file_extensions = %w[.tar .tb2 .tbz .tbz2 .tgz .tlz .txz .tZ] - if tar_file_extensions.any? { |extension| new_url.include? extension } - gnu_tar_gtar_path = HOMEBREW_PREFIX/"opt/gnu-tar/bin/gtar" - gnu_tar_gtar = gnu_tar_gtar_path if gnu_tar_gtar_path.executable? - tar = which("gtar") || gnu_tar_gtar || which("tar") - if Utils.popen_read(tar, "-tf", resource_path) =~ %r{/.*\.} - new_hash = resource_path.sha256 - else - odie "#{resource_path} is not a valid tar file!" - end - else - new_hash = resource_path.sha256 - end - end - - if ARGV.dry_run? - ohai "brew update" - else - safe_system "brew", "update" - end - - old_formula_version = formula_version(formula, requested_spec) - - replacement_pairs = [] - if requested_spec == :stable && formula.revision.nonzero? - replacement_pairs << [/^ revision \d+\n(\n( head "))?/m, "\\2"] - end - - replacement_pairs += formula_spec.mirrors.map do |mirror| - [/ +mirror \"#{Regexp.escape(mirror)}\"\n/m, ""] - end - - replacement_pairs += if new_url_hash - [ - [/#{Regexp.escape(formula_spec.url)}/, new_url], - [old_hash, new_hash], - ] - else - [ - [formula_spec.specs[:tag], new_tag], - [formula_spec.specs[:revision], new_revision], - ] - end - - backup_file = File.read(formula.path) unless ARGV.dry_run? - - if new_mirror - replacement_pairs << [/^( +)(url \"#{Regexp.escape(new_url)}\"\n)/m, "\\1\\2\\1mirror \"#{new_mirror}\"\n"] - end - - if forced_version && forced_version != "0" - if requested_spec == :stable - if File.read(formula.path).include?("version \"#{old_formula_version}\"") - replacement_pairs << [old_formula_version.to_s, forced_version] - elsif new_mirror - replacement_pairs << [/^( +)(mirror \"#{new_mirror}\"\n)/m, "\\1\\2\\1version \"#{forced_version}\"\n"] - else - replacement_pairs << [/^( +)(url \"#{new_url}\"\n)/m, "\\1\\2\\1version \"#{forced_version}\"\n"] - end - elsif requested_spec == :devel - replacement_pairs << [/( devel do.+?version \")#{old_formula_version}(\"\n.+?end\n)/m, "\\1#{forced_version}\\2"] - end - elsif forced_version && forced_version == "0" - if requested_spec == :stable - replacement_pairs << [/^ version \"[\w\.\-\+]+\"\n/m, ""] - elsif requested_spec == :devel - replacement_pairs << [/( devel do.+?)^ +version \"[^\n]+\"\n(.+?end\n)/m, "\\1\\2"] - end - end - new_contents = inreplace_pairs(formula.path, replacement_pairs) - - new_formula_version = formula_version(formula, requested_spec, new_contents) - - if new_formula_version < old_formula_version - formula.path.atomic_write(backup_file) unless ARGV.dry_run? - odie <<~EOS - You probably need to bump this formula manually since changing the - version from #{old_formula_version} to #{new_formula_version} would be a downgrade. - EOS - elsif new_formula_version == old_formula_version - formula.path.atomic_write(backup_file) unless ARGV.dry_run? - odie <<~EOS - You probably need to bump this formula manually since the new version - and old version are both #{new_formula_version}. - EOS - end - - if ARGV.dry_run? - if ARGV.include? "--strict" - ohai "brew audit --strict #{formula.path.basename}" - elsif ARGV.include? "--audit" - ohai "brew audit #{formula.path.basename}" - end - else - failed_audit = false - if ARGV.include? "--strict" - system HOMEBREW_BREW_FILE, "audit", "--strict", formula.path - failed_audit = !$CHILD_STATUS.success? - elsif ARGV.include? "--audit" - system HOMEBREW_BREW_FILE, "audit", formula.path - failed_audit = !$CHILD_STATUS.success? - end - if failed_audit - formula.path.atomic_write(backup_file) - odie "brew audit failed!" - end - end - - formula.path.parent.cd do - branch = "#{formula.name}-#{new_formula_version}" - git_dir = Utils.popen_read("git rev-parse --git-dir").chomp - shallow = !git_dir.empty? && File.exist?("#{git_dir}/shallow") - - if ARGV.dry_run? - ohai "fork repository with GitHub API" - ohai "git fetch --unshallow origin" if shallow - ohai "git checkout --no-track -b #{branch} origin/master" - ohai "git commit --no-edit --verbose --message='#{formula.name} #{new_formula_version}#{devel_message}' -- #{formula.path}" - ohai "git push --set-upstream $HUB_REMOTE #{branch}:#{branch}" - ohai "create pull request with GitHub API" - ohai "git checkout -" - else - - begin - response = GitHub.create_fork(formula.tap.full_name) - # GitHub API responds immediately but fork takes a few seconds to be ready. - sleep 3 - rescue *gh_api_errors => e - formula.path.atomic_write(backup_file) unless ARGV.dry_run? - odie "Unable to fork: #{e.message}!" - end - - remote_url = response.fetch("clone_url") - username = response.fetch("owner").fetch("login") - - safe_system "git", "fetch", "--unshallow", "origin" if shallow - safe_system "git", "checkout", "--no-track", "-b", branch, "origin/master" - safe_system "git", "commit", "--no-edit", "--verbose", - "--message=#{formula.name} #{new_formula_version}#{devel_message}", - "--", formula.path - safe_system "git", "push", "--set-upstream", remote_url, "#{branch}:#{branch}" - safe_system "git", "checkout", "--quiet", "-" - pr_message = <<~EOS - Created with `brew bump-formula-pr`. - EOS - user_message = ARGV.value("message") - if user_message - pr_message += "\n" + <<~EOS - --- - - #{user_message} - EOS - end - pr_title = "#{formula.name} #{new_formula_version}#{devel_message}" - - begin - url = GitHub.create_pull_request(formula.tap.full_name, pr_title, - "#{username}:#{branch}", "master", pr_message)["html_url"] - if ARGV.include?("--no-browse") - puts url - else - exec_browser url - end - rescue *gh_api_errors => e - odie "Unable to open pull request: #{e.message}!" - end - end - end - end end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 478cf9b57..85d0998e6 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1503,7 +1503,6 @@ class Formula # @private def runtime_dependencies(read_from_tab: true) if read_from_tab && - installed_prefix.directory? && (keg = opt_or_installed_prefix_keg) && (tab_deps = keg.runtime_dependencies) return tab_deps.map { |d| Dependency.new d["full_name"] }.compact diff --git a/Library/Homebrew/linkage_checker.rb b/Library/Homebrew/linkage_checker.rb index ab709b530..623dcbaf9 100644 --- a/Library/Homebrew/linkage_checker.rb +++ b/Library/Homebrew/linkage_checker.rb @@ -145,8 +145,12 @@ class LinkageChecker formula.build.without?(dep) end - declared_deps = formula.deps.reject { |dep| filter_out.call(dep) }.map(&:name) - declared_dep_names = declared_deps.map { |dep| dep.split("/").last } + declared_deps_full_names = formula.deps + .reject { |dep| filter_out.call(dep) } + .map(&:name) + declared_deps_names = declared_deps_full_names.map do |dep| + dep.split("/").last + end recursive_deps = formula.declared_runtime_dependencies.map do |dep| begin dep.to_formula.name @@ -161,7 +165,7 @@ class LinkageChecker name = full_name.split("/").last next if name == formula.name if recursive_deps.include?(name) - indirect_deps << full_name unless declared_dep_names.include?(name) + indirect_deps << full_name unless declared_deps_names.include?(name) else undeclared_deps << full_name end @@ -170,9 +174,9 @@ class LinkageChecker sort_by_formula_full_name!(indirect_deps) sort_by_formula_full_name!(undeclared_deps) - unnecessary_deps = declared_dep_names.reject do |full_name| + unnecessary_deps = declared_deps_full_names.reject do |full_name| + next true if Formula[full_name].bin.directory? name = full_name.split("/").last - next true if Formula[name].bin.directory? @brewed_dylibs.keys.map { |x| x.split("/").last }.include?(name) end diff --git a/Library/Homebrew/rubocops/components_order_cop.rb b/Library/Homebrew/rubocops/components_order_cop.rb index 912f4d380..0c181f3ef 100644 --- a/Library/Homebrew/rubocops/components_order_cop.rb +++ b/Library/Homebrew/rubocops/components_order_cop.rb @@ -31,6 +31,7 @@ module RuboCop [{ name: :conflicts_with, type: :method_call }], [{ name: :go_resource, type: :block_call }, { name: :resource, type: :block_call }], [{ name: :install, type: :method_definition }], + [{ name: :post_install, type: :method_definition }], [{ name: :caveats, type: :method_definition }], [{ name: :plist_options, type: :method_call }, { name: :plist, type: :method_definition }], [{ name: :test, type: :block_call }], diff --git a/Library/Homebrew/test/cli_parser_spec.rb b/Library/Homebrew/test/cli_parser_spec.rb index 5f65a80c1..418d3234f 100644 --- a/Library/Homebrew/test/cli_parser_spec.rb +++ b/Library/Homebrew/test/cli_parser_spec.rb @@ -70,4 +70,54 @@ describe Homebrew::CLI::Parser do expect(args.files).to eq %w[random1.txt random2.txt] end end + + describe "test constraints" do + subject(:parser) { + described_class.new do + flag "--flag1" + flag "--flag3" + flag "--flag2", required_for: "--flag1" + flag "--flag4", depends_on: "--flag3" + + conflicts "--flag1", "--flag3" + end + } + + it "raises exception on depends mandatory constraint violation" do + expect { parser.parse(["--flag1"]) }.to raise_error(Homebrew::CLI::OptionConstraintError) + end + + it "raises exception on depends constraint violation" do + expect { parser.parse(["--flag2"]) }.to raise_error(Homebrew::CLI::OptionConstraintError) + end + + it "raises exception for conflict violation" do + expect { parser.parse(["--flag1", "--flag3"]) }.to raise_error(Homebrew::CLI::OptionConflictError) + end + + it "raises no exception" do + args = parser.parse(["--flag1", "--flag2"]) + expect(args.flag1).to be true + expect(args.flag2).to be true + end + + it "raises no exception for optional dependency" do + args = parser.parse(["--flag3"]) + expect(args.flag3).to be true + end + end + + describe "test invalid constraints" do + subject(:parser) { + described_class.new do + flag "--flag1" + flag "--flag2", depends_on: "--flag1" + conflicts "--flag1", "--flag2" + end + } + + it "raises exception due to invalid constraints" do + expect { parser.parse([]) }.to raise_error(Homebrew::CLI::InvalidConstraintError) + end + end end diff --git a/Library/Homebrew/vendor/portable-ruby-version b/Library/Homebrew/vendor/portable-ruby-version index 0bee604df..b68b06849 100644 --- a/Library/Homebrew/vendor/portable-ruby-version +++ b/Library/Homebrew/vendor/portable-ruby-version @@ -1 +1 @@ -2.3.3 +2.3.3_1 diff --git a/README.md b/README.md index d9bc5dc48..fe3bc8f48 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,11 @@ Homebrew's lead maintainer is [Mike McQuaid](https://github.com/mikemcquaid). Homebrew/homebrew-core's lead maintainer is [ilovezfs](https://github.com/ilovezfs). -Homebrew/brew's other current maintainers are [ilovezfs](https://github.com/ilovezfs), [Alyssa Ross](https://github.com/alyssais), [JCount](https://github.com/jcount), [Misty De Meo](https://github.com/mistydemeo), [Gautham Goli](https://github.com/GauthamGoli), [Markus Reiter](https://github.com/reitermarkus) and [William Woodruff](https://github.com/woodruffw). +Homebrew/brew's other current maintainers are [ilovezfs](https://github.com/ilovezfs), [JCount](https://github.com/jcount), [Misty De Meo](https://github.com/mistydemeo), [Gautham Goli](https://github.com/GauthamGoli), [Markus Reiter](https://github.com/reitermarkus) and [William Woodruff](https://github.com/woodruffw). Homebrew/homebrew-core's other current maintainers are [FX Coudert](https://github.com/fxcoudert), [JCount](https://github.com/jcount), [Misty De Meo](https://github.com/mistydemeo) and [Tom Schoonjans](https://github.com/tschoonj). -Former maintainers with significant contributions include [Tim Smith](https://github.com/tdsmith), [Baptiste Fontaine](https://github.com/bfontaine), [Xu Cheng](https://github.com/xu-cheng), [Martin Afanasjew](https://github.com/UniqMartin), [Dominyk Tiller](https://github.com/DomT4), [Brett Koonce](https://github.com/asparagui), [Charlie Sharpsteen](https://github.com/Sharpie), [Jack Nagel](https://github.com/jacknagel), [Adam Vandenberg](https://github.com/adamv), [Andrew Janke](https://github.com/apjanke), [Alex Dunn](https://github.com/dunn), [neutric](https://github.com/neutric), [Tomasz Pajor](https://github.com/nijikon), [Uladzislau Shablinski](https://github.com/vladshablinsky) and Homebrew's creator: [Max Howell](https://github.com/mxcl). +Former maintainers with significant contributions include [Tim Smith](https://github.com/tdsmith), [Baptiste Fontaine](https://github.com/bfontaine), [Xu Cheng](https://github.com/xu-cheng), [Martin Afanasjew](https://github.com/UniqMartin), [Dominyk Tiller](https://github.com/DomT4), [Brett Koonce](https://github.com/asparagui), [Charlie Sharpsteen](https://github.com/Sharpie), [Jack Nagel](https://github.com/jacknagel), [Adam Vandenberg](https://github.com/adamv), [Andrew Janke](https://github.com/apjanke), [Alex Dunn](https://github.com/dunn), [neutric](https://github.com/neutric), [Tomasz Pajor](https://github.com/nijikon), [Uladzislau Shablinski](https://github.com/vladshablinsky), [Alyssa Ross](https://github.com/alyssais), and Homebrew's creator: [Max Howell](https://github.com/mxcl). ## Community - [discourse.brew.sh (forum)](https://discourse.brew.sh) diff --git a/completions/bash/brew b/completions/bash/brew index 406b6a901..6df866577 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -251,17 +251,6 @@ _brew_link() { __brew_complete_installed } -_brew_linkapps() { - local cur="${COMP_WORDS[COMP_CWORD]}" - case "$cur" in - --*) - __brewcomp "--local" - return - ;; - esac - __brew_complete_installed -} - _brew_list() { local allopts="--unbrewed --verbose --pinned --versions --multiple" local cur="${COMP_WORDS[COMP_CWORD]}" @@ -482,17 +471,6 @@ _brew_uninstall() { __brew_complete_installed } -_brew_unlinkapps() { - local cur="${COMP_WORDS[COMP_CWORD]}" - case "$cur" in - --*) - __brewcomp "--dry-run --local" - return - ;; - esac - __brew_complete_installed -} - _brew_unpack() { local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in @@ -827,7 +805,6 @@ _brew() { install|instal) _brew_install ;; irb) _brew_irb ;; link|ln) _brew_link ;; - linkapps) _brew_linkapps ;; list|ls) _brew_list ;; log) _brew_log ;; man) _brew_man ;; @@ -852,7 +829,6 @@ _brew() { tests) _brew_tests ;; uninstall|remove|rm) _brew_uninstall ;; unlink) __brew_complete_installed ;; - unlinkapps) _brew_unlinkapps ;; unpack) _brew_unpack ;; unpin) __brew_complete_formulae ;; untap) __brew_complete_tapped ;; diff --git a/completions/zsh/_brew b/completions/zsh/_brew index 45f50ccb0..41a2bfe50 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -89,7 +89,6 @@ __brew_common_commands() { 'reinstall:install a formula anew; re-using its current options' 'leaves:show installed formulae that are not dependencies of another installed formula' 'link:link a formula' - 'linkapps:symlink .app bundles provided by formulae into /Applications' 'list:list files in a formula or not-installed formulae' 'log:git commit log for a formula' 'missing:check all installed formuale for missing dependencies.' @@ -108,7 +107,6 @@ __brew_common_commands() { 'test-bot:test a formula and build a bottle' 'uninstall:uninstall a formula' 'unlink:unlink a formula' - 'unlinkapps:remove symlinked .app bundles provided by formulae from /Applications' 'unpin:unpin specified formulae' 'untap:remove a tapped repository' 'update:fetch latest version of Homebrew and all formulae' @@ -448,13 +446,6 @@ _brew_linkage() { ':formula:__brew_installed_formulae' } -# brew linkapps [--local] [formulae]: -_brew_linkapps() { - _arguments \ - '(--local)--local[symlink into ~/Application instead of the system directory]' \ - '::formula:__brew_installed_formulae' -} - # brew list, ls [--full-name]: # brew list, ls --unbrewed: # brew list, ls [--versions [--multiple]] [--pinned] [formulae]: @@ -704,14 +695,6 @@ _brew_unlink() { ':formula:__brew_installed_formulae' } -# brew unlinkapps [--local] [--dry-run] [formulae]: -_brew_unlinkapps() { - _arguments \ - '(--local)--local[remove symlinks from ~/Applications instead of the system directory]' \ - '(--dry-run -n)'{--dry-run,-n}'[don''t unlink or delete any files]' \ - ':formula:__brew_installed_formulae' -} - # brew unpack [--git|--patch] [--destdir=path] formulae: _brew_unpack() { _arguments \ diff --git a/docs/Acceptable-Formulae.md b/docs/Acceptable-Formulae.md index 5f6e6fcc6..914e8a94e 100644 --- a/docs/Acceptable-Formulae.md +++ b/docs/Acceptable-Formulae.md @@ -13,20 +13,15 @@ We now accept versioned formulae as long as they [meet the requirements](Version ### We don’t like tools that upgrade themselves Software that can upgrade itself does not integrate well with Homebrew's own -upgrade functionality. +upgrade functionality. The self-update functionality should be disabled (if possible without complicating the formula). -### We don’t like install-scripts that download things -Because that circumvents our hash-checks, makes finding/fixing bugs -harder, often breaks patches and disables the caching. Almost always you -can add a resource to the formula file to handle the -separate download and then the installer script will not attempt to load -that stuff on demand. Or there is a command-line switch where you can -point it to the downloaded archive in order to avoid loading. +### We don’t like install-scripts that download unversioned things +We don't like install scripts that are pulling from the `master` branch of Git repositories or unversioned, unchecksummed tarballs. These should use `resource` blocks with specific revisions or checksummed tarballs instead. Note that we now allow tools like `cargo`, `gem` and `pip` to download things during installation. ### We don’t like binary formulae Our policy is that formulae in the core tap ([homebrew/core](https://github.com/Homebrew/homebrew-core)) must be open-source -and either built from source or produce cross-platform binaries (e.g. Java). +and either built from source or produce cross-platform binaries (e.g. Java, Mono). Binary-only formulae should go to [Homebrew Cask](https://github.com/caskroom/homebrew-cask). @@ -41,7 +36,7 @@ due to upstream changes and we can’t provide [bottles](Bottles.md) for them. ### Niche (or self-submitted) stuff The software in question must: -* be maintained (e.g. upstream is still making new releases) +* be maintained (i.e. the last release wasn't ages ago, it works without patching on all supported macOS releases and has no outstanding, unpatched security vulnerabilites) * be known * be stable (e.g. not declared "unstable" or "beta" by upstream) * be used diff --git a/docs/Manpage.md b/docs/Manpage.md index 86baa2020..e4d6ce390 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -1283,11 +1283,11 @@ Homebrew's lead maintainer is Mike McQuaid. Homebrew/homebrew-core's lead maintainer is ilovezfs. -Homebrew/brew's other current maintainers are ilovezfs, Alyssa Ross, JCount, Misty De Meo, Gautham Goli, Markus Reiter and William Woodruff. +Homebrew/brew's other current maintainers are ilovezfs, JCount, Misty De Meo, Gautham Goli, Markus Reiter and William Woodruff. Homebrew/homebrew-core's other current maintainers are FX Coudert, JCount, Misty De Meo and Tom Schoonjans. -Former maintainers with significant contributions include Tim Smith, Baptiste Fontaine, Xu Cheng, Martin Afanasjew, Dominyk Tiller, Brett Koonce, Charlie Sharpsteen, Jack Nagel, Adam Vandenberg, Andrew Janke, Alex Dunn, neutric, Tomasz Pajor, Uladzislau Shablinski and Homebrew's creator: Max Howell. +Former maintainers with significant contributions include Tim Smith, Baptiste Fontaine, Xu Cheng, Martin Afanasjew, Dominyk Tiller, Brett Koonce, Charlie Sharpsteen, Jack Nagel, Adam Vandenberg, Andrew Janke, Alex Dunn, neutric, Tomasz Pajor, Uladzislau Shablinski, Alyssa Ross, and Homebrew's creator: Max Howell. ## BUGS diff --git a/manpages/brew.1 b/manpages/brew.1 index 9d2811a6a..08fffd289 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1325,13 +1325,13 @@ Homebrew\'s lead maintainer is Mike McQuaid\. Homebrew/homebrew\-core\'s lead maintainer is ilovezfs\. . .P -Homebrew/brew\'s other current maintainers are ilovezfs, Alyssa Ross, JCount, Misty De Meo, Gautham Goli, Markus Reiter and William Woodruff\. +Homebrew/brew\'s other current maintainers are ilovezfs, JCount, Misty De Meo, Gautham Goli, Markus Reiter and William Woodruff\. . .P Homebrew/homebrew\-core\'s other current maintainers are FX Coudert, JCount, Misty De Meo and Tom Schoonjans\. . .P -Former maintainers with significant contributions include Tim Smith, Baptiste Fontaine, Xu Cheng, Martin Afanasjew, Dominyk Tiller, Brett Koonce, Charlie Sharpsteen, Jack Nagel, Adam Vandenberg, Andrew Janke, Alex Dunn, neutric, Tomasz Pajor, Uladzislau Shablinski and Homebrew\'s creator: Max Howell\. +Former maintainers with significant contributions include Tim Smith, Baptiste Fontaine, Xu Cheng, Martin Afanasjew, Dominyk Tiller, Brett Koonce, Charlie Sharpsteen, Jack Nagel, Adam Vandenberg, Andrew Janke, Alex Dunn, neutric, Tomasz Pajor, Uladzislau Shablinski, Alyssa Ross, and Homebrew\'s creator: Max Howell\. . .SH "BUGS" See our issues on GitHub: