brew: Add ScpDownloadStrategy

This commit is contained in:
Ryan Greenberg 2018-03-14 11:38:39 -07:00
parent 94c0d833c3
commit fe8939ddd0
3 changed files with 162 additions and 0 deletions

View File

@ -594,6 +594,70 @@ class GitHubPrivateRepositoryReleaseDownloadStrategy < GitHubPrivateRepositoryDo
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
def initialize(name, resource)
super
@ -1140,6 +1204,8 @@ class DownloadStrategyDetector
when %r{^s3://}
require_aws_sdk
S3DownloadStrategy
when %r{^scp://}
ScpDownloadStrategy
else
CurlDownloadStrategy
end

View File

@ -515,6 +515,13 @@ class CurlDownloadStrategyError < RuntimeError
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
class ErrorDuringExecution < RuntimeError
def initialize(cmd, args = [])

View File

@ -268,6 +268,90 @@ describe CurlDownloadStrategy do
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 "::detect" do
subject { described_class.detect(url, strategy) }
@ -306,6 +390,11 @@ describe DownloadStrategyDetector do
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
expect(subject).to eq(CurlDownloadStrategy)
end