From 20155c8df9569e78a16ea47136a711469a29e5b7 Mon Sep 17 00:00:00 2001 From: Gautham Goli Date: Sun, 25 Mar 2018 23:49:54 +0530 Subject: [PATCH 01/13] bump-formula-pr: Use Parser to parse args --- Library/Homebrew/dev-cmd/bump-formula-pr.rb | 562 ++++++++++---------- 1 file changed, 291 insertions(+), 271 deletions(-) diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index b5c1dccdd..daaf10e01 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -42,16 +42,300 @@ #: 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 :debug + flag "--url", required: true + flag "--sha256", required: true + flag "--mirror", required: true + flag "--tag", required: true + flag "--revision", required: true + flag "--version", required: true + flag "--message", required: true + 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 +343,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 +387,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 From 36c1ad9f64cfa11265e504bea7fe69fdb69e9fb2 Mon Sep 17 00:00:00 2001 From: Gautham Goli Date: Sun, 1 Apr 2018 22:01:06 +0530 Subject: [PATCH 02/13] cli_parser: Add depends, conflicts option constraints --- Library/Homebrew/cli_parser.rb | 62 +++++++++++++++++++++ Library/Homebrew/dev-cmd/bump-formula-pr.rb | 4 ++ Library/Homebrew/test/cli_parser_spec.rb | 37 ++++++++++++ 3 files changed, 103 insertions(+) diff --git a/Library/Homebrew/cli_parser.rb b/Library/Homebrew/cli_parser.rb index 5ce769343..d92a2a4ff 100644 --- a/Library/Homebrew/cli_parser.rb +++ b/Library/Homebrew/cli_parser.rb @@ -13,6 +13,8 @@ module Homebrew @parsed_args = OpenStruct.new # undefine tap to allow --tap argument @parsed_args.instance_eval { undef tap } + @depends = [] + @conflicts = [] instance_eval(&block) end @@ -47,6 +49,14 @@ module Homebrew end end + def depends(primary, secondary, mandatory: false) + @depends << [primary, secondary, mandatory] + end + + def conflicts(primary, secondary) + @conflicts << [primary, secondary] + end + def option_to_name(name) name.sub(/\A--?/, "").tr("-", "_") end @@ -57,6 +67,7 @@ module Homebrew def parse(cmdline_args = ARGV) @parser.parse(cmdline_args) + check_constraint_violations @parsed_args end @@ -82,6 +93,57 @@ module Homebrew else name end end + + def option_passed?(name) + @parsed_args.respond_to?(name) || @parsed_args.respond_to?("#{name}?") + end + + def check_depends + @depends.each do |primary, secondary, required| + primary_passed = option_passed?(primary) + secondary_passed = option_passed?(secondary) + raise OptionDependencyError.new(primary, secondary) if required && primary_passed && + !secondary_passed + raise OptionDependencyError.new(primary, secondary, missing: true) if secondary_passed && + !primary_passed + end + end + + def check_conflicts + @conflicts.each do |primary, secondary| + primary_passed = option_passed?(primary) + secondary_passed = option_passed?(secondary) + raise OptionConflictError.new(primary, secondary) if primary_passed && secondary_passed + end + end + + def check_constraint_violations + check_conflicts + check_depends + end + end + + class OptionDependencyError < 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(arg1, arg2) + super <<~EOS + `#{arg1}` and `#{arg2}` should not be passed together + EOS + end end end end diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index daaf10e01..8a3abba08 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -57,6 +57,7 @@ module Homebrew switch "--no-browse" switch :quiet switch :force + switch :verbose switch :debug flag "--url", required: true flag "--sha256", required: true @@ -65,6 +66,9 @@ module Homebrew flag "--revision", required: true flag "--version", required: true flag "--message", required: true + depends :url, :sha256 + depends :tag, :revision, mandatory: true + conflicts :url, :tag end # As this command is simplifying user run commands then let's just use a diff --git a/Library/Homebrew/test/cli_parser_spec.rb b/Library/Homebrew/test/cli_parser_spec.rb index 5f65a80c1..82ab94436 100644 --- a/Library/Homebrew/test/cli_parser_spec.rb +++ b/Library/Homebrew/test/cli_parser_spec.rb @@ -70,4 +70,41 @@ 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 "--flag2" + flag "--flag3" + flag "--flag4" + depends :flag1, :flag2, mandatory: true + depends :flag3, :flag4 + conflicts :flag1, :flag3 + end + } + + it "raises exception on depends mandatory constraint violation" do + expect { parser.parse(["--flag1"]) }.to raise_error(Homebrew::CLI::OptionDependencyError) + end + + it "raises exception on depends constraint violation" do + expect { parser.parse(["--flag2"]) }.to raise_error(Homebrew::CLI::OptionDependencyError) + 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 end From 27ec9dae5fa4c2c5b19f8bfafaefbdbb3e9a9c8c Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Thu, 26 Apr 2018 08:26:57 +0100 Subject: [PATCH 03/13] Acceptable Formulae: base on current thinking. --- docs/Acceptable-Formulae.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/Acceptable-Formulae.md b/docs/Acceptable-Formulae.md index 5f6e6fcc6..9b7a94473 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 at all possible. -### 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 instead. Note that we now allow tools like `pip`, `cargo`, `gem` 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 (e.g. 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 From 44937fcf9552d9b810202c7b8fabad674ccf9f4a Mon Sep 17 00:00:00 2001 From: commitay Date: Fri, 27 Apr 2018 10:26:58 +1000 Subject: [PATCH 04/13] components_order_cop: add post_install --- Library/Homebrew/rubocops/components_order_cop.rb | 1 + 1 file changed, 1 insertion(+) 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 }], From c22fcac08df5c6ea4214dcb39d87e067b106ddd5 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sat, 28 Apr 2018 15:03:05 +0100 Subject: [PATCH 05/13] docs/Acceptable-Formulae: address feedback. Address comments in: https://github.com/Homebrew/brew/commit/27ec9dae5fa4c2c5b19f8bfafaefbdbb3e9a9c8c --- docs/Acceptable-Formulae.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Acceptable-Formulae.md b/docs/Acceptable-Formulae.md index 9b7a94473..914e8a94e 100644 --- a/docs/Acceptable-Formulae.md +++ b/docs/Acceptable-Formulae.md @@ -13,10 +13,10 @@ 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. The self-update functionality should be disabled if at all possible. +upgrade functionality. The self-update functionality should be disabled (if possible without complicating the formula). ### 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 instead. Note that we now allow tools like `pip`, `cargo`, `gem` to download things during installation. +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 @@ -36,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. it works without patching on all supported macOS releases and has no outstanding, unpatched security vulnerabilites) +* 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 From 191724ce83b165479e2663625cf18ef4b50c11ee Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sat, 28 Apr 2018 15:29:19 +0100 Subject: [PATCH 06/13] formula: read runtime dependencies from more tabs. The fixes the issue mentioned in: https://github.com/Homebrew/brew/pull/4066#issuecomment-381308153 where `brew missing` was not reading from a tab of an outdated formula. The `installed_prefix.directory?` check is unnecessary as the following `opt_or_installed_prefix_keg` call is already checking that when it is necessary. In this case it's not necessary anyway as `opt_prefix` is preferred and makes more sense on outdated formulae. --- Library/Homebrew/formula.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 5a146d4bc..b1bd1484d 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1502,7 +1502,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 From 8f3ddf968faa3d9d7470a417462f98087244c123 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sat, 28 Apr 2018 17:24:43 +0100 Subject: [PATCH 07/13] linkage_checker: correctly handle multiple taps. Fix upgrades of formulae with the same name in multiple taps. As reported in: https://github.com/Homebrew/brew/pull/3789#issuecomment-384890624 --- Library/Homebrew/linkage_checker.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Library/Homebrew/linkage_checker.rb b/Library/Homebrew/linkage_checker.rb index bf390c427..19520b076 100644 --- a/Library/Homebrew/linkage_checker.rb +++ b/Library/Homebrew/linkage_checker.rb @@ -111,8 +111,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 @@ -127,7 +131,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 @@ -136,9 +140,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 From b47b528859cc55b39c834b7c93efc044f7f1dada Mon Sep 17 00:00:00 2001 From: tjt263 Date: Mon, 30 Apr 2018 05:29:28 +0800 Subject: [PATCH 08/13] Fix CaskAlreadyCreatedError message output Minor edit. --- Library/Homebrew/cask/lib/hbc/exceptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From f4e2432284b22859e66819765dab32bfcb3f4e15 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Mon, 30 Apr 2018 08:43:43 +0100 Subject: [PATCH 09/13] portable-ruby: increase to 2.3.1_1. Install the latest Ruby from: https://github.com/Homebrew/homebrew-portable-ruby/pull/55 This updates ncurses and openssl as well as more closely matching the Apple's ruby build options. Notably, this includes the DBM support needed for #3720. --- Library/Homebrew/cmd/vendor-install.sh | 17 ++++++----------- Library/Homebrew/vendor/portable-ruby-version | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) 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/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 From dd79d5760fe83650eab62e852ddb443f0e8974b0 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Mon, 30 Apr 2018 10:15:10 +0100 Subject: [PATCH 10/13] README: goodbye Alyssa! And thanks for all the commits! <3 --- README.md | 4 ++-- docs/Manpage.md | 4 ++-- manpages/brew.1 | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) 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/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: From 07ee23d711954bfada003d9edf1fa7977a00e682 Mon Sep 17 00:00:00 2001 From: Gautham Goli Date: Sat, 14 Apr 2018 16:17:14 +0530 Subject: [PATCH 11/13] cli_parser: Refactor interface for depends, conflicts and add tests --- Library/Homebrew/cli_parser.rb | 77 +++++++++++++++------ Library/Homebrew/dev-cmd/bump-formula-pr.rb | 40 +++++------ Library/Homebrew/test/cli_parser_spec.rb | 31 ++++++--- 3 files changed, 97 insertions(+), 51 deletions(-) diff --git a/Library/Homebrew/cli_parser.rb b/Library/Homebrew/cli_parser.rb index d92a2a4ff..228eab7dd 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,7 +14,7 @@ module Homebrew @parsed_args = OpenStruct.new # undefine tap to allow --tap argument @parsed_args.instance_eval { undef tap } - @depends = [] + @constraints = [] @conflicts = [] instance_eval(&block) end @@ -36,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! "=" @@ -47,18 +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 depends(primary, secondary, mandatory: false) - @depends << [primary, secondary, mandatory] - end - - def conflicts(primary, secondary) - @conflicts << [primary, secondary] + 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) @@ -98,32 +97,57 @@ module Homebrew @parsed_args.respond_to?(name) || @parsed_args.respond_to?("#{name}?") end - def check_depends - @depends.each do |primary, secondary, required| + 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) - raise OptionDependencyError.new(primary, secondary) if required && primary_passed && - !secondary_passed - raise OptionDependencyError.new(primary, secondary, missing: true) if secondary_passed && - !primary_passed + 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 |primary, secondary| - primary_passed = option_passed?(primary) - secondary_passed = option_passed?(secondary) - raise OptionConflictError.new(primary, secondary) if primary_passed && secondary_passed + @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_depends + check_constraints end end - class OptionDependencyError < RuntimeError + class OptionConstraintError < RuntimeError def initialize(arg1, arg2, missing: false) if !missing message = <<~EOS @@ -139,9 +163,18 @@ module Homebrew 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}` should not be passed together + `#{arg1}` and `#{arg2}` cannot be mutually exclusive and mutually dependent simultaneously EOS end end diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index 8a3abba08..66a72fe2a 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -49,26 +49,26 @@ module Homebrew 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", required: true - flag "--sha256", required: true - flag "--mirror", required: true - flag "--tag", required: true - flag "--revision", required: true - flag "--version", required: true - flag "--message", required: true - depends :url, :sha256 - depends :tag, :revision, mandatory: true - conflicts :url, :tag + 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 diff --git a/Library/Homebrew/test/cli_parser_spec.rb b/Library/Homebrew/test/cli_parser_spec.rb index 82ab94436..418d3234f 100644 --- a/Library/Homebrew/test/cli_parser_spec.rb +++ b/Library/Homebrew/test/cli_parser_spec.rb @@ -74,22 +74,21 @@ describe Homebrew::CLI::Parser do describe "test constraints" do subject(:parser) { described_class.new do - flag "--flag1" - flag "--flag2" - flag "--flag3" - flag "--flag4" - depends :flag1, :flag2, mandatory: true - depends :flag3, :flag4 - conflicts :flag1, :flag3 + 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::OptionDependencyError) + 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::OptionDependencyError) + expect { parser.parse(["--flag2"]) }.to raise_error(Homebrew::CLI::OptionConstraintError) end it "raises exception for conflict violation" do @@ -107,4 +106,18 @@ describe Homebrew::CLI::Parser do 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 From 819b210263fa8ddcde2a8d839274b6a00f62410a Mon Sep 17 00:00:00 2001 From: FX Coudert Date: Mon, 30 Apr 2018 17:03:11 +0200 Subject: [PATCH 12/13] Support GCC 8 --- Library/Homebrew/compilers.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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, From 38ae15fe7d9a90166cad8b1d6f180eac485bfcc0 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Tue, 1 May 2018 10:49:44 +0100 Subject: [PATCH 13/13] Remove completions for (un)linkapps commands These were deleted in cffa5a9864e771f19231537b812b9b32a1160be0. --- completions/bash/brew | 24 ------------------------ completions/zsh/_brew | 17 ----------------- 2 files changed, 41 deletions(-) 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 \