diff --git a/Library/Homebrew/cask/lib/hbc/audit.rb b/Library/Homebrew/cask/lib/hbc/audit.rb index cee1fe8070..b8bb6ab814 100644 --- a/Library/Homebrew/cask/lib/hbc/audit.rb +++ b/Library/Homebrew/cask/lib/hbc/audit.rb @@ -143,7 +143,15 @@ module Hbc def check_appcast_http_code odebug "Verifying appcast returns 200 HTTP response code" - 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) + + 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) 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 5003146719..dd9c610899 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 url, "-o", path + curl_download url, to: 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 93e825e038..fab3a3c1c3 100644 --- a/Library/Homebrew/cask/lib/hbc/container.rb +++ b/Library/Homebrew/cask/lib/hbc/container.rb @@ -4,6 +4,7 @@ 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" @@ -14,6 +15,7 @@ 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" @@ -43,6 +45,7 @@ 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 66ecb8c874..52f171d6a1 100644 --- a/Library/Homebrew/cask/lib/hbc/container/criteria.rb +++ b/Library/Homebrew/cask/lib/hbc/container/criteria.rb @@ -13,9 +13,11 @@ 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 =~ regex + @magic_number ||= File.open(path, "rb") { |f| f.read(262) } + @magic_number.match?(regex) end end end diff --git a/Library/Homebrew/cask/lib/hbc/container/directory.rb b/Library/Homebrew/cask/lib/hbc/container/directory.rb new file mode 100644 index 0000000000..e4bb1095b2 --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/container/directory.rb @@ -0,0 +1,24 @@ +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 848f6d4be2..af3b36fd10 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 - MachO.open(criteria.path).header.executable? + criteria.path.file? && 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 new file mode 100644 index 0000000000..cae613b2db --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb @@ -0,0 +1,15 @@ +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 016bb66e6e..245ad4adeb 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(cask, command = SystemCommand) - super + def initialize(*args, **options) + super(*args, **options) @ref_type, @ref = extract_ref @clone = Hbc.cache.join(cache_filename) end @@ -64,11 +64,6 @@ 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 @@ -95,13 +90,8 @@ module Hbc end end - def downloaded_size - temporary_path.size? || 0 - end - def _fetch - odebug "Calling curl with args #{cask_curl_args}" - curl(*cask_curl_args) + curl_download url, *cask_curl_args, to: temporary_path, user_agent: uri_object.user_agent end def fetch @@ -131,33 +121,12 @@ 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 - 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 + cookies_args + referer_args end def cookies_args @@ -191,8 +160,7 @@ module Hbc class CurlPostDownloadStrategy < CurlDownloadStrategy def cask_curl_args - super - default_curl_args.concat(post_args) + super.concat(post_args) end def post_args @@ -225,8 +193,8 @@ module Hbc # super does not provide checks for already-existing downloads def fetch - if tarball_path.exist? - puts "Already downloaded: #{tarball_path}" + if cached_location.directory? + puts "Already downloaded: #{cached_location}" else @url = @url.sub(/^svn\+/, "") if @url =~ %r{^svn\+http://} ohai "Checking out #{@url}" @@ -252,9 +220,8 @@ module Hbc else fetch_repo @clone, @url end - compress end - tarball_path + cached_location end # This primary reason for redefining this method is the trust_cert @@ -288,10 +255,6 @@ 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/ @@ -304,35 +267,5 @@ 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 d302d09460..fc7e83a20d 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb @@ -12,7 +12,11 @@ module Hbc end def calculate_checkpoint - result = SystemCommand.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--fail", @uri], print_stderr: false) + curl_executable, *args = curl_args( + "--compressed", "--location", "--fail", @uri, + user_agent: :fake + ) + result = SystemCommand.run(curl_executable, args: args, print_stderr: false) checkpoint = if result.success? processed_appcast_text = result.stdout.gsub(%r{[^<]*}m, "") diff --git a/Library/Homebrew/cask/lib/hbc/system_command.rb b/Library/Homebrew/cask/lib/hbc/system_command.rb index 901617b71b..b735ae4f9c 100644 --- a/Library/Homebrew/cask/lib/hbc/system_command.rb +++ b/Library/Homebrew/cask/lib/hbc/system_command.rb @@ -112,11 +112,7 @@ 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 15da2ced21..8c652657b6 100644 --- a/Library/Homebrew/cask/lib/hbc/url.rb +++ b/Library/Homebrew/cask/lib/hbc/url.rb @@ -1,8 +1,6 @@ module Hbc class URL - 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 + attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data, :user_agent extend Forwardable def_delegators :uri, :path, :scheme, :to_s @@ -17,7 +15,7 @@ module Hbc def initialize(uri, options = {}) @uri = Hbc::UnderscoreSupportingURI.parse(uri) - @user_agent = options[:user_agent] + @user_agent = options.fetch(:user_agent, :default) @cookies = options[:cookies] @referer = options[:referer] @using = options[:using] @@ -25,10 +23,5 @@ 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 dbb537756d..f4996a5b5e 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(cask.gpg.signature, "-o", sig_path.to_s) unless cached || force + curl_download cask.gpg.signature, to: sig_path unless cached || force sig_path end diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 1f07fa89ea..b3e0785a51 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -242,12 +242,10 @@ class FormulaAuditor def self.http_content_headers_and_checksum(url, hash_needed: false, user_agent: :default) max_time = hash_needed ? "600" : "25" - args = curl_args( - extra_args: ["--connect-timeout", "15", "--include", "--max-time", max_time, url], - show_output: true, - user_agent: user_agent, + output, = curl_output( + "--connect-timeout", "15", "--include", "--max-time", max_time, "--location", url, + 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 e2492203d3..6445bc34cf 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", "-u#{bintray_user}:#{bintray_key}", - "-H", "Content-Type: application/json", - "-d", package_blob, bintray_repo_url + curl "--silent", "--fail", "--user", "#{bintray_user}:#{bintray_key}", + "--header", "Content-Type: application/json", + "--data", 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", "-u#{bintray_user}:#{bintray_key}", - "-T", download, content_url + curl "--silent", "--fail", "--user", "#{bintray_user}:#{bintray_key}", + "--upload-file", 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 9681bb2bc0..dd2bc62707 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", "-o", "/dev/null", "-I", bottle_commit_url + curl "--silent", "--fail", "--output", "/dev/null", "--head", 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 patch_url, "-s", "-o", patchpath + curl_download patch_url, to: patchpath end def apply_patch @@ -433,10 +433,10 @@ module Homebrew end version = info.pkg_version ohai "Publishing on Bintray: #{package} #{version}" - curl "-w", '\n', "--silent", "--fail", - "-u#{creds[:user]}:#{creds[:key]}", "-X", "POST", - "-H", "Content-Type: application/json", - "-d", '{"publish_wait_for_secs": 0}', + curl "--write-out", '\n', "--silent", "--fail", + "--user", "#{creds[:user]}:#{creds[:key]}", "--request", "POST", + "--header", "Content-Type: application/json", + "--data", '{"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 url, "-o", filename + curl_download url, to: 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", "--head", url)[0] + headers, = curl_output("--connect-timeout", "15", "--location", "--head", url) 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 7173347143..2a8b6e5859 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -375,75 +375,59 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy ohai "Downloading from #{url}" end - 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 + curl_download resolved_url(url), to: temporary_path end # Curl options to be always passed to curl, - # with raw head calls (`curl -I`) or with actual `fetch`. + # with raw head calls (`curl --head`) or with actual `fetch`. def _curl_opts - copts = [] - copts << "--user" << meta.fetch(:user) if meta.key?(:user) - copts + return ["--user" << meta.fetch(:user)] if meta.key?(:user) + [] end - 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 + 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 end - urls + + redirect_url end - def downloaded_size - temporary_path.size? || 0 - end - - def curl(*args) + def curl(*args, **options) args.concat _curl_opts args << "--connect-timeout" << "5" unless mirrors.empty? - super + super(*args, **options) end end # Detect and download from Apache Mirror class CurlApacheMirrorDownloadStrategy < CurlDownloadStrategy def apache_mirrors - rd, wr = IO.pipe - buf = "" + mirrors, = Open3.capture3( + *curl_args(*_curl_opts, "--silent", "--location", "#{@url}&asjson=1"), + ) - pid = fork do - ENV.delete "HOMEBREW_CURL_VERBOSE" - rd.close - $stdout.reopen(wr) - $stderr.reopen(wr) - curl "#{@url}&asjson=1" - end - wr.close - - rd.readline if ARGV.verbose? # Remove Homebrew output - buf << rd.read until rd.eof? - rd.close - Process.wait(pid) - buf + JSON.parse(mirrors) end def _fetch return super if @tried_apache_mirror @tried_apache_mirror = true - mirrors = JSON.parse(apache_mirrors) + mirrors = apache_mirrors path_info = mirrors.fetch("path_info") @url = mirrors.fetch("preferred") + path_info @mirrors |= %W[https://archive.apache.org/dist/#{path_info}] @@ -460,7 +444,7 @@ end class CurlPostDownloadStrategy < CurlDownloadStrategy def _fetch base_url, data = @url.split("?") - curl base_url, "-d", data, "-C", downloaded_size, "-o", temporary_path + curl_download base_url, "--data", data, to: temporary_path end end @@ -530,7 +514,7 @@ class S3DownloadStrategy < CurlDownloadStrategy s3url = obj.public_url end - curl s3url, "-C", downloaded_size, "-o", temporary_path + curl_download s3url, to: temporary_path end end @@ -566,7 +550,7 @@ class GitHubPrivateRepositoryDownloadStrategy < CurlDownloadStrategy end def _fetch - curl download_url, "-C", downloaded_size, "-o", temporary_path + curl_download download_url, to: temporary_path end private @@ -615,7 +599,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_url, "-C", downloaded_size, "-o", temporary_path, "-H", "Accept: application/octet-stream" + curl_download download_url, "--header", "Accept: application/octet-stream", to: temporary_path end private @@ -915,18 +899,27 @@ class GitHubGitDownloadStrategy < GitDownloadStrategy def github_last_commit return if ENV["HOMEBREW_NO_GITHUB_API"] - output, _, status = curl_output "-H", "Accept: application/vnd.github.v3.sha", \ - "-I", "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{@ref}" + output, _, status = curl_output( + "--silent", "--head", "--location", + "-H", "Accept: application/vnd.github.v3.sha", + "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{@ref}" + ) - commit = output[/^ETag: \"(\h+)\"/, 1] if status.success? + return unless status.success? + + commit = output[/^ETag: \"(\h+)\"/, 1] 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 "-H", "Accept: application/vnd.github.v3.sha", \ - "-I", "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{commit}" + + output, _, status = curl_output( + "--silent", "--head", "--location", + "-H", "Accept: application/vnd.github.v3.sha", + "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{commit}" + ) !(status.success? && output && output[/^Status: (200)/, 1] == "200") end @@ -1159,15 +1152,13 @@ class DownloadStrategyDetector SubversionDownloadStrategy when %r{^cvs://} CVSDownloadStrategy - when %r{^https?://(.+?\.)?googlecode\.com/hg} - MercurialDownloadStrategy - when %r{^hg://} + when %r{^hg://}, %r{^https?://(.+?\.)?googlecode\.com/hg} MercurialDownloadStrategy when %r{^bzr://} BazaarDownloadStrategy when %r{^fossil://} FossilDownloadStrategy - when %r{^http://svn\.apache\.org/repos/}, %r{^svn\+http://} + when %r{^svn\+http://}, %r{^http://svn\.apache\.org/repos/} SubversionDownloadStrategy when %r{^https?://(.+?\.)?sourceforge\.net/hgweb/} MercurialDownloadStrategy diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 195d15cece..dd67b4f243 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 url, "-o", path + curl_download url, to: 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 222352c07e..60d101bdb8 100644 --- a/Library/Homebrew/test/cask/download_strategy_spec.rb +++ b/Library/Homebrew/test/cask/download_strategy_spec.rb @@ -27,8 +27,11 @@ describe "download strategies", :cask do expect(downloader).to have_received(:curl).with( cask.url.to_s, - "-C", 0, - "-o", kind_of(Pathname) + "--location", + "--remote-time", + "--continue-at", "-", + "--output", kind_of(Pathname), + user_agent: :default ) end @@ -36,25 +39,25 @@ describe "download strategies", :cask do let(:url_options) { { user_agent: "Mozilla/25.0.1" } } it "adds the appropriate curl args" do - curl_args = [] - allow(downloader).to receive(:curl) { |*args| curl_args = args } + expect(downloader).to receive(:safe_system) { |*args| + expect(args.each_cons(2)).to include(["--user-agent", "Mozilla/25.0.1"]) + } 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 - curl_args = [] - allow(downloader).to receive(:curl) { |*args| curl_args = args } + 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/)]) + } 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 @@ -138,7 +141,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, fake_system_command) } + let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, command: fake_system_command) } before do allow(fake_system_command).to receive(:run!) end @@ -147,7 +150,7 @@ describe "download strategies", :cask do allow(downloader).to receive(:compress) allow(downloader).to receive(:fetch_repo) - expect(downloader.fetch).to equal(downloader.tarball_path) + expect(downloader.fetch).to equal(downloader.cached_location) end it "calls fetch_repo with default arguments for a simple Cask" do @@ -237,44 +240,5 @@ 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 b8903b1be8..ccc6a46338 100644 --- a/Library/Homebrew/test/cask/dsl/appcast_spec.rb +++ b/Library/Homebrew/test/cask/dsl/appcast_spec.rb @@ -33,13 +33,18 @@ describe Hbc::DSL::Appcast do describe "#calculate_checkpoint" do before do - expect(Hbc::SystemCommand).to receive(:run).with(*cmd_args).and_return(cmd_result) + 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 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) { ["/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", Hbc::URL::FAKE_USER_AGENT, "--fail", uri], print_stderr: false] } + let(:cmd_args) { [HOMEBREW_USER_AGENT_FAKE_SAFARI, "--compressed", "--location", "--fail", uri] } let(:cmd_result) { double("Hbc::SystemCommand::Result") } let(:cmd_success) { true } let(:cmd_stdout) { "hello world" } @@ -56,7 +61,7 @@ describe Hbc::DSL::Appcast do end context "when server returns a non-successful HTTP status" do - let(:cmd_args) { ["/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", Hbc::URL::FAKE_USER_AGENT, "--fail", uri], print_stderr: false] } + let(:cmd_args) { [HOMEBREW_USER_AGENT_FAKE_SAFARI, "--compressed", "--location", "--fail", uri] } 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 5a40ae8469..6aaf9be0cb 100644 --- a/Library/Homebrew/utils/curl.rb +++ b/Library/Homebrew/utils/curl.rb @@ -1,42 +1,46 @@ require "pathname" require "open3" -def curl_args(options = {}) +def curl_executable curl = Pathname.new ENV["HOMEBREW_CURL"] curl = Pathname.new "/usr/bin/curl" unless curl.exist? - raise "#{curl} is not executable" unless curl.exist? && curl.executable? + return curl if curl.executable? + raise "#{curl} is not executable" +end +def curl_args(*extra_args, show_output: false, user_agent: :default) args = [ - curl.to_s, - "--remote-time", - "--location", + curl_executable.to_s, + "--fail", + "--show-error", ] - case options[:user_agent] - when :browser - args << "--user-agent" << HOMEBREW_USER_AGENT_FAKE_SAFARI + args << "--user-agent" << case user_agent + when :browser, :fake + HOMEBREW_USER_AGENT_FAKE_SAFARI + when :default + HOMEBREW_USER_AGENT_CURL else - args << "--user-agent" << HOMEBREW_USER_AGENT_CURL + user_agent end - unless options[:show_output] + unless 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 += options[:extra_args] if options[:extra_args] - args + args + extra_args end def curl(*args) - safe_system(*curl_args(extra_args: args)) + safe_system(*curl_args(*args)) end -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 +def curl_download(*args, to: nil, **options) + curl(*args, "--location", "--remote-time", "--continue-at", "-", "--output", to, **options) +end + +def curl_output(*args, **options) + Open3.capture3(*curl_args(*args, show_output: true, **options)) end diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index 1a781cee64..07eea43849 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, *args) + output, errors, status = curl_output(url.to_s, "--location", *args) output, _, http_code = output.rpartition("\n") output, _, http_code = output.rpartition("\n") if http_code == "000" headers = headers_tmpfile.read