brew: Add ScpDownloadStrategy
This commit is contained in:
		
							parent
							
								
									94c0d833c3
								
							
						
					
					
						commit
						fe8939ddd0
					
				@ -594,6 +594,70 @@ class GitHubPrivateRepositoryReleaseDownloadStrategy < GitHubPrivateRepositoryDo
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ScpDownloadStrategy downloads files using ssh via scp. To use it, add
 | 
				
			||||||
 | 
					# ":using => ScpDownloadStrategy" to the URL section of your formula or
 | 
				
			||||||
 | 
					# provide a URL starting with scp://. This strategy uses ssh credentials for
 | 
				
			||||||
 | 
					# authentication. If a public/private keypair is configured, it will not
 | 
				
			||||||
 | 
					# prompt for a password.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Usage:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#   class Abc < Formula
 | 
				
			||||||
 | 
					#     url "scp://example.com/src/abc.1.0.tar.gz"
 | 
				
			||||||
 | 
					#     ...
 | 
				
			||||||
 | 
					class ScpDownloadStrategy < AbstractFileDownloadStrategy
 | 
				
			||||||
 | 
					  attr_reader :tarball_path, :temporary_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def initialize(name, resource)
 | 
				
			||||||
 | 
					    super
 | 
				
			||||||
 | 
					    @tarball_path = HOMEBREW_CACHE/"#{name}-#{version}#{ext}"
 | 
				
			||||||
 | 
					    @temporary_path = Pathname.new("#{cached_location}.incomplete")
 | 
				
			||||||
 | 
					    parse_url_pattern
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def parse_url_pattern
 | 
				
			||||||
 | 
					    url_pattern = %r{scp://([^@]+@)?([^@:/]+)(:\d+)?/(\S+)}
 | 
				
			||||||
 | 
					    if @url !~ url_pattern
 | 
				
			||||||
 | 
					      raise ScpDownloadStrategyError, "Invalid URL for scp: #{@url}"
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _, @user, @host, @port, @path = *@url.match(url_pattern)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def fetch
 | 
				
			||||||
 | 
					    ohai "Downloading #{@url}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if cached_location.exist?
 | 
				
			||||||
 | 
					      puts "Already downloaded: #{cached_location}"
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      begin
 | 
				
			||||||
 | 
					        safe_system "scp", scp_source, temporary_path.to_s
 | 
				
			||||||
 | 
					      rescue ErrorDuringExecution
 | 
				
			||||||
 | 
					        raise ScpDownloadStrategyError, "Failed to run scp #{scp_source}"
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ignore_interrupts { temporary_path.rename(cached_location) }
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def cached_location
 | 
				
			||||||
 | 
					    tarball_path
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def clear_cache
 | 
				
			||||||
 | 
					    super
 | 
				
			||||||
 | 
					    rm_rf(temporary_path)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def scp_source
 | 
				
			||||||
 | 
					    path_prefix = "/" unless @path.start_with?("~")
 | 
				
			||||||
 | 
					    port_arg = "-P #{@port[1..-1]} " if @port
 | 
				
			||||||
 | 
					    "#{port_arg}#{@user}#{@host}:#{path_prefix}#{@path}"
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SubversionDownloadStrategy < VCSDownloadStrategy
 | 
					class SubversionDownloadStrategy < VCSDownloadStrategy
 | 
				
			||||||
  def initialize(name, resource)
 | 
					  def initialize(name, resource)
 | 
				
			||||||
    super
 | 
					    super
 | 
				
			||||||
@ -1140,6 +1204,8 @@ class DownloadStrategyDetector
 | 
				
			|||||||
    when %r{^s3://}
 | 
					    when %r{^s3://}
 | 
				
			||||||
      require_aws_sdk
 | 
					      require_aws_sdk
 | 
				
			||||||
      S3DownloadStrategy
 | 
					      S3DownloadStrategy
 | 
				
			||||||
 | 
					    when %r{^scp://}
 | 
				
			||||||
 | 
					      ScpDownloadStrategy
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      CurlDownloadStrategy
 | 
					      CurlDownloadStrategy
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
				
			|||||||
@ -515,6 +515,13 @@ class CurlDownloadStrategyError < RuntimeError
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# raised in ScpDownloadStrategy.fetch
 | 
				
			||||||
 | 
					class ScpDownloadStrategyError < RuntimeError
 | 
				
			||||||
 | 
					  def initialize(cause)
 | 
				
			||||||
 | 
					    super "Download failed: #{cause}"
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# raised by safe_system in utils.rb
 | 
					# raised by safe_system in utils.rb
 | 
				
			||||||
class ErrorDuringExecution < RuntimeError
 | 
					class ErrorDuringExecution < RuntimeError
 | 
				
			||||||
  def initialize(cmd, args = [])
 | 
					  def initialize(cmd, args = [])
 | 
				
			||||||
 | 
				
			|||||||
@ -268,6 +268,90 @@ describe CurlDownloadStrategy do
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe ScpDownloadStrategy do
 | 
				
			||||||
 | 
					  def resource_for(url)
 | 
				
			||||||
 | 
					    double(Resource, url: url, mirrors: [], specs: {}, version: nil)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  subject { described_class.new(name, resource) }
 | 
				
			||||||
 | 
					  let(:name) { "foo" }
 | 
				
			||||||
 | 
					  let(:url) { "scp://example.com/foo.tar.gz" }
 | 
				
			||||||
 | 
					  let(:resource) { resource_for(url) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe "#initialize" do
 | 
				
			||||||
 | 
					    invalid_urls = %w[
 | 
				
			||||||
 | 
					      http://example.com/foo.tar.gz
 | 
				
			||||||
 | 
					      scp://@example.com/foo.tar.gz
 | 
				
			||||||
 | 
					      scp://example.com:/foo.tar.gz
 | 
				
			||||||
 | 
					      scp://example.com
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    invalid_urls.each do |invalid_url|
 | 
				
			||||||
 | 
					      context "with invalid URL #{invalid_url}" do
 | 
				
			||||||
 | 
					        it "raises ScpDownloadStrategyError" do
 | 
				
			||||||
 | 
					          expect {
 | 
				
			||||||
 | 
					            described_class.new(name, resource_for(invalid_url))
 | 
				
			||||||
 | 
					          }.to raise_error(ScpDownloadStrategyError)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe "#fetch" do
 | 
				
			||||||
 | 
					    before do
 | 
				
			||||||
 | 
					      expect(subject.temporary_path).to receive(:rename).and_return(true)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context "when given a valid URL" do
 | 
				
			||||||
 | 
					      let(:url) { "scp://example.com/foo.tar.gz" }
 | 
				
			||||||
 | 
					      it "copies the file via scp" do
 | 
				
			||||||
 | 
					        expect(subject)
 | 
				
			||||||
 | 
					          .to receive(:safe_system)
 | 
				
			||||||
 | 
					          .with("scp", "example.com:/foo.tar.gz", anything)
 | 
				
			||||||
 | 
					          .and_return(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        subject.fetch
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context "when given a URL with a username" do
 | 
				
			||||||
 | 
					      let(:url) { "scp://user@example.com/foo.tar.gz" }
 | 
				
			||||||
 | 
					      it "copies the file via scp" do
 | 
				
			||||||
 | 
					        expect(subject)
 | 
				
			||||||
 | 
					          .to receive(:safe_system)
 | 
				
			||||||
 | 
					          .with("scp", "user@example.com:/foo.tar.gz", anything)
 | 
				
			||||||
 | 
					          .and_return(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        subject.fetch
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context "when given a URL with a port" do
 | 
				
			||||||
 | 
					      let(:url) { "scp://example.com:1234/foo.tar.gz" }
 | 
				
			||||||
 | 
					      it "copies the file via scp" do
 | 
				
			||||||
 | 
					        expect(subject)
 | 
				
			||||||
 | 
					          .to receive(:safe_system)
 | 
				
			||||||
 | 
					          .with("scp", "-P 1234 example.com:/foo.tar.gz", anything)
 | 
				
			||||||
 | 
					          .and_return(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        subject.fetch
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context "when given a URL with /~/" do
 | 
				
			||||||
 | 
					      let(:url) { "scp://example.com/~/foo.tar.gz" }
 | 
				
			||||||
 | 
					      it "treats the path as relative to the home directory" do
 | 
				
			||||||
 | 
					        expect(subject)
 | 
				
			||||||
 | 
					          .to receive(:safe_system)
 | 
				
			||||||
 | 
					          .with("scp", "example.com:~/foo.tar.gz", anything)
 | 
				
			||||||
 | 
					          .and_return(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        subject.fetch
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe DownloadStrategyDetector do
 | 
					describe DownloadStrategyDetector do
 | 
				
			||||||
  describe "::detect" do
 | 
					  describe "::detect" do
 | 
				
			||||||
    subject { described_class.detect(url, strategy) }
 | 
					    subject { described_class.detect(url, strategy) }
 | 
				
			||||||
@ -306,6 +390,11 @@ describe DownloadStrategyDetector do
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context "when given an scp URL" do
 | 
				
			||||||
 | 
					      let(:url) { "scp://example.com/brew.tar.gz" }
 | 
				
			||||||
 | 
					      it { is_expected.to eq(ScpDownloadStrategy) }
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it "defaults to cURL" do
 | 
					    it "defaults to cURL" do
 | 
				
			||||||
      expect(subject).to eq(CurlDownloadStrategy)
 | 
					      expect(subject).to eq(CurlDownloadStrategy)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user