diff options
| author | ilovezfs | 2017-08-07 14:31:56 -0700 | 
|---|---|---|
| committer | GitHub | 2017-08-07 14:31:56 -0700 | 
| commit | 986887b413897413266151c92d5071d0a82cf966 (patch) | |
| tree | e39a40888f007b39909711a76075ae822ab364ae | |
| parent | 6ef49d8b86e436cb37df1344019189a2f2df0bbb (diff) | |
| download | brew-986887b413897413266151c92d5071d0a82cf966.tar.bz2 | |
Revert "Refactor SVN and cURL download strategies."
21 files changed, 246 insertions, 186 deletions
| diff --git a/Library/Homebrew/cask/lib/hbc/audit.rb b/Library/Homebrew/cask/lib/hbc/audit.rb index b8bb6ab81..cee1fe807 100644 --- a/Library/Homebrew/cask/lib/hbc/audit.rb +++ b/Library/Homebrew/cask/lib/hbc/audit.rb @@ -143,15 +143,7 @@ module Hbc      def check_appcast_http_code        odebug "Verifying appcast returns 200 HTTP response code" - -      curl_executable, *args = curl_args( -        "--compressed", "--location", "--fail", -        "--write-out", "%{http_code}", -        "--output", "/dev/null", -        cask.appcast, -        user_agent: :fake -      ) -      result = @command.run(curl_executable, args: args, print_stderr: false) +      result = @command.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--output", "/dev/null", "--write-out", "%{http_code}", cask.appcast], print_stderr: false)        if result.success?          http_code = result.stdout.chomp          add_warning "unexpected HTTP response code retrieving appcast: #{http_code}" unless http_code == "200" diff --git a/Library/Homebrew/cask/lib/hbc/cask_loader.rb b/Library/Homebrew/cask/lib/hbc/cask_loader.rb index dd9c61089..500314671 100644 --- a/Library/Homebrew/cask/lib/hbc/cask_loader.rb +++ b/Library/Homebrew/cask/lib/hbc/cask_loader.rb @@ -71,7 +71,7 @@ module Hbc          begin            ohai "Downloading #{url}." -          curl_download url, to: path +          curl url, "-o", path          rescue ErrorDuringExecution            raise CaskUnavailableError.new(token, "Failed to download #{Formatter.url(url)}.")          end diff --git a/Library/Homebrew/cask/lib/hbc/container.rb b/Library/Homebrew/cask/lib/hbc/container.rb index fab3a3c1c..93e825e03 100644 --- a/Library/Homebrew/cask/lib/hbc/container.rb +++ b/Library/Homebrew/cask/lib/hbc/container.rb @@ -4,7 +4,6 @@ require "hbc/container/bzip2"  require "hbc/container/cab"  require "hbc/container/criteria"  require "hbc/container/dmg" -require "hbc/container/directory"  require "hbc/container/executable"  require "hbc/container/generic_unar"  require "hbc/container/gpg" @@ -15,7 +14,6 @@ require "hbc/container/otf"  require "hbc/container/pkg"  require "hbc/container/seven_zip"  require "hbc/container/sit" -require "hbc/container/svn_repository"  require "hbc/container/tar"  require "hbc/container/ttf"  require "hbc/container/rar" @@ -45,7 +43,6 @@ module Hbc          Xz,    # pure xz          Gpg,   # GnuPG signed data          Executable, -        SvnRepository,        ]        # for explicit use only (never autodetected):        # Hbc::Container::Naked diff --git a/Library/Homebrew/cask/lib/hbc/container/criteria.rb b/Library/Homebrew/cask/lib/hbc/container/criteria.rb index 52f171d6a..66ecb8c87 100644 --- a/Library/Homebrew/cask/lib/hbc/container/criteria.rb +++ b/Library/Homebrew/cask/lib/hbc/container/criteria.rb @@ -13,11 +13,9 @@ module Hbc        end        def magic_number(regex) -        return false if path.directory? -          # 262: length of the longest regex (currently: Hbc::Container::Tar) -        @magic_number ||= File.open(path, "rb") { |f| f.read(262) } -        @magic_number.match?(regex) +        @magic_number ||= File.open(@path, "rb") { |f| f.read(262) } +        @magic_number =~ regex        end      end    end diff --git a/Library/Homebrew/cask/lib/hbc/container/directory.rb b/Library/Homebrew/cask/lib/hbc/container/directory.rb deleted file mode 100644 index e4bb1095b..000000000 --- a/Library/Homebrew/cask/lib/hbc/container/directory.rb +++ /dev/null @@ -1,24 +0,0 @@ -require "hbc/container/base" - -module Hbc -  class Container -    class Directory < Base -      def self.me?(*) -        false -      end - -      def extract -        @path.children.each do |child| -          next if skip_path?(child) -          FileUtils.cp child, @cask.staged_path -        end -      end - -      private - -      def skip_path?(*) -        false -      end -    end -  end -end diff --git a/Library/Homebrew/cask/lib/hbc/container/executable.rb b/Library/Homebrew/cask/lib/hbc/container/executable.rb index af3b36fd1..848f6d4be 100644 --- a/Library/Homebrew/cask/lib/hbc/container/executable.rb +++ b/Library/Homebrew/cask/lib/hbc/container/executable.rb @@ -8,7 +8,7 @@ module Hbc          return true if criteria.magic_number(/^#!\s*\S+/)          begin -          criteria.path.file? && MachO.open(criteria.path).header.executable? +          MachO.open(criteria.path).header.executable?          rescue MachO::MagicError            false          end diff --git a/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb b/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb deleted file mode 100644 index cae613b2d..000000000 --- a/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb +++ /dev/null @@ -1,15 +0,0 @@ -require "hbc/container/directory" - -module Hbc -  class Container -    class SvnRepository < Directory -      def self.me?(criteria) -        criteria.path.join(".svn").directory? -      end - -      def skip_path?(path) -        path.basename.to_s == ".svn" -      end -    end -  end -end diff --git a/Library/Homebrew/cask/lib/hbc/download_strategy.rb b/Library/Homebrew/cask/lib/hbc/download_strategy.rb index 245ad4ade..016bb66e6 100644 --- a/Library/Homebrew/cask/lib/hbc/download_strategy.rb +++ b/Library/Homebrew/cask/lib/hbc/download_strategy.rb @@ -10,7 +10,7 @@ module Hbc    class AbstractDownloadStrategy      attr_reader :cask, :name, :url, :uri_object, :version -    def initialize(cask, command: SystemCommand) +    def initialize(cask, command = SystemCommand)        @cask       = cask        @command    = command        # TODO: this excess of attributes is a function of integrating @@ -33,8 +33,8 @@ module Hbc    class HbVCSDownloadStrategy < AbstractDownloadStrategy      REF_TYPES = [:branch, :revision, :revisions, :tag].freeze -    def initialize(*args, **options) -      super(*args, **options) +    def initialize(cask, command = SystemCommand) +      super        @ref_type, @ref = extract_ref        @clone = Hbc.cache.join(cache_filename)      end @@ -64,6 +64,11 @@ module Hbc    end    class CurlDownloadStrategy < AbstractDownloadStrategy +    # TODO: should be part of url object +    def mirrors +      @mirrors ||= [] +    end +      def tarball_path        @tarball_path ||= Hbc.cache.join("#{name}--#{version}#{ext}")      end @@ -90,8 +95,13 @@ module Hbc        end      end +    def downloaded_size +      temporary_path.size? || 0 +    end +      def _fetch -      curl_download url, *cask_curl_args, to: temporary_path, user_agent: uri_object.user_agent +      odebug "Calling curl with args #{cask_curl_args}" +      curl(*cask_curl_args)      end      def fetch @@ -121,12 +131,33 @@ module Hbc          ignore_interrupts { temporary_path.rename(tarball_path) }        end        tarball_path +    rescue CurlDownloadStrategyError +      raise if mirrors.empty? +      puts "Trying a mirror..." +      @url = mirrors.shift +      retry      end      private      def cask_curl_args -      cookies_args + referer_args +      default_curl_args.tap do |args| +        args.concat(user_agent_args) +        args.concat(cookies_args) +        args.concat(referer_args) +      end +    end + +    def default_curl_args +      [url, "-C", downloaded_size, "-o", temporary_path] +    end + +    def user_agent_args +      if uri_object.user_agent +        ["-A", uri_object.user_agent] +      else +        [] +      end      end      def cookies_args @@ -160,7 +191,8 @@ module Hbc    class CurlPostDownloadStrategy < CurlDownloadStrategy      def cask_curl_args -      super.concat(post_args) +      super +      default_curl_args.concat(post_args)      end      def post_args @@ -193,8 +225,8 @@ module Hbc      # super does not provide checks for already-existing downloads      def fetch -      if cached_location.directory? -        puts "Already downloaded: #{cached_location}" +      if tarball_path.exist? +        puts "Already downloaded: #{tarball_path}"        else          @url = @url.sub(/^svn\+/, "") if @url =~ %r{^svn\+http://}          ohai "Checking out #{@url}" @@ -220,8 +252,9 @@ module Hbc          else            fetch_repo @clone, @url          end +        compress        end -      cached_location +      tarball_path      end      # This primary reason for redefining this method is the trust_cert @@ -255,6 +288,10 @@ module Hbc                      print_stderr: false)      end +    def tarball_path +      @tarball_path ||= cached_location.dirname.join(cached_location.basename.to_s + "-#{@cask.version}.tar") +    end +      def shell_quote(str)        # Oh god escaping shell args.        # See http://notetoself.vrensk.com/2008/08/escaping-single-quotes-in-ruby-harder-than-expected/ @@ -267,5 +304,35 @@ module Hbc          yield name, url        end      end + +    private + +    # TODO/UPDATE: the tar approach explained below is fragile +    # against challenges such as case-sensitive filesystems, +    # and must be re-implemented. +    # +    # Seems nutty: we "download" the contents into a tape archive. +    # Why? +    # * A single file is tractable to the rest of the Cask toolchain, +    # * An alternative would be to create a Directory container type. +    #   However, some type of file-serialization trick would still be +    #   needed in order to enable calculating a single checksum over +    #   a directory.  So, in that alternative implementation, the +    #   special cases would propagate outside this class, including +    #   the use of tar or equivalent. +    # * SubversionDownloadStrategy.cached_location is not versioned +    # * tarball_path provides a needed return value for our overridden +    #   fetch method. +    # * We can also take this private opportunity to strip files from +    #   the download which are protocol-specific. + +    def compress +      Dir.chdir(cached_location) do +        @command.run!("/usr/bin/tar", +                      args:         ['-s/^\.//', "--exclude", ".svn", "-cf", Pathname.new(tarball_path), "--", "."], +                      print_stderr: false) +      end +      clear_cache +    end    end  end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb index fc7e83a20..d302d0946 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb @@ -12,11 +12,7 @@ module Hbc        end        def calculate_checkpoint -        curl_executable, *args = curl_args( -          "--compressed", "--location", "--fail", @uri, -          user_agent: :fake -        ) -        result = SystemCommand.run(curl_executable, args: args, print_stderr: false) +        result = SystemCommand.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--fail", @uri], print_stderr: false)          checkpoint = if result.success?            processed_appcast_text = result.stdout.gsub(%r{<pubDate>[^<]*</pubDate>}m, "") diff --git a/Library/Homebrew/cask/lib/hbc/system_command.rb b/Library/Homebrew/cask/lib/hbc/system_command.rb index b735ae4f9..901617b71 100644 --- a/Library/Homebrew/cask/lib/hbc/system_command.rb +++ b/Library/Homebrew/cask/lib/hbc/system_command.rb @@ -112,7 +112,11 @@ module Hbc                   processed_output[:stderr],                   processed_status.exitstatus)      end +  end +end +module Hbc +  class SystemCommand      class Result        attr_accessor :command, :stdout, :stderr, :exit_status diff --git a/Library/Homebrew/cask/lib/hbc/url.rb b/Library/Homebrew/cask/lib/hbc/url.rb index 8c652657b..15da2ced2 100644 --- a/Library/Homebrew/cask/lib/hbc/url.rb +++ b/Library/Homebrew/cask/lib/hbc/url.rb @@ -1,6 +1,8 @@  module Hbc    class URL -    attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data, :user_agent +    FAKE_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10) https://caskroom.github.io".freeze + +    attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data      extend Forwardable      def_delegators :uri, :path, :scheme, :to_s @@ -15,7 +17,7 @@ module Hbc      def initialize(uri, options = {})        @uri        = Hbc::UnderscoreSupportingURI.parse(uri) -      @user_agent = options.fetch(:user_agent, :default) +      @user_agent = options[:user_agent]        @cookies    = options[:cookies]        @referer    = options[:referer]        @using      = options[:using] @@ -23,5 +25,10 @@ module Hbc        @trust_cert = options[:trust_cert]        @data       = options[:data]      end + +    def user_agent +      return FAKE_USER_AGENT if @user_agent == :fake +      @user_agent +    end    end  end diff --git a/Library/Homebrew/cask/lib/hbc/verify/gpg.rb b/Library/Homebrew/cask/lib/hbc/verify/gpg.rb index f4996a5b5..dbb537756 100644 --- a/Library/Homebrew/cask/lib/hbc/verify/gpg.rb +++ b/Library/Homebrew/cask/lib/hbc/verify/gpg.rb @@ -33,7 +33,7 @@ module Hbc          meta_dir = cached || cask.metadata_subdir("gpg", :now, true)          sig_path = meta_dir.join("signature.asc") -        curl_download cask.gpg.signature, to: sig_path unless cached || force +        curl(cask.gpg.signature, "-o", sig_path.to_s) unless cached || force          sig_path        end diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index b3e0785a5..1f07fa89e 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -242,10 +242,12 @@ class FormulaAuditor    def self.http_content_headers_and_checksum(url, hash_needed: false, user_agent: :default)      max_time = hash_needed ? "600" : "25" -    output, = curl_output( -      "--connect-timeout", "15", "--include", "--max-time", max_time, "--location", url, -      user_agent: user_agent +    args = curl_args( +      extra_args: ["--connect-timeout", "15", "--include", "--max-time", max_time, url], +      show_output: true, +      user_agent: user_agent,      ) +    output = Open3.popen3(*args) { |_, stdout, _, _| stdout.read }      status_code = :unknown      while status_code == :unknown || status_code.to_s.start_with?("3") diff --git a/Library/Homebrew/dev-cmd/mirror.rb b/Library/Homebrew/dev-cmd/mirror.rb index 6445bc34c..e2492203d 100644 --- a/Library/Homebrew/dev-cmd/mirror.rb +++ b/Library/Homebrew/dev-cmd/mirror.rb @@ -25,9 +25,9 @@ module Homebrew             "public_download_numbers": true,             "public_stats": true}          EOS -        curl "--silent", "--fail", "--user", "#{bintray_user}:#{bintray_key}", -             "--header", "Content-Type: application/json", -             "--data", package_blob, bintray_repo_url +        curl "--silent", "--fail", "-u#{bintray_user}:#{bintray_key}", +             "-H", "Content-Type: application/json", +             "-d", package_blob, bintray_repo_url          puts        end @@ -40,8 +40,8 @@ module Homebrew        content_url = "https://api.bintray.com/content/homebrew/mirror"        content_url += "/#{bintray_package}/#{f.pkg_version}/#{filename}"        content_url += "?publish=1" -      curl "--silent", "--fail", "--user", "#{bintray_user}:#{bintray_key}", -           "--upload-file", download, content_url +      curl "--silent", "--fail", "-u#{bintray_user}:#{bintray_key}", +           "-T", download, content_url        puts        ohai "Mirrored #{filename}!"      end diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb index dd2bc6270..9681bb2bc 100644 --- a/Library/Homebrew/dev-cmd/pull.rb +++ b/Library/Homebrew/dev-cmd/pull.rb @@ -228,7 +228,7 @@ module Homebrew            "https://github.com/BrewTestBot/homebrew-#{tap.repo}/compare/homebrew:master...pr-#{issue}"          end -        curl "--silent", "--fail", "--output", "/dev/null", "--head", bottle_commit_url +        curl "--silent", "--fail", "-o", "/dev/null", "-I", bottle_commit_url          safe_system "git", "checkout", "--quiet", "-B", bottle_branch, orig_revision          pull_patch bottle_commit_url, "bottle commit" @@ -303,7 +303,7 @@ module Homebrew        extra_msg = @description ? "(#{@description})" : nil        ohai "Fetching patch #{extra_msg}"        puts "Patch: #{patch_url}" -      curl_download patch_url, to: patchpath +      curl patch_url, "-s", "-o", patchpath      end      def apply_patch @@ -433,10 +433,10 @@ module Homebrew      end      version = info.pkg_version      ohai "Publishing on Bintray: #{package} #{version}" -    curl "--write-out", '\n', "--silent", "--fail", -         "--user", "#{creds[:user]}:#{creds[:key]}", "--request", "POST", -         "--header", "Content-Type: application/json", -         "--data", '{"publish_wait_for_secs": 0}', +    curl "-w", '\n', "--silent", "--fail", +         "-u#{creds[:user]}:#{creds[:key]}", "-X", "POST", +         "-H", "Content-Type: application/json", +         "-d", '{"publish_wait_for_secs": 0}',           "https://api.bintray.com/content/homebrew/#{repo}/#{package}/#{version}/publish"      true    rescue => e @@ -587,7 +587,7 @@ module Homebrew          # We're in the cache; make sure to force re-download          loop do            begin -            curl_download url, to: filename +            curl url, "-o", filename              break            rescue              if retry_count >= max_curl_retries @@ -606,7 +606,7 @@ module Homebrew    end    def check_bintray_mirror(name, url) -    headers, = curl_output("--connect-timeout", "15", "--location", "--head", 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?("2")      opoo "The Bintray mirror #{url} is not reachable (HTTP status code #{status_code})." diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index 2a8b6e585..717334714 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -375,59 +375,75 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy        ohai "Downloading from #{url}"      end -    curl_download resolved_url(url), to: temporary_path +    urls = actual_urls(url) +    unless urls.empty? +      ohai "Downloading from #{urls.last}" +      if !ENV["HOMEBREW_NO_INSECURE_REDIRECT"].nil? && url.start_with?("https://") && +         urls.any? { |u| !u.start_with? "https://" } +        puts "HTTPS to HTTP redirect detected & HOMEBREW_NO_INSECURE_REDIRECT is set." +        raise CurlDownloadStrategyError, url +      end +      url = urls.last +    end + +    curl url, "-C", downloaded_size, "-o", temporary_path    end    # Curl options to be always passed to curl, -  # with raw head calls (`curl --head`) or with actual `fetch`. +  # with raw head calls (`curl -I`) or with actual `fetch`.    def _curl_opts -    return ["--user" << meta.fetch(:user)] if meta.key?(:user) -    [] +    copts = [] +    copts << "--user" << meta.fetch(:user) if meta.key?(:user) +    copts    end -  def resolved_url(url) -    redirect_url, _, status = curl_output( -      *_curl_opts, "--silent", "--head", -      "--write-out", "%{redirect_url}", -      "--output", "/dev/null", -      url.to_s -    ) - -    return url unless status.success? -    return url if redirect_url.empty? - -    ohai "Downloading from #{redirect_url}" -    if ENV["HOMEBREW_NO_INSECURE_REDIRECT"] && -       url.start_with?("https://") && !redirect_url.start_with?("https://") -      puts "HTTPS to HTTP redirect detected & HOMEBREW_NO_INSECURE_REDIRECT is set." -      raise CurlDownloadStrategyError, url +  def actual_urls(url) +    urls = [] +    curl_args = _curl_opts << "-I" << "-L" << url +    Utils.popen_read("curl", *curl_args).scan(/^Location: (.+)$/).map do |m| +      urls << URI.join(urls.last || url, m.first.chomp).to_s      end +    urls +  end -    redirect_url +  def downloaded_size +    temporary_path.size? || 0    end -  def curl(*args, **options) +  def curl(*args)      args.concat _curl_opts      args << "--connect-timeout" << "5" unless mirrors.empty? -    super(*args, **options) +    super    end  end  # Detect and download from Apache Mirror  class CurlApacheMirrorDownloadStrategy < CurlDownloadStrategy    def apache_mirrors -    mirrors, = Open3.capture3( -      *curl_args(*_curl_opts, "--silent", "--location", "#{@url}&asjson=1"), -    ) +    rd, wr = IO.pipe +    buf = "" + +    pid = fork do +      ENV.delete "HOMEBREW_CURL_VERBOSE" +      rd.close +      $stdout.reopen(wr) +      $stderr.reopen(wr) +      curl "#{@url}&asjson=1" +    end +    wr.close -    JSON.parse(mirrors) +    rd.readline if ARGV.verbose? # Remove Homebrew output +    buf << rd.read until rd.eof? +    rd.close +    Process.wait(pid) +    buf    end    def _fetch      return super if @tried_apache_mirror      @tried_apache_mirror = true -    mirrors = apache_mirrors +    mirrors = JSON.parse(apache_mirrors)      path_info = mirrors.fetch("path_info")      @url = mirrors.fetch("preferred") + path_info      @mirrors |= %W[https://archive.apache.org/dist/#{path_info}] @@ -444,7 +460,7 @@ end  class CurlPostDownloadStrategy < CurlDownloadStrategy    def _fetch      base_url, data = @url.split("?") -    curl_download base_url, "--data", data, to: temporary_path +    curl base_url, "-d", data, "-C", downloaded_size, "-o", temporary_path    end  end @@ -514,7 +530,7 @@ class S3DownloadStrategy < CurlDownloadStrategy        s3url = obj.public_url      end -    curl_download s3url, to: temporary_path +    curl s3url, "-C", downloaded_size, "-o", temporary_path    end  end @@ -550,7 +566,7 @@ class GitHubPrivateRepositoryDownloadStrategy < CurlDownloadStrategy    end    def _fetch -    curl_download download_url, to: temporary_path +    curl download_url, "-C", downloaded_size, "-o", temporary_path    end    private @@ -599,7 +615,7 @@ class GitHubPrivateRepositoryReleaseDownloadStrategy < GitHubPrivateRepositoryDo    def _fetch      # HTTP request header `Accept: application/octet-stream` is required.      # Without this, the GitHub API will respond with metadata, not binary. -    curl_download download_url, "--header", "Accept: application/octet-stream", to: temporary_path +    curl download_url, "-C", downloaded_size, "-o", temporary_path, "-H", "Accept: application/octet-stream"    end    private @@ -899,27 +915,18 @@ class GitHubGitDownloadStrategy < GitDownloadStrategy    def github_last_commit      return if ENV["HOMEBREW_NO_GITHUB_API"] -    output, _, status = curl_output( -      "--silent", "--head", "--location", -      "-H", "Accept: application/vnd.github.v3.sha", -      "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{@ref}" -    ) - -    return unless status.success? +    output, _, status = curl_output "-H", "Accept: application/vnd.github.v3.sha", \ +      "-I", "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{@ref}" -    commit = output[/^ETag: \"(\h+)\"/, 1] +    commit = output[/^ETag: \"(\h+)\"/, 1] if status.success?      version.update_commit(commit) if commit      commit    end    def multiple_short_commits_exist?(commit)      return if ENV["HOMEBREW_NO_GITHUB_API"] - -    output, _, status = curl_output( -      "--silent", "--head", "--location", -      "-H", "Accept: application/vnd.github.v3.sha", -      "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{commit}" -    ) +    output, _, status = curl_output "-H", "Accept: application/vnd.github.v3.sha", \ +      "-I", "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{commit}"      !(status.success? && output && output[/^Status: (200)/, 1] == "200")    end @@ -1152,13 +1159,15 @@ class DownloadStrategyDetector        SubversionDownloadStrategy      when %r{^cvs://}        CVSDownloadStrategy -    when %r{^hg://}, %r{^https?://(.+?\.)?googlecode\.com/hg} +    when %r{^https?://(.+?\.)?googlecode\.com/hg} +      MercurialDownloadStrategy +    when %r{^hg://}        MercurialDownloadStrategy      when %r{^bzr://}        BazaarDownloadStrategy      when %r{^fossil://}        FossilDownloadStrategy -    when %r{^svn\+http://}, %r{^http://svn\.apache\.org/repos/} +    when %r{^http://svn\.apache\.org/repos/}, %r{^svn\+http://}        SubversionDownloadStrategy      when %r{^https?://(.+?\.)?sourceforge\.net/hgweb/}        MercurialDownloadStrategy diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index dd67b4f24..195d15cec 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -165,7 +165,7 @@ module Formulary      def load_file        HOMEBREW_CACHE_FORMULA.mkpath        FileUtils.rm_f(path) -      curl_download url, to: path +      curl url, "-o", path        super      rescue MethodDeprecatedError => e        if url =~ %r{github.com/([\w-]+)/homebrew-([\w-]+)/} diff --git a/Library/Homebrew/test/cask/download_strategy_spec.rb b/Library/Homebrew/test/cask/download_strategy_spec.rb index 60d101bdb..222352c07 100644 --- a/Library/Homebrew/test/cask/download_strategy_spec.rb +++ b/Library/Homebrew/test/cask/download_strategy_spec.rb @@ -27,11 +27,8 @@ describe "download strategies", :cask do        expect(downloader).to have_received(:curl).with(          cask.url.to_s, -        "--location", -        "--remote-time", -        "--continue-at", "-", -        "--output", kind_of(Pathname), -        user_agent: :default +        "-C", 0, +        "-o", kind_of(Pathname)        )      end @@ -39,25 +36,25 @@ describe "download strategies", :cask do        let(:url_options) { { user_agent: "Mozilla/25.0.1" } }        it "adds the appropriate curl args" do -        expect(downloader).to receive(:safe_system) { |*args| -          expect(args.each_cons(2)).to include(["--user-agent", "Mozilla/25.0.1"]) -        } +        curl_args = [] +        allow(downloader).to receive(:curl) { |*args| curl_args = args }          downloader.fetch + +        expect(curl_args.each_cons(2)).to include(["-A", "Mozilla/25.0.1"])        end      end      context "with a generalized fake user agent" do -      alias_matcher :a_string_matching, :match -        let(:url_options) { { user_agent: :fake } }        it "adds the appropriate curl args" do -        expect(downloader).to receive(:safe_system) { |*args| -          expect(args.each_cons(2).to_a).to include(["--user-agent", a_string_matching(/Mozilla.*Mac OS X 10.*AppleWebKit/)]) -        } +        curl_args = [] +        allow(downloader).to receive(:curl) { |*args| curl_args = args }          downloader.fetch + +        expect(curl_args.each_cons(2)).to include(["-A", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10) https://caskroom.github.io"])        end      end @@ -141,7 +138,7 @@ describe "download strategies", :cask do    describe Hbc::SubversionDownloadStrategy do      let(:url_options) { { using: :svn } }      let(:fake_system_command) { class_double(Hbc::SystemCommand) } -    let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, command: fake_system_command) } +    let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, fake_system_command) }      before do        allow(fake_system_command).to receive(:run!)      end @@ -150,7 +147,7 @@ describe "download strategies", :cask do        allow(downloader).to receive(:compress)        allow(downloader).to receive(:fetch_repo) -      expect(downloader.fetch).to equal(downloader.cached_location) +      expect(downloader.fetch).to equal(downloader.tarball_path)      end      it "calls fetch_repo with default arguments for a simple Cask" do @@ -240,5 +237,44 @@ describe "download strategies", :cask do          )        end      end + +    it "runs tar to serialize svn downloads" do +      # sneaky stub to remake the directory, since homebrew code removes it +      # before tar is called +      allow(downloader).to receive(:fetch_repo) { +        downloader.cached_location.mkdir +      } + +      downloader.fetch + +      expect(fake_system_command).to have_received(:run!).with( +        "/usr/bin/tar", +        hash_including(args: [ +                         '-s/^\\.//', +                         "--exclude", +                         ".svn", +                         "-cf", +                         downloader.tarball_path, +                         "--", +                         ".", +                       ]), +      ) +    end    end + +  # does not work yet, because (for unknown reasons), the tar command +  # returns an error code when running under the test suite +  # it 'creates a tarball matching the expected checksum' do +  #   cask = Hbc::CaskLoader.load('svn-download-check-cask') +  #   downloader = Hbc::SubversionDownloadStrategy.new(cask) +  #   # special mocking required for tar to have something to work with +  #   def downloader.fetch_repo(target, url, revision = nil, ignore_externals=false) +  #     target.mkpath +  #     FileUtils.touch(target.join('empty_file.txt')) +  #     File.utime(1000,1000,target.join('empty_file.txt')) +  #   end +  #   expect(downloader.fetch).to equal(downloader.tarball_path) +  #   d = Hbc::Download.new(cask) +  #   d.send(:_check_sums, downloader.tarball_path, cask.sums) +  # end  end diff --git a/Library/Homebrew/test/cask/dsl/appcast_spec.rb b/Library/Homebrew/test/cask/dsl/appcast_spec.rb index ccc6a4633..b8903b1be 100644 --- a/Library/Homebrew/test/cask/dsl/appcast_spec.rb +++ b/Library/Homebrew/test/cask/dsl/appcast_spec.rb @@ -33,18 +33,13 @@ describe Hbc::DSL::Appcast do    describe "#calculate_checkpoint" do      before do -      expect(Hbc::SystemCommand).to receive(:run) do |executable, **options| -        expect(executable).to eq "/usr/bin/curl" -        expect(options[:args]).to include(*cmd_args) -        expect(options[:print_stderr]).to be false -        cmd_result -      end +      expect(Hbc::SystemCommand).to receive(:run).with(*cmd_args).and_return(cmd_result)        allow(cmd_result).to receive(:success?).and_return(cmd_success)        allow(cmd_result).to receive(:stdout).and_return(cmd_stdout)      end      context "when server returns a successful HTTP status" do -      let(:cmd_args) { [HOMEBREW_USER_AGENT_FAKE_SAFARI, "--compressed", "--location", "--fail", uri] } +      let(:cmd_args) { ["/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", Hbc::URL::FAKE_USER_AGENT, "--fail", uri], print_stderr: false] }        let(:cmd_result) { double("Hbc::SystemCommand::Result") }        let(:cmd_success) { true }        let(:cmd_stdout) { "hello world" } @@ -61,7 +56,7 @@ describe Hbc::DSL::Appcast do      end      context "when server returns a non-successful HTTP status" do -      let(:cmd_args) { [HOMEBREW_USER_AGENT_FAKE_SAFARI, "--compressed", "--location", "--fail", uri] } +      let(:cmd_args) { ["/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", Hbc::URL::FAKE_USER_AGENT, "--fail", uri], print_stderr: false] }        let(:cmd_result) { double("Hbc::SystemCommand::Result") }        let(:cmd_success) { false }        let(:cmd_stdout) { "some error message from the server" } diff --git a/Library/Homebrew/utils/curl.rb b/Library/Homebrew/utils/curl.rb index 6aaf9be0c..5a40ae846 100644 --- a/Library/Homebrew/utils/curl.rb +++ b/Library/Homebrew/utils/curl.rb @@ -1,46 +1,42 @@  require "pathname"  require "open3" -def curl_executable +def curl_args(options = {})    curl = Pathname.new ENV["HOMEBREW_CURL"]    curl = Pathname.new "/usr/bin/curl" unless curl.exist? -  return curl if curl.executable? -  raise "#{curl} is not executable" -end +  raise "#{curl} is not executable" unless curl.exist? && curl.executable? -def curl_args(*extra_args, show_output: false, user_agent: :default)    args = [ -    curl_executable.to_s, -    "--fail", -    "--show-error", +    curl.to_s, +    "--remote-time", +    "--location",    ] -  args << "--user-agent" << case user_agent -  when :browser, :fake -    HOMEBREW_USER_AGENT_FAKE_SAFARI -  when :default -    HOMEBREW_USER_AGENT_CURL +  case options[:user_agent] +  when :browser +    args << "--user-agent" << HOMEBREW_USER_AGENT_FAKE_SAFARI    else -    user_agent +    args << "--user-agent" << HOMEBREW_USER_AGENT_CURL    end -  unless show_output +  unless options[:show_output]      args << "--progress-bar" unless ARGV.verbose?      args << "--verbose" if ENV["HOMEBREW_CURL_VERBOSE"] +    args << "--fail"      args << "--silent" if !$stdout.tty? || ENV["TRAVIS"]    end -  args + extra_args +  args += options[:extra_args] if options[:extra_args] +  args  end  def curl(*args) -  safe_system(*curl_args(*args)) -end - -def curl_download(*args, to: nil, **options) -  curl(*args, "--location", "--remote-time", "--continue-at", "-", "--output", to, **options) +  safe_system(*curl_args(extra_args: args))  end -def curl_output(*args, **options) -  Open3.capture3(*curl_args(*args, show_output: true, **options)) +def curl_output(*args) +  curl_args = curl_args(extra_args: args, show_output: true) +  Open3.popen3(*curl_args) do |_, stdout, stderr, wait_thread| +    [stdout.read, stderr.read, wait_thread.value] +  end  end diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index 07eea4384..1a781cee6 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -166,7 +166,7 @@ module GitHub        args += ["--dump-header", headers_tmpfile.path] -      output, errors, status = curl_output(url.to_s, "--location", *args) +      output, errors, status = curl_output(url.to_s, *args)        output, _, http_code = output.rpartition("\n")        output, _, http_code = output.rpartition("\n") if http_code == "000"        headers = headers_tmpfile.read | 
