diff options
162 files changed, 3536 insertions, 1897 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9eca6149b..6816a3ae3 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,17 +1,17 @@ # Please always follow these steps: -- [ ] Confirmed this is a problem with Homebrew/brew and not one or more formulae? If it's a formulae-specific problem please file this issue at https://github.com/Homebrew/homebrew-core/issues/new +- [ ] Confirmed this is a problem with running a `brew` command and just `brew install`ing one or more formulae? If it's a formulae-specific problem please file this issue at https://github.com/Homebrew/homebrew-core/issues/new - [ ] Ran `brew update` and retried your prior step? -- [ ] Ran `brew doctor`, fixed as many issues as possible and retried your prior step? +- [ ] Ran `brew doctor`, fixed all issues and retried your prior step? - [ ] Ran `brew config` and `brew doctor` and included their output with your issue? +**Please note we may immediately close your issue without comment if you delete or do not fill out the issue checklist and provide ALL the requested information.** + To help us debug your issue please explain: - What you were trying to do (and why) - What happened (include command output) - What you expected to happen - Step-by-step reproduction instructions (by running `brew` commands) -**Please note we may immediately close your issue without comment if you do not fill out the issue template and provide ALL the requested information.** - # Or propose a feature: -Please replace this section with a detailed description of your proposed feature, the motivation for it and alternatives considered. +Please replace this section with a detailed description of your proposed feature, the motivation for it, how it would be relevant to at least 90% of Homebrew users and alternatives considered. Please note we may close this issue or ask you to create a pull-request if it's something we're not actively planning to work on. diff --git a/.gitignore b/.gitignore index 31dedcc93..f7770612e 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ /docs/bin /docs/vendor /docs/Gemfile.lock +/docs/.jekyll-metadata # Unignore our shell completion !/completions diff --git a/.travis.yml b/.travis.yml index dda1644cf..c6c42a08b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,26 @@ language: ruby +cache: + directories: + - $HOME/.gem/ruby + - Library/Homebrew/vendor/bundle + # For parallel_rspec + - Library/Homebrew/tmp matrix: include: - os: osx - osx_image: xcode8.1 + osx_image: xcode8.3 rvm: system - os: linux + sudo: false rvm: 2.0.0 before_install: - export HOMEBREW_DEVELOPER=1 - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then HOMEBREW_REPOSITORY="$(brew --repo)"; + sudo chown -R "$USER" "$HOMEBREW_REPOSITORY/Library/Taps"; + mv "$HOMEBREW_REPOSITORY/Library/Taps" "$PWD/Library"; sudo rm -rf "$HOMEBREW_REPOSITORY"; sudo ln -s "$PWD" "$HOMEBREW_REPOSITORY"; else @@ -21,3 +30,6 @@ before_install: script: - brew test-bot + +notifications: + slack: machomebrew:1XNF7p1JRCdBUuKaeSwsWEc1 diff --git a/Library/.rubocop.yml b/Library/.rubocop.yml index a782c1117..12886a508 100644 --- a/Library/.rubocop.yml +++ b/Library/.rubocop.yml @@ -8,10 +8,19 @@ AllCops: require: ./Homebrew/rubocops.rb -Homebrew/CorrectBottleBlock: +FormulaAuditStrict/BottleBlock: Enabled: true -Homebrew/FormulaDesc: +FormulaAuditStrict/Desc: + Enabled: true + +FormulaAuditStrict/ComponentsOrder: + Enabled: true + +FormulaAuditStrict/ComponentsRedundancy: + Enabled: true + +FormulaAudit/Homepage: Enabled: true Metrics/AbcSize: @@ -34,6 +43,11 @@ Metrics/MethodLength: Metrics/ModuleLength: CountComments: false + Exclude: + - '**/bin/**/*' + - '**/cmd/**/*' + - '**/lib/**/*' + - '**/spec/**/*' Metrics/PerceivedComplexity: Enabled: false diff --git a/Library/Homebrew/PATH.rb b/Library/Homebrew/PATH.rb new file mode 100644 index 000000000..de7167eb4 --- /dev/null +++ b/Library/Homebrew/PATH.rb @@ -0,0 +1,74 @@ +class PATH + include Enumerable + extend Forwardable + + def_delegator :@paths, :each + + def initialize(*paths) + @paths = parse(*paths) + end + + def prepend(*paths) + @paths = parse(*paths, *@paths) + self + end + + def append(*paths) + @paths = parse(*@paths, *paths) + self + end + + def insert(index, *paths) + @paths = parse(*@paths.insert(index, *paths)) + self + end + + def select(&block) + self.class.new(@paths.select(&block)) + end + + def reject(&block) + self.class.new(@paths.reject(&block)) + end + + def to_ary + @paths + end + alias to_a to_ary + + def to_str + @paths.join(File::PATH_SEPARATOR) + end + alias to_s to_str + + def ==(other) + if other.respond_to?(:to_ary) + return true if to_ary == other.to_ary + end + + if other.respond_to?(:to_str) + return true if to_str == other.to_str + end + + false + end + + def empty? + @paths.empty? + end + + def existing + existing_path = select(&File.method(:directory?)) + # return nil instead of empty PATH, to unset environment variables + existing_path unless existing_path.empty? + end + + private + + def parse(*paths) + paths.flatten + .compact + .flat_map { |p| Pathname.new(p).to_path.split(File::PATH_SEPARATOR) } + .uniq + end +end diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb index aa38b54f4..e07599ac6 100644 --- a/Library/Homebrew/brew.rb +++ b/Library/Homebrew/brew.rb @@ -12,9 +12,9 @@ require "pathname" HOMEBREW_LIBRARY_PATH = Pathname.new(__FILE__).realpath.parent $:.unshift(HOMEBREW_LIBRARY_PATH.to_s) require "global" +require "tap" if ARGV == %w[--version] || ARGV == %w[-v] - require "tap" puts "Homebrew #{HOMEBREW_VERSION}" puts "Homebrew/homebrew-core #{CoreTap.instance.version_string}" exit 0 @@ -23,8 +23,7 @@ end def require?(path) require path rescue LoadError => e - # HACK: ( because we should raise on syntax errors but - # not if the file doesn't exist. TODO make robust! + # we should raise on syntax errors but not if the file doesn't exist. raise unless e.to_s.include? path end @@ -48,13 +47,15 @@ begin end end + path = PATH.new(ENV["PATH"]) + # Add contributed commands to PATH before checking. - Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/cmd"].each do |tap_cmd_dir| - ENV["PATH"] += "#{File::PATH_SEPARATOR}#{tap_cmd_dir}" - end + path.append(Pathname.glob(Tap::TAP_DIRECTORY/"*/*/cmd")) # Add SCM wrappers. - ENV["PATH"] += "#{File::PATH_SEPARATOR}#{HOMEBREW_SHIMS_PATH}/scm" + path.append(HOMEBREW_SHIMS_PATH/"scm") + + ENV["PATH"] = path if cmd internal_cmd = require? HOMEBREW_LIBRARY_PATH.join("cmd", cmd) @@ -122,7 +123,7 @@ rescue SystemExit => e onoe "Kernel.exit" if ARGV.verbose? && !e.success? $stderr.puts e.backtrace if ARGV.debug? raise -rescue Interrupt => e +rescue Interrupt $stderr.puts # seemingly a newline is typical exit 130 rescue BuildError => e diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index 11c1a6c59..02ce5e1c1 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -2,7 +2,7 @@ HOMEBREW_VERSION="$(git -C "$HOMEBREW_REPOSITORY" describe --tags --dirty 2>/dev HOMEBREW_USER_AGENT_VERSION="$HOMEBREW_VERSION" if [[ -z "$HOMEBREW_VERSION" ]] then - HOMEBREW_VERSION=">1.1.0 (no git repository)" + HOMEBREW_VERSION=">1.2.0 (no git repository)" HOMEBREW_USER_AGENT_VERSION="1.X.Y" fi @@ -101,6 +101,8 @@ then [[ "$HOMEBREW_PROCESSOR" = "i386" ]] && HOMEBREW_PROCESSOR="Intel" HOMEBREW_MACOS_VERSION="$(/usr/bin/sw_vers -productVersion)" HOMEBREW_OS_VERSION="macOS $HOMEBREW_MACOS_VERSION" + # Don't change this from Mac OS X to match what macOS itself does in Safari on 10.12 + HOMEBREW_OS_USER_AGENT_VERSION="Mac OS X $HOMEBREW_MACOS_VERSION" printf -v HOMEBREW_MACOS_VERSION_NUMERIC "%02d%02d%02d" ${HOMEBREW_MACOS_VERSION//./ } if [[ "$HOMEBREW_MACOS_VERSION_NUMERIC" -lt "100900" && @@ -113,8 +115,9 @@ else HOMEBREW_PRODUCT="${HOMEBREW_SYSTEM}brew" [[ -n "$HOMEBREW_LINUX" ]] && HOMEBREW_OS_VERSION="$(lsb_release -sd 2>/dev/null)" : "${HOMEBREW_OS_VERSION:=$(uname -r)}" + HOMEBREW_OS_USER_AGENT_VERSION="$HOMEBREW_OS_VERSION" fi -HOMEBREW_USER_AGENT="$HOMEBREW_PRODUCT/$HOMEBREW_USER_AGENT_VERSION ($HOMEBREW_SYSTEM; $HOMEBREW_PROCESSOR $HOMEBREW_OS_VERSION)" +HOMEBREW_USER_AGENT="$HOMEBREW_PRODUCT/$HOMEBREW_USER_AGENT_VERSION ($HOMEBREW_SYSTEM; $HOMEBREW_PROCESSOR $HOMEBREW_OS_USER_AGENT_VERSION)" HOMEBREW_CURL_VERSION="$("$HOMEBREW_CURL" --version 2>/dev/null | head -n1 | /usr/bin/awk '{print $1"/"$2}')" HOMEBREW_USER_AGENT_CURL="$HOMEBREW_USER_AGENT $HOMEBREW_CURL_VERSION" diff --git a/Library/Homebrew/build_options.rb b/Library/Homebrew/build_options.rb index d9020ecba..6c6952d71 100644 --- a/Library/Homebrew/build_options.rb +++ b/Library/Homebrew/build_options.rb @@ -47,7 +47,6 @@ class BuildOptions def bottle? include? "build-bottle" end - alias build_bottle? bottle? # True if a {Formula} is being built with {Formula.head} instead of {Formula.stable}. # <pre>args << "--some-new-stuff" if build.head?</pre> diff --git a/Library/Homebrew/cask/lib/hbc/artifact/binary.rb b/Library/Homebrew/cask/lib/hbc/artifact/binary.rb index 06bdfe157..21d123ab9 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/binary.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/binary.rb @@ -9,7 +9,12 @@ module Hbc def link super - FileUtils.chmod "+x", source + return if source.executable? + if source.writable? + FileUtils.chmod "+x", source + else + @command.run!("/bin/chmod", args: ["+x", source], sudo: true) + end end end end diff --git a/Library/Homebrew/cask/lib/hbc/cask.rb b/Library/Homebrew/cask/lib/hbc/cask.rb index cf5f2b37a..a193a394e 100644 --- a/Library/Homebrew/cask/lib/hbc/cask.rb +++ b/Library/Homebrew/cask/lib/hbc/cask.rb @@ -1,10 +1,12 @@ require "forwardable" require "hbc/dsl" +require "hbc/metadata" module Hbc class Cask extend Forwardable + include Metadata attr_reader :token, :sourcefile_path def initialize(token, sourcefile_path: nil, &block) @@ -20,55 +22,9 @@ module Hbc define_method(method_name) { @dsl.send(method_name) } end - METADATA_SUBDIR = ".metadata".freeze - - def metadata_master_container_path - @metadata_master_container_path ||= caskroom_path.join(METADATA_SUBDIR) - end - - def metadata_versioned_container_path - cask_version = version ? version : :unknown - metadata_master_container_path.join(cask_version.to_s) - end - - def metadata_path(timestamp = :latest, create = false) - if create && timestamp == :latest - raise CaskError, "Cannot create metadata path when timestamp is :latest" - end - path = if timestamp == :latest - Pathname.glob(metadata_versioned_container_path.join("*")).sort.last - elsif timestamp == :now - Utils.nowstamp_metadata_path(metadata_versioned_container_path) - else - metadata_versioned_container_path.join(timestamp) - end - if create - odebug "Creating metadata directory #{path}" - FileUtils.mkdir_p path - end - path - end - - def metadata_subdir(leaf, timestamp = :latest, create = false) - if create && timestamp == :latest - raise CaskError, "Cannot create metadata subdir when timestamp is :latest" - end - unless leaf.respond_to?(:length) && !leaf.empty? - raise CaskError, "Cannot create metadata subdir for empty leaf" - end - parent = metadata_path(timestamp, create) - return nil unless parent.respond_to?(:join) - subdir = parent.join(leaf) - if create - odebug "Creating metadata subdirectory #{subdir}" - FileUtils.mkdir_p subdir - end - subdir - end - def timestamped_versions - Pathname.glob(metadata_master_container_path.join("*", "*")) - .map { |p| p.relative_path_from(metadata_master_container_path) } + Pathname.glob(metadata_timestamped_path(version: "*", timestamp: "*")) + .map { |p| p.relative_path_from(p.parent.parent) } .sort_by(&:basename) # sort by timestamp .map { |p| p.split.map(&:to_s) } end diff --git a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb index 6b2f4caab..031f78824 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb @@ -92,7 +92,7 @@ module Hbc def self.render_staging_location(path) path = Pathname.new(user_tilde(path.to_s)) if !path.exist? - "#{path} #{error_string "error: path does not exist"}}" + "#{path} #{error_string "error: path does not exist"}" elsif !path.writable? "#{path} #{error_string "error: not writable by current user"}" else diff --git a/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb b/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb index b52c43328..c2ed8f462 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb @@ -7,30 +7,10 @@ module Hbc begin cask = CaskLoader.load(cask_token) - installer = Installer.new(cask, - force: force, - skip_cask_deps: skip_cask_deps, - require_sha: require_sha) - installer.print_caveats - installer.fetch - - if cask.installed? - # use copy of cask for uninstallation to avoid 'No such file or directory' bug - installed_cask = cask - - # use the same cask file that was used for installation, if possible - if (installed_caskfile = installed_cask.installed_caskfile).exist? - installed_cask = CaskLoader.load_from_file(installed_caskfile) - end - - # Always force uninstallation, ignore method parameter - Installer.new(installed_cask, force: true).uninstall - end - - installer.stage - installer.install_artifacts - installer.enable_accessibility_access - puts installer.summary + Installer.new(cask, + force: force, + skip_cask_deps: skip_cask_deps, + require_sha: require_sha).reinstall count += 1 rescue CaskUnavailableError => e diff --git a/Library/Homebrew/cask/lib/hbc/cli/search.rb b/Library/Homebrew/cask/lib/hbc/cli/search.rb index 992aca583..7abd744e4 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/search.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/search.rb @@ -13,6 +13,15 @@ module Hbc end end + def self.search_remote(query) + matches = GitHub.search_code("user:caskroom", "path:Casks", "filename:#{query}", "extension:rb") + [*matches].map do |match| + tap = Tap.fetch(match["repository"]["full_name"]) + next if tap.installed? + "#{tap.name}/#{File.basename(match["path"], ".rb")}" + end.compact + end + def self.search(*arguments) exact_match = nil partial_matches = [] @@ -29,27 +38,34 @@ module Hbc partial_matches = simplified_tokens.grep(/#{simplified_search_term}/i) { |t| all_tokens[simplified_tokens.index(t)] } partial_matches.delete(exact_match) end - [exact_match, partial_matches, search_term] + + remote_matches = search_remote(search_term) + + [exact_match, partial_matches, remote_matches, search_term] end - def self.render_results(exact_match, partial_matches, search_term) + def self.render_results(exact_match, partial_matches, remote_matches, search_term) if !exact_match && partial_matches.empty? puts "No Cask found for \"#{search_term}\"." return end if exact_match - ohai "Exact match" + ohai "Exact Match" puts highlight_installed exact_match end - return if partial_matches.empty? - - if extract_regexp search_term - ohai "Regexp matches" - else - ohai "Partial matches" + unless partial_matches.empty? + if extract_regexp search_term + ohai "Regexp Matches" + else + ohai "Partial Matches" + end + puts Formatter.columns(partial_matches.map(&method(:highlight_installed))) end - puts Formatter.columns(partial_matches.map(&method(:highlight_installed))) + + return if remote_matches.empty? + ohai "Remote Matches" + puts Formatter.columns(remote_matches.map(&method(:highlight_installed))) end def self.highlight_installed(token) diff --git a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb index 6887aaf4f..1ee3230ad 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb @@ -12,15 +12,9 @@ module Hbc raise CaskNotInstalledError, cask unless cask.installed? || force - latest_installed_version = cask.timestamped_versions.last - - unless latest_installed_version.nil? - latest_installed_cask_file = cask.metadata_master_container_path - .join(latest_installed_version.join(File::Separator), - "Casks", "#{cask_token}.rb") - + if cask.installed? && !cask.installed_caskfile.nil? # use the same cask file that was used for installation, if possible - cask = CaskLoader.load_from_file(latest_installed_cask_file) if latest_installed_cask_file.exist? + cask = CaskLoader.load_from_file(cask.installed_caskfile) if cask.installed_caskfile.exist? end Installer.new(cask, force: force).uninstall diff --git a/Library/Homebrew/cask/lib/hbc/container.rb b/Library/Homebrew/cask/lib/hbc/container.rb index c6b2c3c37..961e31968 100644 --- a/Library/Homebrew/cask/lib/hbc/container.rb +++ b/Library/Homebrew/cask/lib/hbc/container.rb @@ -61,7 +61,7 @@ module Hbc begin const_get(type.to_s.split("_").map(&:capitalize).join) rescue NameError - false + nil end end end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/container.rb b/Library/Homebrew/cask/lib/hbc/dsl/container.rb index ab260c98c..caaf25bca 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/container.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/container.rb @@ -1,3 +1,5 @@ +require "hbc/container" + module Hbc class DSL class Container @@ -13,9 +15,12 @@ module Hbc @pairs = pairs pairs.each do |key, value| raise "invalid container key: '#{key.inspect}'" unless VALID_KEYS.include?(key) - writer_method = "#{key}=".to_sym - send(writer_method, value) + send(:"#{key}=", value) end + + return if type.nil? + return unless Hbc::Container.from_type(type).nil? + raise "invalid container type: #{type.inspect}" end def to_yaml diff --git a/Library/Homebrew/cask/lib/hbc/installer.rb b/Library/Homebrew/cask/lib/hbc/installer.rb index 824c1b1be..f02f07806 100644 --- a/Library/Homebrew/cask/lib/hbc/installer.rb +++ b/Library/Homebrew/cask/lib/hbc/installer.rb @@ -24,6 +24,7 @@ module Hbc @force = force @skip_cask_deps = skip_cask_deps @require_sha = require_sha + @reinstall = false end def self.print_caveats(cask) @@ -76,13 +77,16 @@ module Hbc def install odebug "Hbc::Installer#install" - if @cask.installed? && !force + if @cask.installed? && !force && !@reinstall raise CaskAlreadyInstalledAutoUpdatesError, @cask if @cask.auto_updates raise CaskAlreadyInstalledError, @cask end print_caveats fetch + uninstall_existing_cask if @reinstall + + oh1 "Installing Cask #{@cask}" stage install_artifacts enable_accessibility_access @@ -90,6 +94,23 @@ module Hbc puts summary end + def reinstall + odebug "Hbc::Installer#reinstall" + @reinstall = true + install + end + + def uninstall_existing_cask + return unless @cask.installed? + + # use the same cask file that was used for installation, if possible + installed_caskfile = @cask.installed_caskfile + installed_cask = installed_caskfile.exist? ? CaskLoader.load_from_file(installed_caskfile) : @cask + + # Always force uninstallation, ignore method parameter + Installer.new(installed_cask, force: true).uninstall + end + def summary s = "" s << "#{Emoji.install_badge} " if Emoji.enabled? @@ -295,19 +316,17 @@ module Hbc end def save_caskfile - unless (old_savedirs = Pathname.glob(@cask.metadata_path("*"))).empty? - old_savedirs.each(&:rmtree) - end + old_savedir = @cask.metadata_timestamped_path return unless @cask.sourcefile_path - savedir = @cask.metadata_subdir("Casks", :now, true) - savedir.mkpath + savedir = @cask.metadata_subdir("Casks", timestamp: :now, create: true) FileUtils.copy @cask.sourcefile_path, savedir + old_savedir.rmtree unless old_savedir.nil? end def uninstall - odebug "Hbc::Installer#uninstall" + oh1 "Uninstalling Cask #{@cask}" disable_accessibility_access uninstall_artifacts purge_versioned_files @@ -355,15 +374,15 @@ module Hbc gain_permissions_remove(@cask.staged_path) if !@cask.staged_path.nil? && @cask.staged_path.exist? # Homebrew-Cask metadata - if @cask.metadata_versioned_container_path.respond_to?(:children) && - @cask.metadata_versioned_container_path.exist? - @cask.metadata_versioned_container_path.children.each do |subdir| + if @cask.metadata_versioned_path.respond_to?(:children) && + @cask.metadata_versioned_path.exist? + @cask.metadata_versioned_path.children.each do |subdir| unless PERSISTENT_METADATA_SUBDIRS.include?(subdir.basename) gain_permissions_remove(subdir) end end end - @cask.metadata_versioned_container_path.rmdir_if_possible + @cask.metadata_versioned_path.rmdir_if_possible @cask.metadata_master_container_path.rmdir_if_possible # toplevel staged distribution diff --git a/Library/Homebrew/cask/lib/hbc/metadata.rb b/Library/Homebrew/cask/lib/hbc/metadata.rb new file mode 100644 index 000000000..344c38cee --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/metadata.rb @@ -0,0 +1,73 @@ +module Hbc + module Metadata + METADATA_SUBDIR = ".metadata".freeze + + def metadata_master_container_path + @metadata_master_container_path ||= caskroom_path.join(METADATA_SUBDIR) + end + + def metadata_versioned_path(version: self.version) + cask_version = (version || :unknown).to_s + + if cask_version.empty? + raise CaskError, "Cannot create metadata path with empty version." + end + + metadata_master_container_path.join(cask_version) + end + + def metadata_timestamped_path(version: self.version, timestamp: :latest, create: false) + if create && timestamp == :latest + raise CaskError, "Cannot create metadata path when timestamp is :latest." + end + + path = if timestamp == :latest + Pathname.glob(metadata_versioned_path(version: version).join("*")).sort.last + else + timestamp = new_timestamp if timestamp == :now + metadata_versioned_path(version: version).join(timestamp) + end + + if create && !path.directory? + odebug "Creating metadata directory #{path}." + path.mkpath + end + + path + end + + def metadata_subdir(leaf, version: self.version, timestamp: :latest, create: false) + if create && timestamp == :latest + raise CaskError, "Cannot create metadata subdir when timestamp is :latest." + end + + unless leaf.respond_to?(:empty?) && !leaf.empty? + raise CaskError, "Cannot create metadata subdir for empty leaf." + end + + parent = metadata_timestamped_path(version: version, timestamp: timestamp, create: create) + + return nil if parent.nil? + + subdir = parent.join(leaf) + + if create && !subdir.directory? + odebug "Creating metadata subdirectory #{subdir}." + subdir.mkpath + end + + subdir + end + + private + + def new_timestamp(time = Time.now) + time = time.utc + + timestamp = time.strftime("%Y%m%d%H%M%S") + fraction = format("%.3f", time.to_f - time.to_i)[1..-1] + + timestamp.concat(fraction) + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/pkg.rb b/Library/Homebrew/cask/lib/hbc/pkg.rb index 902d56449..c9aa3180f 100644 --- a/Library/Homebrew/cask/lib/hbc/pkg.rb +++ b/Library/Homebrew/cask/lib/hbc/pkg.rb @@ -63,7 +63,10 @@ module Hbc end def pkgutil_bom_all - @pkgutil_bom_all ||= info.fetch("paths").keys.map { |p| root.join(p) } + @pkgutil_bom_all ||= @command.run!("/usr/sbin/pkgutil", args: ["--files", package_id]) + .stdout + .split("\n") + .map { |path| root.join(path) } end def root @@ -71,7 +74,7 @@ module Hbc end def info - @info ||= @command.run!("/usr/sbin/pkgutil", args: ["--export-plist", package_id]) + @info ||= @command.run!("/usr/sbin/pkgutil", args: ["--pkg-info-plist", package_id]) .plist end diff --git a/Library/Homebrew/cask/lib/hbc/scopes.rb b/Library/Homebrew/cask/lib/hbc/scopes.rb index db12409e5..149c2c343 100644 --- a/Library/Homebrew/cask/lib/hbc/scopes.rb +++ b/Library/Homebrew/cask/lib/hbc/scopes.rb @@ -6,8 +6,7 @@ module Hbc module ClassMethods def all - @all_casks ||= {} - all_tokens.map { |t| @all_casks[t] ||= load(t) } + all_tokens.map(&CaskLoader.public_method(:load)) end def all_tapped_cask_dirs diff --git a/Library/Homebrew/cask/lib/hbc/utils.rb b/Library/Homebrew/cask/lib/hbc/utils.rb index ecb565e8e..b2b65a08e 100644 --- a/Library/Homebrew/cask/lib/hbc/utils.rb +++ b/Library/Homebrew/cask/lib/hbc/utils.rb @@ -43,7 +43,7 @@ module Hbc p.rmtree else command.run("/bin/rm", - args: command_args + ["-r", "-f", "--", p], + args: ["-r", "-f", "--", p], sudo: true) end end @@ -115,16 +115,5 @@ module Hbc opoo(poo.join(" ") + "\n" + error_message_with_suggestions) end - - def self.nowstamp_metadata_path(container_path) - @timenow ||= Time.now.gmtime - return unless container_path.respond_to?(:join) - - precision = 3 - timestamp = @timenow.strftime("%Y%m%d%H%M%S") - fraction = format("%.#{precision}f", @timenow.to_f - @timenow.to_i)[1..-1] - timestamp.concat(fraction) - container_path.join(timestamp) - end end end diff --git a/Library/Homebrew/caveats.rb b/Library/Homebrew/caveats.rb index 61b703469..ee09063fd 100644 --- a/Library/Homebrew/caveats.rb +++ b/Library/Homebrew/caveats.rb @@ -44,15 +44,17 @@ class Caveats def keg_only_text return unless f.keg_only? - s = "This formula is keg-only, which means it was not symlinked into #{HOMEBREW_PREFIX}." - s << "\n\n#{f.keg_only_reason}\n" + s = <<-EOS.undent + This formula is keg-only, which means it was not symlinked into #{HOMEBREW_PREFIX}, + because #{f.keg_only_reason.to_s.chomp}. + EOS if f.bin.directory? || f.sbin.directory? s << "\nIf you need to have this software first in your PATH run:\n" if f.bin.directory? - s << " #{Utils::Shell.prepend_path_in_shell_profile(f.opt_bin.to_s)}\n" + s << " #{Utils::Shell.prepend_path_in_profile(f.opt_bin.to_s)}\n" end if f.sbin.directory? - s << " #{Utils::Shell.prepend_path_in_shell_profile(f.opt_sbin.to_s)}\n" + s << " #{Utils::Shell.prepend_path_in_profile(f.opt_sbin.to_s)}\n" end end diff --git a/Library/Homebrew/cmd/--env.rb b/Library/Homebrew/cmd/--env.rb index 323964dad..90beee89c 100644 --- a/Library/Homebrew/cmd/--env.rb +++ b/Library/Homebrew/cmd/--env.rb @@ -22,9 +22,9 @@ module Homebrew # legacy behavior shell = :bash unless $stdout.tty? elsif shell_value == "auto" - shell = Utils::Shell.parent_shell || Utils::Shell.preferred_shell + shell = Utils::Shell.parent || Utils::Shell.preferred elsif shell_value - shell = Utils::Shell.path_to_shell(shell_value) + shell = Utils::Shell.from_path(shell_value) end env_keys = build_env_keys(ENV) diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index 1808c4d9c..394b31db0 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -206,7 +206,8 @@ module Homebrew Migrator.migrate_if_needed(f) install_formula(f) end - rescue FormulaClassUnavailableError => e + rescue FormulaUnreadableError, FormulaClassUnavailableError, + TapFormulaUnreadableError, TapFormulaClassUnavailableError => e # Need to rescue before `FormulaUnavailableError` (superclass of this) # is handled, as searching for a formula doesn't make sense here (the # formula was found, but there's a problem with its implementation). @@ -240,6 +241,8 @@ module Homebrew puts "To install one of them, run (for example):\n brew install #{formulae_search_results.first}" end + # Do not search taps if the formula name is qualified + return if e.name.include?("/") ohai "Searching taps..." taps_search_results = search_taps(query) case taps_search_results.length diff --git a/Library/Homebrew/cmd/link.rb b/Library/Homebrew/cmd/link.rb index 5ce6bea48..b8bd135e0 100644 --- a/Library/Homebrew/cmd/link.rb +++ b/Library/Homebrew/cmd/link.rb @@ -86,8 +86,8 @@ module Homebrew opt = HOMEBREW_PREFIX/"opt/#{keg.name}" puts "\nIf you need to have this software first in your PATH instead consider running:" - puts " #{Utils::Shell.prepend_path_in_shell_profile(opt/"bin")}" if bin.directory? - puts " #{Utils::Shell.prepend_path_in_shell_profile(opt/"sbin")}" if sbin.directory? + puts " #{Utils::Shell.prepend_path_in_profile(opt/"bin")}" if bin.directory? + puts " #{Utils::Shell.prepend_path_in_profile(opt/"sbin")}" if sbin.directory? end def keg_only?(rack) diff --git a/Library/Homebrew/cmd/postinstall.rb b/Library/Homebrew/cmd/postinstall.rb index 8808a2602..f5d091227 100644 --- a/Library/Homebrew/cmd/postinstall.rb +++ b/Library/Homebrew/cmd/postinstall.rb @@ -7,7 +7,10 @@ module Homebrew module_function def postinstall - ARGV.resolved_formulae.each { |f| run_post_install(f) if f.post_install_defined? } + ARGV.resolved_formulae.each do |f| + ohai "Postinstalling #{f}" + run_post_install(f) + end end def run_post_install(formula) diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index 443739f8c..f71a14ba1 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -16,15 +16,12 @@ require "formula" require "missing_formula" require "utils" -require "thread" require "official_taps" require "descriptions" module Homebrew module_function - SEARCH_ERROR_QUEUE = Queue.new - def search if ARGV.empty? puts Formatter.columns(Formula.full_names) @@ -61,7 +58,7 @@ module Homebrew regex = query_regexp(query) local_results = search_formulae(regex) puts Formatter.columns(local_results) unless local_results.empty? - tap_results = search_taps(regex) + tap_results = search_taps(query) puts Formatter.columns(tap_results) unless tap_results.empty? if $stdout.tty? @@ -75,36 +72,25 @@ module Homebrew puts reason elsif count.zero? puts "No formula found for #{query.inspect}." - begin - GitHub.print_pull_requests_matching(query) - rescue GitHub::Error => e - SEARCH_ERROR_QUEUE << e - end + GitHub.print_pull_requests_matching(query) end end end - if $stdout.tty? - metacharacters = %w[\\ | ( ) [ ] { } ^ $ * + ?] - bad_regex = metacharacters.any? do |char| - ARGV.any? do |arg| - arg.include?(char) && !arg.start_with?("/") - end - end - if !ARGV.empty? && bad_regex - ohai "Did you mean to perform a regular expression search?" - ohai "Surround your query with /slashes/ to search by regex." + return unless $stdout.tty? + return if ARGV.empty? + metacharacters = %w[\\ | ( ) [ ] { } ^ $ * + ?].freeze + return unless metacharacters.any? do |char| + ARGV.any? do |arg| + arg.include?(char) && !arg.start_with?("/") end end - - raise SEARCH_ERROR_QUEUE.pop unless SEARCH_ERROR_QUEUE.empty? + ohai <<-EOS.undent + Did you mean to perform a regular expression search? + Surround your query with /slashes/ to search locally by regex. + EOS end - SEARCHABLE_TAPS = OFFICIAL_TAPS.map { |tap| ["Homebrew", tap] } + [ - %w[Caskroom cask], - %w[Caskroom versions], - ] - def query_regexp(query) case query when %r{^/(.*)/$} then Regexp.new($1) @@ -114,53 +100,16 @@ module Homebrew odie "#{query} is not a valid regex" end - def search_taps(regex_or_string) - SEARCHABLE_TAPS.map do |user, repo| - Thread.new { search_tap(user, repo, regex_or_string) } - end.inject([]) do |results, t| - results.concat(t.value) - end - end - - def search_tap(user, repo, regex_or_string) - regex = regex_or_string.is_a?(String) ? /^#{Regexp.escape(regex_or_string)}$/ : regex_or_string - - if (HOMEBREW_LIBRARY/"Taps/#{user.downcase}/homebrew-#{repo.downcase}").directory? && \ - user != "Caskroom" - return [] - end - - remote_tap_formulae = Hash.new do |cache, key| - user, repo = key.split("/", 2) - tree = {} - - GitHub.open "https://api.github.com/repos/#{user}/homebrew-#{repo}/git/trees/HEAD?recursive=1" do |json| - json["tree"].each do |object| - next unless object["type"] == "blob" - - subtree, file = File.split(object["path"]) - - if File.extname(file) == ".rb" - tree[subtree] ||= [] - tree[subtree] << file - end - end - end - - paths = tree["Formula"] || tree["HomebrewFormula"] || tree["."] || [] - paths += tree["Casks"] || [] - cache[key] = paths.map { |path| File.basename(path, ".rb") } - end - - names = remote_tap_formulae["#{user}/#{repo}"] - user = user.downcase if user == "Homebrew" # special handling for the Homebrew organization - names.select { |name| name =~ regex }.map { |name| "#{user}/#{repo}/#{name}" } - rescue GitHub::HTTPNotFoundError - opoo "Failed to search tap: #{user}/#{repo}. Please run `brew update`" - [] - rescue GitHub::Error => e - SEARCH_ERROR_QUEUE << e - [] + def search_taps(query) + valid_dirnames = ["Formula", "HomebrewFormula", "Casks", "."].freeze + matches = GitHub.search_code("user:Homebrew", "user:caskroom", "filename:#{query}", "extension:rb") + [*matches].map do |match| + dirname, filename = File.split(match["path"]) + next unless valid_dirnames.include?(dirname) + tap = Tap.fetch(match["repository"]["full_name"]) + next if tap.installed? + "#{tap.name}/#{File.basename(filename, ".rb")}" + end.compact end def search_formulae(regex) diff --git a/Library/Homebrew/cmd/sh.rb b/Library/Homebrew/cmd/sh.rb index 249753355..69f329cb3 100644 --- a/Library/Homebrew/cmd/sh.rb +++ b/Library/Homebrew/cmd/sh.rb @@ -23,7 +23,7 @@ module Homebrew ENV.setup_build_environment if superenv? # superenv stopped adding brew's bin but generally users will want it - ENV["PATH"] = ENV["PATH"].split(File::PATH_SEPARATOR).insert(1, "#{HOMEBREW_PREFIX}/bin").join(File::PATH_SEPARATOR) + ENV["PATH"] = PATH.new(ENV["PATH"]).insert(1, HOMEBREW_PREFIX/"bin") end ENV["PS1"] = 'brew \[\033[1;32m\]\w\[\033[0m\]$ ' ENV["VERBOSE"] = "1" diff --git a/Library/Homebrew/cmd/style.rb b/Library/Homebrew/cmd/style.rb index 2a7f37031..cf41d91ee 100644 --- a/Library/Homebrew/cmd/style.rb +++ b/Library/Homebrew/cmd/style.rb @@ -1,4 +1,4 @@ -#: * `style` [`--fix`] [`--display-cop-names`] [<files>|<taps>|<formulae>]: +#: * `style` [`--fix`] [`--display-cop-names`] [`--only-cops=`[COP1,COP2..]|`--except-cops=`[COP1,COP2..]] [<files>|<taps>|<formulae>]: #: Check formulae or files for conformance to Homebrew style guidelines. #: #: <formulae> and <files> may not be combined. If both are omitted, style will run @@ -11,6 +11,10 @@ #: If `--display-cop-names` is passed, the RuboCop cop name for each violation #: is included in the output. #: +#: If `--only-cops` is passed, only the given Rubocop cop(s)' violations would be checked. +#: +#: If `--except-cops` is passed, the given Rubocop cop(s)' checks would be skipped. +#: #: Exits with a non-zero status if any style violations are found. require "utils" @@ -30,7 +34,20 @@ module Homebrew ARGV.formulae.map(&:path) end - Homebrew.failed = check_style_and_print(target, fix: ARGV.flag?("--fix")) + only_cops = ARGV.value("only-cops").to_s.split(",") + except_cops = ARGV.value("except-cops").to_s.split(",") + if !only_cops.empty? && !except_cops.empty? + odie "--only-cops and --except-cops cannot be used simultaneously!" + end + + options = { fix: ARGV.flag?("--fix") } + if !only_cops.empty? + options[:only_cops] = only_cops + elsif !except_cops.empty? + options[:except_cops] = except_cops + end + + Homebrew.failed = check_style_and_print(target, options) end # Checks style for a list of files, printing simple RuboCop output. @@ -47,13 +64,34 @@ module Homebrew def check_style_impl(files, output_type, options = {}) fix = options[:fix] + Homebrew.install_gem_setup_path! "rubocop", HOMEBREW_RUBOCOP_VERSION + require "rubocop" + require_relative "../rubocops" args = %w[ --force-exclusion ] args << "--auto-correct" if fix + if options[:except_cops] + options[:except_cops].map! { |cop| RuboCop::Cop::Cop.registry.qualified_cop_name(cop.to_s, "") } + cops_to_exclude = options[:except_cops].select do |cop| + RuboCop::Cop::Cop.registry.names.include?(cop) || + RuboCop::Cop::Cop.registry.departments.include?(cop.to_sym) + end + + args << "--except" << cops_to_exclude.join(",") unless cops_to_exclude.empty? + elsif options[:only_cops] + options[:only_cops].map! { |cop| RuboCop::Cop::Cop.registry.qualified_cop_name(cop.to_s, "") } + cops_to_include = options[:only_cops].select do |cop| + RuboCop::Cop::Cop.registry.names.include?(cop) || + RuboCop::Cop::Cop.registry.departments.include?(cop.to_sym) + end + + args << "--only" << cops_to_include.join(",") unless cops_to_include.empty? + end + if files.nil? args << "--config" << HOMEBREW_LIBRARY_PATH/".rubocop.yml" args += [HOMEBREW_LIBRARY_PATH] diff --git a/Library/Homebrew/cmd/tap.rb b/Library/Homebrew/cmd/tap.rb index 114c4a8b6..2a07c1b2f 100644 --- a/Library/Homebrew/cmd/tap.rb +++ b/Library/Homebrew/cmd/tap.rb @@ -63,17 +63,4 @@ module Homebrew def full_clone? ARGV.include?("--full") || ARGV.homebrew_developer? end - - # @deprecated this method will be removed in the future, if no external commands use it. - def install_tap(user, repo, clone_target = nil) - opoo "Homebrew.install_tap is deprecated, use Tap#install." - tap = Tap.fetch(user, repo) - begin - tap.install(clone_target: clone_target, full_clone: full_clone?) - rescue TapAlreadyTappedError - false - else - true - end - end end diff --git a/Library/Homebrew/cmd/update.sh b/Library/Homebrew/cmd/update.sh index 197a99f2e..77a5c1cd2 100644 --- a/Library/Homebrew/cmd/update.sh +++ b/Library/Homebrew/cmd/update.sh @@ -23,7 +23,7 @@ git() { } git_init_if_necessary() { - if [[ -n "$HOMEBREW_MACOS" ]] + if [[ -n "$HOMEBREW_MACOS" ]] || [[ -n "$HOMEBREW_FORCE_HOMEBREW_ORG" ]] then BREW_OFFICIAL_REMOTE="https://github.com/Homebrew/brew" CORE_OFFICIAL_REMOTE="https://github.com/Homebrew/homebrew-core" diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb index b1122c90a..bab174184 100644 --- a/Library/Homebrew/cmd/uses.rb +++ b/Library/Homebrew/cmd/uses.rb @@ -28,7 +28,16 @@ module Homebrew def uses raise FormulaUnspecifiedError if ARGV.named.empty? - used_formulae = ARGV.formulae + used_formulae_missing = false + used_formulae = begin + ARGV.formulae + rescue FormulaUnavailableError => e + opoo e + used_formulae_missing = true + # If the formula doesn't exist: fake the needed formula object name. + ARGV.named.map { |name| OpenStruct.new name: name, full_name: name } + end + formulae = ARGV.include?("--installed") ? Formula.installed : Formula recursive = ARGV.flag? "--recursive" includes = [] @@ -115,5 +124,6 @@ module Homebrew return if uses.empty? puts Formatter.columns(uses.map(&:full_name)) + odie "Missing formulae should not have dependents!" if used_formulae_missing end end diff --git a/Library/Homebrew/compat.rb b/Library/Homebrew/compat.rb index 92b687725..3c080f616 100644 --- a/Library/Homebrew/compat.rb +++ b/Library/Homebrew/compat.rb @@ -25,3 +25,4 @@ require "compat/tab" require "compat/ENV/shared" require "compat/ENV/std" require "compat/ENV/super" +require "compat/utils/shell" diff --git a/Library/Homebrew/compat/ARGV.rb b/Library/Homebrew/compat/ARGV.rb index 23d02ce1a..e5fa8188f 100644 --- a/Library/Homebrew/compat/ARGV.rb +++ b/Library/Homebrew/compat/ARGV.rb @@ -1,6 +1,6 @@ module HomebrewArgvExtension def build_32_bit? - # odeprecated "ARGV.build_32_bit?" + odeprecated "ARGV.build_32_bit?" include? "--32-bit" end end diff --git a/Library/Homebrew/compat/build_options.rb b/Library/Homebrew/compat/build_options.rb index 52aa9b951..73722dadb 100644 --- a/Library/Homebrew/compat/build_options.rb +++ b/Library/Homebrew/compat/build_options.rb @@ -1,6 +1,11 @@ class BuildOptions def build_32_bit? - # odeprecated "build.build_32_bit?" + odeprecated "build.build_32_bit?" include?("32-bit") && option_defined?("32-bit") end + + def build_bottle? + odeprecated "build.build_bottle?", "build.bottle?" + bottle? + end end diff --git a/Library/Homebrew/compat/dependency_collector.rb b/Library/Homebrew/compat/dependency_collector.rb index bd72c55d4..fbcf1c2a0 100644 --- a/Library/Homebrew/compat/dependency_collector.rb +++ b/Library/Homebrew/compat/dependency_collector.rb @@ -14,7 +14,6 @@ class DependencyCollector output_deprecation(spec, tags) Dependency.new(spec.to_s, tags) when :apr - # TODO: reenable in future when we've fixed a few of the audits. # output_deprecation(spec, tags, "apr-util") Dependency.new("apr-util", tags) when :libltdl diff --git a/Library/Homebrew/compat/global.rb b/Library/Homebrew/compat/global.rb index 797e9ffe5..82c452cc0 100644 --- a/Library/Homebrew/compat/global.rb +++ b/Library/Homebrew/compat/global.rb @@ -3,7 +3,7 @@ module Homebrew def method_missing(method, *args, &block) if instance_methods.include?(method) - # odeprecated "#{self}##{method}", "'module_function' or 'def self.#{method}' to convert it to a class method" + odeprecated "#{self}##{method}", "'module_function' or 'def self.#{method}' to convert it to a class method" return instance_method(method).bind(self).call(*args, &block) end super diff --git a/Library/Homebrew/compat/software_spec.rb b/Library/Homebrew/compat/software_spec.rb index 51b0f3a0b..5efd2aeb4 100644 --- a/Library/Homebrew/compat/software_spec.rb +++ b/Library/Homebrew/compat/software_spec.rb @@ -1,7 +1,5 @@ class BottleSpecification def revision(*args) - # Don't announce deprecation yet as this is quite a big change - # to a public interface. # odeprecated "BottleSpecification.revision", "BottleSpecification.rebuild" rebuild(*args) end diff --git a/Library/Homebrew/compat/tab.rb b/Library/Homebrew/compat/tab.rb index 58fdc4913..2cf71c923 100644 --- a/Library/Homebrew/compat/tab.rb +++ b/Library/Homebrew/compat/tab.rb @@ -1,6 +1,6 @@ class Tab < OpenStruct def build_32_bit? - # odeprecated "Tab.build_32_bit?" + odeprecated "Tab.build_32_bit?" include?("32-bit") end end diff --git a/Library/Homebrew/compat/tap.rb b/Library/Homebrew/compat/tap.rb index d1cf7f1d5..37b1eeac1 100644 --- a/Library/Homebrew/compat/tap.rb +++ b/Library/Homebrew/compat/tap.rb @@ -6,5 +6,3 @@ class Tap core_tap? end end - -CoreFormulaRepository = CoreTap diff --git a/Library/Homebrew/compat/utils.rb b/Library/Homebrew/compat/utils.rb index 56b0824ba..3842e8a83 100644 --- a/Library/Homebrew/compat/utils.rb +++ b/Library/Homebrew/compat/utils.rb @@ -1,18 +1,13 @@ -# return the shell profile file based on users' preference shell def shell_profile - opoo "shell_profile has been deprecated in favor of Utils::Shell.profile" - case ENV["SHELL"] - when %r{/(ba)?sh} then "~/.bash_profile" - when %r{/zsh} then "~/.zshrc" - when %r{/ksh} then "~/.kshrc" - else "~/.bash_profile" - end + # odeprecated "shell_profile", "Utils::Shell.profile" + Utils::Shell.profile end module Tty module_function def white + odeprecated "Tty.white", "Tty.reset.bold" reset.bold end end diff --git a/Library/Homebrew/compat/utils/shell.rb b/Library/Homebrew/compat/utils/shell.rb new file mode 100644 index 000000000..161f10ebb --- /dev/null +++ b/Library/Homebrew/compat/utils/shell.rb @@ -0,0 +1,8 @@ +module Utils + module Shell + def self.shell_profile + odeprecated "Utils::Shell.shell_profile", "Utils::Shell.profile" + Utils::Shell.profile + end + end +end diff --git a/Library/Homebrew/dependable.rb b/Library/Homebrew/dependable.rb index 0834b08ec..785eb94d8 100644 --- a/Library/Homebrew/dependable.rb +++ b/Library/Homebrew/dependable.rb @@ -20,8 +20,6 @@ module Dependable end def required? - # FIXME: Should `required?` really imply `!build?`? And if so, why doesn't - # any of `optional?` and `recommended?` equally imply `!build?`? !build? && !optional? && !recommended? end diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index ba6c3f333..cbe26b422 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -1,4 +1,4 @@ -#: * `audit` [`--strict`] [`--fix`] [`--online`] [`--new-formula`] [`--display-cop-names`] [`--display-filename`] [<formulae>]: +#: * `audit` [`--strict`] [`--fix`] [`--online`] [`--new-formula`] [`--display-cop-names`] [`--display-filename`] [`--only=`<method>|`--except=`<method>] [`--only-cops=`[COP1,COP2..]|`--except-cops=`[COP1,COP2..]] [<formulae>]: #: Check <formulae> for Homebrew coding style violations. This should be #: run before submitting a new formula. #: @@ -23,6 +23,14 @@ #: If `--display-filename` is passed, every line of output is prefixed with the #: name of the file or formula being audited, to make the output easy to grep. #: +#: If `--only` is passed, only the methods named `audit_<method>` will be run. +#: +#: If `--except` is passed, the methods named `audit_<method>` will not be run. +#: +#: If `--only-cops` is passed, only the given Rubocop cop(s)' violations would be checked. +#: +#: If `--except-cops` is passed, the given Rubocop cop(s)' checks would be skipped. +#: #: `audit` exits with a non-zero status if any errors are found. This is useful, #: for instance, for implementing pre-commit hooks. @@ -65,15 +73,30 @@ module Homebrew files = ARGV.resolved_formulae.map(&:path) end - if strict - options = { fix: ARGV.flag?("--fix"), realpath: true } - # Check style in a single batch run up front for performance - style_results = check_style_json(files, options) + only_cops = ARGV.value("only-cops").to_s.split(",") + except_cops = ARGV.value("except-cops").to_s.split(",") + if !only_cops.empty? && !except_cops.empty? + odie "--only-cops and --except-cops cannot be used simultaneously!" + elsif (!only_cops.empty? || !except_cops.empty?) && strict + odie "--only-cops/--except-cops and --strict cannot be used simultaneously" + end + + options = { fix: ARGV.flag?("--fix"), realpath: true } + + if !only_cops.empty? + options[:only_cops] = only_cops + elsif !except_cops.empty? + options[:except_cops] = except_cops + elsif !strict + options[:except_cops] = [:FormulaAuditStrict] end + # Check style in a single batch run up front for performance + style_results = check_style_json(files, options) + ff.each do |f| options = { new_formula: new_formula, strict: strict, online: online } - options[:style_offenses] = style_results.file_offenses(f.path) if strict + options[:style_offenses] = style_results.file_offenses(f.path) fa = FormulaAuditor.new(f, options) fa.audit @@ -244,70 +267,6 @@ class FormulaAuditor end end - def component_problem(before, after, offset = 0) - problem "`#{before[1]}` (line #{before[0] + offset}) should be put before `#{after[1]}` (line #{after[0] + offset})" - end - - # scan in the reverse direction for remaining problems but report problems - # in the forward direction so that contributors don't reverse the order of - # lines in the file simply by following instructions - def audit_components(reverse = true, previous_pair = nil) - component_list = [ - [/^ include Language::/, "include directive"], - [/^ desc ["'][\S\ ]+["']/, "desc"], - [/^ homepage ["'][\S\ ]+["']/, "homepage"], - [/^ url ["'][\S\ ]+["']/, "url"], - [/^ mirror ["'][\S\ ]+["']/, "mirror"], - [/^ version ["'][\S\ ]+["']/, "version"], - [/^ (sha1|sha256) ["'][\S\ ]+["']/, "checksum"], - [/^ revision/, "revision"], - [/^ version_scheme/, "version_scheme"], - [/^ head ["'][\S\ ]+["']/, "head"], - [/^ stable do/, "stable block"], - [/^ bottle do/, "bottle block"], - [/^ devel do/, "devel block"], - [/^ head do/, "head block"], - [/^ bottle (:unneeded|:disable)/, "bottle modifier"], - [/^ keg_only/, "keg_only"], - [/^ option/, "option"], - [/^ depends_on/, "depends_on"], - [/^ conflicts_with/, "conflicts_with"], - [/^ (go_)?resource/, "resource"], - [/^ def install/, "install method"], - [/^ def caveats/, "caveats method"], - [/^ (plist_options|def plist)/, "plist block"], - [/^ test do/, "test block"], - ] - if previous_pair - previous_before = previous_pair[0] - previous_after = previous_pair[1] - end - offset = previous_after && previous_after[0] && previous_after[0] >= 1 ? previous_after[0] - 1 : 0 - present = component_list.map do |regex, name| - lineno = if reverse - text.reverse_line_number regex - else - text.line_number regex, offset - end - next unless lineno - [lineno, name] - end.compact - no_problem = true - present.each_cons(2) do |c1, c2| - if reverse - # scan in the forward direction from the offset - audit_components(false, [c1, c2]) if c1[0] > c2[0] # at least one more offense - elsif c1[0] > c2[0] && (offset.zero? || previous_pair.nil? || (c1[0] + offset) != previous_before[0] || (c2[0] + offset) != previous_after[0]) - component_problem c1, c2, offset - no_problem = false - end - end - if no_problem && previous_pair - component_problem previous_before, previous_after - end - present - end - def audit_file # Under normal circumstances (umask 0022), we expect a file mode of 644. If # the user's umask is more restrictive, respect that by masking out the @@ -332,53 +291,60 @@ class FormulaAuditor problem "File should end with a newline" unless text.trailing_newline? if formula.versioned_formula? - unversioned_formula = Pathname.new formula.path.to_s.gsub(/@.*\.rb$/, ".rb") + unversioned_formula = begin + # build this ourselves as we want e.g. homebrew/core to be present + full_name = if formula.tap + "#{formula.tap}/#{formula.name}" + else + formula.name + end + Formulary.factory(full_name.gsub(/@.*$/, "")).path + rescue FormulaUnavailableError, TapFormulaAmbiguityError, + TapFormulaWithOldnameAmbiguityError + Pathname.new formula.path.to_s.gsub(/@.*\.rb$/, ".rb") + end unless unversioned_formula.exist? unversioned_name = unversioned_formula.basename(".rb") problem "#{formula} is versioned but no #{unversioned_name} formula exists" end - else - versioned_formulae = Dir[formula.path.to_s.gsub(/\.rb$/, "@*.rb")] - needs_versioned_alias = !versioned_formulae.empty? && - formula.tap && - formula.aliases.grep(/.@\d/).empty? - if needs_versioned_alias - _, last_alias_version = File.basename(versioned_formulae.sort.reverse.first) - .gsub(/\.rb$/, "") - .split("@") - major, minor, = formula.version.to_s.split(".") - alias_name = if last_alias_version.split(".").length == 1 - "#{formula.name}@#{major}" - else - "#{formula.name}@#{major}.#{minor}" - end - problem <<-EOS.undent - Formula has other versions so create an alias: - cd #{formula.tap.alias_dir} - ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name} - EOS + elsif ARGV.build_stable? && + !(versioned_formulae = Dir[formula.path.to_s.gsub(/\.rb$/, "@*.rb")]).empty? + versioned_aliases = formula.aliases.grep(/.@\d/) + _, last_alias_version = + File.basename(versioned_formulae.sort.reverse.first) + .gsub(/\.rb$/, "").split("@") + major, minor, = formula.version.to_s.split(".") + alias_name_major = "#{formula.name}@#{major}" + alias_name_major_minor = "#{alias_name_major}.#{minor}" + alias_name = if last_alias_version.split(".").length == 1 + alias_name_major + else + alias_name_major_minor end - end - - return unless @strict + valid_alias_names = [alias_name_major, alias_name_major_minor] - present = audit_components + valid_versioned_aliases = versioned_aliases & valid_alias_names + invalid_versioned_aliases = versioned_aliases - valid_alias_names - present.map!(&:last) - if present.include?("stable block") - %w[url checksum mirror].each do |component| - if present.include?(component) - problem "`#{component}` should be put inside `stable block`" + if valid_versioned_aliases.empty? + if formula.tap + problem <<-EOS.undent + Formula has other versions so create a versioned alias: + cd #{formula.tap.alias_dir} + ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name} + EOS + else + problem "Formula has other versions so create an alias named #{alias_name}." end end - end - if present.include?("head") && present.include?("head block") - problem "Should not have both `head` and `head do`" + unless invalid_versioned_aliases.empty? + problem <<-EOS.undent + Formula has invalid versioned aliases: + #{invalid_versioned_aliases.join("\n ")} + EOS + end end - - return unless present.include?("bottle modifier") && present.include?("bottle block") - problem "Should not have `bottle :unneeded/:disable` and `bottle do`" end def audit_class @@ -437,11 +403,11 @@ class FormulaAuditor same_name_tap_formulae = @@local_official_taps_name_map[name] || [] if @online - @@remote_official_taps ||= OFFICIAL_TAPS - Tap.select(&:official?).map(&:repo) - - same_name_tap_formulae += @@remote_official_taps.map do |tap| - Thread.new { Homebrew.search_tap "homebrew", tap, name } - end.flat_map(&:value) + Homebrew.search_taps(name).each do |tap_formula_full_name| + tap_formula_name = tap_formula_full_name.split("/").last + next if tap_formula_name != name + same_name_tap_formulae << tap_formula_full_name + end end same_name_tap_formulae.delete(full_name) @@ -558,6 +524,38 @@ class FormulaAuditor EOS end + def audit_keg_only_style + return unless @strict + return unless formula.keg_only? + + whitelist = %w[ + Apple + macOS + OS + Homebrew + Xcode + GPG + GNOME + BSD + Firefox + ].freeze + + reason = formula.keg_only_reason.to_s + # Formulae names can legitimately be uppercase/lowercase/both. + name = Regexp.new(formula.name, Regexp::IGNORECASE) + reason.sub!(name, "") + first_word = reason.split[0] + + if reason =~ /\A[A-Z]/ && !reason.start_with?(*whitelist) + problem <<-EOS.undent + '#{first_word}' from the keg_only reason should be '#{first_word.downcase}'. + EOS + end + + return unless reason.end_with?(".") + problem "keg_only reason should not end with a period." + end + def audit_options formula.options.each do |o| if o.name == "32-bit" @@ -590,78 +588,9 @@ class FormulaAuditor homepage = formula.homepage if homepage.nil? || homepage.empty? - problem "Formula should have a homepage." return end - unless homepage =~ %r{^https?://} - problem "The homepage should start with http or https (URL is #{homepage})." - end - - # Check for http:// GitHub homepage urls, https:// is preferred. - # Note: only check homepages that are repo pages, not *.github.com hosts - if homepage.start_with? "http://github.com/" - problem "Please use https:// for #{homepage}" - end - - # Savannah has full SSL/TLS support but no auto-redirect. - # Doesn't apply to the download URLs, only the homepage. - if homepage.start_with? "http://savannah.nongnu.org/" - problem "Please use https:// for #{homepage}" - end - - # Freedesktop is complicated to handle - It has SSL/TLS, but only on certain subdomains. - # To enable https Freedesktop change the URL from http://project.freedesktop.org/wiki to - # https://wiki.freedesktop.org/project_name. - # "Software" is redirected to https://wiki.freedesktop.org/www/Software/project_name - if homepage =~ %r{^http://((?:www|nice|libopenraw|liboil|telepathy|xorg)\.)?freedesktop\.org/(?:wiki/)?} - if homepage =~ /Software/ - problem "#{homepage} should be styled `https://wiki.freedesktop.org/www/Software/project_name`" - else - problem "#{homepage} should be styled `https://wiki.freedesktop.org/project_name`" - end - end - - # Google Code homepages should end in a slash - if homepage =~ %r{^https?://code\.google\.com/p/[^/]+[^/]$} - problem "#{homepage} should end with a slash" - end - - # People will run into mixed content sometimes, but we should enforce and then add - # exemptions as they are discovered. Treat mixed content on homepages as a bug. - # Justify each exemptions with a code comment so we can keep track here. - case homepage - when %r{^http://[^/]*\.github\.io/}, - %r{^http://[^/]*\.sourceforge\.io/} - problem "Please use https:// for #{homepage}" - end - - if homepage =~ %r{^http://([^/]*)\.(sf|sourceforge)\.net(/|$)} - problem "#{homepage} should be `https://#{$1}.sourceforge.io/`" - end - - # There's an auto-redirect here, but this mistake is incredibly common too. - # Only applies to the homepage and subdomains for now, not the FTP URLs. - if homepage =~ %r{^http://((?:build|cloud|developer|download|extensions|git|glade|help|library|live|nagios|news|people|projects|rt|static|wiki|www)\.)?gnome\.org} - problem "Please use https:// for #{homepage}" - end - - # Compact the above into this list as we're able to remove detailed notations, etc over time. - case homepage - when %r{^http://[^/]*\.apache\.org}, - %r{^http://packages\.debian\.org}, - %r{^http://wiki\.freedesktop\.org/}, - %r{^http://((?:www)\.)?gnupg\.org/}, - %r{^http://ietf\.org}, - %r{^http://[^/.]+\.ietf\.org}, - %r{^http://[^/.]+\.tools\.ietf\.org}, - %r{^http://www\.gnu\.org/}, - %r{^http://code\.google\.com/}, - %r{^http://bitbucket\.org/}, - %r{^http://(?:[^/]*\.)?archive\.org} - problem "Please use https:// for #{homepage}" - end - return unless @online return unless DevelopmentTools.curl_handles_most_https_homepages? @@ -728,7 +657,10 @@ class FormulaAuditor } end - spec.patches.each { |p| audit_patch(p) if p.external? } + next if spec.patches.empty? + spec.patches.each { |p| patch_problems(p) if p.external? } + next unless @new_formula + problem "New formulae should not require patches to build. Patches should be submitted and accepted upstream first." end %w[Stable Devel].each do |name| @@ -809,51 +741,67 @@ class FormulaAuditor return unless formula.tap.git? # git log is required return if @new_formula - fv = FormulaVersions.new(formula, max_depth: 1) + fv = FormulaVersions.new(formula) attributes = [:revision, :version_scheme] - attributes_map = fv.version_attributes_map(attributes, "origin/master") - attributes.each do |attribute| - stable_attribute_map = attributes_map[attribute][:stable] - next if stable_attribute_map.nil? || stable_attribute_map.empty? - - attributes_for_version = stable_attribute_map[formula.version] - next if attributes_for_version.nil? || attributes_for_version.empty? - - old_attribute = formula.send(attribute) - max_attribute = attributes_for_version.max - if max_attribute && old_attribute < max_attribute - problem "#{attribute} should not decrease (from #{max_attribute} to #{old_attribute})" - end - end - + current_version_scheme = formula.version_scheme [:stable, :devel].each do |spec| spec_version_scheme_map = attributes_map[:version_scheme][spec] - next if spec_version_scheme_map.nil? || spec_version_scheme_map.empty? + next if spec_version_scheme_map.empty? - max_version_scheme = spec_version_scheme_map.values.flatten.max + version_schemes = spec_version_scheme_map.values.flatten + max_version_scheme = version_schemes.max max_version = spec_version_scheme_map.select do |_, version_scheme| version_scheme.first == max_version_scheme end.keys.max - formula_spec = formula.send(spec) - next if formula_spec.nil? + if max_version_scheme && current_version_scheme < max_version_scheme + problem "version_scheme should not decrease (from #{max_version_scheme} to #{current_version_scheme})" + end - if max_version && formula_spec.version < max_version - problem "#{spec} version should not decrease (from #{max_version} to #{formula_spec.version})" + if max_version_scheme && current_version_scheme >= max_version_scheme && + current_version_scheme > 1 && + !version_schemes.include?(current_version_scheme - 1) + problem "version_schemes should only increment by 1" end + + formula_spec = formula.send(spec) + next unless formula_spec + + spec_version = formula_spec.version + next unless max_version + next if spec_version >= max_version + + above_max_version_scheme = current_version_scheme > max_version_scheme + map_includes_version = spec_version_scheme_map.keys.include?(spec_version) + next if !current_version_scheme.zero? && + (above_max_version_scheme || map_includes_version) + problem "#{spec} version should not decrease (from #{max_version} to #{spec_version})" end - return if formula.revision.zero? - if formula.stable - revision_map = attributes_map[:revision][:stable] - stable_revisions = revision_map[formula.stable.version] if revision_map - if !stable_revisions || stable_revisions.empty? + current_revision = formula.revision + revision_map = attributes_map[:revision][:stable] + if formula.stable && !revision_map.empty? + stable_revisions = revision_map[formula.stable.version] + stable_revisions ||= [] + max_revision = stable_revisions.max || 0 + + if current_revision < max_revision + problem "revision should not decrease (from #{max_revision} to #{current_revision})" + end + + stable_revisions -= [formula.revision] + if !current_revision.zero? && stable_revisions.empty? && + revision_map.keys.length > 1 problem "'revision #{formula.revision}' should be removed" + elsif current_revision > 1 && + current_revision != max_revision && + !stable_revisions.include?(current_revision - 1) + problem "revisions should only increment by 1" end - else # head/devel-only formula - problem "'revision #{formula.revision}' should be removed" + elsif !current_revision.zero? # head/devel-only formula + problem "'revision #{current_revision}' should be removed" end end @@ -864,10 +812,10 @@ class FormulaAuditor return if legacy_patches.empty? problem "Use the patch DSL instead of defining a 'patches' method" - legacy_patches.each { |p| audit_patch(p) } + legacy_patches.each { |p| patch_problems(p) } end - def audit_patch(patch) + def patch_problems(patch) case patch.url when /raw\.github\.com/, %r{gist\.github\.com/raw}, %r{gist\.github\.com/.+/raw}, %r{gist\.githubusercontent\.com/.+/raw} @@ -935,11 +883,21 @@ class FormulaAuditor problem "Formulae using virtualenvs do not need a `setuptools` resource." end + if text =~ /system\s+['"]go['"],\s+['"]get['"]/ + problem "Formulae should not use `go get`. If non-vendored resources are required use `go_resource`s." + end + return unless text.include?('require "language/go"') && !text.include?("go_resource") problem "require \"language/go\" is unnecessary unless using `go_resource`s" end - def audit_line(line, _lineno) + def audit_lines + text.without_patch.split("\n").each_with_index do |line, lineno| + line_problems(line, lineno+1) + end + end + + def line_problems(line, _lineno) if line =~ /<(Formula|AmazonWebServicesFormula|ScriptFileFormula|GithubGistFormula)/ problem "Use a space in class inheritance: class Foo < #{$1}" end @@ -1018,7 +976,19 @@ class FormulaAuditor end if line =~ /depends_on :tex/ - problem ":tex is deprecated." + problem ":tex is deprecated" + end + + if line =~ /depends_on\s+['"](.+)['"]\s+=>\s+:(lua|perl|python|ruby)(\d*)/ + problem "#{$2} modules should be vendored rather than use deprecated `depends_on \"#{$1}\" => :#{$2}#{$3}`" + end + + if line =~ /depends_on\s+['"](.+)['"]\s+=>\s+(.*)/ + dep = $1 + $2.split(" ").map do |o| + next unless o =~ /^\[?['"](.*)['"]/ + problem "Dependency #{dep} should not use option #{$1}" + end end # Commented-out depends_on @@ -1049,6 +1019,14 @@ class FormulaAuditor problem "Use ENV instead of invoking '#{$1}' to modify the environment" end + if formula.name != "wine" && line =~ /ENV\.universal_binary/ + problem "macOS has been 64-bit only since 10.6 so ENV.universal_binary is deprecated." + end + + if line =~ /build\.universal\?/ + problem "macOS has been 64-bit only so build.universal? is deprecated." + end + if line =~ /version == ['"]HEAD['"]/ problem "Use 'build.head?' instead of inspecting 'version'" end @@ -1078,11 +1056,11 @@ class FormulaAuditor end if line =~ /(not\s|!)\s*build\.with?\?/ - problem "Don't negate 'build.without?': use 'build.with?'" + problem "Don't negate 'build.with?': use 'build.without?'" end if line =~ /(not\s|!)\s*build\.without?\?/ - problem "Don't negate 'build.with?': use 'build.without?'" + problem "Don't negate 'build.without?': use 'build.with?'" end if line =~ /ARGV\.(?!(debug\?|verbose\?|value[\(\s]))/ @@ -1130,11 +1108,11 @@ class FormulaAuditor end if line =~ /depends_on :(.+) (if.+|unless.+)$/ - audit_conditional_dep($1.to_sym, $2, $&) + conditional_dep_problems($1.to_sym, $2, $&) end if line =~ /depends_on ['"](.+)['"] (if.+|unless.+)$/ - audit_conditional_dep($1, $2, $&) + conditional_dep_problems($1, $2, $&) end if line =~ /(Dir\[("[^\*{},]+")\])/ @@ -1171,6 +1149,10 @@ class FormulaAuditor end end + if line =~ /((revision|version_scheme)\s+0)/ + problem "'#{$1}' should be removed" + end + return unless @strict problem "`#{$1}` in formulae is deprecated" if line =~ /(env :(std|userpaths))/ @@ -1222,7 +1204,7 @@ class FormulaAuditor EOS end - def audit_conditional_dep(dep, condition, line) + def conditional_dep_problems(dep, condition, line) quoted_dep = quote_dep(dep) dep = Regexp.escape(dep.to_s) @@ -1238,30 +1220,26 @@ class FormulaAuditor dep.is_a?(Symbol) ? dep.inspect : "'#{dep}'" end - def audit_check_output(output) + def problem_if_output(output) problem(output) if output end def audit - audit_file - audit_formula_name - audit_class - audit_specs - audit_revision_and_version_scheme - audit_homepage - audit_bottle_spec - audit_github_repository - audit_deps - audit_conflicts - audit_options - audit_legacy_patches - audit_text - audit_caveats - text.without_patch.split("\n").each_with_index { |line, lineno| audit_line(line, lineno+1) } - audit_installed - audit_prefix_has_contents - audit_reverse_migration - audit_style + only_audits = ARGV.value("only").to_s.split(",") + except_audits = ARGV.value("except").to_s.split(",") + if !only_audits.empty? && !except_audits.empty? + odie "--only and --except cannot be used simultaneously!" + end + + methods.map(&:to_s).grep(/^audit_/).each do |audit_method_name| + name = audit_method_name.gsub(/^audit_/, "") + if !only_audits.empty? + next unless only_audits.include?(name) + elsif !except_audits.empty? + next if except_audits.include?(name) + end + send(audit_method_name) + end end private @@ -1392,8 +1370,8 @@ class ResourceAuditor def audit_urls # Check GNU urls; doesn't apply to mirrors - if url =~ %r{^(?:https?|ftp)://(?!alpha).+/gnu/} - problem "Please use \"https://ftpmirror.gnu.org\" instead of #{url}." + if url =~ %r{^(?:https?|ftp)://ftpmirror.gnu.org/(.*)} + problem "Please use \"https://ftp.gnu.org/gnu/#{$1}\" instead of #{url}." end if mirrors.include?(url) diff --git a/Library/Homebrew/dev-cmd/create.rb b/Library/Homebrew/dev-cmd/create.rb index 6855d6f37..908f65f8f 100644 --- a/Library/Homebrew/dev-cmd/create.rb +++ b/Library/Homebrew/dev-cmd/create.rb @@ -10,7 +10,8 @@ #: If `--meson` is passed, create a basic template for a Meson-style build. #: #: If `--no-fetch` is passed, Homebrew will not download <URL> to the cache and -#: will thus not add the SHA256 to the formula for you. +#: will thus not add the SHA256 to the formula for you. It will also not check +#: the GitHub API for GitHub projects (to fill out the description and homepage). #: #: The options `--set-name` and `--set-version` each take an argument and allow #: you to explicitly set the name and version of the package you are creating. @@ -28,15 +29,6 @@ module Homebrew # Create a formula from a tarball URL def create - # Allow searching MacPorts or Fink. - if ARGV.include? "--macports" - opoo "`brew create --macports` is deprecated; use `brew search --macports` instead" - exec_browser "https://www.macports.org/ports.php?by=name&substr=#{ARGV.next}" - elsif ARGV.include? "--fink" - opoo "`brew create --fink` is deprecated; use `brew search --fink` instead" - exec_browser "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}" - end - raise UsageError if ARGV.named.empty? # Ensure that the cache exists so we can fetch the tarball @@ -100,7 +92,7 @@ module Homebrew end class FormulaCreator - attr_reader :url, :sha256 + attr_reader :url, :sha256, :desc, :homepage attr_accessor :name, :version, :tap, :path, :mode def url=(url) @@ -108,11 +100,15 @@ class FormulaCreator path = Pathname.new(url) if @name.nil? case url - when %r{github\.com/\S+/(\S+)\.git} - @name = $1 + when %r{github\.com/(\S+)/(\S+)\.git} + @user = $1 + @name = $2 @head = true - when %r{github\.com/\S+/(\S+)/archive/} - @name = $1 + @github = true + when %r{github\.com/(\S+)/(\S+)/(archive|releases)/} + @user = $1 + @name = $2 + @github = true else @name = path.basename.to_s[/(.*?)[-_.]?#{Regexp.escape(path.version.to_s)}/, 1] end @@ -131,7 +127,7 @@ class FormulaCreator end def fetch? - !head? && !ARGV.include?("--no-fetch") + !ARGV.include?("--no-fetch") end def head? @@ -145,11 +141,25 @@ class FormulaCreator opoo "Version cannot be determined from URL." puts "You'll need to add an explicit 'version' to the formula." elsif fetch? - r = Resource.new - r.url(url) - r.version(version) - r.owner = self - @sha256 = r.fetch.sha256 if r.download_strategy == CurlDownloadStrategy + unless head? + r = Resource.new + r.url(url) + r.version(version) + r.owner = self + @sha256 = r.fetch.sha256 if r.download_strategy == CurlDownloadStrategy + end + + if @user && @name + begin + metadata = GitHub.repository(@user, @name) + @desc = metadata["description"] + @homepage = metadata["homepage"] + rescue GitHub::HTTPNotFoundError + # If there was no repository found assume the network connection is at + # fault rather than the input URL. + nil + end + end end path.write ERB.new(template, nil, ">").result(binding) @@ -161,8 +171,8 @@ class FormulaCreator # PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST! class #{Formulary.class_s(name)} < Formula - desc "" - homepage "" + desc "#{desc}" + homepage "#{homepage}" <% if head? %> head "#{url}" <% else %> diff --git a/Library/Homebrew/dev-cmd/mirror.rb b/Library/Homebrew/dev-cmd/mirror.rb index 10811493c..e2492203d 100644 --- a/Library/Homebrew/dev-cmd/mirror.rb +++ b/Library/Homebrew/dev-cmd/mirror.rb @@ -8,10 +8,10 @@ module Homebrew def mirror odie "This command requires at least formula argument!" if ARGV.named.empty? - bintray_user = ENV["BINTRAY_USER"] - bintray_key = ENV["BINTRAY_KEY"] + bintray_user = ENV["HOMEBREW_BINTRAY_USER"] + bintray_key = ENV["HOMEBREW_BINTRAY_KEY"] if !bintray_user || !bintray_key - raise "Missing BINTRAY_USER or BINTRAY_KEY variables!" + raise "Missing HOMEBREW_BINTRAY_USER or HOMEBREW_BINTRAY_KEY variables!" end ARGV.formulae.each do |f| diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb index 36c9ac27c..9d08da95b 100644 --- a/Library/Homebrew/dev-cmd/pull.rb +++ b/Library/Homebrew/dev-cmd/pull.rb @@ -263,7 +263,7 @@ module Homebrew end published = [] - bintray_creds = { user: ENV["BINTRAY_USER"], key: ENV["BINTRAY_KEY"] } + bintray_creds = { user: ENV["HOMEBREW_BINTRAY_USER"], key: ENV["HOMEBREW_BINTRAY_KEY"] } if bintray_creds[:user] && bintray_creds[:key] changed_formulae_names.each do |name| f = Formula[name] @@ -272,7 +272,7 @@ module Homebrew published << f.full_name end else - opoo "You must set BINTRAY_USER and BINTRAY_KEY to add or update bottles on Bintray!" + opoo "You must set HOMEBREW_BINTRAY_USER and HOMEBREW_BINTRAY_KEY to add or update bottles on Bintray!" end published end @@ -608,7 +608,7 @@ module Homebrew def check_bintray_mirror(name, url) headers = curl_output("--connect-timeout", "15", "--head", url)[0] status_code = headers.scan(%r{^HTTP\/.* (\d+)}).last.first - return if status_code.start_with?("3") + return if status_code.start_with?("2") opoo "The Bintray mirror #{url} is not reachable (HTTP status code #{status_code})." opoo "Do you need to upload it with `brew mirror #{name}`?" end diff --git a/Library/Homebrew/dev-cmd/tap-new.rb b/Library/Homebrew/dev-cmd/tap-new.rb index df295bf26..dcb41265c 100644 --- a/Library/Homebrew/dev-cmd/tap-new.rb +++ b/Library/Homebrew/dev-cmd/tap-new.rb @@ -47,8 +47,12 @@ module Homebrew language: ruby os: osx env: OSX=10.12 - osx_image: xcode8.1 + osx_image: xcode8.3 rvm: system + cache: + directories: + - $HOME/.gem/ruby + - Library/Homebrew/vendor/bundle before_install: - export TRAVIS_COMMIT="$(git rev-parse --verify -q HEAD)" diff --git a/Library/Homebrew/dev-cmd/tests.rb b/Library/Homebrew/dev-cmd/tests.rb index 91c7d880b..d90326768 100644 --- a/Library/Homebrew/dev-cmd/tests.rb +++ b/Library/Homebrew/dev-cmd/tests.rb @@ -33,7 +33,12 @@ module Homebrew ENV["HOMEBREW_DEVELOPER"] = "1" ENV["HOMEBREW_NO_COMPAT"] = "1" if ARGV.include? "--no-compat" ENV["HOMEBREW_TEST_GENERIC_OS"] = "1" if ARGV.include? "--generic" - ENV["HOMEBREW_NO_GITHUB_API"] = "1" unless ARGV.include? "--online" + + if ARGV.include? "--online" + ENV["HOMEBREW_TEST_ONLINE"] = "1" + else + ENV["HOMEBREW_NO_GITHUB_API"] = "1" + end if ARGV.include? "--official-cmd-taps" ENV["HOMEBREW_TEST_OFFICIAL_CMD_TAPS"] = "1" @@ -74,20 +79,24 @@ module Homebrew Dir.glob("test/**/*_spec.rb").reject { |p| p =~ %r{^test/vendor/bundle/} } end - opts = [] - - if ENV["CI"] - opts << "--combine-stderr" - opts << "--serialize-stdout" + opts = if ENV["CI"] + %w[ + --combine-stderr + --serialize-stdout + ] + else + %w[ + --nice + ] end - args = [ - "--color", - "-I", HOMEBREW_LIBRARY_PATH/"test", - "--require", "spec_helper", - "--format", "progress", - "--format", "ParallelTests::RSpec::RuntimeLogger", - "--out", "tmp/parallel_runtime_rspec.log" + args = ["-I", HOMEBREW_LIBRARY_PATH/"test"] + args += %w[ + --color + --require spec_helper + --format progress + --format ParallelTests::RSpec::RuntimeLogger + --out tmp/parallel_runtime_rspec.log ] args << "--seed" << ARGV.next if ARGV.include? "--seed" @@ -97,6 +106,10 @@ module Homebrew files = files.reject { |p| p =~ %r{^test/(os/mac|cask)(/.*|_spec\.rb)$} } end + unless OS.linux? + files = files.reject { |p| p =~ %r{^test/os/linux(/.*|_spec\.rb)$} } + end + if parallel system "bundle", "exec", "parallel_rspec", *opts, "--", *args, "--", *files else diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index 61cdf2f1a..6bb5c8b8e 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -100,8 +100,7 @@ module Homebrew # See https://github.com/Homebrew/legacy-homebrew/pull/9986 def check_path_for_trailing_slashes - all_paths = ENV["PATH"].split(File::PATH_SEPARATOR) - bad_paths = all_paths.select { |p| p[-1..-1] == "/" } + bad_paths = PATH.new(ENV["HOMEBREW_PATH"]).select { |p| p.end_with?("/") } return if bad_paths.empty? inject_file_list bad_paths, <<-EOS.undent @@ -439,7 +438,7 @@ module Homebrew message = "" - paths.each do |p| + paths(ENV["HOMEBREW_PATH"]).each do |p| case p when "/usr/bin" unless $seen_prefix_bin @@ -460,7 +459,7 @@ module Homebrew Consider setting your PATH so that #{HOMEBREW_PREFIX}/bin occurs before /usr/bin. Here is a one-liner: - #{Utils::Shell.prepend_path_in_shell_profile("#{HOMEBREW_PREFIX}/bin")} + #{Utils::Shell.prepend_path_in_profile("#{HOMEBREW_PREFIX}/bin")} EOS end end @@ -480,7 +479,7 @@ module Homebrew <<-EOS.undent Homebrew's bin was not found in your PATH. Consider setting the PATH for example like so - #{Utils::Shell.prepend_path_in_shell_profile("#{HOMEBREW_PREFIX}/bin")} + #{Utils::Shell.prepend_path_in_profile("#{HOMEBREW_PREFIX}/bin")} EOS end @@ -495,7 +494,7 @@ module Homebrew Homebrew's sbin was not found in your PATH but you have installed formulae that put executables in #{HOMEBREW_PREFIX}/sbin. Consider setting the PATH for example like so - #{Utils::Shell.prepend_path_in_shell_profile("#{HOMEBREW_PREFIX}/sbin")} + #{Utils::Shell.prepend_path_in_profile("#{HOMEBREW_PREFIX}/sbin")} EOS end @@ -609,7 +608,7 @@ module Homebrew /Applications/Server.app/Contents/ServerRoot/usr/sbin ].map(&:downcase) - paths.each do |p| + paths(ENV["HOMEBREW_PATH"]).each do |p| next if whitelist.include?(p.downcase) || !File.directory?(p) realpath = Pathname.new(p).realpath.to_s diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index a0025cef3..d74efe0bb 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -520,6 +520,9 @@ class S3DownloadStrategy < CurlDownloadStrategy bucket = $1 key = $2 + ENV["AWS_ACCESS_KEY_ID"] = ENV["HOMEBREW_AWS_ACCESS_KEY_ID"] + ENV["AWS_SECRET_ACCESS_KEY"] = ENV["HOMEBREW_AWS_SECRET_ACCESS_KEY"] + obj = AWS::S3.new.buckets[bucket].objects[key] begin s3url = obj.url_for(:get) diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index cfdf5e12d..6751b2224 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -77,35 +77,11 @@ class FormulaUnavailableError < RuntimeError end end -class TapFormulaUnavailableError < FormulaUnavailableError - attr_reader :tap, :user, :repo - - def initialize(tap, name) - @tap = tap - @user = tap.user - @repo = tap.repo - super "#{tap}/#{name}" - end - - def to_s - s = super - s += "\nPlease tap it and then try again: brew tap #{tap}" unless tap.installed? - s - end -end - -class FormulaClassUnavailableError < FormulaUnavailableError +module FormulaClassUnavailableErrorModule attr_reader :path attr_reader :class_name attr_reader :class_list - def initialize(name, path, class_name, class_list) - @path = path - @class_name = class_name - @class_list = class_list - super name - end - def to_s s = super s += "\nIn formula file: #{path}" @@ -131,16 +107,70 @@ class FormulaClassUnavailableError < FormulaUnavailableError end end -class FormulaUnreadableError < FormulaUnavailableError +class FormulaClassUnavailableError < FormulaUnavailableError + include FormulaClassUnavailableErrorModule + + def initialize(name, path, class_name, class_list) + @path = path + @class_name = class_name + @class_list = class_list + super name + end +end + +module FormulaUnreadableErrorModule attr_reader :formula_error + def to_s + "#{name}: " + formula_error.to_s + end +end + +class FormulaUnreadableError < FormulaUnavailableError + include FormulaUnreadableErrorModule + def initialize(name, error) super(name) @formula_error = error end +end + +class TapFormulaUnavailableError < FormulaUnavailableError + attr_reader :tap, :user, :repo + + def initialize(tap, name) + @tap = tap + @user = tap.user + @repo = tap.repo + super "#{tap}/#{name}" + end def to_s - "#{name}: " + formula_error.to_s + s = super + s += "\nPlease tap it and then try again: brew tap #{tap}" unless tap.installed? + s + end +end + +class TapFormulaClassUnavailableError < TapFormulaUnavailableError + include FormulaClassUnavailableErrorModule + + attr_reader :tap + + def initialize(tap, name, path, class_name, class_list) + @path = path + @class_name = class_name + @class_list = class_list + super tap, name + end +end + +class TapFormulaUnreadableError < TapFormulaUnavailableError + include FormulaUnreadableErrorModule + + def initialize(tap, name, error) + super(tap, name) + @formula_error = error end end diff --git a/Library/Homebrew/extend/ENV.rb b/Library/Homebrew/extend/ENV.rb index 729598e28..283e90b69 100644 --- a/Library/Homebrew/extend/ENV.rb +++ b/Library/Homebrew/extend/ENV.rb @@ -26,6 +26,13 @@ module EnvActivation ensure replace(old_env) end + + def clear_sensitive_environment! + ENV.keys.each do |key| + next unless /(cookie|key|token)/i =~ key + ENV.delete key + end + end end ENV.extend(EnvActivation) diff --git a/Library/Homebrew/extend/ENV/shared.rb b/Library/Homebrew/extend/ENV/shared.rb index 2cdc2f83a..b51ade48b 100644 --- a/Library/Homebrew/extend/ENV/shared.rb +++ b/Library/Homebrew/extend/ENV/shared.rb @@ -1,6 +1,7 @@ require "formula" require "compilers" require "development_tools" +require "PATH" # Homebrew extends Ruby's `ENV` to make our code more readable. # Implemented in {SharedEnvExtension} and either {Superenv} or @@ -80,7 +81,7 @@ module SharedEnvExtension end def append_path(key, path) - append key, path, File::PATH_SEPARATOR if File.directory? path + self[key] = PATH.new(self[key]).append(path) end # Prepends a directory to `PATH`. @@ -92,7 +93,7 @@ module SharedEnvExtension # (e.g. <pre>ENV.prepend_path "PATH", which("emacs").dirname</pre>) def prepend_path(key, path) return if %w[/usr/bin /bin /usr/sbin /sbin].include? path.to_s - prepend key, path, File::PATH_SEPARATOR if File.directory? path + self[key] = PATH.new(self[key]).prepend(path) end def prepend_create_path(key, path) @@ -196,22 +197,23 @@ module SharedEnvExtension # @private def userpaths! - paths = self["PATH"].split(File::PATH_SEPARATOR) - # put Superenv.bin and opt path at the first - new_paths = paths.select { |p| p.start_with?("#{HOMEBREW_REPOSITORY}/Library/ENV", "#{HOMEBREW_PREFIX}/opt") } - # XXX hot fix to prefer brewed stuff (e.g. python) over /usr/bin. - new_paths << "#{HOMEBREW_PREFIX}/bin" - # reset of self["PATH"] - new_paths += paths - # user paths - new_paths += ORIGINAL_PATHS.map do |p| - begin - p.realpath.to_s - rescue - nil - end - end - %w[/usr/X11/bin /opt/X11/bin] - self["PATH"] = new_paths.uniq.join(File::PATH_SEPARATOR) + path = PATH.new(self["PATH"]).select do |p| + # put Superenv.bin and opt path at the first + p.start_with?("#{HOMEBREW_REPOSITORY}/Library/ENV", "#{HOMEBREW_PREFIX}/opt") + end + path.append(HOMEBREW_PREFIX/"bin") # XXX hot fix to prefer brewed stuff (e.g. python) over /usr/bin. + path.append(self["PATH"]) # reset of self["PATH"] + path.append( + # user paths + ORIGINAL_PATHS.map do |p| + begin + p.realpath.to_s + rescue + nil + end + end - %w[/usr/X11/bin /opt/X11/bin], + ) + self["PATH"] = path end def fortran @@ -244,7 +246,7 @@ module SharedEnvExtension else if (gfortran = which("gfortran", (HOMEBREW_PREFIX/"bin").to_s)) ohai "Using Homebrew-provided fortran compiler." - elsif (gfortran = which("gfortran", ORIGINAL_PATHS.join(File::PATH_SEPARATOR))) + elsif (gfortran = which("gfortran", PATH.new(ORIGINAL_PATHS))) ohai "Using a fortran compiler found at #{gfortran}." end if gfortran diff --git a/Library/Homebrew/extend/ENV/std.rb b/Library/Homebrew/extend/ENV/std.rb index 403ea1978..c4cc0985f 100644 --- a/Library/Homebrew/extend/ENV/std.rb +++ b/Library/Homebrew/extend/ENV/std.rb @@ -1,7 +1,6 @@ require "hardware" require "extend/ENV/shared" -# TODO: deprecate compiling related codes after it's only used by brew test. # @private module Stdenv include SharedEnvExtension @@ -58,12 +57,12 @@ module Stdenv end def determine_pkg_config_libdir - paths = [] - paths << "#{HOMEBREW_PREFIX}/lib/pkgconfig" - paths << "#{HOMEBREW_PREFIX}/share/pkgconfig" - paths += homebrew_extra_pkg_config_paths - paths << "/usr/lib/pkgconfig" - paths.select { |d| File.directory? d }.join(File::PATH_SEPARATOR) + PATH.new( + HOMEBREW_PREFIX/"lib/pkgconfig", + HOMEBREW_PREFIX/"share/pkgconfig", + homebrew_extra_pkg_config_paths, + "/usr/lib/pkgconfig", + ).existing end # Removes the MAKEFLAGS environment variable, causing make to use a single job. diff --git a/Library/Homebrew/extend/ENV/super.rb b/Library/Homebrew/extend/ENV/super.rb index 0935647f5..ef41161af 100644 --- a/Library/Homebrew/extend/ENV/super.rb +++ b/Library/Homebrew/extend/ENV/super.rb @@ -101,29 +101,28 @@ module Superenv end def determine_path - paths = [Superenv.bin] + path = PATH.new(Superenv.bin) # Formula dependencies can override standard tools. - paths += deps.map { |d| d.opt_bin.to_s } - - paths += homebrew_extra_paths - paths += %w[/usr/bin /bin /usr/sbin /sbin] + path.append(deps.map(&:opt_bin)) + path.append(homebrew_extra_paths) + path.append("/usr/bin", "/bin", "/usr/sbin", "/sbin") # Homebrew's apple-gcc42 will be outside the PATH in superenv, # so xcrun may not be able to find it begin case homebrew_cc when "gcc-4.2" - paths << Formulary.factory("apple-gcc42").opt_bin + path.append(Formulary.factory("apple-gcc42").opt_bin) when GNU_GCC_REGEXP - paths << gcc_version_formula($&).opt_bin + path.append(gcc_version_formula($&).opt_bin) end rescue FormulaUnavailableError # Don't fail and don't add these formulae to the path if they don't exist. nil end - paths.to_path_s + path.existing end def homebrew_extra_pkg_config_paths @@ -131,15 +130,17 @@ module Superenv end def determine_pkg_config_path - paths = deps.map { |d| "#{d.opt_lib}/pkgconfig" } - paths += deps.map { |d| "#{d.opt_share}/pkgconfig" } - paths.to_path_s + PATH.new( + deps.map { |d| d.opt_lib/"pkgconfig" }, + deps.map { |d| d.opt_share/"pkgconfig" }, + ).existing end def determine_pkg_config_libdir - paths = %w[/usr/lib/pkgconfig] - paths += homebrew_extra_pkg_config_paths - paths.to_path_s + PATH.new( + "/usr/lib/pkgconfig", + homebrew_extra_pkg_config_paths, + ).existing end def homebrew_extra_aclocal_paths @@ -147,10 +148,11 @@ module Superenv end def determine_aclocal_path - paths = keg_only_deps.map { |d| "#{d.opt_share}/aclocal" } - paths << "#{HOMEBREW_PREFIX}/share/aclocal" - paths += homebrew_extra_aclocal_paths - paths.to_path_s + PATH.new( + keg_only_deps.map { |d| d.opt_share/"aclocal" }, + HOMEBREW_PREFIX/"share/aclocal", + homebrew_extra_aclocal_paths, + ).existing end def homebrew_extra_isystem_paths @@ -158,13 +160,14 @@ module Superenv end def determine_isystem_paths - paths = ["#{HOMEBREW_PREFIX}/include"] - paths += homebrew_extra_isystem_paths - paths.to_path_s + PATH.new( + HOMEBREW_PREFIX/"include", + homebrew_extra_isystem_paths, + ).existing end def determine_include_paths - keg_only_deps.map { |d| d.opt_include.to_s }.to_path_s + PATH.new(keg_only_deps.map(&:opt_include)).existing end def homebrew_extra_library_paths @@ -172,10 +175,11 @@ module Superenv end def determine_library_paths - paths = keg_only_deps.map { |d| d.opt_lib.to_s } - paths << "#{HOMEBREW_PREFIX}/lib" - paths += homebrew_extra_library_paths - paths.to_path_s + PATH.new( + keg_only_deps.map(&:opt_lib), + HOMEBREW_PREFIX/"lib", + homebrew_extra_library_paths, + ).existing end def determine_dependencies @@ -183,9 +187,10 @@ module Superenv end def determine_cmake_prefix_path - paths = keg_only_deps.map { |d| d.opt_prefix.to_s } - paths << HOMEBREW_PREFIX.to_s - paths.to_path_s + PATH.new( + keg_only_deps.map(&:opt_prefix), + HOMEBREW_PREFIX.to_s, + ).existing end def homebrew_extra_cmake_include_paths @@ -193,9 +198,7 @@ module Superenv end def determine_cmake_include_path - paths = [] - paths += homebrew_extra_cmake_include_paths - paths.to_path_s + PATH.new(homebrew_extra_cmake_include_paths).existing end def homebrew_extra_cmake_library_paths @@ -203,9 +206,7 @@ module Superenv end def determine_cmake_library_path - paths = [] - paths += homebrew_extra_cmake_library_paths - paths.to_path_s + PATH.new(homebrew_extra_cmake_library_paths).existing end def homebrew_extra_cmake_frameworks_paths @@ -213,9 +214,10 @@ module Superenv end def determine_cmake_frameworks_path - paths = deps.map { |d| d.opt_frameworks.to_s } - paths += homebrew_extra_cmake_frameworks_paths - paths.to_path_s + PATH.new( + deps.map(&:opt_frameworks), + homebrew_extra_cmake_frameworks_paths, + ).existing end def determine_make_jobs @@ -326,15 +328,7 @@ module Superenv def set_x11_env_if_installed end - # This method does nothing in superenv since there's no custom CFLAGS API - # @private - def set_cpu_flags(*_args) - end -end - -class Array - def to_path_s - map(&:to_s).uniq.select { |s| File.directory? s }.join(File::PATH_SEPARATOR).chuzzle + def set_cpu_flags(*) end end diff --git a/Library/Homebrew/extend/io.rb b/Library/Homebrew/extend/io.rb index 1357293cd..53bca196e 100644 --- a/Library/Homebrew/extend/io.rb +++ b/Library/Homebrew/extend/io.rb @@ -1,10 +1,17 @@ class IO def readline_nonblock(sep = $INPUT_RECORD_SEPARATOR) + line = "" buffer = "" - buffer.concat(read_nonblock(1)) while buffer[-1] != sep - buffer + + loop do + break if buffer == sep + read_nonblock(1, buffer) + line.concat(buffer) + end + + line rescue IO::WaitReadable, EOFError => e - raise e if buffer.empty? - buffer + raise e if line.empty? + line end end diff --git a/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb b/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb new file mode 100644 index 000000000..3fd847bc4 --- /dev/null +++ b/Library/Homebrew/extend/os/linux/requirements/osxfuse_requirement.rb @@ -0,0 +1,39 @@ +require "requirement" + +class OsxfuseRequirement < Requirement + download "https://github.com/libfuse/libfuse" + + satisfy(build_env: false) do + next true if libfuse_formula_exists? && Formula["libfuse"].installed? + includedirs = %w[ + /usr/include + /usr/local/include + ] + next true if (includedirs.map do |dir| + File.exist? "#{dir}/fuse.h" + end).any? + false + end + + def message + msg = "libfuse is required to install this formula.\n" + if libfuse_formula_exists? + msg + <<-EOS.undent + Run "brew install libfuse" to install it. + EOS + else + msg + super + end + end + + private + + def libfuse_formula_exists? + begin + Formula["libfuse"] + rescue FormulaUnavailableError + return false + end + true + end +end diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb index fb6b30836..ff936c75a 100644 --- a/Library/Homebrew/extend/os/mac/diagnostic.rb +++ b/Library/Homebrew/extend/os/mac/diagnostic.rb @@ -263,7 +263,7 @@ module Homebrew SSL_CERT_DIR support was removed from Apple's curl. If fetching formulae fails you should: unset SSL_CERT_DIR - and remove it from #{Utils::Shell.shell_profile} if present. + and remove it from #{Utils::Shell.profile} if present. EOS end diff --git a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb index 5b1c648bf..10379c981 100644 --- a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb +++ b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb @@ -67,7 +67,7 @@ module FormulaCellarChecks checker = LinkageChecker.new(keg, formula) return unless checker.broken_dylibs? - audit_check_output <<-EOS.undent + problem_if_output <<-EOS.undent The installation was broken. Broken dylib links found: #{checker.broken_dylibs.to_a * "\n "} @@ -76,9 +76,9 @@ module FormulaCellarChecks def audit_installed generic_audit_installed - audit_check_output(check_shadowed_headers) - audit_check_output(check_openssl_links) - audit_check_output(check_python_framework_links(formula.lib)) + problem_if_output(check_shadowed_headers) + problem_if_output(check_openssl_links) + problem_if_output(check_python_framework_links(formula.lib)) check_linkage end end diff --git a/Library/Homebrew/extend/os/mac/hardware/cpu.rb b/Library/Homebrew/extend/os/mac/hardware/cpu.rb index f180995fb..22d118e1a 100644 --- a/Library/Homebrew/extend/os/mac/hardware/cpu.rb +++ b/Library/Homebrew/extend/os/mac/hardware/cpu.rb @@ -107,6 +107,20 @@ module Hardware end end + # Determines whether the current CPU and macOS combination + # can run an executable of the specified architecture. + # `arch` is a symbol in the same format returned by + # Hardware::CPU.family + def can_run?(arch) + if Hardware::CPU.intel? + intel_can_run? arch + elsif Hardware::CPU.ppc? + ppc_can_run? arch + else + false + end + end + def features @features ||= sysctl_n( "machdep.cpu.features", @@ -162,6 +176,35 @@ module Hardware @properties[keys] = Utils.popen_read("/usr/sbin/sysctl", "-n", *keys) end end + + def intel_can_run?(arch) + case arch + when :ppc + # Rosetta is still available + MacOS.version < :lion + when :ppc64 + # Rosetta never supported PPC64 + false + when :x86_64 + Hardware::CPU.is_64_bit? + when :i386 + true + else # dunno + false + end + end + + def ppc_can_run?(arch) + case arch + when :ppc + true + when :ppc64 + Hardware::CPU.is_64_bit? + else + # Intel is never supported + false + end + end end end end diff --git a/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb b/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb new file mode 100644 index 000000000..8c898a272 --- /dev/null +++ b/Library/Homebrew/extend/os/mac/requirements/osxfuse_requirement.rb @@ -0,0 +1,34 @@ +require "requirement" + +class OsxfuseRequirement < Requirement + cask "osxfuse" + download "https://osxfuse.github.io/" + + satisfy(build_env: false) { self.class.binary_osxfuse_installed? } + + def self.binary_osxfuse_installed? + File.exist?("/usr/local/include/osxfuse/fuse.h") && + !File.symlink?("/usr/local/include/osxfuse") + end + + env do + ENV.append_path "PKG_CONFIG_PATH", HOMEBREW_LIBRARY/"Homebrew/os/mac/pkgconfig/fuse" + + unless HOMEBREW_PREFIX.to_s == "/usr/local" + ENV.append_path "HOMEBREW_LIBRARY_PATHS", "/usr/local/lib" + ENV.append_path "HOMEBREW_INCLUDE_PATHS", "/usr/local/include/osxfuse" + end + end +end + +class NonBinaryOsxfuseRequirement < Requirement + fatal true + satisfy(build_env: false) { HOMEBREW_PREFIX.to_s != "/usr/local" || !OsxfuseRequirement.binary_osxfuse_installed? } + + def message + <<-EOS.undent + osxfuse is already installed from the binary distribution and + conflicts with this formula. + EOS + end +end diff --git a/Library/Homebrew/extend/os/requirements/osxfuse_requirement.rb b/Library/Homebrew/extend/os/requirements/osxfuse_requirement.rb new file mode 100644 index 000000000..5f56d48c4 --- /dev/null +++ b/Library/Homebrew/extend/os/requirements/osxfuse_requirement.rb @@ -0,0 +1,7 @@ +require "requirements/osxfuse_requirement" + +if OS.mac? + require "extend/os/mac/requirements/osxfuse_requirement" +elsif OS.linux? + require "extend/os/linux/requirements/osxfuse_requirement" +end diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index eb254c624..c413e9e94 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -331,7 +331,6 @@ class Pathname raise ChecksumMismatchError.new(self, expected, actual) unless expected == actual end - # FIXME: eliminate the places where we rely on this method alias to_str to_s unless method_defined?(:to_str) def cd @@ -365,9 +364,8 @@ class Pathname unless method_defined?(:/) def /(other) - unless other.respond_to?(:to_str) || other.respond_to?(:to_path) - opoo "Pathname#/ called on #{inspect} with #{other.inspect} as an argument" - puts "This behavior is deprecated, please pass either a String or a Pathname" + if !other.respond_to?(:to_str) && !other.respond_to?(:to_path) + odeprecated "Pathname#/ with #{other.class}", "a String or a Pathname" end self + other.to_s end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index d544bd668..1230199bf 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -13,6 +13,7 @@ require "pkg_version" require "tap" require "keg" require "migrator" +require "extend/ENV" # A formula provides instructions and metadata for Homebrew to install a piece # of software. Every Homebrew formula is a {Formula}. @@ -1004,19 +1005,26 @@ class Formula def post_install; end # @private - def post_install_defined? - method(:post_install).owner == self.class - end - - # @private def run_post_install @prefix_returns_versioned_prefix = true build = self.build self.build = Tab.for_formula(self) + old_tmpdir = ENV["TMPDIR"] old_temp = ENV["TEMP"] old_tmp = ENV["TMP"] + old_path = ENV["HOMEBREW_PATH"] + ENV["TMPDIR"] = ENV["TEMP"] = ENV["TMP"] = HOMEBREW_TEMP + ENV["HOMEBREW_PATH"] = nil + + ENV.clear_sensitive_environment! + + Pathname.glob("#{bottle_prefix}/{etc,var}/**/*") do |path| + path.extend(InstallRenamed) + path.cp_path_sub(bottle_prefix, HOMEBREW_PREFIX) + end + with_logging("post_install") do post_install end @@ -1025,6 +1033,7 @@ class Formula ENV["TMPDIR"] = old_tmpdir ENV["TEMP"] = old_temp ENV["TMP"] = old_tmp + ENV["HOMEBREW_PATH"] = old_path @prefix_returns_versioned_prefix = false end @@ -1664,9 +1673,15 @@ class Formula old_temp = ENV["TEMP"] old_tmp = ENV["TMP"] old_term = ENV["TERM"] + old_path = ENV["HOMEBREW_PATH"] + ENV["CURL_HOME"] = old_curl_home || old_home ENV["TMPDIR"] = ENV["TEMP"] = ENV["TMP"] = HOMEBREW_TEMP ENV["TERM"] = "dumb" + ENV["HOMEBREW_PATH"] = nil + + ENV.clear_sensitive_environment! + mktemp("#{name}-test") do |staging| staging.retain! if ARGV.keep_tmp? @testpath = staging.tmpdir @@ -1689,6 +1704,7 @@ class Formula ENV["TEMP"] = old_temp ENV["TMP"] = old_tmp ENV["TERM"] = old_term + ENV["HOMEBREW_PATH"] = old_path @prefix_returns_versioned_prefix = false end @@ -1892,7 +1908,6 @@ class Formula def exec_cmd(cmd, args, out, logfn) ENV["HOMEBREW_CC_LOG_PATH"] = logfn - # TODO: system "xcodebuild" is deprecated, this should be removed soon. ENV.remove_cc_etc if cmd.to_s.start_with? "xcodebuild" # Turn on argument filtering in the superenv compiler wrapper. @@ -1925,17 +1940,28 @@ class Formula mkdir_p env_home old_home = ENV["HOME"] - ENV["HOME"] = env_home old_curl_home = ENV["CURL_HOME"] - ENV["CURL_HOME"] = old_curl_home || old_home + old_path = ENV["HOMEBREW_PATH"] + + unless ARGV.interactive? + ENV["HOME"] = env_home + ENV["CURL_HOME"] = old_curl_home || old_home + end + ENV["HOMEBREW_PATH"] = nil + setup_home env_home + ENV.clear_sensitive_environment! + begin yield staging ensure @buildpath = nil - ENV["HOME"] = old_home - ENV["CURL_HOME"] = old_curl_home + unless ARGV.interactive? + ENV["HOME"] = old_home + ENV["CURL_HOME"] = old_curl_home + end + ENV["HOMEBREW_PATH"] = old_path end end end @@ -2396,7 +2422,6 @@ class Formula # version '4.8.1' # end</pre> def fails_with(compiler, &block) - # TODO: deprecate this in future. # odeprecated "fails_with :llvm" if compiler == :llvm specs.each { |spec| spec.fails_with(compiler, &block) } end diff --git a/Library/Homebrew/formula_cellar_checks.rb b/Library/Homebrew/formula_cellar_checks.rb index 7db5e748b..7f7d77569 100644 --- a/Library/Homebrew/formula_cellar_checks.rb +++ b/Library/Homebrew/formula_cellar_checks.rb @@ -14,7 +14,7 @@ module FormulaCellarChecks <<-EOS.undent #{prefix_bin} is not in your PATH - You can amend this by altering your #{Utils::Shell.shell_profile} file + You can amend this by altering your #{Utils::Shell.profile} file EOS end @@ -154,17 +154,17 @@ module FormulaCellarChecks end def audit_installed - audit_check_output(check_manpages) - audit_check_output(check_infopages) - audit_check_output(check_jars) - audit_check_output(check_non_libraries) - audit_check_output(check_non_executables(formula.bin)) - audit_check_output(check_generic_executables(formula.bin)) - audit_check_output(check_non_executables(formula.sbin)) - audit_check_output(check_generic_executables(formula.sbin)) - audit_check_output(check_easy_install_pth(formula.lib)) - audit_check_output(check_elisp_dirname(formula.share, formula.name)) - audit_check_output(check_elisp_root(formula.share, formula.name)) + problem_if_output(check_manpages) + problem_if_output(check_infopages) + problem_if_output(check_jars) + problem_if_output(check_non_libraries) + problem_if_output(check_non_executables(formula.bin)) + problem_if_output(check_generic_executables(formula.bin)) + problem_if_output(check_non_executables(formula.sbin)) + problem_if_output(check_generic_executables(formula.sbin)) + problem_if_output(check_easy_install_pth(formula.lib)) + problem_if_output(check_elisp_dirname(formula.share, formula.name)) + problem_if_output(check_elisp_root(formula.share, formula.name)) end alias generic_audit_installed audit_installed diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 017be51dc..f50d9ed9e 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -173,34 +173,22 @@ class FormulaInstaller EOS end - if ENV["HOMEBREW_CHECK_RECURSIVE_VERSION_DEPENDENCIES"] - version_hash = {} - version_conflicts = Set.new - recursive_formulae.each do |f| - name = f.name - unversioned_name, = name.split("@") - version_hash[unversioned_name] ||= Set.new - version_hash[unversioned_name] << name - next if version_hash[unversioned_name].length < 2 - version_conflicts += version_hash[unversioned_name] - end - unless version_conflicts.empty? - raise CannotInstallFormulaError, <<-EOS.undent - #{formula.full_name} contains conflicting version recursive dependencies: - #{version_conflicts.to_a.join ", "} - View these with `brew deps --tree #{formula.full_name}`. - EOS - end - end - - unless ENV["HOMEBREW_NO_CHECK_UNLINKED_DEPENDENCIES"] - unlinked_deps = recursive_formulae.select do |dep| - dep.installed? && !dep.keg_only? && !dep.linked_keg.directory? - end - - unless unlinked_deps.empty? - raise CannotInstallFormulaError, "You must `brew link #{unlinked_deps*" "}` before #{formula.full_name} can be installed" - end + version_hash = {} + version_conflicts = Set.new + recursive_formulae.each do |f| + name = f.name + unversioned_name, = name.split("@") + version_hash[unversioned_name] ||= Set.new + version_hash[unversioned_name] << name + next if version_hash[unversioned_name].length < 2 + version_conflicts += version_hash[unversioned_name] + end + unless version_conflicts.empty? + raise CannotInstallFormulaError, <<-EOS.undent + #{formula.full_name} contains conflicting version recursive dependencies: + #{version_conflicts.to_a.join ", "} + View these with `brew deps --tree #{formula.full_name}`. + EOS end pinned_unsatisfied_deps = recursive_deps.select do |dep| @@ -229,11 +217,20 @@ class FormulaInstaller # function but after instantiating this class so that it can avoid having to # relink the active keg if possible (because it is slow). if formula.linked_keg.directory? - # some other version is already installed *and* linked - raise CannotInstallFormulaError, <<-EOS.undent - #{formula.name}-#{formula.linked_keg.resolved_path.basename} already installed - To install this version, first `brew unlink #{formula.name}` + message = <<-EOS.undent + #{formula.name} #{formula.linked_keg.resolved_path.basename} is already installed EOS + message += if formula.outdated? && !formula.head? + <<-EOS.undent + To upgrade to #{formula.version}, run `brew upgrade #{formula.name}` + EOS + else + # some other version is already installed *and* linked + <<-EOS.undent + To install #{formula.version}, first run `brew unlink #{formula.name}` + EOS + end + raise CannotInstallFormulaError, message end check_conflicts @@ -567,13 +564,11 @@ class FormulaInstaller fix_dynamic_linkage(keg) end - if formula.post_install_defined? - if build_bottle? - ohai "Not running post_install as we're building a bottle" - puts "You can run it manually using `brew postinstall #{formula.full_name}`" - else - post_install - end + if build_bottle? + ohai "Not running post_install as we're building a bottle" + puts "You can run it manually using `brew postinstall #{formula.full_name}`" + else + post_install end caveats @@ -661,13 +656,15 @@ class FormulaInstaller Sandbox.print_sandbox_message if Sandbox.formula?(formula) Utils.safe_fork do - # Invalidate the current sudo timestamp in case a build script calls sudo - system "/usr/bin/sudo", "-k" + # Invalidate the current sudo timestamp in case a build script calls sudo. + # Travis CI's Linux sudoless workers have a weird sudo that fails here. + system "/usr/bin/sudo", "-k" unless ENV["TRAVIS_SUDO"] == "false" if Sandbox.formula?(formula) sandbox = Sandbox.new formula.logs.mkpath sandbox.record_log(formula.logs/"build.sandbox.log") + sandbox.allow_write_path(ENV["HOME"]) if ARGV.interactive? sandbox.allow_write_temp_and_cache sandbox.allow_write_log(formula) sandbox.allow_write_xcode @@ -835,12 +832,6 @@ class FormulaInstaller skip_linkage = formula.bottle_specification.skip_relocation? keg.replace_placeholders_with_locations tab.changed_files, skip_linkage: skip_linkage - Pathname.glob("#{formula.bottle_prefix}/{etc,var}/**/*") do |path| - path.extend(InstallRenamed) - path.cp_path_sub(formula.bottle_prefix, HOMEBREW_PREFIX) - end - FileUtils.rm_rf formula.bottle_prefix - tab = Tab.for_keg(keg) CxxStdlib.check_compatibility( @@ -858,15 +849,15 @@ class FormulaInstaller tab.write end - def audit_check_output(output) + def problem_if_output(output) return unless output opoo output @show_summary_heading = true end def audit_installed - audit_check_output(check_env_path(formula.bin)) - audit_check_output(check_env_path(formula.sbin)) + problem_if_output(check_env_path(formula.bin)) + problem_if_output(check_env_path(formula.sbin)) super end @@ -899,6 +890,6 @@ class FormulaInstaller def puts_requirement_messages return unless @requirement_messages return if @requirement_messages.empty? - puts @requirement_messages + $stderr.puts @requirement_messages end end diff --git a/Library/Homebrew/formula_support.rb b/Library/Homebrew/formula_support.rb index b8476f5cc..4d963a55e 100644 --- a/Library/Homebrew/formula_support.rb +++ b/Library/Homebrew/formula_support.rb @@ -32,30 +32,30 @@ class KegOnlyReason return @explanation unless @explanation.empty? case @reason when :versioned_formula then <<-EOS.undent - This is an alternate version of another formula. + this is an alternate version of another formula EOS when :provided_by_macos, :provided_by_osx then <<-EOS.undent macOS already provides this software and installing another version in - parallel can cause all kinds of trouble. + parallel can cause all kinds of trouble EOS when :shadowed_by_macos, :shadowed_by_osx then <<-EOS.undent macOS provides similar software and installing this software in - parallel can cause all kinds of trouble. + parallel can cause all kinds of trouble EOS when :provided_pre_mountain_lion then <<-EOS.undent - macOS already provides this software in versions before Mountain Lion. + macOS already provides this software in versions before Mountain Lion EOS when :provided_pre_mavericks then <<-EOS.undent - macOS already provides this software in versions before Mavericks. + macOS already provides this software in versions before Mavericks EOS when :provided_pre_el_capitan then <<-EOS.undent - macOS already provides this software in versions before El Capitan. + macOS already provides this software in versions before El Capitan EOS when :provided_until_xcode43 then <<-EOS.undent - Xcode provides this software prior to version 4.3. + Xcode provides this software prior to version 4.3 EOS when :provided_until_xcode5 then <<-EOS.undent - Xcode provides this software prior to version 5. + Xcode provides this software prior to version 5 EOS else @reason diff --git a/Library/Homebrew/formula_versions.rb b/Library/Homebrew/formula_versions.rb index 28c2a3be8..70706a2f0 100644 --- a/Library/Homebrew/formula_versions.rb +++ b/Library/Homebrew/formula_versions.rb @@ -7,21 +7,22 @@ class FormulaVersions ErrorDuringExecution, LoadError, MethodDeprecatedError ].freeze + MAX_VERSIONS_DEPTH = 2 + attr_reader :name, :path, :repository, :entry_name - def initialize(formula, options = {}) + def initialize(formula) @name = formula.name @path = formula.path @repository = formula.tap.path @entry_name = @path.relative_path_from(repository).to_s - @max_depth = options[:max_depth] + @current_formula = formula end def rev_list(branch) repository.cd do - depth = 0 Utils.popen_read("git", "rev-list", "--abbrev-commit", "--remove-empty", branch, "--", entry_name) do |io| - yield io.readline.chomp until io.eof? || (@max_depth && (depth += 1) > @max_depth) + yield io.readline.chomp until io.eof? end end end @@ -49,11 +50,15 @@ class FormulaVersions def bottle_version_map(branch) map = Hash.new { |h, k| h[k] = [] } + + versions_seen = 0 rev_list(branch) do |rev| formula_at_revision(rev) do |f| bottle = f.bottle_specification map[f.pkg_version] << bottle.rebuild unless bottle.checksums.empty? + versions_seen = (map.keys + [f.pkg_version]).uniq.length end + return map if versions_seen > MAX_VERSIONS_DEPTH end map end @@ -63,26 +68,38 @@ class FormulaVersions return attributes_map if attributes.empty? attributes.each do |attribute| - attributes_map[attribute] ||= {} + attributes_map[attribute] ||= { + stable: {}, + devel: {}, + } end + stable_versions_seen = 0 rev_list(branch) do |rev| formula_at_revision(rev) do |f| attributes.each do |attribute| map = attributes_map[attribute] - if f.stable - map[:stable] ||= {} - map[:stable][f.stable.version] ||= [] - map[:stable][f.stable.version] << f.send(attribute) - end - next unless f.devel - map[:devel] ||= {} - map[:devel][f.devel.version] ||= [] - map[:devel][f.devel.version] << f.send(attribute) + set_attribute_map(map, f, attribute) + + stable_keys_length = (map[:stable].keys + [f.version]).uniq.length + stable_versions_seen = [stable_versions_seen, stable_keys_length].max end end + break if stable_versions_seen > MAX_VERSIONS_DEPTH end attributes_map end + + private + + def set_attribute_map(map, f, attribute) + if f.stable + map[:stable][f.stable.version] ||= [] + map[:stable][f.stable.version] << f.send(attribute) + end + return unless f.devel + map[:devel][f.devel.version] ||= [] + map[:devel][f.devel.version] << f.send(attribute) + end end diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 4a65f8704..446d5dc10 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -105,7 +105,18 @@ module Formulary # Loads formulae from bottles. class BottleLoader < FormulaLoader def initialize(bottle_name) - @bottle_filename = Pathname(bottle_name).realpath + case bottle_name + when %r{(https?|ftp|file)://} + # The name of the formula is found between the last slash and the last hyphen. + resource = Resource.new bottle_name[%r{([^/]+)-}, 1] { url bottle_name } + downloader = CurlBottleDownloadStrategy.new resource.name, resource + @bottle_filename = downloader.cached_location + cached = @bottle_filename.exist? + downloader.fetch + ohai "Pouring the cached bottle" if cached + else + @bottle_filename = Pathname(bottle_name).realpath + end name, full_name = Utils::Bottles.resolve_formula_names @bottle_filename super name, Formulary.path(full_name) end @@ -210,6 +221,10 @@ module Formulary def get_formula(spec, alias_path: nil) super + rescue FormulaUnreadableError => e + raise TapFormulaUnreadableError.new(tap, name, e.formula_error), "", e.backtrace + rescue FormulaClassUnavailableError => e + raise TapFormulaClassUnavailableError.new(tap, name, e.path, e.class_name, e.class_list), "", e.backtrace rescue FormulaUnavailableError => e raise TapFormulaUnavailableError.new(tap, name), "", e.backtrace end @@ -331,10 +346,10 @@ module Formulary def self.loader_for(ref, from: nil) case ref - when %r{(https?|ftp|file)://} - return FromUrlLoader.new(ref) when Pathname::BOTTLE_EXTNAME_RX return BottleLoader.new(ref) + when %r{(https?|ftp|file)://} + return FromUrlLoader.new(ref) when HOMEBREW_TAP_FORMULA_REGEX return TapLoader.new(ref, from: from) end @@ -402,7 +417,7 @@ module Formulary end def self.tap_paths(name, taps = Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/"]) - name = name.downcase + name = name.to_s.downcase taps.map do |tap| Pathname.glob([ "#{tap}Formula/#{name}.rb", diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb index 391f5b012..877253072 100644 --- a/Library/Homebrew/global.rb +++ b/Library/Homebrew/global.rb @@ -3,6 +3,7 @@ require "extend/fileutils" require "extend/pathname" require "extend/git_repository" require "extend/ARGV" +require "PATH" require "extend/string" require "os" require "utils" @@ -10,6 +11,7 @@ require "exceptions" require "set" require "rbconfig" require "official_taps" +require "pp" ARGV.extend(HomebrewArgvExtension) @@ -53,7 +55,8 @@ HOMEBREW_PULL_OR_COMMIT_URL_REGEX = %r[https://github\.com/([\w-]+)/([\w-]+)?/(? require "compat" unless ARGV.include?("--no-compat") || ENV["HOMEBREW_NO_COMPAT"] -ORIGINAL_PATHS = ENV["PATH"].split(File::PATH_SEPARATOR).map do |p| +ENV["HOMEBREW_PATH"] ||= ENV["PATH"] +ORIGINAL_PATHS = PATH.new(ENV["HOMEBREW_PATH"]).map do |p| begin Pathname.new(p).expand_path rescue @@ -61,7 +64,6 @@ ORIGINAL_PATHS = ENV["PATH"].split(File::PATH_SEPARATOR).map do |p| end end.compact.freeze -# TODO: remove this as soon as it's removed from commands.rb. HOMEBREW_INTERNAL_COMMAND_ALIASES = { "ls" => "list", "homepage" => "home", diff --git a/Library/Homebrew/gpg.rb b/Library/Homebrew/gpg.rb index 777542055..cb9e367df 100644 --- a/Library/Homebrew/gpg.rb +++ b/Library/Homebrew/gpg.rb @@ -6,6 +6,7 @@ class Gpg gpg_short_version = Utils.popen_read(gpg, "--version")[/\d\.\d/, 0] next unless gpg_short_version gpg_version = Version.create(gpg_short_version.to_s) + @version = gpg_version gpg_version == Version.create("2.0") || gpg_version == Version.create("2.1") end @@ -25,6 +26,10 @@ class Gpg File.executable?(GPG_EXECUTABLE.to_s) end + def self.version + @version if available? + end + def self.create_test_key(path) odie "No GPG present to test against!" unless available? diff --git a/Library/Homebrew/hardware.rb b/Library/Homebrew/hardware.rb index fe07aee9d..997598def 100644 --- a/Library/Homebrew/hardware.rb +++ b/Library/Homebrew/hardware.rb @@ -80,6 +80,18 @@ module Hardware def feature?(name) features.include?(name) end + + def can_run?(arch) + if is_32_bit? + arch_32_bit == arch + elsif intel? + [:i386, :x86_64].include? arch + elsif ppc? + [:ppc, :ppc64].include? arch + else + false + end + end end end diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index d4b9c5d77..8733def27 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -468,7 +468,10 @@ class Keg end def aliases - Formulary.from_rack(rack).aliases + formula = Formulary.from_rack(rack) + aliases = formula.aliases + return aliases if formula.stable? + aliases.reject { |a| a.include?("@") } rescue FormulaUnavailableError [] end diff --git a/Library/Homebrew/language/python.rb b/Library/Homebrew/language/python.rb index 23c5246ba..0f8e3a4e6 100644 --- a/Library/Homebrew/language/python.rb +++ b/Library/Homebrew/language/python.rb @@ -53,29 +53,6 @@ module Language quiet_system python, "-c", script end - # deprecated; use system "python", *setup_install_args(prefix) instead - def self.setup_install(python, prefix, *args) - opoo <<-EOS.undent - Language::Python.setup_install is deprecated. - If you are a formula author, please use - system "python", *Language::Python.setup_install_args(prefix) - instead. - EOS - - # force-import setuptools, which monkey-patches distutils, to make - # sure that we always call a setuptools setup.py. trick borrowed from pip: - # https://github.com/pypa/pip/blob/043af83/pip/req/req_install.py#L743-L780 - shim = <<-EOS.undent - import setuptools, tokenize - __file__ = 'setup.py' - exec(compile(getattr(tokenize, 'open', open)(__file__).read() - .replace('\\r\\n', '\\n'), __file__, 'exec')) - EOS - args += %w[--single-version-externally-managed --record=installed.txt] - args << "--prefix=#{prefix}" - system python, "-c", shim, "install", *args - end - def self.setup_install_args(prefix) shim = <<-EOS.undent import setuptools, tokenize diff --git a/Library/Homebrew/manpages/brew.1.md.erb b/Library/Homebrew/manpages/brew.1.md.erb index 5b0228e3f..29f8d0bec 100644 --- a/Library/Homebrew/manpages/brew.1.md.erb +++ b/Library/Homebrew/manpages/brew.1.md.erb @@ -98,8 +98,15 @@ can take several different forms: The formula file will be cached for later use. ## ENVIRONMENT + * `HOMEBREW_ARTIFACT_DOMAIN`: + If set, instructs Homebrew to use the given URL as a download mirror for bottles and binaries. + + * `HOMEBREW_AUTO_UPDATE_SECS`: + If set, Homebrew will only check for autoupdates once per this seconds interval. + + *Default:* `60`. - * `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`: + * `HOMEBREW_AWS_ACCESS_KEY_ID`, `HOMEBREW_AWS_SECRET_ACCESS_KEY`: When using the `S3` download strategy, Homebrew will look in these variables for access credentials (see <https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-environment> @@ -107,30 +114,9 @@ can take several different forms: the `S3` download strategy will download with a public (unsigned) URL. - * `BROWSER`: - If set, and `HOMEBREW_BROWSER` is not, use `BROWSER` as the web browser - when opening project homepages. - - * `EDITOR`: - If set, and `HOMEBREW_EDITOR` and `VISUAL` are not, use `EDITOR` as the text editor. - - * `GIT`: - When using Git, Homebrew will use `GIT` if set, - a Homebrew-built Git if installed, or the system-provided binary. - - Set this to force Homebrew to use a particular git binary. - * `HOMEBREW_BOTTLE_DOMAIN`: If set, instructs Homebrew to use the given URL as a download mirror for bottles. - * `HOMEBREW_ARTIFACT_DOMAIN`: - If set, instructs Homebrew to use the given URL as a download mirror for bottles and binaries. - - * `HOMEBREW_AUTO_UPDATE_SECS`: - If set, Homebrew will only check for autoupdates once per this seconds interval. - - *Default:* `60`. - * `HOMEBREW_BROWSER`: If set, uses this setting as the browser when opening project homepages, instead of the OS default browser. @@ -178,6 +164,12 @@ can take several different forms: If set, Homebrew will always use its vendored, relocatable Ruby 2.0 version even if the system version of Ruby is >=2.0. + * `HOMEBREW_GIT`: + When using Git, Homebrew will use `GIT` if set, + a Homebrew-built Git if installed, or the system-provided binary. + + Set this to force Homebrew to use a particular git binary. + * `HOMEBREW_GITHUB_API_TOKEN`: A personal access token for the GitHub API, which you can create at <https://github.com/settings/tokens>. If set, GitHub will allow you a @@ -243,9 +235,6 @@ can take several different forms: * `HOMEBREW_VERBOSE`: If set, Homebrew always assumes `--verbose` when running commands. - * `VISUAL`: - If set, and `HOMEBREW_EDITOR` is not, use `VISUAL` as the text editor. - ## USING HOMEBREW BEHIND A PROXY Homebrew uses several commands for downloading files (e.g. `curl`, `git`, `svn`). diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb index 5b903b899..eac3d7725 100644 --- a/Library/Homebrew/missing_formula.rb +++ b/Library/Homebrew/missing_formula.rb @@ -122,7 +122,7 @@ module Homebrew path = Formulary.path name return if File.exist? path tap = Tap.from_path(path) - return unless File.exist? tap.path + return if tap.nil? || !File.exist?(tap.path) relative_path = path.relative_path_from tap.path tap.path.cd do diff --git a/Library/Homebrew/official_taps.rb b/Library/Homebrew/official_taps.rb index a7bc4a1d6..dcb65d9f8 100644 --- a/Library/Homebrew/official_taps.rb +++ b/Library/Homebrew/official_taps.rb @@ -5,8 +5,29 @@ OFFICIAL_TAPS = %w[ science ].freeze +OFFICIAL_CASK_TAPS = %w[ + cask + versions +].freeze + OFFICIAL_CMD_TAPS = { "homebrew/bundle" => ["bundle"], "homebrew/test-bot" => ["test-bot"], "homebrew/services" => ["services"], }.freeze + +DEPRECATED_OFFICIAL_TAPS = %w[ + binary + completions + devel-only + dupes + emacs + fuse + games + gui + head-only + python + tex + versions + x11 +].freeze diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 518936647..dba2480ef 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -205,6 +205,8 @@ module OS "8.2" => { clang: "8.0", clang_build: 800 }, "8.2.1" => { clang: "8.0", clang_build: 800 }, "8.3" => { clang: "8.1", clang_build: 802 }, + "8.3.1" => { clang: "8.1", clang_build: 802 }, + "8.3.2" => { clang: "8.1", clang_build: 802 }, }.freeze def compilers_standard? diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb index 70fe3c14d..9ed95d3f2 100644 --- a/Library/Homebrew/os/mac/xcode.rb +++ b/Library/Homebrew/os/mac/xcode.rb @@ -17,12 +17,12 @@ module OS when "10.9" then "6.2" when "10.10" then "7.2.1" when "10.11" then "8.2.1" - when "10.12" then "8.3" + when "10.12" then "8.3.2" else raise "macOS '#{MacOS.version}' is invalid" unless OS::Mac.prerelease? # Default to newest known version of Xcode for unreleased macOS versions. - "8.3" + "8.3.2" end end @@ -128,11 +128,10 @@ module OS end end - # The remaining logic provides a fake Xcode version for CLT-only - # systems. This behavior only exists because Homebrew used to assume - # Xcode.version would always be non-nil. This is deprecated, and will - # be removed in a future version. To remain compatible, guard usage of - # Xcode.version with an Xcode.installed? check. + # The remaining logic provides a fake Xcode version based on the + # installed CLT version. This is useful as they are packaged + # simultaneously so workarounds need to apply to both based on their + # comparable version. case (DevelopmentTools.clang_version.to_f * 10).to_i when 0 then "dunno" when 1..14 then "3.2.2" @@ -214,7 +213,7 @@ module OS # on the older supported platform for that Xcode release, i.e there's no # CLT package for 10.11 that contains the Clang version from Xcode 8. case MacOS.version - when "10.12" then "802.0.38" + when "10.12" then "802.0.42" when "10.11" then "800.0.42.1" when "10.10" then "700.1.81" when "10.9" then "600.0.57" diff --git a/Library/Homebrew/os/mac/xquartz.rb b/Library/Homebrew/os/mac/xquartz.rb index b82772faf..2fdf0abea 100644 --- a/Library/Homebrew/os/mac/xquartz.rb +++ b/Library/Homebrew/os/mac/xquartz.rb @@ -5,7 +5,6 @@ module OS module XQuartz module_function - # TODO: confirm this path when you have internet DEFAULT_BUNDLE_PATH = Pathname.new("Applications/Utilities/XQuartz.app").freeze FORGE_BUNDLE_ID = "org.macosforge.xquartz.X11".freeze APPLE_BUNDLE_ID = "org.x.X11".freeze diff --git a/Library/Homebrew/readall.rb b/Library/Homebrew/readall.rb index 3dd7075ec..ddac58444 100644 --- a/Library/Homebrew/readall.rb +++ b/Library/Homebrew/readall.rb @@ -24,14 +24,21 @@ module Readall !failed end - def valid_aliases?(alias_dirs) + def valid_aliases?(alias_dir, formula_dir) + return true unless alias_dir.directory? + failed = false - alias_dirs.each do |alias_dir| - next unless alias_dir.directory? - alias_dir.children.each do |f| - next unless f.symlink? - next if f.file? - onoe "Broken alias: #{f}" + alias_dir.each_child do |f| + if !f.symlink? + onoe "Non-symlink alias: #{f}" + failed = true + elsif !f.file? + onoe "Non-file alias: #{f}" + failed = true + end + + if (formula_dir/"#{f.basename}.rb").exist? + onoe "Formula duplicating alias: #{f}" failed = true end end @@ -57,7 +64,7 @@ module Readall def valid_tap?(tap, options = {}) failed = false if options[:aliases] - valid_aliases = valid_aliases?([tap.alias_dir]) + valid_aliases = valid_aliases?(tap.alias_dir, tap.formula_dir) failed = true unless valid_aliases end valid_formulae = valid_formulae?(tap.formula_files) diff --git a/Library/Homebrew/requirement.rb b/Library/Homebrew/requirement.rb index bd8cf20c8..6c20e7917 100644 --- a/Library/Homebrew/requirement.rb +++ b/Library/Homebrew/requirement.rb @@ -96,7 +96,7 @@ class Requirement # PATH. parent = satisfied_result_parent return unless parent - return if ENV["PATH"].split(File::PATH_SEPARATOR).include?(parent.to_s) + return if PATH.new(ENV["PATH"]).include?(parent.to_s) ENV.append_path("PATH", parent) end @@ -151,21 +151,19 @@ class Requirement end def which(cmd) - super(cmd, ORIGINAL_PATHS.join(File::PATH_SEPARATOR)) + super(cmd, PATH.new(ORIGINAL_PATHS)) end def which_all(cmd) - super(cmd, ORIGINAL_PATHS.join(File::PATH_SEPARATOR)) + super(cmd, PATH.new(ORIGINAL_PATHS)) end class << self include BuildEnvironmentDSL - attr_reader :env_proc + attr_reader :env_proc, :build attr_rw :fatal, :default_formula attr_rw :cask, :download - # build is deprecated, use `depends_on <requirement> => :build` instead - attr_rw :build def satisfy(options = {}, &block) @satisfied ||= Requirement::Satisfier.new(options, &block) diff --git a/Library/Homebrew/requirements/osxfuse_requirement.rb b/Library/Homebrew/requirements/osxfuse_requirement.rb index d5a341567..9a07209d4 100644 --- a/Library/Homebrew/requirements/osxfuse_requirement.rb +++ b/Library/Homebrew/requirements/osxfuse_requirement.rb @@ -2,34 +2,10 @@ require "requirement" class OsxfuseRequirement < Requirement fatal true - cask "osxfuse" - download "https://osxfuse.github.io/" - - satisfy(build_env: false) { self.class.binary_osxfuse_installed? } - - def self.binary_osxfuse_installed? - File.exist?("/usr/local/include/osxfuse/fuse.h") && - !File.symlink?("/usr/local/include/osxfuse") - end - - env do - ENV.append_path "PKG_CONFIG_PATH", HOMEBREW_LIBRARY/"Homebrew/os/mac/pkgconfig/fuse" - - unless HOMEBREW_PREFIX.to_s == "/usr/local" - ENV.append_path "HOMEBREW_LIBRARY_PATHS", "/usr/local/lib" - ENV.append_path "HOMEBREW_INCLUDE_PATHS", "/usr/local/include/osxfuse" - end - end end class NonBinaryOsxfuseRequirement < Requirement - fatal true - satisfy(build_env: false) { HOMEBREW_PREFIX.to_s != "/usr/local" || !OsxfuseRequirement.binary_osxfuse_installed? } - - def message - <<-EOS.undent - osxfuse is already installed from the binary distribution and - conflicts with this formula. - EOS - end + fatal false end + +require "extend/os/requirements/osxfuse_requirement" diff --git a/Library/Homebrew/rubocops.rb b/Library/Homebrew/rubocops.rb index 3625f2004..587bf4b20 100644 --- a/Library/Homebrew/rubocops.rb +++ b/Library/Homebrew/rubocops.rb @@ -1,2 +1,5 @@ require_relative "./rubocops/bottle_block_cop" require_relative "./rubocops/formula_desc_cop" +require_relative "./rubocops/components_order_cop" +require_relative "./rubocops/components_redundancy_cop" +require_relative "./rubocops/homepage_cop" diff --git a/Library/Homebrew/rubocops/bottle_block_cop.rb b/Library/Homebrew/rubocops/bottle_block_cop.rb index 4d7a94461..f0c7d59bb 100644 --- a/Library/Homebrew/rubocops/bottle_block_cop.rb +++ b/Library/Homebrew/rubocops/bottle_block_cop.rb @@ -2,18 +2,18 @@ require_relative "./extend/formula_cop" module RuboCop module Cop - module Homebrew + module FormulaAuditStrict # This cop audits `bottle` block in Formulae # # - `rebuild` should be used instead of `revision` in `bottle` block - class CorrectBottleBlock < FormulaCop + class BottleBlock < FormulaCop MSG = "Use rebuild instead of revision in bottle block".freeze def audit_formula(_node, _class_node, _parent_class_node, formula_class_body_node) bottle = find_block(formula_class_body_node, :bottle) return if bottle.nil? || block_size(bottle).zero? - problem "Use rebuild instead of revision in bottle block" if method_called?(bottle, :revision) + problem "Use rebuild instead of revision in bottle block" if method_called_in_block?(bottle, :revision) end private diff --git a/Library/Homebrew/rubocops/components_order_cop.rb b/Library/Homebrew/rubocops/components_order_cop.rb new file mode 100644 index 000000000..dfddbe145 --- /dev/null +++ b/Library/Homebrew/rubocops/components_order_cop.rb @@ -0,0 +1,114 @@ +require_relative "./extend/formula_cop" + +module RuboCop + module Cop + module FormulaAuditStrict + # This cop checks for correct order of components in a Formula + # + # - component_precedence_list has component hierarchy in a nested list + # where each sub array contains components' details which are at same precedence level + class ComponentsOrder < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, formula_class_body_node) + component_precedence_list = [ + [{ name: :include, type: :method_call }], + [{ name: :desc, type: :method_call }], + [{ name: :homepage, type: :method_call }], + [{ name: :url, type: :method_call }], + [{ name: :mirror, type: :method_call }], + [{ name: :version, type: :method_call }], + [{ name: :sha256, type: :method_call }], + [{ name: :revision, type: :method_call }], + [{ name: :version_scheme, type: :method_call }], + [{ name: :head, type: :method_call }], + [{ name: :stable, type: :block_call }], + [{ name: :bottle, type: :block_call }], + [{ name: :devel, type: :block_call }], + [{ name: :head, type: :block_call }], + [{ name: :bottle, type: :method_call }], + [{ name: :keg_only, type: :method_call }], + [{ name: :option, type: :method_call }], + [{ name: :depends_on, type: :method_call }], + [{ name: :conflicts_with, type: :method_call }], + [{ name: :go_resource, type: :block_call }, { name: :resource, type: :block_call }], + [{ name: :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 }], + ] + + @present_components = component_precedence_list.map do |components| + relevant_components = [] + components.each do |component| + case component[:type] + when :method_call + relevant_components += find_method_calls_by_name(formula_class_body_node, component[:name]).to_a + when :block_call + relevant_components += find_blocks(formula_class_body_node, component[:name]).to_a + when :method_definition + relevant_components << find_method_def(formula_class_body_node, component[:name]) + end + end + relevant_components.delete_if(&:nil?) + end + + # Check if each present_components is above rest of the present_components + @present_components.take(@present_components.size-1).each_with_index do |preceding_component, p_idx| + next if preceding_component.empty? + @present_components.drop(p_idx+1).each do |succeeding_component| + next if succeeding_component.empty? + @offensive_nodes = check_precedence(preceding_component, succeeding_component) + component_problem @offensive_nodes[0], @offensive_nodes[1] if @offensive_nodes + end + end + end + + private + + # Method to format message for reporting component precedence violations + def component_problem(c1, c2) + problem "`#{format_component(c1)}` (line #{line_number(c1)}) should be put before `#{format_component(c2)}` (line #{line_number(c2)})" + end + + # autocorrect method gets called just after component_problem method call + def autocorrect(_node) + succeeding_node = @offensive_nodes[0] + preceding_node = @offensive_nodes[1] + lambda do |corrector| + reorder_components(corrector, succeeding_node, preceding_node) + end + end + + # Reorder two nodes in the source, using the corrector instance in autocorrect method + # Components of same type are grouped together when rewriting the source + # Linebreaks are introduced if components are of two different methods/blocks/multilines + def reorder_components(corrector, node1, node2) + # order_idx : node1's index in component_precedence_list + # curr_p_idx: node1's index in preceding_comp_arr + # preceding_comp_arr: array containing components of same type + order_idx, curr_p_idx, preceding_comp_arr = get_state(node1) + + # curr_p_idx > 0 means node1 needs to be grouped with its own kind + if curr_p_idx>0 + node2 = preceding_comp_arr[curr_p_idx-1] + indentation = " " * (start_column(node2) - line_start_column(node2)) + line_breaks = node2.multiline? ? "\n\n" : "\n" + corrector.insert_after(node2.source_range, line_breaks+indentation+node1.source) + else + indentation = " " * (start_column(node2) - line_start_column(node2)) + # No line breaks upto version_scheme, order_idx == 8 + line_breaks = order_idx>8 ? "\n\n" : "\n" + corrector.insert_before(node2.source_range, node1.source+line_breaks+indentation) + end + corrector.remove(range_with_surrounding_space(node1.source_range, :left)) + end + + # Returns precedence index and component's index to properly reorder and group during autocorrect + def get_state(node1) + @present_components.each_with_index do |comp, idx| + return [idx, comp.index(node1), comp] if comp.member?(node1) + end + end + end + end + end +end diff --git a/Library/Homebrew/rubocops/components_redundancy_cop.rb b/Library/Homebrew/rubocops/components_redundancy_cop.rb new file mode 100644 index 000000000..52dba4718 --- /dev/null +++ b/Library/Homebrew/rubocops/components_redundancy_cop.rb @@ -0,0 +1,33 @@ +require_relative "./extend/formula_cop" + +module RuboCop + module Cop + module FormulaAuditStrict + # This cop checks if redundant components are present and other component errors + # + # - `url|checksum|mirror` should be inside `stable` block + # - `head` and `head do` should not be simultaneously present + # - `bottle :unneeded/:disable` and `bottle do` should not be simultaneously present + + class ComponentsRedundancy < FormulaCop + HEAD_MSG = "`head` and `head do` should not be simultaneously present".freeze + BOTTLE_MSG = "`bottle :modifier` and `bottle do` should not be simultaneously present".freeze + + def audit_formula(_node, _class_node, _parent_class_node, formula_class_body_node) + stable_block = find_block(formula_class_body_node, :stable) + if stable_block + [:url, :sha256, :mirror].each do |method_name| + problem "`#{method_name}` should be put inside `stable` block" if method_called?(formula_class_body_node, method_name) + end + end + + problem HEAD_MSG if method_called?(formula_class_body_node, :head) && + find_block(formula_class_body_node, :head) + + problem BOTTLE_MSG if method_called?(formula_class_body_node, :bottle) && + find_block(formula_class_body_node, :bottle) + end + end + end + end +end diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb index 49108bd48..3f70086b3 100644 --- a/Library/Homebrew/rubocops/extend/formula_cop.rb +++ b/Library/Homebrew/rubocops/extend/formula_cop.rb @@ -1,143 +1,212 @@ module RuboCop module Cop - module Homebrew - class FormulaCop < Cop - @registry = Cop.registry - - def on_class(node) - # This method is called by RuboCop and is the main entry point - file_path = processed_source.buffer.name - return unless file_path_allowed?(file_path) - class_node, parent_class_node, body = *node - return unless formula_class?(parent_class_node) - return unless respond_to?(:audit_formula) - @formula_name = class_name(class_node) - audit_formula(node, class_node, parent_class_node, body) - end + class FormulaCop < Cop + @registry = Cop.registry + + # This method is called by RuboCop and is the main entry point + def on_class(node) + file_path = processed_source.buffer.name + return unless file_path_allowed?(file_path) + class_node, parent_class_node, body = *node + return unless formula_class?(parent_class_node) + return unless respond_to?(:audit_formula) + @formula_name = class_name(class_node) + audit_formula(node, class_node, parent_class_node, body) + end - def regex_match_group(node, pattern) - # Checks for regex match of pattern in the node and - # Sets the appropriate instance variables to report the match - string_repr = string_content(node) - match_object = string_repr.match(pattern) - return unless match_object - node_begin_pos = start_column(node) - line_begin_pos = line_start_column(node) - @column = node_begin_pos + match_object.begin(0) - line_begin_pos + 1 - @length = match_object.to_s.length - @line_no = line_number(node) - @source_buf = source_buffer(node) - @offense_source_range = source_range(@source_buf, @line_no, @column, @length) - @offensive_node = node - match_object - end + # Checks for regex match of pattern in the node and + # Sets the appropriate instance variables to report the match + def regex_match_group(node, pattern) + string_repr = string_content(node) + match_object = string_repr.match(pattern) + return unless match_object + node_begin_pos = start_column(node) + line_begin_pos = line_start_column(node) + @column = node_begin_pos + match_object.begin(0) - line_begin_pos + 1 + @length = match_object.to_s.length + @line_no = line_number(node) + @source_buf = source_buffer(node) + @offense_source_range = source_range(@source_buf, @line_no, @column, @length) + @offensive_node = node + match_object + end - def find_node_method_by_name(node, method_name) - # Returns method_node matching method_name - return if node.nil? - node.each_child_node(:send) do |method_node| - next unless method_node.method_name == method_name - @offensive_node = method_node - @offense_source_range = method_node.source_range - return method_node - end - # If not found then, parent node becomes the offensive node - @offensive_node = node.parent - @offense_source_range = node.parent.source_range - nil + # Returns method_node matching method_name + def find_node_method_by_name(node, method_name) + return if node.nil? + node.each_child_node(:send) do |method_node| + next unless method_node.method_name == method_name + @offensive_node = method_node + @offense_source_range = method_node.source_range + return method_node + end + # If not found then, parent node becomes the offensive node + @offensive_node = node.parent + @offense_source_range = node.parent.source_range + nil + end + + # Returns an array of method call nodes matching method_name inside node + def find_method_calls_by_name(node, method_name) + return if node.nil? + node.each_child_node(:send).select { |method_node| method_name == method_node.method_name } + end + + # Returns a block named block_name inside node + def find_block(node, block_name) + return if node.nil? + node.each_child_node(:block) do |block_node| + next if block_node.method_name != block_name + @offensive_node = block_node + @offense_source_range = block_node.source_range + return block_node + end + # If not found then, parent node becomes the offensive node + @offensive_node = node.parent + @offense_source_range = node.parent.source_range + nil + end + + # Returns an array of block nodes named block_name inside node + def find_blocks(node, block_name) + return if node.nil? + node.each_child_node(:block).select { |block_node| block_name == block_node.method_name } + end + + # Returns a method definition node with method_name + def find_method_def(node, method_name) + return if node.nil? + node.each_child_node(:def) do |def_node| + def_method_name = method_name(def_node) + next unless method_name == def_method_name + @offensive_node = def_node + @offense_source_range = def_node.source_range + return def_node + end + # If not found then, parent node becomes the offensive node + @offensive_node = node.parent + @offense_source_range = node.parent.source_range + nil + end + + # Check if a method is called inside a block + def method_called_in_block?(node, method_name) + block_body = node.children[2] + block_body.each_child_node(:send) do |call_node| + next unless call_node.method_name == method_name + @offensive_node = call_node + @offense_source_range = call_node.source_range + return true end + false + end - def find_block(node, block_name) - # Returns a block named block_name inside node - return if node.nil? - node.each_child_node(:block) do |block_node| - next if block_node.method_name != block_name - @offensive_node = block_node - @offense_source_range = block_node.source_range - return block_node - end - # If not found then, parent node becomes the offensive node - @offensive_node = node.parent - @offense_source_range = node.parent.source_range - nil + # Check if method_name is called among the direct children nodes in the given node + def method_called?(node, method_name) + node.each_child_node(:send) do |call_node| + next unless call_node.method_name == method_name + @offensive_node = call_node + @offense_source_range = call_node.source_range + return true end + false + end - def method_called?(node, method_name) - # Check if a method is called inside a block - block_body = node.children[2] - block_body.each_child_node(:send) do |call_node| - next unless call_node.method_name == method_name - @offensive_node = call_node - @offense_source_range = call_node.source_range - return true + # Checks for precedence, returns the first pair of precedence violating nodes + def check_precedence(first_nodes, next_nodes) + next_nodes.each do |each_next_node| + first_nodes.each do |each_first_node| + if component_precedes?(each_first_node, each_next_node) + return [each_first_node, each_next_node] + end end - false end + nil + end - def parameters(method_node) - # Returns the array of arguments of the method_node - return unless method_node.send_type? - method_node.method_args - end + # If first node does not precede next_node, sets appropriate instance variables for reporting + def component_precedes?(first_node, next_node) + return false if line_number(first_node) < line_number(next_node) + @offense_source_range = first_node.source_range + @offensive_node = first_node + true + end - def line_start_column(node) - # Returns the begin position of the node's line in source code - node.source_range.source_buffer.line_range(node.loc.line).begin_pos - end + # Returns the array of arguments of the method_node + def parameters(method_node) + return unless method_node.send_type? + method_node.method_args + end - def start_column(node) - # Returns the begin position of the node in source code - node.source_range.begin_pos - end + # Returns the begin position of the node's line in source code + def line_start_column(node) + node.source_range.source_buffer.line_range(node.loc.line).begin_pos + end - def line_number(node) - # Returns the line number of the node - node.loc.line - end + # Returns the begin position of the node in source code + def start_column(node) + node.source_range.begin_pos + end - def class_name(node) - # Returns the class node's name, nil if not a class node - @offensive_node = node - @offense_source_range = node.source_range - node.const_name - end + # Returns the line number of the node + def line_number(node) + node.loc.line + end - def size(node) - # Returns the node size in the source code - node.source_range.size - end + # Returns the class node's name, nil if not a class node + def class_name(node) + @offensive_node = node + @offense_source_range = node.source_range + node.const_name + end - def block_size(block) - # Returns the block length of the block node - block_length(block) - end + # Returns the method name for a def node + def method_name(node) + node.children[0] if node.def_type? + end - def source_buffer(node) - # Source buffer is required as an argument to report style violations - node.source_range.source_buffer - end + # Returns the node size in the source code + def size(node) + node.source_range.size + end - def string_content(node) - # Returns the string representation if node is of type str - node.str_content if node.type == :str - end + # Returns the block length of the block node + def block_size(block) + block_length(block) + end - def problem(msg) - add_offense(@offensive_node, @offense_source_range, msg) - end + # Source buffer is required as an argument to report style violations + def source_buffer(node) + node.source_range.source_buffer + end - private + # Returns the string representation if node is of type str(plain) or dstr(interpolated) + def string_content(node) + return node.str_content if node.type == :str + node.each_child_node(:str).map(&:str_content).join("") if node.type == :dstr + end - def formula_class?(parent_class_node) - parent_class_node && parent_class_node.const_name == "Formula" - end + # Returns printable component name + def format_component(component_node) + return component_node.method_name if component_node.send_type? || component_node.block_type? + method_name(component_node) if component_node.def_type? + end - def file_path_allowed?(file_path) - paths_to_exclude = [%r{/Library/Homebrew/compat/}, - %r{/Library/Homebrew/test/}] - return true if file_path.nil? # file_path is nil when source is directly passed to the cop eg., in specs - file_path !~ Regexp.union(paths_to_exclude) - end + def problem(msg) + add_offense(@offensive_node, @offense_source_range, msg) + end + + private + + def formula_class?(parent_class_node) + parent_class_node && parent_class_node.const_name == "Formula" + end + + def file_path_allowed?(file_path) + paths_to_exclude = [%r{/Library/Homebrew/compat/}, + %r{/Library/Homebrew/test/}] + return true if file_path.nil? # file_path is nil when source is directly passed to the cop eg., in specs + file_path !~ Regexp.union(paths_to_exclude) end end end diff --git a/Library/Homebrew/rubocops/formula_desc_cop.rb b/Library/Homebrew/rubocops/formula_desc_cop.rb index 7d69a48e7..1fbf1ddbf 100644 --- a/Library/Homebrew/rubocops/formula_desc_cop.rb +++ b/Library/Homebrew/rubocops/formula_desc_cop.rb @@ -3,7 +3,7 @@ require_relative "../extend/string" module RuboCop module Cop - module Homebrew + module FormulaAuditStrict # This cop audits `desc` in Formulae # # - Checks for existence of `desc` @@ -11,7 +11,7 @@ module RuboCop # - Checks if `desc` begins with an article # - Checks for correct usage of `command-line` in `desc` # - Checks if `desc` contains the formula name - class FormulaDesc < FormulaCop + class Desc < FormulaCop def audit_formula(_node, _class_node, _parent_class_node, body) desc_call = find_node_method_by_name(body, :desc) diff --git a/Library/Homebrew/rubocops/homepage_cop.rb b/Library/Homebrew/rubocops/homepage_cop.rb new file mode 100644 index 000000000..a40c7b049 --- /dev/null +++ b/Library/Homebrew/rubocops/homepage_cop.rb @@ -0,0 +1,82 @@ +require_relative "./extend/formula_cop" + +module RuboCop + module Cop + module FormulaAudit + # This cop audits `homepage` url in Formulae + class Homepage < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, formula_class_body_node) + homepage_node = find_node_method_by_name(formula_class_body_node, :homepage) + homepage = if homepage_node + string_content(parameters(homepage_node).first) + else + "" + end + + problem "Formula should have a homepage." if homepage_node.nil? || homepage.empty? + + unless homepage =~ %r{^https?://} + problem "The homepage should start with http or https (URL is #{homepage})." + end + + case homepage + # Check for http:// GitHub homepage urls, https:// is preferred. + # Note: only check homepages that are repo pages, not *.github.com hosts + when %r{^http://github.com/} + problem "Please use https:// for #{homepage}" + + # Savannah has full SSL/TLS support but no auto-redirect. + # Doesn't apply to the download URLs, only the homepage. + when %r{^http://savannah.nongnu.org/} + problem "Please use https:// for #{homepage}" + + # Freedesktop is complicated to handle - It has SSL/TLS, but only on certain subdomains. + # To enable https Freedesktop change the URL from http://project.freedesktop.org/wiki to + # https://wiki.freedesktop.org/project_name. + # "Software" is redirected to https://wiki.freedesktop.org/www/Software/project_name + when %r{^http://((?:www|nice|libopenraw|liboil|telepathy|xorg)\.)?freedesktop\.org/(?:wiki/)?} + if homepage =~ /Software/ + problem "#{homepage} should be styled `https://wiki.freedesktop.org/www/Software/project_name`" + else + problem "#{homepage} should be styled `https://wiki.freedesktop.org/project_name`" + end + + # Google Code homepages should end in a slash + when %r{^https?://code\.google\.com/p/[^/]+[^/]$} + problem "#{homepage} should end with a slash" + + # People will run into mixed content sometimes, but we should enforce and then add + # exemptions as they are discovered. Treat mixed content on homepages as a bug. + # Justify each exemptions with a code comment so we can keep track here. + + when %r{^http://[^/]*\.github\.io/}, + %r{^http://[^/]*\.sourceforge\.io/} + problem "Please use https:// for #{homepage}" + + when %r{^http://([^/]*)\.(sf|sourceforge)\.net(/|$)} + problem "#{homepage} should be `https://#{$1}.sourceforge.io/`" + + # There's an auto-redirect here, but this mistake is incredibly common too. + # Only applies to the homepage and subdomains for now, not the FTP URLs. + when %r{^http://((?:build|cloud|developer|download|extensions|git|glade|help|library|live|nagios|news|people|projects|rt|static|wiki|www)\.)?gnome\.org} + problem "Please use https:// for #{homepage}" + + # Compact the above into this list as we're able to remove detailed notations, etc over time. + when %r{^http://[^/]*\.apache\.org}, + %r{^http://packages\.debian\.org}, + %r{^http://wiki\.freedesktop\.org/}, + %r{^http://((?:www)\.)?gnupg\.org/}, + %r{^http://ietf\.org}, + %r{^http://[^/.]+\.ietf\.org}, + %r{^http://[^/.]+\.tools\.ietf\.org}, + %r{^http://www\.gnu\.org/}, + %r{^http://code\.google\.com/}, + %r{^http://bitbucket\.org/}, + %r{^http://(?:[^/]*\.)?archive\.org} + problem "Please use https:// for #{homepage}" + end + end + end + end + end +end diff --git a/Library/Homebrew/sandbox.rb b/Library/Homebrew/sandbox.rb index 9597dafa8..b16bbde1a 100644 --- a/Library/Homebrew/sandbox.rb +++ b/Library/Homebrew/sandbox.rb @@ -5,10 +5,6 @@ class Sandbox SANDBOX_EXEC = "/usr/bin/sandbox-exec".freeze SANDBOXED_TAPS = %w[ homebrew/core - homebrew/dupes - homebrew/fuse - homebrew/devel-only - homebrew/tex ].freeze def self.available? diff --git a/Library/Homebrew/shims/scm/git b/Library/Homebrew/shims/scm/git index 82bb47c25..bfb779c25 100755 --- a/Library/Homebrew/shims/scm/git +++ b/Library/Homebrew/shims/scm/git @@ -86,7 +86,6 @@ fi case "$(lowercase "$SCM_FILE")" in git) [[ -n "$HOMEBREW_GIT" ]] && safe_exec "$(which "$HOMEBREW_GIT")" "$@" - [[ -n "$GIT" ]] && safe_exec "$(which "$GIT")" "$@" ;; svn) [[ -n "$HOMEBREW_SVN" ]] && safe_exec "$(which "$HOMEBREW_SVN")" "$@" diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index fb07f6c55..be851ca16 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -8,6 +8,7 @@ require "dependency_collector" require "utils/bottles" require "patch" require "compilers" +require "os/mac/version" class SoftwareSpec extend Forwardable @@ -117,8 +118,7 @@ class SoftwareSpec def option(name, description = "") opt = PREDEFINED_OPTIONS.fetch(name) do if name.is_a?(Symbol) - opoo "Passing arbitrary symbols to `option` is deprecated: #{name.inspect}" - puts "Symbols are reserved for future use, please pass a string instead" + odeprecated "passing arbitrary symbols (i.e. #{name.inspect}) to `option`" name = name.to_s end unless name.is_a?(String) @@ -172,7 +172,6 @@ class SoftwareSpec end def fails_with(compiler, &block) - # TODO: deprecate this in future. # odeprecated "fails_with :llvm" if compiler == :llvm compiler_failures << CompilerFailure.create(compiler, &block) end @@ -340,8 +339,8 @@ class BottleSpecification tags = collector.keys.sort_by do |tag| # Sort non-MacOS tags below MacOS tags. begin - MacOS::Version.from_symbol tag - rescue + OS::Mac::Version.from_symbol tag + rescue ArgumentError "0.#{tag}" end end diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index 99138330b..66aa10158 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -201,6 +201,10 @@ class Tap quiet = options.fetch(:quiet, false) requested_remote = options[:clone_target] || default_remote + if official? && DEPRECATED_OFFICIAL_TAPS.include?(repo) + opoo "#{name} was deprecated. This tap is now empty as all its formulae were migrated." + end + if installed? raise TapAlreadyTappedError, name unless full_clone raise TapAlreadyUnshallowError, name unless shallow? @@ -522,13 +526,11 @@ end # A specialized {Tap} class for the core formulae class CoreTap < Tap - if OS.mac? - def default_remote - "https://github.com/Homebrew/homebrew-core" - end - else - def default_remote - "https://github.com/Linuxbrew/homebrew-core" + def default_remote + if OS.mac? || ENV["HOMEBREW_FORCE_HOMEBREW_ORG"] + "https://github.com/Homebrew/homebrew-core".freeze + else + "https://github.com/Linuxbrew/homebrew-core".freeze end end diff --git a/Library/Homebrew/test/.bundle/config b/Library/Homebrew/test/.bundle/config index e451829e9..20549341c 100644 --- a/Library/Homebrew/test/.bundle/config +++ b/Library/Homebrew/test/.bundle/config @@ -1,3 +1,4 @@ --- BUNDLE_PATH: "../vendor/bundle" BUNDLE_DISABLE_SHARED_GEMS: "true" +BUNDLE_BIN: "../bin" diff --git a/Library/Homebrew/test/Gemfile.lock b/Library/Homebrew/test/Gemfile.lock index 4d4eefd7d..1f6adae93 100644 --- a/Library/Homebrew/test/Gemfile.lock +++ b/Library/Homebrew/test/Gemfile.lock @@ -2,51 +2,53 @@ GEM remote: https://rubygems.org/ specs: ast (2.3.0) - codecov (0.1.9) + codecov (0.1.10) json simplecov url diff-lcs (1.3) docile (1.1.5) - json (2.0.3) - parallel (1.10.0) - parallel_tests (2.13.0) + json (2.1.0) + parallel (1.11.2) + parallel_tests (2.14.1) parallel parser (2.4.0.0) ast (~> 2.2) powerpack (0.1.1) - rainbow (2.2.1) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.4) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) + rainbow (2.2.2) + rake + rake (12.0.0) + rspec (3.6.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-core (3.6.0) + rspec-support (~> 3.6.0) + rspec-expectations (3.6.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) + rspec-support (~> 3.6.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.5.0) + rspec-mocks (3.6.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) + rspec-support (~> 3.6.0) + rspec-support (3.6.0) rspec-wait (0.0.9) rspec (>= 3, < 4) - rubocop (0.47.1) + rubocop (0.48.1) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) - simplecov (0.13.0) + simplecov (0.14.1) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) - unicode-display_width (1.1.3) + unicode-display_width (1.2.1) url (0.3.2) PLATFORMS @@ -62,4 +64,4 @@ DEPENDENCIES simplecov BUNDLED WITH - 1.14.5 + 1.14.6 diff --git a/Library/Homebrew/test/PATH_spec.rb b/Library/Homebrew/test/PATH_spec.rb new file mode 100644 index 000000000..68233c23c --- /dev/null +++ b/Library/Homebrew/test/PATH_spec.rb @@ -0,0 +1,115 @@ +require "PATH" + +describe PATH do + describe "#initialize" do + it "can take multiple arguments" do + expect(described_class.new("/path1", "/path2")).to eq("/path1:/path2") + end + + it "can parse a mix of arrays and arguments" do + expect(described_class.new(["/path1", "/path2"], "/path3")).to eq("/path1:/path2:/path3") + end + + it "splits an existing PATH" do + expect(described_class.new("/path1:/path2")).to eq(["/path1", "/path2"]) + end + + it "removes duplicates" do + expect(described_class.new("/path1", "/path1")).to eq("/path1") + end + end + + describe "#to_ary" do + it "returns a PATH array" do + expect(described_class.new("/path1", "/path2").to_ary).to eq(["/path1", "/path2"]) + end + end + + describe "#to_str" do + it "returns a PATH string" do + expect(described_class.new("/path1", "/path2").to_str).to eq("/path1:/path2") + end + end + + describe "#prepend" do + it "prepends a path to a PATH" do + expect(described_class.new("/path1").prepend("/path2").to_str).to eq("/path2:/path1") + end + + it "removes duplicates" do + expect(described_class.new("/path1").prepend("/path1").to_str).to eq("/path1") + end + end + + describe "#append" do + it "prepends a path to a PATH" do + expect(described_class.new("/path1").append("/path2").to_str).to eq("/path1:/path2") + end + + it "removes duplicates" do + expect(described_class.new("/path1").append("/path1").to_str).to eq("/path1") + end + end + + describe "#insert" do + it "inserts a path at a given index" do + expect(described_class.new("/path1").insert(0, "/path2").to_str).to eq("/path2:/path1") + end + + it "can insert multiple paths" do + expect(described_class.new("/path1").insert(0, "/path2", "/path3")).to eq("/path2:/path3:/path1") + end + end + + describe "#include?" do + it "returns true if a path is included" do + path = described_class.new("/path1", "/path2") + expect(path).to include("/path1") + expect(path).to include("/path2") + end + + it "returns false if a path is not included" do + expect(described_class.new("/path1")).not_to include("/path2") + end + + it "returns false if the given string contains a separator" do + expect(described_class.new("/path1", "/path2")).not_to include("/path1:") + end + end + + describe "#each" do + it "loops through each path" do + enum = described_class.new("/path1", "/path2").each + + expect(enum.next).to eq("/path1") + expect(enum.next).to eq("/path2") + end + end + + describe "#select" do + it "returns an object of the same class instead of an Array" do + expect(described_class.new.select { true }).to be_a(described_class) + end + end + + describe "#reject" do + it "returns an object of the same class instead of an Array" do + expect(described_class.new.reject { true }).to be_a(described_class) + end + end + + describe "#existing" do + it "returns a new PATH without non-existent paths" do + allow(File).to receive(:directory?).with("/path1").and_return(true) + allow(File).to receive(:directory?).with("/path2").and_return(false) + + path = described_class.new("/path1", "/path2") + expect(path.existing.to_ary).to eq(["/path1"]) + expect(path.to_ary).to eq(["/path1", "/path2"]) + end + + it "returns nil instead of an empty #{described_class}" do + expect(described_class.new.existing).to be nil + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/binary_spec.rb b/Library/Homebrew/test/cask/artifact/binary_spec.rb index 69bde3438..ee62e6439 100644 --- a/Library/Homebrew/test/cask/artifact/binary_spec.rb +++ b/Library/Homebrew/test/cask/artifact/binary_spec.rb @@ -6,9 +6,7 @@ describe Hbc::Artifact::Binary, :cask do end end } - let(:expected_path) { - Hbc.binarydir.join("binary") - } + let(:expected_path) { Hbc.binarydir.join("binary") } before(:each) do Hbc.binarydir.mkpath @@ -26,15 +24,28 @@ describe Hbc::Artifact::Binary, :cask do expect(expected_path.readlink).to exist end - it "makes the binary executable" do - expect(FileUtils).to receive(:chmod).with("+x", cask.staged_path.join("binary")) + context "when the binary is not executable" do + let(:cask) { + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-non-executable-binary.rb").tap do |cask| + shutup do + InstallHelper.install_without_artifacts(cask) + end + end + } - shutup do - Hbc::Artifact::Binary.new(cask).install_phase - end + let(:expected_path) { Hbc.binarydir.join("naked_non_executable") } - expect(expected_path).to be_a_symlink - expect(expected_path.readlink).to be_executable + it "makes the binary executable" do + expect(FileUtils).to receive(:chmod) + .with("+x", cask.staged_path.join("naked_non_executable")).and_call_original + + shutup do + Hbc::Artifact::Binary.new(cask).install_phase + end + + expect(expected_path).to be_a_symlink + expect(expected_path.readlink).to be_executable + end end it "avoids clobbering an existing binary by linking over it" do diff --git a/Library/Homebrew/test/cask/cask_spec.rb b/Library/Homebrew/test/cask/cask_spec.rb index 2ab966f82..3736f3c01 100644 --- a/Library/Homebrew/test/cask/cask_spec.rb +++ b/Library/Homebrew/test/cask/cask_spec.rb @@ -85,8 +85,8 @@ describe Hbc::Cask, :cask do it "proposes a versioned metadata directory name for each instance" do cask_token = "local-caffeine" c = Hbc::CaskLoader.load(cask_token) - metadata_path = Hbc.caskroom.join(cask_token, ".metadata", c.version) - expect(c.metadata_versioned_container_path.to_s).to eq(metadata_path.to_s) + metadata_timestamped_path = Hbc.caskroom.join(cask_token, ".metadata", c.version) + expect(c.metadata_versioned_path.to_s).to eq(metadata_timestamped_path.to_s) end end diff --git a/Library/Homebrew/test/cask/cli/install_spec.rb b/Library/Homebrew/test/cask/cli/install_spec.rb index 5a40017e8..219b9522e 100644 --- a/Library/Homebrew/test/cask/cli/install_spec.rb +++ b/Library/Homebrew/test/cask/cli/install_spec.rb @@ -1,4 +1,18 @@ describe Hbc::CLI::Install, :cask do + it "displays the installation progress" do + output = Regexp.new <<-EOS.undent + ==> Downloading file:.*caffeine.zip + ==> Verifying checksum for Cask local-caffeine + ==> Installing Cask local-caffeine + ==> Moving App 'Caffeine.app' to '.*Caffeine.app'. + .*local-caffeine was successfully installed! + EOS + + expect { + Hbc::CLI::Install.run("local-caffeine") + }.to output(output).to_stdout + end + it "allows staging and activation of multiple Casks at once" do shutup do Hbc::CLI::Install.run("local-transmission", "local-caffeine") diff --git a/Library/Homebrew/test/cask/cli/reinstall_spec.rb b/Library/Homebrew/test/cask/cli/reinstall_spec.rb index e573a3470..8885fa199 100644 --- a/Library/Homebrew/test/cask/cli/reinstall_spec.rb +++ b/Library/Homebrew/test/cask/cli/reinstall_spec.rb @@ -1,4 +1,27 @@ describe Hbc::CLI::Reinstall, :cask do + it "displays the reinstallation progress" do + caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + + shutup do + Hbc::Installer.new(caffeine).install + end + + output = Regexp.new <<-EOS.undent + ==> Downloading file:.*caffeine.zip + Already downloaded: .*local-caffeine--1.2.3.zip + ==> Verifying checksum for Cask local-caffeine + ==> Uninstalling Cask local-caffeine + ==> Removing App '.*Caffeine.app'. + ==> Installing Cask local-caffeine + ==> Moving App 'Caffeine.app' to '.*Caffeine.app'. + .*local-caffeine was successfully installed! + EOS + + expect { + Hbc::CLI::Reinstall.run("local-caffeine") + }.to output(output).to_stdout + end + it "allows reinstalling a Cask" do shutup do Hbc::CLI::Install.run("local-transmission") diff --git a/Library/Homebrew/test/cask/cli/search_spec.rb b/Library/Homebrew/test/cask/cli/search_spec.rb index 9843a6de6..00fcf7382 100644 --- a/Library/Homebrew/test/cask/cli/search_spec.rb +++ b/Library/Homebrew/test/cask/cli/search_spec.rb @@ -3,7 +3,7 @@ describe Hbc::CLI::Search, :cask do expect { Hbc::CLI::Search.run("local") }.to output(<<-EOS.undent).to_stdout - ==> Partial matches + ==> Partial Matches local-caffeine local-transmission EOS @@ -42,13 +42,13 @@ describe Hbc::CLI::Search, :cask do it "accepts a regexp argument" do expect { Hbc::CLI::Search.run("/^local-c[a-z]ffeine$/") - }.to output("==> Regexp matches\nlocal-caffeine\n").to_stdout + }.to output("==> Regexp Matches\nlocal-caffeine\n").to_stdout end it "Returns both exact and partial matches" do expect { Hbc::CLI::Search.run("test-opera") - }.to output(/^==> Exact match\ntest-opera\n==> Partial matches\ntest-opera-mail/).to_stdout + }.to output(/^==> Exact Match\ntest-opera\n==> Partial Matches\ntest-opera-mail/).to_stdout end it "does not search the Tap name" do diff --git a/Library/Homebrew/test/cask/cli/style_spec.rb b/Library/Homebrew/test/cask/cli/style_spec.rb index ca17c5e46..d41636beb 100644 --- a/Library/Homebrew/test/cask/cli/style_spec.rb +++ b/Library/Homebrew/test/cask/cli/style_spec.rb @@ -81,7 +81,7 @@ describe Hbc::CLI::Style, :cask do end context "version" do - it "matches `HOMEBREW_RUBOCOP_VERSION`" do + it "matches `HOMEBREW_RUBOCOP_VERSION`", :needs_network do stdout, status = Open3.capture2("gem", "dependency", "rubocop-cask", "--version", HOMEBREW_RUBOCOP_CASK_VERSION, "--pipe", "--remote") expect(status).to be_a_success diff --git a/Library/Homebrew/test/cask/cli/uninstall_spec.rb b/Library/Homebrew/test/cask/cli/uninstall_spec.rb index fb196ee72..4089c47b4 100644 --- a/Library/Homebrew/test/cask/cli/uninstall_spec.rb +++ b/Library/Homebrew/test/cask/cli/uninstall_spec.rb @@ -1,4 +1,21 @@ describe Hbc::CLI::Uninstall, :cask do + it "displays the uninstallation progress" do + caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + + shutup do + Hbc::Installer.new(caffeine).install + end + + output = Regexp.new <<-EOS.undent + ==> Uninstalling Cask local-caffeine + ==> Removing App '.*Caffeine.app'. + EOS + + expect { + Hbc::CLI::Uninstall.run("local-caffeine") + }.to output(output).to_stdout + end + it "shows an error when a bad Cask is provided" do expect { Hbc::CLI::Uninstall.run("notacask") @@ -13,7 +30,9 @@ describe Hbc::CLI::Uninstall, :cask do it "tries anyway on a non-present Cask when --force is given" do expect { - Hbc::CLI::Uninstall.run("local-caffeine", "--force") + shutup do + Hbc::CLI::Uninstall.run("local-caffeine", "--force") + end }.not_to raise_error end diff --git a/Library/Homebrew/test/cask/installer_spec.rb b/Library/Homebrew/test/cask/installer_spec.rb index 7dd5b2bda..59d61bbdd 100644 --- a/Library/Homebrew/test/cask/installer_spec.rb +++ b/Library/Homebrew/test/cask/installer_spec.rb @@ -336,9 +336,8 @@ describe Hbc::Installer, :cask do Hbc::Installer.new(caffeine).install end - m_path = caffeine.metadata_path(:now, true) - expect(caffeine.metadata_path(:now, false)).to eq(m_path) - expect(caffeine.metadata_path(:latest)).to eq(m_path) + m_path = caffeine.metadata_timestamped_path(timestamp: :now, create: true) + expect(caffeine.metadata_timestamped_path(timestamp: :latest)).to eq(m_path) end it "generates and finds a metadata subdirectory for an installed Cask" do @@ -349,9 +348,8 @@ describe Hbc::Installer, :cask do end subdir_name = "Casks" - m_subdir = caffeine.metadata_subdir(subdir_name, :now, true) - expect(caffeine.metadata_subdir(subdir_name, :now, false)).to eq(m_subdir) - expect(caffeine.metadata_subdir(subdir_name, :latest)).to eq(m_subdir) + m_subdir = caffeine.metadata_subdir(subdir_name, timestamp: :now, create: true) + expect(caffeine.metadata_subdir(subdir_name, timestamp: :latest)).to eq(m_subdir) end end diff --git a/Library/Homebrew/test/cask/pkg_spec.rb b/Library/Homebrew/test/cask/pkg_spec.rb index 9930cd00f..e507ceda0 100644 --- a/Library/Homebrew/test/cask/pkg_spec.rb +++ b/Library/Homebrew/test/cask/pkg_spec.rb @@ -36,7 +36,12 @@ describe Hbc::Pkg, :cask do it "forgets the pkg" do allow(fake_system_command).to receive(:run!).with( "/usr/sbin/pkgutil", - args: ["--export-plist", "my.fake.pkg"], + args: ["--pkg-info-plist", "my.fake.pkg"], + ).and_return(empty_response) + + expect(fake_system_command).to receive(:run!).with( + "/usr/sbin/pkgutil", + args: ["--files", "my.fake.pkg"], ).and_return(empty_response) expect(fake_system_command).to receive(:run!).with( @@ -139,7 +144,7 @@ describe Hbc::Pkg, :cask do expect(fake_system_command).to receive(:run!).with( "/usr/sbin/pkgutil", - args: ["--export-plist", pkg_id], + args: ["--pkg-info-plist", pkg_id], ).and_return( Hbc::SystemCommand::Result.new(nil, pkg_info_plist, nil, 0), ) diff --git a/Library/Homebrew/test/cmd/bundle_spec.rb b/Library/Homebrew/test/cmd/bundle_spec.rb index 13f13485c..286ddba97 100644 --- a/Library/Homebrew/test/cmd/bundle_spec.rb +++ b/Library/Homebrew/test/cmd/bundle_spec.rb @@ -1,6 +1,6 @@ describe "brew bundle", :integration_test, :needs_test_cmd_taps do describe "check" do - it "checks if a Brewfile's dependencies are satisfied" do + it "checks if a Brewfile's dependencies are satisfied", :needs_network do setup_remote_tap "homebrew/bundle" HOMEBREW_REPOSITORY.cd do diff --git a/Library/Homebrew/test/cmd/install_spec.rb b/Library/Homebrew/test/cmd/install_spec.rb index c1240a30e..b819c17be 100644 --- a/Library/Homebrew/test/cmd/install_spec.rb +++ b/Library/Homebrew/test/cmd/install_spec.rb @@ -77,7 +77,7 @@ describe "brew install", :integration_test do EOS expect { brew "install", "testball1" } - .to output(/first `brew unlink testball1`/).to_stderr + .to output(/`brew upgrade testball1`/).to_stderr .and not_to_output.to_stdout .and be_a_failure @@ -217,10 +217,9 @@ describe "brew install", :integration_test do depends_on NonFatalRequirement EOS - # FIXME: This should output to STDERR. expect { brew "install", "testball1" } - .to output(/NonFatalRequirement unsatisfied!/).to_stdout - .and not_to_output.to_stderr + .to output(/NonFatalRequirement unsatisfied!/).to_stderr + .and output(/built in/).to_stdout .and be_a_success end @@ -234,10 +233,9 @@ describe "brew install", :integration_test do depends_on FatalRequirement EOS - # FIXME: This should output to STDERR. expect { brew "install", "testball1" } - .to output(/FatalRequirement unsatisfied!/).to_stdout - .and output(/An unsatisfied requirement failed this build./).to_stderr + .to output(/FatalRequirement unsatisfied!/).to_stderr + .and not_to_output.to_stdout .and be_a_failure end end diff --git a/Library/Homebrew/test/cmd/search_remote_tap_spec.rb b/Library/Homebrew/test/cmd/search_remote_tap_spec.rb index 99c2cc20b..b0beb122c 100644 --- a/Library/Homebrew/test/cmd/search_remote_tap_spec.rb +++ b/Library/Homebrew/test/cmd/search_remote_tap_spec.rb @@ -1,19 +1,21 @@ require "cmd/search" describe Homebrew do - specify "#search_tap" do + specify "#search_taps" do json_response = { - "tree" => [ + "items" => [ { - "path" => "Formula/not-a-formula.rb", - "type" => "blob", + "path" => "Formula/some-formula.rb", + "repository" => { + "full_name" => "Homebrew/homebrew-foo", + }, }, ], } allow(GitHub).to receive(:open).and_yield(json_response) - expect(described_class.search_tap("homebrew", "not-a-tap", "not-a-formula")) - .to eq(["homebrew/not-a-tap/not-a-formula"]) + expect(described_class.search_taps("some-formula")) + .to match(["homebrew/foo/some-formula"]) end end diff --git a/Library/Homebrew/test/compiler_selector_spec.rb b/Library/Homebrew/test/compiler_selector_spec.rb index 0f6f6b5f2..18efbfd42 100644 --- a/Library/Homebrew/test/compiler_selector_spec.rb +++ b/Library/Homebrew/test/compiler_selector_spec.rb @@ -3,7 +3,7 @@ require "software_spec" describe CompilerSelector do subject { described_class.new(software_spec, versions, compilers) } - let(:compilers) { [:clang, :gcc, :llvm, :gnu] } + let(:compilers) { [:clang, :gcc, :gnu] } let(:software_spec) { SoftwareSpec.new } let(:cc) { :clang } let(:versions) do @@ -28,7 +28,6 @@ describe CompilerSelector do describe "#compiler" do it "raises an error if no matching compiler can be found" do software_spec.fails_with(:clang) - software_spec.fails_with(:llvm) software_spec.fails_with(:gcc) software_spec.fails_with(gcc: "4.8") software_spec.fails_with(gcc: "4.7") @@ -45,11 +44,6 @@ describe CompilerSelector do expect(subject.compiler).to eq(:gcc) end - it "returns clang if it fails with llvm" do - software_spec.fails_with(:llvm) - expect(subject.compiler).to eq(:clang) - end - it "returns clang if it fails with gcc" do software_spec.fails_with(:gcc) expect(subject.compiler).to eq(:clang) @@ -68,13 +62,11 @@ describe CompilerSelector do it "returns gcc if it fails with clang and llvm" do software_spec.fails_with(:clang) - software_spec.fails_with(:llvm) expect(subject.compiler).to eq(:gcc) end it "returns clang if it fails with gcc and llvm" do software_spec.fails_with(:gcc) - software_spec.fails_with(:llvm) expect(subject.compiler).to eq(:clang) end @@ -87,7 +79,6 @@ describe CompilerSelector do example "returns a lower version of gcc if it fails with the highest version" do software_spec.fails_with(:clang) software_spec.fails_with(:gcc) - software_spec.fails_with(:llvm) software_spec.fails_with(gcc: "4.8") expect(subject.compiler).to eq("gcc-4.7") end @@ -102,7 +93,6 @@ describe CompilerSelector do allow(versions).to receive(:gcc_build_version).and_return(Version::NULL) software_spec.fails_with(:clang) - software_spec.fails_with(:llvm) software_spec.fails_with(gcc: "4.8") software_spec.fails_with(gcc: "4.7") diff --git a/Library/Homebrew/test/dev-cmd/audit_spec.rb b/Library/Homebrew/test/dev-cmd/audit_spec.rb index a6bb22837..b07ffaadc 100644 --- a/Library/Homebrew/test/dev-cmd/audit_spec.rb +++ b/Library/Homebrew/test/dev-cmd/audit_spec.rb @@ -5,6 +5,13 @@ RSpec::Matchers.alias_matcher :have_data, :be_data RSpec::Matchers.alias_matcher :have_end, :be_end RSpec::Matchers.alias_matcher :have_trailing_newline, :be_trailing_newline +module Count + def self.increment + @count ||= 0 + @count += 1 + end +end + describe FormulaText do let(:dir) { mktmpdir } @@ -141,100 +148,6 @@ describe FormulaAuditor do fa.audit_file expect(fa.problems).to eq([]) end - - specify "strict: ordering issue" do - fa = formula_auditor "foo", <<-EOS.undent, strict: true - class Foo < Formula - url "http://example.com/foo-1.0.tgz" - homepage "http://example.com" - end - EOS - - fa.audit_file - expect(fa.problems) - .to eq(["`homepage` (line 3) should be put before `url` (line 2)"]) - end - - specify "strict: resource placement" do - fa = formula_auditor "foo", <<-EOS.undent, strict: true - class Foo < Formula - url "https://example.com/foo-1.0.tgz" - - resource "foo2" do - url "https://example.com/foo-2.0.tgz" - end - - depends_on "openssl" - end - EOS - - fa.audit_file - expect(fa.problems) - .to eq(["`depends_on` (line 8) should be put before `resource` (line 4)"]) - end - - specify "strict: plist placement" do - fa = formula_auditor "foo", <<-EOS.undent, strict: true - class Foo < Formula - url "https://example.com/foo-1.0.tgz" - - test do - expect(shell_output("./dogs")).to match("Dogs are terrific") - end - - def plist - end - end - EOS - - fa.audit_file - expect(fa.problems) - .to eq(["`plist block` (line 8) should be put before `test block` (line 4)"]) - end - - specify "strict: url outside of stable block" do - fa = formula_auditor "foo", <<-EOS.undent, strict: true - class Foo < Formula - url "http://example.com/foo-1.0.tgz" - stable do - # stuff - end - end - EOS - - fa.audit_file - expect(fa.problems).to eq(["`url` should be put inside `stable block`"]) - end - - specify "strict: head and head do" do - fa = formula_auditor "foo", <<-EOS.undent, strict: true - class Foo < Formula - head "http://example.com/foo.git" - head do - # stuff - end - end - EOS - - fa.audit_file - expect(fa.problems).to eq(["Should not have both `head` and `head do`"]) - end - - specify "strict: bottle and bottle do" do - fa = formula_auditor "foo", <<-EOS.undent, strict: true - class Foo < Formula - url "http://example.com/foo-1.0.tgz" - bottle do - # bottles go here - end - bottle :unneeded - end - EOS - - fa.audit_file - expect(fa.problems) - .to eq(["Should not have `bottle :unneeded/:disable` and `bottle do`"]) - end end describe "#audit_class" do @@ -303,7 +216,7 @@ describe FormulaAuditor do end end - describe "#audit_line" do + describe "#line_problems" do specify "pkgshare" do fa = formula_auditor "foo", <<-EOS.undent, strict: true class Foo < Formula @@ -311,25 +224,25 @@ describe FormulaAuditor do end EOS - fa.audit_line 'ohai "#{share}/foo"', 3 + fa.line_problems 'ohai "#{share}/foo"', 3 expect(fa.problems.shift).to eq("Use \#{pkgshare} instead of \#{share}/foo") - fa.audit_line 'ohai "#{share}/foo/bar"', 3 + fa.line_problems 'ohai "#{share}/foo/bar"', 3 expect(fa.problems.shift).to eq("Use \#{pkgshare} instead of \#{share}/foo") - fa.audit_line 'ohai share/"foo"', 3 + fa.line_problems 'ohai share/"foo"', 3 expect(fa.problems.shift).to eq('Use pkgshare instead of (share/"foo")') - fa.audit_line 'ohai share/"foo/bar"', 3 + fa.line_problems 'ohai share/"foo/bar"', 3 expect(fa.problems.shift).to eq('Use pkgshare instead of (share/"foo")') - fa.audit_line 'ohai "#{share}/foo-bar"', 3 + fa.line_problems 'ohai "#{share}/foo-bar"', 3 expect(fa.problems).to eq([]) - fa.audit_line 'ohai share/"foo-bar"', 3 + fa.line_problems 'ohai share/"foo-bar"', 3 expect(fa.problems).to eq([]) - fa.audit_line 'ohai share/"bar"', 3 + fa.line_problems 'ohai share/"bar"', 3 expect(fa.problems).to eq([]) end @@ -344,11 +257,11 @@ describe FormulaAuditor do end EOS - fa.audit_line 'ohai "#{share}/foolibc++"', 3 + fa.line_problems 'ohai "#{share}/foolibc++"', 3 expect(fa.problems.shift) .to eq("Use \#{pkgshare} instead of \#{share}/foolibc++") - fa.audit_line 'ohai share/"foolibc++"', 3 + fa.line_problems 'ohai share/"foolibc++"', 3 expect(fa.problems.shift) .to eq('Use pkgshare instead of (share/"foolibc++")') end @@ -360,7 +273,7 @@ describe FormulaAuditor do end EOS - fa.audit_line "class Foo<Formula", 1 + fa.line_problems "class Foo<Formula", 1 expect(fa.problems.shift) .to eq("Use a space in class inheritance: class Foo < Formula") end @@ -368,10 +281,10 @@ describe FormulaAuditor do specify "default template" do fa = formula_auditor "foo", "class Foo < Formula; url '/foo-1.0.tgz'; end" - fa.audit_line '# system "cmake", ".", *std_cmake_args', 3 + fa.line_problems '# system "cmake", ".", *std_cmake_args', 3 expect(fa.problems.shift).to eq("Commented cmake call found") - fa.audit_line "# PLEASE REMOVE", 3 + fa.line_problems "# PLEASE REMOVE", 3 expect(fa.problems.shift).to eq("Please remove default template comments") end end @@ -409,77 +322,66 @@ describe FormulaAuditor do .to eq(["Don't recommend setuid in the caveats, suggest sudo instead."]) end - describe "#audit_homepage" do - specify "homepage URLs" do - fa = formula_auditor "foo", <<-EOS.undent, online: true + describe "#audit_keg_only_style" do + specify "keg_only_needs_downcasing" do + fa = formula_auditor "foo", <<-EOS.undent, strict: true class Foo < Formula - homepage "ftp://example.com/foo" url "http://example.com/foo-1.0.tgz" + + keg_only "Because why not" end EOS - fa.audit_homepage + fa.audit_keg_only_style expect(fa.problems) - .to eq(["The homepage should start with http or https (URL is #{fa.formula.homepage})."]) - - formula_homepages = { - "bar" => "http://www.freedesktop.org/wiki/bar", - "baz" => "http://www.freedesktop.org/wiki/Software/baz", - "qux" => "https://code.google.com/p/qux", - "quux" => "http://github.com/quux", - "corge" => "http://savannah.nongnu.org/corge", - "grault" => "http://grault.github.io/", - "garply" => "http://www.gnome.org/garply", - "sf1" => "http://foo.sourceforge.net/", - "sf2" => "http://foo.sourceforge.net", - "sf3" => "http://foo.sf.net/", - "sf4" => "http://foo.sourceforge.io/", - "waldo" => "http://www.gnu.org/waldo", - } - - formula_homepages.each do |name, homepage| - fa = formula_auditor name, <<-EOS.undent - class #{Formulary.class_s(name)} < Formula - homepage "#{homepage}" - url "http://example.com/#{name}-1.0.tgz" - end - EOS + .to eq(["'Because' from the keg_only reason should be 'because'.\n"]) + end - fa.audit_homepage - if homepage =~ %r{http:\/\/www\.freedesktop\.org} - if homepage =~ /Software/ - expect(fa.problems.first).to match( - "#{homepage} should be styled " \ - "`https://wiki.freedesktop.org/www/Software/project_name`", - ) - else - expect(fa.problems.first).to match( - "#{homepage} should be styled " \ - "`https://wiki.freedesktop.org/project_name`", - ) - end - elsif homepage =~ %r{https:\/\/code\.google\.com} - expect(fa.problems.first) - .to match("#{homepage} should end with a slash") - elsif homepage =~ /foo\.(sf|sourceforge)\.net/ - expect(fa.problems.first) - .to match("#{homepage} should be `https://foo.sourceforge.io/`") - else - expect(fa.problems.first) - .to match("Please use https:// for #{homepage}") + specify "keg_only_redundant_period" do + fa = formula_auditor "foo", <<-EOS.undent, strict: true + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + + keg_only "because this line ends in a period." end - end + EOS + + fa.audit_keg_only_style + expect(fa.problems) + .to eq(["keg_only reason should not end with a period."]) + end + + specify "keg_only_handles_block_correctly" do + fa = formula_auditor "foo", <<-EOS.undent, strict: true + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + + keg_only <<-EOF.undent + this line starts with a lowercase word. + + This line does not but that shouldn't be a + problem + EOF + end + EOS + + fa.audit_keg_only_style + expect(fa.problems) + .to eq([]) end - specify "missing homepage" do - fa = formula_auditor "foo", <<-EOS.undent, online: true + specify "keg_only_handles_whitelist_correctly" do + fa = formula_auditor "foo", <<-EOS.undent, strict: true class Foo < Formula url "http://example.com/foo-1.0.tgz" + + keg_only "Apple ships foo in the CLT package" end EOS - fa.audit_homepage - expect(fa.problems.first).to match("Formula should have a homepage.") + fa.audit_keg_only_style + expect(fa.problems) + .to eq([]) end end @@ -517,5 +419,203 @@ describe FormulaAuditor do expect(fa.problems.first) .to match('xcodebuild should be passed an explicit "SYMROOT"') end + + specify "disallow go get usage" do + fa = formula_auditor "foo", <<-EOS.undent + class Foo <Formula + url "http://example.com/foo-1.0.tgz" + + def install + system "go", "get", "bar" + end + end + EOS + fa.audit_text + expect(fa.problems.first) + .to match("Formulae should not use `go get`. If non-vendored resources are required use `go_resource`s.") + end + end + + describe "#audit_revision_and_version_scheme" do + let(:origin_tap_path) { Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" } + let(:formula_subpath) { "Formula/foo#{@foo_version}.rb" } + let(:origin_formula_path) { origin_tap_path/formula_subpath } + let(:tap_path) { Tap::TAP_DIRECTORY/"homebrew/homebrew-bar" } + let(:formula_path) { tap_path/formula_subpath } + + before(:each) do + @foo_version = Count.increment + + origin_formula_path.write <<-EOS.undent + class Foo#{@foo_version} < Formula + url "https://example.com/foo-1.0.tar.gz" + revision 2 + version_scheme 1 + end + EOS + + origin_tap_path.mkpath + origin_tap_path.cd do + shutup do + system "git", "init" + system "git", "add", "--all" + system "git", "commit", "-m", "init" + end + end + + tap_path.mkpath + tap_path.cd do + shutup do + system "git", "clone", origin_tap_path, "." + end + end + end + + subject do + fa = described_class.new(Formulary.factory(formula_path)) + fa.audit_revision_and_version_scheme + fa.problems.first + end + + def formula_gsub(before, after = "") + text = formula_path.read + text.gsub! before, after + formula_path.unlink + formula_path.write text + end + + def formula_gsub_commit(before, after = "") + text = origin_formula_path.read + text.gsub!(before, after) + origin_formula_path.unlink + origin_formula_path.write text + + origin_tap_path.cd do + shutup do + system "git", "commit", "-am", "commit" + end + end + + tap_path.cd do + shutup do + system "git", "fetch" + system "git", "reset", "--hard", "origin/master" + end + end + end + + context "revisions" do + context "should not be removed when first committed above 0" do + it { is_expected.to be_nil } + end + + context "should not decrease with the same version" do + before { formula_gsub_commit "revision 2", "revision 1" } + + it { is_expected.to match("revision should not decrease (from 2 to 1)") } + end + + context "should not be removed with the same version" do + before { formula_gsub_commit "revision 2" } + + it { is_expected.to match("revision should not decrease (from 2 to 0)") } + end + + context "should not decrease with the same, uncommitted version" do + before { formula_gsub "revision 2", "revision 1" } + + it { is_expected.to match("revision should not decrease (from 2 to 1)") } + end + + context "should be removed with a newer version" do + before { formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz" } + + it { is_expected.to match("'revision 2' should be removed") } + end + + context "should not warn on an newer version revision removal" do + before do + formula_gsub_commit "revision 2", "" + formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz" + end + + it { is_expected.to be_nil } + end + + context "should only increment by 1 with an uncommitted version" do + before do + formula_gsub "foo-1.0.tar.gz", "foo-1.1.tar.gz" + formula_gsub "revision 2", "revision 4" + end + + it { is_expected.to match("revisions should only increment by 1") } + end + + context "should not warn on past increment by more than 1" do + before do + formula_gsub_commit "revision 2", "# no revision" + formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz" + formula_gsub_commit "# no revision", "revision 3" + end + + it { is_expected.to be_nil } + end + end + + context "version_schemes" do + context "should not decrease with the same version" do + before { formula_gsub_commit "version_scheme 1" } + + it { is_expected.to match("version_scheme should not decrease (from 1 to 0)") } + end + + context "should not decrease with a new version" do + before do + formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz" + formula_gsub_commit "version_scheme 1", "" + formula_gsub_commit "revision 2", "" + end + + it { is_expected.to match("version_scheme should not decrease (from 1 to 0)") } + end + + context "should only increment by 1" do + before do + formula_gsub_commit "version_scheme 1", "# no version_scheme" + formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz" + formula_gsub_commit "revision 2", "" + formula_gsub_commit "# no version_scheme", "version_scheme 3" + end + + it { is_expected.to match("version_schemes should only increment by 1") } + end + end + + context "versions" do + context "uncommitted should not decrease" do + before { formula_gsub "foo-1.0.tar.gz", "foo-0.9.tar.gz" } + + it { is_expected.to match("stable version should not decrease (from 1.0 to 0.9)") } + end + + context "committed can decrease" do + before do + formula_gsub_commit "revision 2" + formula_gsub_commit "foo-1.0.tar.gz", "foo-0.9.tar.gz" + end + + it { is_expected.to be_nil } + end + + context "can decrease with version_scheme increased" do + before do + formula_gsub "revision 2" + formula_gsub "foo-1.0.tar.gz", "foo-0.9.tar.gz" + formula_gsub "version_scheme 1", "version_scheme 2" + end + + it { is_expected.to be_nil } + end + end end end diff --git a/Library/Homebrew/test/dev-cmd/pull_spec.rb b/Library/Homebrew/test/dev-cmd/pull_spec.rb index 3c0108df2..2ebe144bb 100644 --- a/Library/Homebrew/test/dev-cmd/pull_spec.rb +++ b/Library/Homebrew/test/dev-cmd/pull_spec.rb @@ -6,9 +6,7 @@ describe "brew pull", :integration_test do .and be_a_failure end - it "fetches a patch from a GitHub commit or pull request and applies it" do - skip "Requires network connection." if ENV["HOMEBREW_NO_GITHUB_API"] - + it "fetches a patch from a GitHub commit or pull request and applies it", :needs_network do CoreTap.instance.path.cd do shutup do system "git", "init" diff --git a/Library/Homebrew/test/diagnostic_spec.rb b/Library/Homebrew/test/diagnostic_spec.rb index 59560127c..c4373671e 100644 --- a/Library/Homebrew/test/diagnostic_spec.rb +++ b/Library/Homebrew/test/diagnostic_spec.rb @@ -7,7 +7,7 @@ describe Homebrew::Diagnostic::Checks do end specify "#check_path_for_trailing_slashes" do - ENV["PATH"] += File::PATH_SEPARATOR + "/foo/bar/" + ENV["HOMEBREW_PATH"] += File::PATH_SEPARATOR + "/foo/bar/" expect(subject.check_path_for_trailing_slashes) .to match("Some directories in your path end in a slash") end @@ -41,6 +41,20 @@ describe Homebrew::Diagnostic::Checks do end end + specify "#check_access_lock_dir" do + begin + prev_mode = HOMEBREW_LOCK_DIR.stat.mode + mode = HOMEBREW_LOCK_DIR.stat.mode & 0777 + HOMEBREW_LOCK_DIR.chmod 0555 + expect(HOMEBREW_LOCK_DIR.stat.mode).not_to eq(prev_mode) + + expect(subject.check_access_lock_dir) + .to match("#{HOMEBREW_LOCK_DIR} isn't writable.") + ensure + HOMEBREW_LOCK_DIR.chmod mode + end + end + specify "#check_access_logs" do begin mode = HOMEBREW_LOGS.stat.mode & 0777 @@ -110,8 +124,9 @@ describe Homebrew::Diagnostic::Checks do specify "#check_user_path_3" do begin sbin = HOMEBREW_PREFIX/"sbin" - ENV["PATH"] = "#{HOMEBREW_PREFIX}/bin#{File::PATH_SEPARATOR}" + - ENV["PATH"].gsub(/(?:^|#{Regexp.escape(File::PATH_SEPARATOR)})#{Regexp.escape(sbin)}/, "") + ENV["HOMEBREW_PATH"] = + "#{HOMEBREW_PREFIX}/bin#{File::PATH_SEPARATOR}" + + ENV["HOMEBREW_PATH"].gsub(/(?:^|#{Regexp.escape(File::PATH_SEPARATOR)})#{Regexp.escape(sbin)}/, "") (sbin/"something").mkpath expect(subject.check_user_path_1).to be nil @@ -137,7 +152,9 @@ describe Homebrew::Diagnostic::Checks do file = "#{path}/foo-config" FileUtils.touch file FileUtils.chmod 0755, file - ENV["PATH"] = "#{path}#{File::PATH_SEPARATOR}#{ENV["PATH"]}" + ENV["HOMEBREW_PATH"] = + ENV["PATH"] = + "#{path}#{File::PATH_SEPARATOR}#{ENV["PATH"]}" expect(subject.check_for_config_scripts) .to match('"config" scripts exist') diff --git a/Library/Homebrew/test/formula_spec.rb b/Library/Homebrew/test/formula_spec.rb index 2309c36fb..1f98ca525 100644 --- a/Library/Homebrew/test/formula_spec.rb +++ b/Library/Homebrew/test/formula_spec.rb @@ -10,7 +10,6 @@ RSpec::Matchers.alias_matcher :supersede_an_installed_formula, :be_supersedes_an RSpec::Matchers.alias_matcher :have_changed_alias, :be_alias_changed RSpec::Matchers.alias_matcher :have_option_defined, :be_option_defined -RSpec::Matchers.alias_matcher :have_post_install_defined, :be_post_install_defined RSpec::Matchers.alias_matcher :have_test_defined, :be_test_defined RSpec::Matchers.alias_matcher :pour_bottle, :be_pour_bottle @@ -624,23 +623,6 @@ describe Formula do expect(f.desc).to eq("a formula") end - specify "#post_install_defined?" do - f1 = formula do - url "foo-1.0" - - def post_install - # do nothing - end - end - - f2 = formula do - url "foo-1.0" - end - - expect(f1).to have_post_install_defined - expect(f2).not_to have_post_install_defined - end - specify "#test_defined?" do f1 = formula do url "foo-1.0" diff --git a/Library/Homebrew/test/gpg2_requirement_spec.rb b/Library/Homebrew/test/gpg2_requirement_spec.rb index d7767abd3..a5501c84e 100644 --- a/Library/Homebrew/test/gpg2_requirement_spec.rb +++ b/Library/Homebrew/test/gpg2_requirement_spec.rb @@ -9,7 +9,7 @@ describe GPG2Requirement do ENV["PATH"] = dir/"bin" (dir/"bin/gpg").write <<-EOS.undent #!/bin/bash - echo 2.0.30 + echo 2.1.20 EOS FileUtils.chmod 0755, dir/"bin/gpg" diff --git a/Library/Homebrew/test/gpg_spec.rb b/Library/Homebrew/test/gpg_spec.rb index aa00d79f5..160e55379 100644 --- a/Library/Homebrew/test/gpg_spec.rb +++ b/Library/Homebrew/test/gpg_spec.rb @@ -13,7 +13,12 @@ describe Gpg do shutup do subject.create_test_key(dir) end - expect(dir/".gnupg/secring.gpg").to exist + + if subject.version == Version.create("2.0") + expect(dir/".gnupg/secring.gpg").to be_a_file + else + expect(dir/".gnupg/pubring.kbx").to be_a_file + end end end end diff --git a/Library/Homebrew/test/hardware_spec.rb b/Library/Homebrew/test/hardware_spec.rb index c5f8daf4e..de8d77e68 100644 --- a/Library/Homebrew/test/hardware_spec.rb +++ b/Library/Homebrew/test/hardware_spec.rb @@ -36,5 +36,52 @@ module Hardware ).to include(described_class.family) end end + + describe "::can_run?" do + it "reports that Intel machines can run Intel executables" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(Hardware::CPU).to receive(:bits).and_return 64 + expect(Hardware::CPU.can_run?(:i386)).to be true + expect(Hardware::CPU.can_run?(:x86_64)).to be true + end + + it "reports that PowerPC machines can run PowerPC executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + allow(Hardware::CPU).to receive(:bits).and_return 64 + expect(Hardware::CPU.can_run?(:ppc)).to be true + expect(Hardware::CPU.can_run?(:ppc64)).to be true + end + + it "reports that 32-bit Intel machines can't run x86_64 executables" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(Hardware::CPU).to receive(:bits).and_return 32 + expect(Hardware::CPU.can_run?(:x86_64)).to be false + end + + it "reports that 32-bit PowerPC machines can't run ppc64 executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + allow(Hardware::CPU).to receive(:bits).and_return 32 + expect(Hardware::CPU.can_run?(:ppc64)).to be false + end + + it "identifies that Intel and PowerPC machines can't run each others' executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + expect(Hardware::CPU.can_run?(:i386)).to be false + expect(Hardware::CPU.can_run?(:x86_64)).to be false + + allow(Hardware::CPU).to receive(:type).and_return :intel + expect(Hardware::CPU.can_run?(:ppc)).to be false + expect(Hardware::CPU.can_run?(:ppc64)).to be false + end + + it "returns false for unknown CPU types" do + allow(Hardware::CPU).to receive(:type).and_return :dunno + expect(Hardware::CPU.can_run?(:i386)).to be false + end + + it "returns false for unknown arches" do + expect(Hardware::CPU.can_run?(:blah)).to be false + end + end end end diff --git a/Library/Homebrew/test/missing_formula_spec.rb b/Library/Homebrew/test/missing_formula_spec.rb index 215cf17f7..69bb3e70d 100644 --- a/Library/Homebrew/test/missing_formula_spec.rb +++ b/Library/Homebrew/test/missing_formula_spec.rb @@ -139,7 +139,7 @@ describe Homebrew::MissingFormula do end context "::deleted_reason" do - subject { described_class.deleted_reason(formula) } + subject { described_class.deleted_reason(formula, silent: true) } before do Tap.clear_cache diff --git a/Library/Homebrew/test/os/linux/osxfuse_requirement_spec.rb b/Library/Homebrew/test/os/linux/osxfuse_requirement_spec.rb new file mode 100644 index 000000000..c45af2fa7 --- /dev/null +++ b/Library/Homebrew/test/os/linux/osxfuse_requirement_spec.rb @@ -0,0 +1,9 @@ +require "requirements/osxfuse_requirement" + +describe OsxfuseRequirement do + subject { described_class.new([]) } + + describe "#message" do + its(:message) { is_expected.to match("libfuse is required to install this formula") } + end +end diff --git a/Library/Homebrew/test/os/mac/hardware_spec.rb b/Library/Homebrew/test/os/mac/hardware_spec.rb new file mode 100644 index 000000000..fa577ba7d --- /dev/null +++ b/Library/Homebrew/test/os/mac/hardware_spec.rb @@ -0,0 +1,56 @@ +require "hardware" +require "extend/os/mac/hardware/cpu" + +describe Hardware::CPU do + describe "::can_run?" do + it "reports that Intel Macs can run Intel executables" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(Hardware::CPU).to receive(:bits).and_return 64 + expect(Hardware::CPU.can_run?(:i386)).to be true + expect(Hardware::CPU.can_run?(:x86_64)).to be true + end + + it "reports that PowerPC Macs can run PowerPC executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + allow(Hardware::CPU).to receive(:bits).and_return 64 + expect(Hardware::CPU.can_run?(:ppc)).to be true + expect(Hardware::CPU.can_run?(:ppc64)).to be true + end + + it "reports that 32-bit Intel Macs can't run x86_64 executables" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(Hardware::CPU).to receive(:bits).and_return 32 + expect(Hardware::CPU.can_run?(:x86_64)).to be false + end + + it "reports that 32-bit PowerPC Macs can't run ppc64 executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + allow(Hardware::CPU).to receive(:bits).and_return 32 + expect(Hardware::CPU.can_run?(:ppc64)).to be false + end + + it "reports that Intel Macs can only run 32-bit PowerPC executables on 10.6 and older" do + allow(Hardware::CPU).to receive(:type).and_return :intel + allow(OS::Mac).to receive(:version).and_return OS::Mac::Version.new "10.6" + expect(Hardware::CPU.can_run?(:ppc)).to be true + + allow(OS::Mac).to receive(:version).and_return OS::Mac::Version.new "10.7" + expect(Hardware::CPU.can_run?(:ppc)).to be false + end + + it "reports that PowerPC Macs can't run Intel executables" do + allow(Hardware::CPU).to receive(:type).and_return :ppc + expect(Hardware::CPU.can_run?(:i386)).to be false + expect(Hardware::CPU.can_run?(:x86_64)).to be false + end + + it "returns false for unknown CPU types" do + allow(Hardware::CPU).to receive(:type).and_return :dunno + expect(Hardware::CPU.can_run?(:i386)).to be false + end + + it "returns false for unknown arches" do + expect(Hardware::CPU.can_run?(:blah)).to be false + end + end +end diff --git a/Library/Homebrew/test/os/mac/osxfuse_requirement_spec.rb b/Library/Homebrew/test/os/mac/osxfuse_requirement_spec.rb new file mode 100644 index 000000000..06d3d885e --- /dev/null +++ b/Library/Homebrew/test/os/mac/osxfuse_requirement_spec.rb @@ -0,0 +1,36 @@ +require "requirements/osxfuse_requirement" + +describe OsxfuseRequirement do + subject { described_class.new([]) } + + describe "::binary_osxfuse_installed?" do + it "returns false if fuse.h does not exist" do + allow(File).to receive(:exist?).and_return(false) + expect(described_class).not_to be_binary_osxfuse_installed + end + + it "returns false if osxfuse include directory is a symlink" do + allow(File).to receive(:exist?).and_return(true) + allow(File).to receive(:symlink?).and_return(true) + expect(described_class).not_to be_binary_osxfuse_installed + end + end + + describe "environment" do + it "adds the fuse directories to the appropriate paths" do + expect(ENV).to receive(:append_path).with("PKG_CONFIG_PATH", any_args) + expect(ENV).to receive(:append_path).with("HOMEBREW_LIBRARY_PATHS", any_args) + expect(ENV).to receive(:append_path).with("HOMEBREW_INCLUDE_PATHS", any_args) + subject.modify_build_environment + end + end +end + +describe NonBinaryOsxfuseRequirement do + subject { described_class.new([]) } + + describe "#message" do + msg = /osxfuse is already installed from the binary distribution/ + its(:message) { is_expected.to match(msg) } + end +end diff --git a/Library/Homebrew/test/requirement_spec.rb b/Library/Homebrew/test/requirement_spec.rb index 110a7ac4f..959041cf4 100644 --- a/Library/Homebrew/test/requirement_spec.rb +++ b/Library/Homebrew/test/requirement_spec.rb @@ -146,17 +146,13 @@ describe Requirement do end describe "#build?" do - context "#build true is specified" do - let(:klass) do - Class.new(described_class) do - build true - end - end + context ":build tag is specified" do + subject { described_class.new([:build]) } it { is_expected.to be_a_build_requirement } end - context "#build ommitted" do + context "#build omitted" do it { is_expected.not_to be_a_build_requirement } end end diff --git a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb index 5be2d6cf5..a775b0b17 100644 --- a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb @@ -3,7 +3,7 @@ require "rubocop/rspec/support" require_relative "../../extend/string" require_relative "../../rubocops/bottle_block_cop" -describe RuboCop::Cop::Homebrew::CorrectBottleBlock do +describe RuboCop::Cop::FormulaAuditStrict::BottleBlock do subject(:cop) { described_class.new } context "When auditing Bottle Block" do diff --git a/Library/Homebrew/test/rubocops/components_order_cop_spec.rb b/Library/Homebrew/test/rubocops/components_order_cop_spec.rb new file mode 100644 index 000000000..25467c635 --- /dev/null +++ b/Library/Homebrew/test/rubocops/components_order_cop_spec.rb @@ -0,0 +1,163 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/components_order_cop" + +describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do + subject(:cop) { described_class.new } + + context "When auditing formula components order" do + it "When url precedes homepage" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + end + EOS + + expected_offenses = [{ message: "`homepage` (line 3) should be put before `url` (line 2)", + severity: :convention, + line: 3, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When `resource` precedes `depends_on`" do + source = <<-EOS.undent + class Foo < Formula + url "https://example.com/foo-1.0.tgz" + + resource "foo2" do + url "https://example.com/foo-2.0.tgz" + end + + depends_on "openssl" + end + EOS + + expected_offenses = [{ message: "`depends_on` (line 8) should be put before `resource` (line 4)", + severity: :convention, + line: 8, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When `test` precedes `plist`" do + source = <<-EOS.undent + class Foo < Formula + url "https://example.com/foo-1.0.tgz" + + test do + expect(shell_output("./dogs")).to match("Dogs are terrific") + end + + def plist + end + end + EOS + + expected_offenses = [{ message: "`plist` (line 8) should be put before `test` (line 4)", + severity: :convention, + line: 8, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When only one of many `depends_on` precedes `conflicts_with`" do + source = <<-EOS.undent + class Foo < Formula + depends_on "autoconf" => :build + conflicts_with "visionmedia-watch" + depends_on "automake" => :build + depends_on "libtool" => :build + depends_on "pkg-config" => :build + depends_on "gettext" + end + EOS + + expected_offenses = [{ message: "`depends_on` (line 4) should be put before `conflicts_with` (line 3)", + severity: :convention, + line: 4, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + def expect_offense(expected, actual) + expect(actual.message).to eq(expected[:message]) + expect(actual.severity).to eq(expected[:severity]) + expect(actual.line).to eq(expected[:line]) + expect(actual.column).to eq(expected[:column]) + end + end + + context "When auditing formula components order with autocorrect" do + it "When url precedes homepage" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + end + EOS + correct_source = <<-EOS.undent + class Foo < Formula + homepage "http://example.com" + url "http://example.com/foo-1.0.tgz" + end + EOS + + corrected_source = autocorrect_source(cop, source) + expect(corrected_source).to eq(correct_source) + end + + it "When `resource` precedes `depends_on`" do + source = <<-EOS.undent + class Foo < Formula + url "https://example.com/foo-1.0.tgz" + + resource "foo2" do + url "https://example.com/foo-2.0.tgz" + end + + depends_on "openssl" + end + EOS + correct_source = <<-EOS.undent + class Foo < Formula + url "https://example.com/foo-1.0.tgz" + + depends_on "openssl" + + resource "foo2" do + url "https://example.com/foo-2.0.tgz" + end + end + EOS + corrected_source = autocorrect_source(cop, source) + expect(corrected_source).to eq(correct_source) + end + end +end diff --git a/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb b/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb new file mode 100644 index 000000000..fd635a126 --- /dev/null +++ b/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb @@ -0,0 +1,87 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/components_redundancy_cop" + +describe RuboCop::Cop::FormulaAuditStrict::ComponentsRedundancy do + subject(:cop) { described_class.new } + + context "When auditing formula components common errors" do + it "When url outside stable block" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + stable do + # stuff + end + end + EOS + + expected_offenses = [{ message: "`url` should be put inside `stable` block", + severity: :convention, + line: 2, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When both `head` and `head do` are present" do + source = <<-EOS.undent + class Foo < Formula + head "http://example.com/foo.git" + head do + # stuff + end + end + EOS + + expected_offenses = [{ message: "`head` and `head do` should not be simultaneously present", + severity: :convention, + line: 3, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When both `bottle :modifier` and `bottle do` are present" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + bottle do + # bottles go here + end + bottle :unneeded + end + EOS + + expected_offenses = [{ message: "`bottle :modifier` and `bottle do` should not be simultaneously present", + severity: :convention, + line: 3, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + def expect_offense(expected, actual) + expect(actual.message).to eq(expected[:message]) + expect(actual.severity).to eq(expected[:severity]) + expect(actual.line).to eq(expected[:line]) + expect(actual.column).to eq(expected[:column]) + end + end +end diff --git a/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb index 04c4c27da..432b15e3c 100644 --- a/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb @@ -3,7 +3,7 @@ require "rubocop/rspec/support" require_relative "../../extend/string" require_relative "../../rubocops/formula_desc_cop" -describe RuboCop::Cop::Homebrew::FormulaDesc do +describe RuboCop::Cop::FormulaAuditStrict::Desc do subject(:cop) { described_class.new } context "When auditing formula desc" do @@ -51,6 +51,31 @@ describe RuboCop::Cop::Homebrew::FormulaDesc do end end + it "When desc is multiline string" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + desc '#{"bar"*10}'\ + '#{"foo"*21}' + end + EOS + + msg = <<-EOS.undent + Description is too long. "name: desc" should be less than 80 characters. + Length is calculated as Foo + desc. (currently 98) + EOS + expected_offenses = [{ message: msg, + severity: :convention, + line: 3, + column: 2, + source: source }] + + inspect_source(cop, source) + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + it "When wrong \"command-line\" usage in desc" do source = <<-EOS.undent class Foo < Formula diff --git a/Library/Homebrew/test/rubocops/homepage_cop_spec.rb b/Library/Homebrew/test/rubocops/homepage_cop_spec.rb new file mode 100644 index 000000000..c03efd825 --- /dev/null +++ b/Library/Homebrew/test/rubocops/homepage_cop_spec.rb @@ -0,0 +1,124 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/homepage_cop" + +describe RuboCop::Cop::FormulaAudit::Homepage do + subject(:cop) { described_class.new } + + context "When auditing homepage" do + it "When there is no homepage" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + end + EOS + + expected_offenses = [{ message: "Formula should have a homepage.", + severity: :convention, + line: 1, + column: 0, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "Homepage with ftp" do + source = <<-EOS.undent + class Foo < Formula + homepage "ftp://example.com/foo" + url "http://example.com/foo-1.0.tgz" + end + EOS + + expected_offenses = [{ message: "The homepage should start with http or https (URL is ftp://example.com/foo).", + severity: :convention, + line: 2, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "Homepage URLs" do + formula_homepages = { + "bar" => "http://www.freedesktop.org/wiki/bar", + "baz" => "http://www.freedesktop.org/wiki/Software/baz", + "qux" => "https://code.google.com/p/qux", + "quux" => "http://github.com/quux", + "corge" => "http://savannah.nongnu.org/corge", + "grault" => "http://grault.github.io/", + "garply" => "http://www.gnome.org/garply", + "sf1" => "http://foo.sourceforge.net/", + "sf2" => "http://foo.sourceforge.net", + "sf3" => "http://foo.sf.net/", + "sf4" => "http://foo.sourceforge.io/", + "waldo" => "http://www.gnu.org/waldo", + } + + formula_homepages.each do |name, homepage| + source = <<-EOS.undent + class #{name.capitalize} < Formula + homepage "#{homepage}" + url "http://example.com/#{name}-1.0.tgz" + end + EOS + + inspect_source(cop, source) + if homepage =~ %r{http:\/\/www\.freedesktop\.org} + if homepage =~ /Software/ + expected_offenses = [{ message: "#{homepage} should be styled " \ + "`https://wiki.freedesktop.org/www/Software/project_name`", + severity: :convention, + line: 2, + column: 2, + source: source }] + else + expected_offenses = [{ message: "#{homepage} should be styled " \ + "`https://wiki.freedesktop.org/project_name`", + severity: :convention, + line: 2, + column: 2, + source: source }] + end + elsif homepage =~ %r{https:\/\/code\.google\.com} + expected_offenses = [{ message: "#{homepage} should end with a slash", + severity: :convention, + line: 2, + column: 2, + source: source }] + elsif homepage =~ /foo\.(sf|sourceforge)\.net/ + expected_offenses = [{ message: "#{homepage} should be `https://foo.sourceforge.io/`", + severity: :convention, + line: 2, + column: 2, + source: source }] + else + expected_offenses = [{ message: "Please use https:// for #{homepage}", + severity: :convention, + line: 2, + column: 2, + source: source }] + end + expected_offenses.zip([cop.offenses.last]).each do |expected, actual| + expect_offense(expected, actual) + end + end + end + + def expect_offense(expected, actual) + expect(actual.message).to eq(expected[:message]) + expect(actual.severity).to eq(expected[:severity]) + expect(actual.line).to eq(expected[:line]) + expect(actual.column).to eq(expected[:column]) + end + end +end diff --git a/Library/Homebrew/test/spec_helper.rb b/Library/Homebrew/test/spec_helper.rb index 2f6274fd1..e193b91a0 100644 --- a/Library/Homebrew/test/spec_helper.rb +++ b/Library/Homebrew/test/spec_helper.rb @@ -61,6 +61,10 @@ RSpec.configure do |config| skip "Python not installed." unless which("python") end + config.before(:each, :needs_network) do + skip "Requires network connection." unless ENV["HOMEBREW_TEST_ONLINE"] + end + config.around(:each) do |example| begin TEST_DIRECTORIES.each(&:mkpath) @@ -89,6 +93,7 @@ RSpec.configure do |config| HOMEBREW_PREFIX/"opt", HOMEBREW_PREFIX/"Caskroom", HOMEBREW_LIBRARY/"Taps/caskroom", + HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-bar", HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-bundle", HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-foo", HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-services", diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-non-executable-binary.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-non-executable-binary.rb new file mode 100644 index 000000000..4bd2f0882 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-non-executable-binary.rb @@ -0,0 +1,9 @@ +cask 'with-non-executable-binary' do + version '1.2.3' + sha256 'd5b2dfbef7ea28c25f7a77cd7fa14d013d82b626db1d82e00e25822464ba19e2' + + url "file://#{TEST_FIXTURE_DIR}/cask/naked_non_executable" + homepage 'http://example.com/with-binary' + + binary "naked_non_executable" +end diff --git a/Library/Homebrew/test/support/fixtures/cask/naked_non_executable b/Library/Homebrew/test/support/fixtures/cask/naked_non_executable new file mode 100644 index 000000000..039e4d006 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/naked_non_executable @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0 diff --git a/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb b/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb index b037068d2..ae1854f58 100644 --- a/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb +++ b/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb @@ -72,6 +72,7 @@ RSpec.shared_context "integration test" do env.merge!( "PATH" => path, + "HOMEBREW_PATH" => path, "HOMEBREW_BREW_FILE" => HOMEBREW_PREFIX/"bin/brew", "HOMEBREW_INTEGRATION_TEST" => command_id_from_args(args), "HOMEBREW_TEST_TMPDIR" => TEST_TMPDIR, diff --git a/Library/Homebrew/test/utils/github_spec.rb b/Library/Homebrew/test/utils/github_spec.rb new file mode 100644 index 000000000..9b539262f --- /dev/null +++ b/Library/Homebrew/test/utils/github_spec.rb @@ -0,0 +1,13 @@ +require "utils/github" + +describe GitHub do + describe "::search_code", :needs_network do + it "searches code" do + results = subject.search_code("repo:Homebrew/brew", "path:/", "filename:readme", "language:markdown") + + expect(results.count).to eq(1) + expect(results.first["name"]).to eq("README.md") + expect(results.first["path"]).to eq("README.md") + end + end +end diff --git a/Library/Homebrew/test/utils/shell_spec.rb b/Library/Homebrew/test/utils/shell_spec.rb index c44bd8253..d32f9928f 100644 --- a/Library/Homebrew/test/utils/shell_spec.rb +++ b/Library/Homebrew/test/utils/shell_spec.rb @@ -1,92 +1,92 @@ require "utils/shell" describe Utils::Shell do - describe "::shell_profile" do + describe "::profile" do it "returns ~/.bash_profile by default" do ENV["SHELL"] = "/bin/another_shell" - expect(subject.shell_profile).to eq("~/.bash_profile") + expect(subject.profile).to eq("~/.bash_profile") end it "returns ~/.bash_profile for Sh" do ENV["SHELL"] = "/bin/another_shell" - expect(subject.shell_profile).to eq("~/.bash_profile") + expect(subject.profile).to eq("~/.bash_profile") end it "returns ~/.bash_profile for Bash" do ENV["SHELL"] = "/bin/bash" - expect(subject.shell_profile).to eq("~/.bash_profile") + expect(subject.profile).to eq("~/.bash_profile") end it "returns ~/.zshrc for Zsh" do ENV["SHELL"] = "/bin/zsh" - expect(subject.shell_profile).to eq("~/.zshrc") + expect(subject.profile).to eq("~/.zshrc") end it "returns ~/.kshrc for Ksh" do ENV["SHELL"] = "/bin/ksh" - expect(subject.shell_profile).to eq("~/.kshrc") + expect(subject.profile).to eq("~/.kshrc") end end - describe "::path_to_shell" do + describe "::from_path" do it "supports a raw command name" do - expect(subject.path_to_shell("bash")).to eq(:bash) + expect(subject.from_path("bash")).to eq(:bash) end it "supports full paths" do - expect(subject.path_to_shell("/bin/bash")).to eq(:bash) + expect(subject.from_path("/bin/bash")).to eq(:bash) end it "supports versions" do - expect(subject.path_to_shell("zsh-5.2")).to eq(:zsh) + expect(subject.from_path("zsh-5.2")).to eq(:zsh) end it "strips newlines" do - expect(subject.path_to_shell("zsh-5.2\n")).to eq(:zsh) + expect(subject.from_path("zsh-5.2\n")).to eq(:zsh) end it "returns nil when input is invalid" do - expect(subject.path_to_shell("")).to be nil - expect(subject.path_to_shell("@@@@@@")).to be nil - expect(subject.path_to_shell("invalid_shell-4.2")).to be nil + expect(subject.from_path("")).to be nil + expect(subject.from_path("@@@@@@")).to be nil + expect(subject.from_path("invalid_shell-4.2")).to be nil end end specify "::sh_quote" do - expect(subject.sh_quote("")).to eq("''") - expect(subject.sh_quote("\\")).to eq("\\\\") - expect(subject.sh_quote("\n")).to eq("'\n'") - expect(subject.sh_quote("$")).to eq("\\$") - expect(subject.sh_quote("word")).to eq("word") + expect(subject.send(:sh_quote, "")).to eq("''") + expect(subject.send(:sh_quote, "\\")).to eq("\\\\") + expect(subject.send(:sh_quote, "\n")).to eq("'\n'") + expect(subject.send(:sh_quote, "$")).to eq("\\$") + expect(subject.send(:sh_quote, "word")).to eq("word") end specify "::csh_quote" do - expect(subject.csh_quote("")).to eq("''") - expect(subject.csh_quote("\\")).to eq("\\\\") + expect(subject.send(:csh_quote, "")).to eq("''") + expect(subject.send(:csh_quote, "\\")).to eq("\\\\") # note this test is different than for sh - expect(subject.csh_quote("\n")).to eq("'\\\n'") - expect(subject.csh_quote("$")).to eq("\\$") - expect(subject.csh_quote("word")).to eq("word") + expect(subject.send(:csh_quote, "\n")).to eq("'\\\n'") + expect(subject.send(:csh_quote, "$")).to eq("\\$") + expect(subject.send(:csh_quote, "word")).to eq("word") end - describe "::prepend_path_in_shell_profile" do + describe "::prepend_path_in_profile" do let(:path) { "/my/path" } it "supports Tcsh" do ENV["SHELL"] = "/bin/tcsh" - expect(subject.prepend_path_in_shell_profile(path)) + expect(subject.prepend_path_in_profile(path)) .to start_with("echo 'setenv PATH #{path}:$") end it "supports Bash" do ENV["SHELL"] = "/bin/bash" - expect(subject.prepend_path_in_shell_profile(path)) + expect(subject.prepend_path_in_profile(path)) .to start_with("echo 'export PATH=\"#{path}:$") end it "supports Fish" do ENV["SHELL"] = "/usr/local/bin/fish" - expect(subject.prepend_path_in_shell_profile(path)) + expect(subject.prepend_path_in_profile(path)) .to start_with("echo 'set -g fish_user_paths \"#{path}\" $fish_user_paths' >>") end end diff --git a/Library/Homebrew/test/utils_spec.rb b/Library/Homebrew/test/utils_spec.rb index 314c299a8..be224990a 100644 --- a/Library/Homebrew/test/utils_spec.rb +++ b/Library/Homebrew/test/utils_spec.rb @@ -189,7 +189,13 @@ describe "globally-scoped helper methods" do specify "#which_editor" do ENV["HOMEBREW_EDITOR"] = "vemate" - expect(which_editor).to eq("vemate") + ENV["HOMEBREW_PATH"] = dir + + editor = "#{dir}/vemate" + FileUtils.touch editor + FileUtils.chmod 0755, editor + + expect(which_editor).to eql editor end specify "#gzip" do diff --git a/Library/Homebrew/test/version_spec.rb b/Library/Homebrew/test/version_spec.rb index d3d63a25c..41e05019c 100644 --- a/Library/Homebrew/test/version_spec.rb +++ b/Library/Homebrew/test/version_spec.rb @@ -433,6 +433,11 @@ describe Version do .to be_detected_from("https://homebrew.bintray.com/bottles/imagemagick-6.7.5-7.lion.bottle.1.tar.gz") end + specify "date-based version style" do + expect(Version.create("2017-04-17")) + .to be_detected_from("https://example.com/dada-v2017-04-17.tar.gz") + end + specify "dash version style" do expect(Version.create("3.4")) .to be_detected_from("http://www.antlr.org/download/antlr-3.4-complete.jar") diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index 46a8cc68e..28d7fc283 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -190,10 +190,10 @@ module Homebrew Gem::Specification.reset # Add Gem binary directory and (if missing) Ruby binary directory to PATH. - path = ENV["PATH"].split(File::PATH_SEPARATOR) - path.unshift(RUBY_BIN) if which("ruby") != RUBY_PATH - path.unshift(Gem.bindir) - ENV["PATH"] = path.join(File::PATH_SEPARATOR) + path = PATH.new(ENV["PATH"]) + path.prepend(RUBY_BIN) if which("ruby") != RUBY_PATH + path.prepend(Gem.bindir) + ENV["PATH"] = path if Gem::Specification.find_all_by_name(name, version).empty? ohai "Installing or updating '#{name}' gem" @@ -293,7 +293,7 @@ def quiet_system(cmd, *args) end def which(cmd, path = ENV["PATH"]) - path.split(File::PATH_SEPARATOR).each do |p| + PATH.new(path).each do |p| begin pcmd = File.expand_path(cmd, p) rescue ArgumentError @@ -307,7 +307,7 @@ def which(cmd, path = ENV["PATH"]) end def which_all(cmd, path = ENV["PATH"]) - path.to_s.split(File::PATH_SEPARATOR).map do |p| + PATH.new(path).map do |p| begin pcmd = File.expand_path(cmd, p) rescue ArgumentError @@ -320,25 +320,34 @@ def which_all(cmd, path = ENV["PATH"]) end def which_editor - editor = ENV.values_at("HOMEBREW_EDITOR", "VISUAL", "EDITOR").compact.first - return editor unless editor.nil? + editor = ENV.values_at("HOMEBREW_EDITOR", "HOMEBREW_VISUAL").compact.reject(&:empty?).first + if editor + editor_name, _, editor_args = editor.partition " " + editor_path = which(editor_name, ENV["HOMEBREW_PATH"]) + editor = if editor_args.to_s.empty? + editor_path.to_s + else + "#{editor_path} #{editor_args}" + end + return editor + end # Find Textmate - editor = "mate" if which "mate" + editor = which("mate", ENV["HOMEBREW_PATH"]) # Find BBEdit / TextWrangler - editor ||= "edit" if which "edit" + editor ||= which("edit", ENV["HOMEBREW_PATH"]) # Find vim - editor ||= "vim" if which "vim" + editor ||= which("vim", ENV["HOMEBREW_PATH"]) # Default to standard vim editor ||= "/usr/bin/vim" opoo <<-EOS.undent Using #{editor} because no editor was set in the environment. - This may change in the future, so we recommend setting EDITOR, VISUAL, + This may change in the future, so we recommend setting EDITOR, or HOMEBREW_EDITOR to your preferred text editor. EOS - editor + editor.to_s end def exec_editor(*args) @@ -347,7 +356,7 @@ def exec_editor(*args) end def exec_browser(*args) - browser = ENV["HOMEBREW_BROWSER"] || ENV["BROWSER"] + browser = ENV["HOMEBREW_BROWSER"] browser ||= OS::PATH_OPEN if defined?(OS::PATH_OPEN) return unless browser safe_exec(browser, *args) @@ -406,8 +415,8 @@ def nostdout end end -def paths - @paths ||= ENV["PATH"].split(File::PATH_SEPARATOR).collect do |p| +def paths(env_path = ENV["PATH"]) + @paths ||= PATH.new(env_path).collect do |p| begin File.expand_path(p).chomp("/") rescue ArgumentError diff --git a/Library/Homebrew/utils/analytics.sh b/Library/Homebrew/utils/analytics.sh index 35f91eabc..8d5cf2ff7 100644 --- a/Library/Homebrew/utils/analytics.sh +++ b/Library/Homebrew/utils/analytics.sh @@ -85,8 +85,6 @@ report-analytics-screenview-command() { fi # Don't report commands used mostly by our scripts and not users. - # TODO: list more e.g. shell completion things here perhaps using a single - # script as a shell-completion entry point. case "$HOMEBREW_COMMAND" in --prefix|analytics|command|commands) return diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index a5ed5394a..88c5199c2 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -4,7 +4,7 @@ require "tempfile" module GitHub module_function - ISSUES_URI = URI.parse("https://api.github.com/search/issues") + API_URL = "https://api.github.com".freeze CREATE_GIST_SCOPES = ["gist"].freeze CREATE_ISSUE_SCOPES = ["public_repo"].freeze @@ -228,13 +228,19 @@ module GitHub end def issues_matching(query, qualifiers = {}) - uri = ISSUES_URI.dup + uri = URI.parse("#{API_URL}/search/issues") uri.query = build_query_string(query, qualifiers) open(uri) { |json| json["items"] } end def repository(user, repo) - open(URI.parse("https://api.github.com/repos/#{user}/#{repo}")) { |j| j } + open(URI.parse("#{API_URL}/repos/#{user}/#{repo}")) + end + + def search_code(*params) + uri = URI.parse("#{API_URL}/search/code") + uri.query = "q=#{uri_escape(params.join(" "))}" + open(uri) { |json| json["items"] } end def build_query_string(query, qualifiers) @@ -286,7 +292,7 @@ module GitHub end def private_repo?(user, repo) - uri = URI.parse("https://api.github.com/repos/#{user}/#{repo}") + uri = URI.parse("#{API_URL}/repos/#{user}/#{repo}") open(uri) { |json| json["private"] } end end diff --git a/Library/Homebrew/utils/shell.rb b/Library/Homebrew/utils/shell.rb index 302167d47..5327f6ecf 100644 --- a/Library/Homebrew/utils/shell.rb +++ b/Library/Homebrew/utils/shell.rb @@ -1,62 +1,24 @@ module Utils - SHELL_PROFILE_MAP = { - bash: "~/.bash_profile", - csh: "~/.cshrc", - fish: "~/.config/fish/config.fish", - ksh: "~/.kshrc", - sh: "~/.bash_profile", - tcsh: "~/.tcshrc", - zsh: "~/.zshrc", - }.freeze - module Shell - UNSAFE_SHELL_CHAR = %r{([^A-Za-z0-9_\-.,:/@\n])} + module_function # take a path and heuristically convert it # to a shell name, return nil if there's no match - def path_to_shell(path) + def from_path(path) # we only care about the basename shell_name = File.basename(path) # handle possible version suffix like `zsh-5.2` shell_name.sub!(/-.*\z/m, "") shell_name.to_sym if %w[bash csh fish ksh sh tcsh zsh].include?(shell_name) end - module_function :path_to_shell - - def preferred_shell - path_to_shell(ENV.fetch("SHELL", "")) - end - module_function :preferred_shell - def parent_shell - path_to_shell(`ps -p #{Process.ppid} -o ucomm=`.strip) + def preferred + from_path(ENV.fetch("SHELL", "")) end - module_function :parent_shell - def csh_quote(str) - # ruby's implementation of shell_escape - str = str.to_s - return "''" if str.empty? - str = str.dup - # anything that isn't a known safe character is padded - str.gsub!(UNSAFE_SHELL_CHAR, "\\\\" + "\\1") - # newlines have to be specially quoted in csh - str.gsub!(/\n/, "'\\\n'") - str + def parent + from_path(`ps -p #{Process.ppid} -o ucomm=`.strip) end - module_function :csh_quote - - def sh_quote(str) - # ruby's implementation of shell_escape - str = str.to_s - return "''" if str.empty? - str = str.dup - # anything that isn't a known safe character is padded - str.gsub!(UNSAFE_SHELL_CHAR, "\\\\" + "\\1") - str.gsub!(/\n/, "'\n'") - str - end - module_function :sh_quote # quote values. quoting keys is overkill def export_value(shell, key, value) @@ -72,24 +34,60 @@ module Utils "setenv #{key} #{csh_quote(value)};" end end - module_function :export_value # return the shell profile file based on users' preferred shell - def shell_profile - SHELL_PROFILE_MAP.fetch(preferred_shell, "~/.bash_profile") + def profile + SHELL_PROFILE_MAP.fetch(preferred, "~/.bash_profile") end - module_function :shell_profile - def prepend_path_in_shell_profile(path) - case preferred_shell + def prepend_path_in_profile(path) + case preferred when :bash, :ksh, :sh, :zsh, nil - "echo 'export PATH=\"#{sh_quote(path)}:$PATH\"' >> #{shell_profile}" + "echo 'export PATH=\"#{sh_quote(path)}:$PATH\"' >> #{profile}" when :csh, :tcsh - "echo 'setenv PATH #{csh_quote(path)}:$PATH' >> #{shell_profile}" + "echo 'setenv PATH #{csh_quote(path)}:$PATH' >> #{profile}" when :fish - "echo 'set -g fish_user_paths \"#{sh_quote(path)}\" $fish_user_paths' >> #{shell_profile}" + "echo 'set -g fish_user_paths \"#{sh_quote(path)}\" $fish_user_paths' >> #{profile}" end end - module_function :prepend_path_in_shell_profile + + private + + SHELL_PROFILE_MAP = { + bash: "~/.bash_profile", + csh: "~/.cshrc", + fish: "~/.config/fish/config.fish", + ksh: "~/.kshrc", + sh: "~/.bash_profile", + tcsh: "~/.tcshrc", + zsh: "~/.zshrc", + }.freeze + + UNSAFE_SHELL_CHAR = %r{([^A-Za-z0-9_\-.,:/@\n])} + + module_function + + def csh_quote(str) + # ruby's implementation of shell_escape + str = str.to_s + return "''" if str.empty? + str = str.dup + # anything that isn't a known safe character is padded + str.gsub!(UNSAFE_SHELL_CHAR, "\\\\" + "\\1") + # newlines have to be specially quoted in csh + str.gsub!(/\n/, "'\\\n'") + str + end + + def sh_quote(str) + # ruby's implementation of shell_escape + str = str.to_s + return "''" if str.empty? + str = str.dup + # anything that isn't a known safe character is padded + str.gsub!(UNSAFE_SHELL_CHAR, "\\\\" + "\\1") + str.gsub!(/\n/, "'\n'") + str + end end end diff --git a/Library/Homebrew/vendor/README.md b/Library/Homebrew/vendor/README.md index 906d42918..b408631c7 100644 --- a/Library/Homebrew/vendor/README.md +++ b/Library/Homebrew/vendor/README.md @@ -1,7 +1,7 @@ Vendored Dependencies ===================== -* [plist](https://github.com/bleything/plist), version 3.1.0 +* [plist](https://github.com/patsplat/plist), version 3.3.0 * [ruby-macho](https://github.com/Homebrew/ruby-macho), version 1.1.0 diff --git a/Library/Homebrew/vendor/plist/plist.rb b/Library/Homebrew/vendor/plist/plist.rb index 0b828afc6..82ecb27d2 100755 --- a/Library/Homebrew/vendor/plist/plist.rb +++ b/Library/Homebrew/vendor/plist/plist.rb @@ -1,5 +1,5 @@ -#!/usr/bin/env ruby -# +# encoding: utf-8 + # = plist # # This is the main file for plist. Everything interesting happens in @@ -15,7 +15,7 @@ require 'stringio' require_relative 'plist/generator' require_relative 'plist/parser' +require_relative 'plist/version' module Plist - VERSION = '3.1.0' end diff --git a/Library/Homebrew/vendor/plist/plist/generator.rb b/Library/Homebrew/vendor/plist/plist/generator.rb index 3b84c301f..84bef3aaf 100644..100755 --- a/Library/Homebrew/vendor/plist/plist/generator.rb +++ b/Library/Homebrew/vendor/plist/plist/generator.rb @@ -1,12 +1,12 @@ -#!/usr/bin/env ruby -# +# encoding: utf-8 + # = plist # # Copyright 2006-2010 Ben Bleything and Patrick May # Distributed under the MIT License # -module Plist ; end +module Plist; end # === Create a plist # You can dump an object to a plist in one of two ways: @@ -94,7 +94,7 @@ module Plist::Emit output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ')) when Date # also catches DateTime output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ')) - when String, Symbol, Fixnum, Bignum, Integer, Float + when String, Symbol, Integer, Float output << tag(element_type(element), CGI::escapeHTML(element.to_s)) when IO, StringIO element.rewind @@ -159,7 +159,7 @@ module Plist::Emit when String, Symbol 'string' - when Fixnum, Bignum, Integer + when Integer 'integer' when Float diff --git a/Library/Homebrew/vendor/plist/plist/parser.rb b/Library/Homebrew/vendor/plist/plist/parser.rb index de441fcc5..4de13f881 100644..100755 --- a/Library/Homebrew/vendor/plist/plist/parser.rb +++ b/Library/Homebrew/vendor/plist/plist/parser.rb @@ -1,5 +1,5 @@ -#!/usr/bin/env ruby -# +# encoding: utf-8 + # = plist # # Copyright 2006-2010 Ben Bleything and Patrick May @@ -73,10 +73,10 @@ module Plist end TEXT = /([^<]+)/ - XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um - DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um - COMMENT_START = /\A<!--/u - COMMENT_END = /.*?-->/um + XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/m + DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/m + COMMENT_START = /\A<!--/ + COMMENT_END = /.*?-->/m def parse @@ -91,7 +91,14 @@ module Plist if @scanner.scan(COMMENT_START) @scanner.scan(COMMENT_END) elsif @scanner.scan(XMLDECL_PATTERN) + encoding = parse_encoding_from_xml_declaration(@scanner[1]) + next if encoding.nil? + + # use the specified encoding for the rest of the file + next unless String.method_defined?(:force_encoding) + @scanner.string = @scanner.rest.force_encoding(encoding) elsif @scanner.scan(DOCTYPE_PATTERN) + next elsif @scanner.scan(start_tag) @listener.tag_start(@scanner[1], nil) if (@scanner[2] =~ /\/$/) @@ -106,6 +113,22 @@ module Plist end end end + + private + + def parse_encoding_from_xml_declaration(xml_declaration) + return unless defined?(Encoding) + + xml_encoding = xml_declaration.match(/(?:\A|\s)encoding=(?:"(.*?)"|'(.*?)')(?:\s|\Z)/) + + return if xml_encoding.nil? + + begin + Encoding.find(xml_encoding[1]) + rescue ArgumentError + nil + end + end end class PTag @@ -213,7 +236,7 @@ module Plist data = Base64.decode64(text.gsub(/\s+/, '')) unless text.nil? begin return Marshal.load(data) - rescue Exception => e + rescue Exception io = StringIO.new io.write data io.rewind diff --git a/Library/Homebrew/vendor/plist/plist/version.rb b/Library/Homebrew/vendor/plist/plist/version.rb new file mode 100755 index 000000000..80b1f73dd --- /dev/null +++ b/Library/Homebrew/vendor/plist/plist/version.rb @@ -0,0 +1,5 @@ +# encoding: utf-8 + +module Plist + VERSION = '3.3.0'.freeze +end diff --git a/Library/Homebrew/version.rb b/Library/Homebrew/version.rb index b9f512a50..da239f788 100644 --- a/Library/Homebrew/version.rb +++ b/Library/Homebrew/version.rb @@ -316,6 +316,11 @@ class Version spec.stem end + # date-based versioning + # e.g. ltopers-v2017-04-14.tar.gz + m = /-v?(\d{4}-\d{2}-\d{2})/.match(stem) + return m.captures.first unless m.nil? + # GitHub tarballs # e.g. https://github.com/foo/bar/tarball/v1.2.3 # e.g. https://github.com/sam-github/libnet/tarball/libnet-1.1.4 @@ -32,22 +32,17 @@ We'd love you to contribute to Homebrew. First, please read our [Contribution Gu We explicitly welcome contributions from people who have never contributed to open-source before: we were all beginners once! We can help build on a partially working pull request with the aim of getting it merged. We are also actively seeking to diversify our contributors and especially welcome contributions from women from all backgrounds and people of colour. -A good starting point for contributing is running `brew audit --strict`with some of the packages you use (e.g. `brew audit --strict wget` if you use `wget`) and then read through the warnings, try to fix them until `brew audit --strict` shows no results and [submit a pull request](http://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request.html). If no formulae you use have warnings you can run `brew audit --strict` without arguments to have it run on all packages and pick one. Good luck! +A good starting point for contributing is running `brew audit --strict` with some of the packages you use (e.g. `brew audit --strict wget` if you use `wget`) and then read through the warnings, try to fix them until `brew audit --strict` shows no results and [submit a pull request](http://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request.html). If no formulae you use have warnings you can run `brew audit --strict` without arguments to have it run on all packages and pick one. Good luck! ## Security -Please report security issues to security@brew.sh. - -This is our PGP key which is valid until May 24, 2017. -* Key ID: `0xE33A3D3CCE59E297` -* Fingerprint: `C657 8F76 2E23 441E C879 EC5C E33A 3D3C CE59 E297` -* Full key: https://keybase.io/homebrew/key.asc +Please report security issues to our [HackerOne](https://hackerone.com/homebrew/). ## Who Are You? Homebrew's lead maintainer is [Mike McQuaid](https://github.com/mikemcquaid). Homebrew's current maintainers are [Alyssa Ross](https://github.com/alyssais), [Andrew Janke](https://github.com/apjanke), [Baptiste Fontaine](https://github.com/bfontaine), [Alex Dunn](https://github.com/dunn), [FX Coudert](https://github.com/fxcoudert), [ilovezfs](https://github.com/ilovezfs), [Josh Hagins](https://github.com/jawshooah), [JCount](https://github.com/jcount), [Misty De Meo](https://github.com/mistydemeo), [neutric](https://github.com/neutric), [Tomasz Pajor](https://github.com/nijikon), [Markus Reiter](https://github.com/reitermarkus), [Tim Smith](https://github.com/tdsmith), [Tom Schoonjans](https://github.com/tschoonj), [Uladzislau Shablinski](https://github.com/vladshablinsky) and [William Woodruff](https://github.com/woodruffw). -Former maintainers with significant contributions include [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), [Jack Nagel](https://github.com/jacknagel), [Adam Vandenberg](https://github.com/adamv) and Homebrew's creator: [Max Howell](https://github.com/mxcl). +Former maintainers with significant contributions include [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) and Homebrew's creator: [Max Howell](https://github.com/mxcl). ## Community - [discourse.brew.sh (forum)](https://discourse.brew.sh) @@ -70,9 +65,7 @@ Our Xserve ESXi boxes for CI are hosted by [MacStadium](https://www.macstadium.c [](https://www.macstadium.com) -Our Mac Minis for CI were paid for by [our Kickstarter supporters](http://docs.brew.sh/Kickstarter-Supporters.html). - -Our Mac Minis for CI are hosted by [The Positive Internet Company](http://www.positive-internet.com). +Our Jenkins CI installation is hosted by [DigitalOcean](https://m.do.co/c/7e39c35d5581). Our bottles (binary packages) are hosted by [Bintray](https://bintray.com/homebrew). @@ -44,6 +44,20 @@ fi HOMEBREW_LIBRARY="$HOMEBREW_REPOSITORY/Library" +# Whitelist and copy to HOMEBREW_* all variables previously mentioned in +# manpage or used elsewhere by Homebrew. +for VAR in AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY BINTRAY_USER BINTRAY_KEY \ + BROWSER EDITOR GIT PATH VISUAL +do + # Skip if variable value is empty. + [[ -z "${!VAR}" ]] && continue + + VAR_NEW="HOMEBREW_${VAR}" + # Skip if existing HOMEBREW_* variable is set. + [[ -n "${!VAR_NEW}" ]] && continue + export "$VAR_NEW"="${!VAR}" +done + if [[ -n "$HOMEBREW_ENV_FILTERING" ]] then PATH="/usr/bin:/bin:/usr/sbin:/sbin" diff --git a/docs/.well-known/acme-challenge/CgQNmjNTd6fUriinp4XpuRI5PBGp7zEXbEEsQceSD0k b/docs/.well-known/acme-challenge/CgQNmjNTd6fUriinp4XpuRI5PBGp7zEXbEEsQceSD0k new file mode 100644 index 000000000..d3ba42ff4 --- /dev/null +++ b/docs/.well-known/acme-challenge/CgQNmjNTd6fUriinp4XpuRI5PBGp7zEXbEEsQceSD0k @@ -0,0 +1 @@ +CgQNmjNTd6fUriinp4XpuRI5PBGp7zEXbEEsQceSD0k.unNjkXuFqjKx4BO8gem4nzeMm1tSZxPPeBjNqQhFCqQ diff --git a/docs/Acceptable-Formulae.md b/docs/Acceptable-Formulae.md index d600a31a0..d59129ecc 100644 --- a/docs/Acceptable-Formulae.md +++ b/docs/Acceptable-Formulae.md @@ -80,12 +80,12 @@ useful to people. Just install the stuff! Having to faff around with foo-ruby, foo-perl etc. sucks. ### Niche (or self-submitted) stuff -The software in question must be: +The software in question must: -* maintained (e.g. upstream is still making new releases) -* known -* stable (e.g. not declared "unstable" or "beta" by upstream) -* used +* be maintained (e.g. upstream is still making new releases) +* be known +* be stable (e.g. not declared "unstable" or "beta" by upstream) +* be used * have a homepage We will reject formulae that seem too obscure, partly because they wonβt @@ -93,7 +93,8 @@ get maintained and partly because we have to draw the line somewhere. We frown on authors submitting their own work unless it is very popular. -Donβt forget Homebrew is all Git underneath! Maintain your own tap if you have to! +Donβt forget Homebrew is all Git underneath! +[Maintain your own tap](How-to-Create-and-Maintain-a-Tap.md) if you have to! There may be exceptions to these rules in the main repository; we may include things that don't meet these criteria or reject things that do. diff --git a/docs/Interesting-Taps-&-Forks.md b/docs/Interesting-Taps-&-Forks.md index 54f6de25e..6b70fadec 100644 --- a/docs/Interesting-Taps-&-Forks.md +++ b/docs/Interesting-Taps-&-Forks.md @@ -15,7 +15,7 @@ Homebrew has the capability to add (and remove) multiple taps to your local inst `brew search` looks in these taps as well as in [homebrew/core](https://github.com/Homebrew/homebrew-core) so don't worry about missing stuff. -You can be added as a maintainer for one of the Homebrew organization taps and aid the project! If you are interested write to our list: homebrew-discuss@googlegroups.com. We want your help! +You can be added as a maintainer for one of the Homebrew organization taps and aid the project! If you are interested please feel free to ask in an issue or pull request after submitting multiple high-quality pull requests. We want your help! ## Other interesting taps diff --git a/docs/Manpage.md b/docs/Manpage.md index 23e338213..2dac89443 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -402,7 +402,7 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note If `--env=std` is passed, use the standard `PATH` instead of superenv's. - * `style` [`--fix`] [`--display-cop-names`] [`files`|`taps`|`formulae`]: + * `style` [`--fix`] [`--display-cop-names`] [`--only-cops=`[COP1,COP2..]|`--except-cops=`[COP1,COP2..]] [`files`|`taps`|`formulae`]: Check formulae or files for conformance to Homebrew style guidelines. `formulae` and `files` may not be combined. If both are omitted, style will run @@ -415,6 +415,10 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note If `--display-cop-names` is passed, the RuboCop cop name for each violation is included in the output. + If `--only-cops` is passed, only the given Rubocop cop(s)' violations would be checked. + + If `--except-cops` is passed, the given Rubocop cop(s)' checks would be skipped. + Exits with a non-zero status if any style violations are found. * `switch` `name` `version`: @@ -606,7 +610,7 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note ## DEVELOPER COMMANDS - * `audit` [`--strict`] [`--fix`] [`--online`] [`--new-formula`] [`--display-cop-names`] [`--display-filename`] [`formulae`]: + * `audit` [`--strict`] [`--fix`] [`--online`] [`--new-formula`] [`--display-cop-names`] [`--display-filename`] [`--only=``method`|`--except=``method`] [`--only-cops=`[COP1,COP2..]|`--except-cops=`[COP1,COP2..]] [`formulae`]: Check `formulae` for Homebrew coding style violations. This should be run before submitting a new formula. @@ -631,6 +635,14 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note If `--display-filename` is passed, every line of output is prefixed with the name of the file or formula being audited, to make the output easy to grep. + If `--only` is passed, only the methods named `audit_`method`` will be run. + + If `--except` is passed, the methods named `audit_`method`` will not be run. + + If `--only-cops` is passed, only the given Rubocop cop(s)' violations would be checked. + + If `--except-cops` is passed, the given Rubocop cop(s)' checks would be skipped. + `audit` exits with a non-zero status if any errors are found. This is useful, for instance, for implementing pre-commit hooks. @@ -709,7 +721,8 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note If `--meson` is passed, create a basic template for a Meson-style build. If `--no-fetch` is passed, Homebrew will not download `URL` to the cache and - will thus not add the SHA256 to the formula for you. + will thus not add the SHA256 to the formula for you. It will also not check + the GitHub API for GitHub projects (to fill out the description and homepage). The options `--set-name` and `--set-version` each take an argument and allow you to explicitly set the name and version of the package you are creating. @@ -897,8 +910,15 @@ can take several different forms: The formula file will be cached for later use. ## ENVIRONMENT + * `HOMEBREW_ARTIFACT_DOMAIN`: + If set, instructs Homebrew to use the given URL as a download mirror for bottles and binaries. - * `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`: + * `HOMEBREW_AUTO_UPDATE_SECS`: + If set, Homebrew will only check for autoupdates once per this seconds interval. + + *Default:* `60`. + + * `HOMEBREW_AWS_ACCESS_KEY_ID`, `HOMEBREW_AWS_SECRET_ACCESS_KEY`: When using the `S3` download strategy, Homebrew will look in these variables for access credentials (see <https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-environment> @@ -906,30 +926,9 @@ can take several different forms: the `S3` download strategy will download with a public (unsigned) URL. - * `BROWSER`: - If set, and `HOMEBREW_BROWSER` is not, use `BROWSER` as the web browser - when opening project homepages. - - * `EDITOR`: - If set, and `HOMEBREW_EDITOR` and `VISUAL` are not, use `EDITOR` as the text editor. - - * `GIT`: - When using Git, Homebrew will use `GIT` if set, - a Homebrew-built Git if installed, or the system-provided binary. - - Set this to force Homebrew to use a particular git binary. - * `HOMEBREW_BOTTLE_DOMAIN`: If set, instructs Homebrew to use the given URL as a download mirror for bottles. - * `HOMEBREW_ARTIFACT_DOMAIN`: - If set, instructs Homebrew to use the given URL as a download mirror for bottles and binaries. - - * `HOMEBREW_AUTO_UPDATE_SECS`: - If set, Homebrew will only check for autoupdates once per this seconds interval. - - *Default:* `60`. - * `HOMEBREW_BROWSER`: If set, uses this setting as the browser when opening project homepages, instead of the OS default browser. @@ -977,6 +976,12 @@ can take several different forms: If set, Homebrew will always use its vendored, relocatable Ruby 2.0 version even if the system version of Ruby is >=2.0. + * `HOMEBREW_GIT`: + When using Git, Homebrew will use `GIT` if set, + a Homebrew-built Git if installed, or the system-provided binary. + + Set this to force Homebrew to use a particular git binary. + * `HOMEBREW_GITHUB_API_TOKEN`: A personal access token for the GitHub API, which you can create at <https://github.com/settings/tokens>. If set, GitHub will allow you a @@ -1042,9 +1047,6 @@ can take several different forms: * `HOMEBREW_VERBOSE`: If set, Homebrew always assumes `--verbose` when running commands. - * `VISUAL`: - If set, and `HOMEBREW_EDITOR` is not, use `VISUAL` as the text editor. - ## USING HOMEBREW BEHIND A PROXY Homebrew uses several commands for downloading files (e.g. `curl`, `git`, `svn`). @@ -1072,7 +1074,7 @@ Homebrew's lead maintainer is Mike McQuaid. Homebrew's current maintainers are Alyssa Ross, Andrew Janke, Baptiste Fontaine, Alex Dunn, FX Coudert, ilovezfs, Josh Hagins, JCount, Misty De Meo, neutric, Tomasz Pajor, Markus Reiter, Tim Smith, Tom Schoonjans, Uladzislau Shablinski and William Woodruff. -Former maintainers with significant contributions include Xu Cheng, Martin Afanasjew, Dominyk Tiller, Brett Koonce, Jack Nagel, Adam Vandenberg and Homebrew's creator: Max Howell. +Former maintainers with significant contributions include Xu Cheng, Martin Afanasjew, Dominyk Tiller, Brett Koonce, Charlie Sharpsteen, Jack Nagel, Adam Vandenberg and Homebrew's creator: Max Howell. ## BUGS diff --git a/docs/Python-for-Formula-Authors.md b/docs/Python-for-Formula-Authors.md index d3e7543db..ea86cf84c 100644 --- a/docs/Python-for-Formula-Authors.md +++ b/docs/Python-for-Formula-Authors.md @@ -143,7 +143,7 @@ def install %w[six parsedatetime].each do |r| venv.pip_install resource(r) end - venv.link_scripts(bin) { venv.pip_install buildpath } + venv.pip_install_and_link buildpath end ``` diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md index 180dac1fd..2ffa256d1 100644 --- a/docs/Troubleshooting.md +++ b/docs/Troubleshooting.md @@ -13,12 +13,11 @@ Follow these steps to fix common problems: * Check that **Command Line Tools for Xcode (CLT)** and **Xcode** are up to date. * If commands fail with permissions errors, check the permissions of `/usr/local`'s subdirectories. If youβre unsure what to do, you can run `cd /usr/local && sudo chown -R $(whoami) bin etc include lib sbin share var Frameworks`. * Read through the [Common Issues](Common-Issues.md). -* If youβre installing something Java-related, make sure you have installed Java (you can run `brew cask install java`). ## Check to see if the issue has been reported * Search the [issue tracker](https://github.com/Homebrew/homebrew-core/issues) to see if someone else has already reported the same issue. -* Make sure you search issues on the correct repository. If a formula that has failed to build is part of a tap like [homebrew/science](https://github.com/Homebrew/homebrew-science/issues) or [homebrew/dupes](https://github.com/Homebrew/homebrew-dupes/issues) check those issue trackers instead. +* Make sure you search issues on the correct repository. If a formula that has failed to build is part of a tap like [homebrew/science](https://github.com/Homebrew/homebrew-science/issues) or a cask is part of [caskroom/cask](https://github.com/caskroom/homebrew-cask/issues) check those issue trackers instead. ## Create an issue diff --git a/docs/Xcode.md b/docs/Xcode.md index 759bf63c4..7c2cca5c8 100644 --- a/docs/Xcode.md +++ b/docs/Xcode.md @@ -12,7 +12,7 @@ Tools available for your platform: | 10.9 | 6.2 | 6.2 | | 10.10 | 7.2.1 | 7.2 | | 10.11 | 8.2.1 | 8.2 | -| 10.12 | 8.3 | 8.3 | +| 10.12 | 8.3.2 | 8.3 | ## Compiler version database @@ -70,6 +70,8 @@ Tools available for your platform: | 8.2 | β | β | β | β | 8.0 (800.0.42.1)| β | | 8.2.1 | β | β | β | β | 8.0 (800.0.42.1)| β | | 8.3 | β | β | β | β | 8.1 (802.0.38) | β | +| 8.3.1 | β | β | β | β | 8.1 (802.0.41) | β | +| 8.3.2 | β | β | β | β | 8.1 (802.0.42) | β | ## References to Xcode and compiler versions in code When a new Xcode release is made, the following things need to be diff --git a/docs/_config.yml b/docs/_config.yml index dce1f3bd3..2bdba509e 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,3 +1,4 @@ +include: [.well-known] exclude: [bin, vendor, CNAME, Gemfile, Gemfile.lock] gems: diff --git a/docs/_layouts/index.html b/docs/_layouts/index.html index 570718287..daf4fb6c0 100644 --- a/docs/_layouts/index.html +++ b/docs/_layouts/index.html @@ -1,7 +1,7 @@ --- layout: base --- -<div id="informations"> +<div id="information"> <ul> <li> <div class="group row"> diff --git a/docs/css/reset.css b/docs/css/reset.css deleted file mode 100644 index 1c85489d6..000000000 --- a/docs/css/reset.css +++ /dev/null @@ -1,53 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ */ -/* v1.0 | 20080212 */ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, font, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td { - margin: 0; - padding: 0; - border: 0; - outline: 0; - font-size: 100%; - vertical-align: baseline; - background: transparent; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} - -/* remember to define focus styles! */ -:focus { - outline: 0; -} - -/* remember to highlight inserts somehow! */ -ins { - text-decoration: none; -} -del { - text-decoration: line-through; -} - -/* tables still need 'cellspacing="0"' in the markup */ -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/docs/css/screen.css b/docs/css/screen.css deleted file mode 100644 index 2f1e84ce1..000000000 --- a/docs/css/screen.css +++ /dev/null @@ -1,362 +0,0 @@ -/* **************************************************** - - @file screen.css - @description Screen stylesheet - vim: set noet ts=4 fdm=marker fenc=utf-8: - -***************************************************** */ - -@import url("./reset.css"); - -/* @section Basic {{{ -******************************************************************************/ - -html { - font-size: 62.5%; - font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Roboto", sans-serif; -} - -html, body { height: 100%; } - -body { - font-size: 150%; - line-height: 1.4; - color: #F9D094; - background: #2E2A24; - position: relative; - behavior: url("/js/ie6/csshover.htc"); - padding: 0 30px; -} - -p,ul,ol,dl,table,pre { margin-bottom: 1em; } -ul { margin-left: 20px; } -a { text-decoration: none; cursor: pointer; color: #ba832c; font-weight: bold; } -a:focus { outline: 1px dotted; } -a:visited { } -a:hover, a:focus { color: #d3a459; text-decoration: none; } -a *, button * { cursor: pointer; } -hr { display: none; } -small { font-size: 90%; } -input, select, button, textarea, option { font-size: 100%; } -button, label, select, option, input[type=submit] { cursor: pointer; } -.group:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } .group {display: inline-block;} -/* Hides from IE-mac \*/ * html .group {height: 1%;} .group {display: block;} /* End hide from IE-mac */ -sup { font-size: 80%; line-height: 1; vertical-align: super; } -button::-moz-focus-inner { border: 0; padding: 1px; } -span.amp { font-weight: normal; font-style: italic; font-size: 1.2em; line-height: 0.8; } -h1,h2,h3,h4,h5,h6 { line-height: 1.1; } - -::selection { background: #745626; } -::-moz-selection { background: #745626; } - -h1, h2, h3 { - font-size: 420%; - margin: 0 0 0.1em; - font-weight: 900; - text-shadow: 1px 1px 10px rgba(0,0,0,0.25); -} - -h2 { - font-size: 300%; - text-align: center; - font-weight: 800; - color: #F9D094; - margin-top: 0.5em; - margin-bottom: 0.1em; -} - -h3 { - font-size: 125%; - text-align: center; - font-weight: 800; - color: #F9D094; - margin-top: 0.5em; - margin-bottom: 0.1em; -} - -#forkme { - width: 149px; - height: 149px; - position: absolute; - top: 0; - right: 0; - border: 0 -} - -h1 a, -h1 a:hover { - color: #F9D094; - font-weight: 900; - text-decoration: none; -} - -#wrap { - width: 57em; - /*width: 760px;*/ - max-width: 100%; - margin: 0 auto; - padding: 15px 0 0; -} - -#header { - text-align: center; - margin-bottom: 1em; -} - -#language { - margin-bottom: 2em; -} - -pre { - background: rgba(0,0,0,0.3); - color: #fff; - padding: 8px 10px; - border-radius: 0.4em; - -moz-border-radius: 0.4em; - -webkit-border-radius: 0.4em; - overflow-x: auto; -} - -pre code { - font-family: "Monaco", "Menlo", monospace; - font-size: 11px; - line-height: 1.6; -} - -#selectable { - font-size: 13px; -} - -.avatar { - border-radius: 0.4em; - overflow: hidden; - margin-right: 0.5em; - vertical-align: middle; -} - -#home, #page, .postcontent { - font-size: 1.2em; - min-width: 25em; - max-width: 35em; - margin: 0 auto; - margin-top: 1em; - padding-top: 1em; - padding-bottom: 1em; -} - -#home img, #page img, .postcontent img { - min-width: 25em; - max-width: 35em; -} - -#home th, #page th, .postcontent th, #home td, #page td, .postcontent td { - padding: 0.25em 0.5em; -} - -#post, #home, #page, .singlepostcontent, .posts li { - border-top: 1px solid rgba(255,255,255,0.08); - box-shadow: 0 -1px 0 rgba(0,0,0,0.5); -} - -#home ol, #page ol, .postcontent ol { - list-style: decimal; -} - -#home ul, #page ul, .postcontent ul { - list-style: disc; -} - -#home h1, #page h1 { - font-size: 250%; - font-weight: 800; - text-align: center; - padding-bottom: 0.5em; -} - -#home h2, #page h2 { - font-size: 175%; - font-weight: 700; - text-align: left; - padding-bottom: 0.3em; -} - -#home h3, #page h3 { - font-size: 150%; - font-weight: 700; - text-align: left; - padding-bottom: 0.3em; -} - -#home code, #page code { - font-size: 100%; -} - -#home pre code, #page pre code { - font-size: 80%; -} - -/*}}}*/ - -/* @section Informations {{{ -******************************************************************************/ - -#informations { - border-top: 1px solid rgba(0,0,0,0.5); -} - -#informations ul { - margin: 0; -} - -#informations .row, #border-bottom { - border-bottom: 1px solid rgba(0,0,0,0.5); - border-top: 1px solid rgba(255,255,255,0.08); - padding: 2em 20px 0; -} - -#informations .row .col-1 { - width: 49%; - float: left; - padding: 0 0 1em; -} - -#informations .row .col-2 { - width: 49%; - float: right; - padding: 0 0 1em; -} - -@media screen and (min-width: 700px) { - #informations .highlight { - margin-inline-end: 0; - -moz-margin-end: 0; - -webkit-margin-end: 0; - } -} - -.button { - text-align: center; - margin: 1em 0 2em; -} - -#informations .button a { - background: rgba(162,107,20,0.3); - padding: 8px 10px 6px; - border-radius: 0.4em; - -moz-border-radius: 0.4em; - -webkit-border-radius: 0.4em; - box-shadow: 0 0 5px rgba(0,0,0,0.4); - -moz-box-shadow: 0 0 5px rgba(0,0,0,0.4); - -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.4); - text-decoration: none; - font-size: larger; -} - -#informations .button a:hover { - background: rgba(162,107,20,0.25); -} - -#informations .button-large { - padding: 2em 0 1em; - font-size: 120%; -} - -#informations .quote { - text-align: center; - color: #816f51; - padding-bottom: 2em; -} - -#informations .quote blockquote { - font-size: 140%; - padding: 0 15%; -} - -#informations .quote blockquote span { - font-size: 140%; - line-height: 0.5; - vertical-align: sub; -} - -#informations .quote cite { - font-style: normal; -} - -#informations .quote cite a { - font-weight: normal; -} - -#informations .credits, #border-no-bottom { - border-bottom: none; - font-size: 70%; - text-align: center; - padding-top: 1.8em; - opacity: 0.5; -} - -#informations .credits p { - margin: 0; - padding: 0 0 0.7em; -} - -/*}}}*/ - -/* @section Mobile {{{ -******************************************************************************/ -@media screen and (max-width: 700px) { - body { - padding: 0px; - } - - h1 { - font-size: 350%; - } - - h2 { - font-size: 250%; - } - - #forkme { - width: 100px; - height: 100px; - } - - #informations .row .col-1 { - width: 100%; - padding: 0; - margin: 0; - } - - #informations .row .col-2 { - width: 100%; - float: left; - } - pre code#selectable { - width: 90%; - margin: 0 auto; - } -} -/*}}}*/ - -/* @section RTL {{{ -******************************************************************************/ -[dir="rtl"] ul { margin-left: 0; margin-right: 20px; } - -[dir="rtl"] pre { - direction: ltr; - text-align: left; -} - -[dir="rtl"] #informations .row .col-1 { - float: right; -} - -[dir="rtl"] #informations .row .col-2 { - float: left; -} - -@media screen and (max-width: 700px) { - [dir="rtl"] #informations .row .col-2 { - float: right; - } -} diff --git a/docs/css/screen.scss b/docs/css/screen.scss new file mode 100644 index 000000000..5acdf4657 --- /dev/null +++ b/docs/css/screen.scss @@ -0,0 +1,488 @@ +--- +--- + +$color_peach_orange_approx: #f9d094; +$color_rangitoto_approx: #2e2a24; +$color_marigold_approx: #ba832c; +$color_di_serria_approx: #d3a459; +$color_dallas_approx: #745626; +$black_25: rgba(0, 0, 0, 0.25); +$black_30: rgba(0, 0, 0, 0.3); +$white: #fff; +$white_8: rgba(255, 255, 255, 0.08); +$black_50: rgba(0, 0, 0, 0.5); +$black_40: rgba(0, 0, 0, 0.4); +$black_20: rgba(0, 0, 0, 0.2); +$color_reno_sand_30_approx: rgba(162, 107, 20, 0.3); +$color_reno_sand_25_approx: rgba(162, 107, 20, 0.25); +$color_shadow_approx: #816f51; + +@mixin border-radius($radius) { + border-radius: $radius; + -moz-border-radius: $radius; + -webkit-border-radius: $radius; +} + +@mixin box-shadow($x, $y, $blur, $color) { + box-shadow: $x $y $blur $color; + -moz-box-shadow: $x $y $blur $color; + -webkit-box-shadow: $x $y $blur $color; +} + +@mixin margin-inline-end($margin) { + margin-inline-end: $margin; + -moz-margin-end: $margin; + -webkit-margin-end: $margin; +} + +h1, h2, h3 { + font-size: 420%; + color: $color_peach_orange_approx; + margin: 0 0 0.1em; + text-align: center; + text-shadow: 1px 1px 10px $black_25; +} + +h1, h2, h3, h4, h5, h6 { + line-height: 1.1; +} + +h1 { + font-weight: 900; + a, a:hover { + font-weight: 900; + color: $color_peach_orange_approx; + } +} + +h2, h3 { + font-weight: 800; + margin-top: 0.5em; + margin-bottom: 0.1em; +} + +h2 { + font-size: 300%; +} + +h3 { + font-size: 125%; +} + +#home, #page { + h1 { + font-size: 250%; + font-weight: 800; + padding-bottom: 0.5em; + } + + h2, h3 { + font-weight: 700; + text-align: left; + padding-bottom: 0.3em; + } + + h2 { + font-size: 175%; + } + + h3 { + font-size: 150%; + } +} + +#home, #page, .postcontent { + font-size: 1.2em; + min-width: 25em; + max-width: 35em; + margin: 0 auto; + margin-top: 1em; + padding-top: 1em; + padding-bottom: 1em; +} + +#information .row, #border-bottom { + border-bottom: 1px solid $black_50; + border-top: 1px solid $white_8; + padding: 2em 20px 0; +} + +html { + margin: 0; + padding: 0; + font-size: 62.5%; + font-family: "-apple-system", "BlinkMacSystemFont", "Helvetica Neue", "Roboto", "sans-serif"; + height: 100%; +} + +body { + height: 100%; + font-size: 150%; + line-height: 1.4; + color: $color_peach_orange_approx; + background: $color_rangitoto_approx; + position: relative; + margin: 0; + padding: 0 30px; +} + +p { + margin: 0 0 1em 0; +} + +ul, ol, dl { + margin-bottom: 1em; +} + +ul { + margin-left: 20px; +} + +table { + margin-bottom: 1em; + border-collapse: separate; + border-spacing: 0; + border: solid $black_40 1px; + @include border-radius(0.4em); + margin-top: 1em; + td { + padding: 0.1em 1em; + } + + code { + font-size: 130%; + } + + tr { + &:nth-child(odd) { + background: $black_20; + } + + th { + background: $black_40; + } + + &:nth-child(even) { + background: $black_40; + } + } +} + +pre { + margin: 0 0 1em 0; + background: $black_30; + color: $white; + padding: 8px 10px; + @include border-radius(0.4em); + overflow-x: auto; + code { + font-family: "Monaco", "Menlo", "monospace"; + font-size: 11px; + line-height: 1.6; + } +} + +a { + text-decoration: none; + color: $color_marigold_approx; + font-weight: bold; + &:focus { + outline: 1px dotted; + color: $color_di_serria_approx; + text-decoration: underline; + } + + &:hover { + color: $color_di_serria_approx; + text-decoration: underline; + } +} + +button, input, select, textarea, option { + font-size: 100%; +} + +a, a *, button, button *, select, option, label, input[type=submit] { + cursor: pointer; +} + +hr { + display: none; +} + +small { + font-size: 90%; +} + +.group { + display: block; + &:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + } +} + +sup { + font-size: 80%; + line-height: 1; + vertical-align: super; +} + +button::-moz-focus-inner { + border: 0; + padding: 1px; +} + +::selection, ::-moz-selection { + background: $color_dallas_approx; +} + +#forkme { + width: 149px; + height: 149px; + position: absolute; + top: 0; + right: 0; + border: 0; +} + +#wrap { + width: 57em; + max-width: 100%; + margin: 0 auto; + padding: 15px 0 0; +} + +#header { + text-align: center; + margin-bottom: 1em; +} + +#language { + margin-bottom: 2em; +} + +#selectable { + font-size: 13px; +} + +.avatar { + @include border-radius(0.4em); + overflow: hidden; + margin-right: 0.5em; + vertical-align: middle; +} + +#home, #page, #post, .singlepostcontent { + border-top: 1px solid $white_8; + @include box-shadow(0, -1px, 0, $black_50); +} + +#home, #page { + code { + font-size: 100%; + } + + pre code { + font-size: 80%; + } +} + +#home, #page, .postcontent { + img { + min-width: 25em; + max-width: 35em; + } + + th, td { + padding: 0.25em 0.5em; + } +} + +.full-width { + width: 100%; +} + +th { + padding: 0.1em 1em; +} + +.number-data { + text-align: right; +} + +#information ul, .posts { + list-style: none; + padding: 0; + margin: 0; +} + +#information { + border-top: 1px solid $black_50; + .row { + .col-1 { + width: 49%; + float: left; + padding: 0 0 1em; + } + + .col-2 { + width: 49%; + float: right; + padding: 0 0 1em; + } + } + + .button-large { + padding: 2em 0 1em; + font-size: 120%; + } + + .quote { + text-align: center; + color: $color_shadow_approx; + padding-bottom: 2em; + blockquote { + font-size: 140%; + padding: 0 15%; + span { + font-size: 140%; + line-height: 0.5; + vertical-align: sub; + } + } + + cite { + font-style: normal; + a { + font-weight: normal; + } + } + } + + .credits { + border-bottom: none; + font-size: 70%; + text-align: center; + padding-top: 1.8em; + opacity: 0.5; + p { + margin: 0; + padding: 0 0 0.7em; + } + } + .button a { + background: $color_reno_sand_30_approx; + padding: 8px 10px 6px; + @include border-radius(0.4em); + @include box-shadow(0, 0, 5px, $black_40); + font-size: larger; + &:hover { + background: $color_reno_sand_25_approx; + } + } +} + +.button { + text-align: center; + margin: 1em 0 2em; +} + +#border-no-bottom { + border-bottom: none; + font-size: 70%; + text-align: center; + padding-top: 1.8em; + opacity: 0.5; +} + +* html .group { + height: 1%; +} + +span .amp { + font-weight: normal; + font-style: italic; + font-size: 1.2em; + line-height: 0.8; +} + +.posts li { + border-top: 1px solid $white_8; + @include box-shadow(0, -1px, 0, $black_50); +} + +[dir="rtl"] { + ul { + margin-left: 0; + margin-right: 20px; + } + + pre { + direction: ltr; + text-align: left; + } + + #information .row { + .col-1 { + float: right; + } + .col-2 { + float: left; + } + } +} + +@media screen and(min-width: 700px) { + #information .highlight { + @include margin-inline-end(0); + } +} + +@media screen and(max-width: 700px) { + body { + padding: 0; + } + + #post, #page, .posts { + margin: 0 1em; + } + + h1 { + font-size: 350%; + } + + h2 { + font-size: 250%; + } + + #forkme { + width: 100px; + height: 100px; + } + + #information .row { + .col-1 { + width: 100%; + padding: 0; + margin: 0; + } + + .col-2 { + width: 100%; + float: left; + } + } + + pre code#selectable { + width: 90%; + margin: 0 auto; + } + + [dir="rtl"] #information .row .col-2 { + float: right; + } +} diff --git a/manpages/brew-cask.1 b/manpages/brew-cask.1 index e45824927..bde8ff3c2 100644 --- a/manpages/brew-cask.1 +++ b/manpages/brew-cask.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BREW\-CASK" "1" "April 2017" "Homebrew" "brew-cask" +.TH "BREW\-CASK" "1" "May 2017" "Homebrew" "brew-cask" . .SH "NAME" \fBbrew\-cask\fR \- a friendly binary installer for macOS diff --git a/manpages/brew.1 b/manpages/brew.1 index 6c30b60cf..ca11439a6 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BREW" "1" "April 2017" "Homebrew" "brew" +.TH "BREW" "1" "May 2017" "Homebrew" "brew" . .SH "NAME" \fBbrew\fR \- The missing package manager for macOS @@ -415,7 +415,7 @@ Start a Homebrew build environment shell\. Uses our years\-battle\-hardened Home If \fB\-\-env=std\fR is passed, use the standard \fBPATH\fR instead of superenv\'s\. . .TP -\fBstyle\fR [\fB\-\-fix\fR] [\fB\-\-display\-cop\-names\fR] [\fIfiles\fR|\fItaps\fR|\fIformulae\fR] +\fBstyle\fR [\fB\-\-fix\fR] [\fB\-\-display\-cop\-names\fR] [\fB\-\-only\-cops=\fR[COP1,COP2\.\.]|\fB\-\-except\-cops=\fR[COP1,COP2\.\.]] [\fIfiles\fR|\fItaps\fR|\fIformulae\fR] Check formulae or files for conformance to Homebrew style guidelines\. . .IP @@ -428,6 +428,12 @@ If \fB\-\-fix\fR is passed, style violations will be automatically fixed using R If \fB\-\-display\-cop\-names\fR is passed, the RuboCop cop name for each violation is included in the output\. . .IP +If \fB\-\-only\-cops\fR is passed, only the given Rubocop cop(s)\' violations would be checked\. +. +.IP +If \fB\-\-except\-cops\fR is passed, the given Rubocop cop(s)\' checks would be skipped\. +. +.IP Exits with a non\-zero status if any style violations are found\. . .TP @@ -631,7 +637,7 @@ Print the version number of Homebrew to standard output and exit\. .SH "DEVELOPER COMMANDS" . .TP -\fBaudit\fR [\fB\-\-strict\fR] [\fB\-\-fix\fR] [\fB\-\-online\fR] [\fB\-\-new\-formula\fR] [\fB\-\-display\-cop\-names\fR] [\fB\-\-display\-filename\fR] [\fIformulae\fR] +\fBaudit\fR [\fB\-\-strict\fR] [\fB\-\-fix\fR] [\fB\-\-online\fR] [\fB\-\-new\-formula\fR] [\fB\-\-display\-cop\-names\fR] [\fB\-\-display\-filename\fR] [\fB\-\-only=\fR\fImethod\fR|\fB\-\-except=\fR\fImethod\fR] [\fB\-\-only\-cops=\fR[COP1,COP2\.\.]|\fB\-\-except\-cops=\fR[COP1,COP2\.\.]] [\fIformulae\fR] Check \fIformulae\fR for Homebrew coding style violations\. This should be run before submitting a new formula\. . .IP @@ -656,6 +662,18 @@ If \fB\-\-display\-cop\-names\fR is passed, the RuboCop cop name for each violat If \fB\-\-display\-filename\fR is passed, every line of output is prefixed with the name of the file or formula being audited, to make the output easy to grep\. . .IP +If \fB\-\-only\fR is passed, only the methods named \fBaudit_<method>\fR will be run\. +. +.IP +If \fB\-\-except\fR is passed, the methods named \fBaudit_<method>\fR will not be run\. +. +.IP +If \fB\-\-only\-cops\fR is passed, only the given Rubocop cop(s)\' violations would be checked\. +. +.IP +If \fB\-\-except\-cops\fR is passed, the given Rubocop cop(s)\' checks would be skipped\. +. +.IP \fBaudit\fR exits with a non\-zero status if any errors are found\. This is useful, for instance, for implementing pre\-commit hooks\. . .TP @@ -729,7 +747,7 @@ Generate a formula for the downloadable file at \fIURL\fR and open it in the edi If \fB\-\-autotools\fR is passed, create a basic template for an Autotools\-style build\. If \fB\-\-cmake\fR is passed, create a basic template for a CMake\-style build\. If \fB\-\-meson\fR is passed, create a basic template for a Meson\-style build\. . .IP -If \fB\-\-no\-fetch\fR is passed, Homebrew will not download \fIURL\fR to the cache and will thus not add the SHA256 to the formula for you\. +If \fB\-\-no\-fetch\fR is passed, Homebrew will not download \fIURL\fR to the cache and will thus not add the SHA256 to the formula for you\. It will also not check the GitHub API for GitHub projects (to fill out the description and homepage)\. . .IP The options \fB\-\-set\-name\fR and \fB\-\-set\-version\fR each take an argument and allow you to explicitly set the name and version of the package you are creating\. @@ -923,29 +941,6 @@ Homebrew can install formulae via URL, e\.g\. \fBhttps://raw\.github\.com/Homebr .SH "ENVIRONMENT" . .TP -\fBAWS_ACCESS_KEY_ID\fR, \fBAWS_SECRET_ACCESS_KEY\fR -When using the \fBS3\fR download strategy, Homebrew will look in these variables for access credentials (see \fIhttps://docs\.aws\.amazon\.com/cli/latest/userguide/cli\-chap\-getting\-started\.html#cli\-environment\fR to retrieve these access credentials from AWS)\. If they are not set, the \fBS3\fR download strategy will download with a public (unsigned) URL\. -. -.TP -\fBBROWSER\fR -If set, and \fBHOMEBREW_BROWSER\fR is not, use \fBBROWSER\fR as the web browser when opening project homepages\. -. -.TP -\fBEDITOR\fR -If set, and \fBHOMEBREW_EDITOR\fR and \fBVISUAL\fR are not, use \fBEDITOR\fR as the text editor\. -. -.TP -\fBGIT\fR -When using Git, Homebrew will use \fBGIT\fR if set, a Homebrew\-built Git if installed, or the system\-provided binary\. -. -.IP -Set this to force Homebrew to use a particular git binary\. -. -.TP -\fBHOMEBREW_BOTTLE_DOMAIN\fR -If set, instructs Homebrew to use the given URL as a download mirror for bottles\. -. -.TP \fBHOMEBREW_ARTIFACT_DOMAIN\fR If set, instructs Homebrew to use the given URL as a download mirror for bottles and binaries\. . @@ -957,6 +952,14 @@ If set, Homebrew will only check for autoupdates once per this seconds interval\ \fIDefault:\fR \fB60\fR\. . .TP +\fBHOMEBREW_AWS_ACCESS_KEY_ID\fR, \fBHOMEBREW_AWS_SECRET_ACCESS_KEY\fR +When using the \fBS3\fR download strategy, Homebrew will look in these variables for access credentials (see \fIhttps://docs\.aws\.amazon\.com/cli/latest/userguide/cli\-chap\-getting\-started\.html#cli\-environment\fR to retrieve these access credentials from AWS)\. If they are not set, the \fBS3\fR download strategy will download with a public (unsigned) URL\. +. +.TP +\fBHOMEBREW_BOTTLE_DOMAIN\fR +If set, instructs Homebrew to use the given URL as a download mirror for bottles\. +. +.TP \fBHOMEBREW_BROWSER\fR If set, uses this setting as the browser when opening project homepages, instead of the OS default browser\. . @@ -1003,6 +1006,13 @@ If set, Homebrew will use this editor when editing a single formula, or several If set, Homebrew will always use its vendored, relocatable Ruby 2\.0 version even if the system version of Ruby is >=2\.0\. . .TP +\fBHOMEBREW_GIT\fR +When using Git, Homebrew will use \fBGIT\fR if set, a Homebrew\-built Git if installed, or the system\-provided binary\. +. +.IP +Set this to force Homebrew to use a particular git binary\. +. +.TP \fBHOMEBREW_GITHUB_API_TOKEN\fR A personal access token for the GitHub API, which you can create at \fIhttps://github\.com/settings/tokens\fR\. If set, GitHub will allow you a greater number of API requests\. See \fIhttps://developer\.github\.com/v3/#rate\-limiting\fR for more information\. Homebrew uses the GitHub API for features such as \fBbrew search\fR\. . @@ -1068,10 +1078,6 @@ This issue typically occurs when using FileVault or custom SSD configurations\. \fBHOMEBREW_VERBOSE\fR If set, Homebrew always assumes \fB\-\-verbose\fR when running commands\. . -.TP -\fBVISUAL\fR -If set, and \fBHOMEBREW_EDITOR\fR is not, use \fBVISUAL\fR as the text editor\. -. .SH "USING HOMEBREW BEHIND A PROXY" Homebrew uses several commands for downloading files (e\.g\. \fBcurl\fR, \fBgit\fR, \fBsvn\fR)\. Many of these tools can download via a proxy\. It\'s common for these tools to read proxy parameters from environment variables\. . @@ -1114,7 +1120,7 @@ Homebrew\'s lead maintainer is Mike McQuaid\. Homebrew\'s current maintainers are Alyssa Ross, Andrew Janke, Baptiste Fontaine, Alex Dunn, FX Coudert, ilovezfs, Josh Hagins, JCount, Misty De Meo, neutric, Tomasz Pajor, Markus Reiter, Tim Smith, Tom Schoonjans, Uladzislau Shablinski and William Woodruff\. . .P -Former maintainers with significant contributions include Xu Cheng, Martin Afanasjew, Dominyk Tiller, Brett Koonce, Jack Nagel, Adam Vandenberg and Homebrew\'s creator: Max Howell\. +Former maintainers with significant contributions include Xu Cheng, Martin Afanasjew, Dominyk Tiller, Brett Koonce, Charlie Sharpsteen, Jack Nagel, Adam Vandenberg and Homebrew\'s creator: Max Howell\. . .SH "BUGS" See our issues on GitHub: |
