New feature: GitHubReleaseDownloadStrategy

GitHubReleaseDownloadStrategy downloads tarballs from GitHub Release assets.
To use it, add ":using => GitHubReleaseDownloadStrategy" to the URL section
of your formula. This download strategy uses GitHub access tokens (in the
environment variables GITHUB_TOKEN) to sign the request.
This strategy is suitable for corporate use just like S3DownloadStrategy,
because it lets you use a private GttHub repository for internal distribution.
It works with public one, but in that case simply use CurlDownloadStrategy.
This commit is contained in:
Masayuki Morita 2017-01-02 12:56:20 +09:00
parent 3e0d54d8b9
commit b9cc52db45
2 changed files with 122 additions and 1 deletions

View File

@ -532,6 +532,82 @@ class S3DownloadStrategy < CurlDownloadStrategy
end
end
# GitHubReleaseDownloadStrategy downloads tarballs from GitHub Release assets.
# To use it, add ":using => GitHubReleaseDownloadStrategy" to the URL section
# of your formula. This download strategy uses GitHub access tokens (in the
# environment variables GITHUB_TOKEN) to sign the request.
# This strategy is suitable for corporate use just like S3DownloadStrategy,
# because it lets you use a private GttHub repository for internal distribution.
# It works with public one, but in that case simply use CurlDownloadStrategy.
class GitHubReleaseDownloadStrategy < CurlDownloadStrategy
require 'open-uri'
def initialize(name, resource)
super
@github_token = ENV["GITHUB_TOKEN"]
unless @github_token
puts "Environmental variable GITHUB_TOKEN is required."
raise CurlDownloadStrategyError, @url
end
url_pattern = %r|https://github.com/(\S+)/(\S+)/releases/download/(\S+)/(\S+)|
unless @url =~ url_pattern
puts "Invalid url pattern for GitHub Release."
raise CurlDownloadStrategyError, @url
end
_, @owner, @repo, @tag, @filename = *(@url.match(url_pattern))
end
def _fetch
puts "Download asset_id: #{asset_id}"
# HTTP request header `Accept: application/octet-stream` is required.
# Without this, the GitHub API will respond with metadata, not binary.
curl asset_url, "-C", downloaded_size, "-o", temporary_path, "-H", 'Accept: application/octet-stream'
end
private
def asset_url
"https://#{@github_token}@api.github.com/repos/#{@owner}/#{@repo}/releases/assets/#{asset_id}"
end
def asset_id
@asset_id ||= resolve_asset_id
end
def resolve_asset_id
release_metadata = fetch_release_metadata
assets = release_metadata["assets"].select{ |a| a["name"] == @filename }
if assets.empty?
puts "Asset file not found."
raise CurlDownloadStrategyError, @url
end
return assets.first["id"]
end
def release_url
"https://api.github.com/repos/#{@owner}/#{@repo}/releases/tags/#{@tag}"
end
def fetch_release_metadata
begin
release_response = open(release_url, {:http_basic_authentication => [@github_token]}).read
rescue OpenURI::HTTPError => e
if e.message == '404 Not Found'
puts "GitHub Release not found."
raise CurlDownloadStrategyError, @url
else
raise e
end
end
return JSON.parse(release_response)
end
end
class SubversionDownloadStrategy < VCSDownloadStrategy
def initialize(name, resource)
super

View File

@ -2,11 +2,12 @@ require "testing_env"
require "download_strategy"
class ResourceDouble
attr_reader :url, :specs, :version
attr_reader :url, :specs, :version, :mirrors
def initialize(url = "http://example.com/foo.tar.gz", specs = {})
@url = url
@specs = specs
@mirrors = []
end
end
@ -60,6 +61,50 @@ class VCSDownloadStrategyTests < Homebrew::TestCase
end
end
class GitHubReleaseDownloadStrategyTests < Homebrew::TestCase
def setup
resource = ResourceDouble.new("https://github.com/owner/repo/releases/download/tag/foo_v0.1.0_darwin_amd64.tar.gz")
ENV["GITHUB_TOKEN"] = "token"
@strategy = GitHubReleaseDownloadStrategy.new("foo", resource)
end
def test_initialize
assert_equal "token", @strategy.instance_variable_get(:@github_token)
assert_equal "owner", @strategy.instance_variable_get(:@owner)
assert_equal "repo", @strategy.instance_variable_get(:@repo)
assert_equal "tag", @strategy.instance_variable_get(:@tag)
assert_equal "foo_v0.1.0_darwin_amd64.tar.gz", @strategy.instance_variable_get(:@filename)
end
def test_asset_url
@strategy.stubs(:resolve_asset_id).returns(456)
expected = "https://token@api.github.com/repos/owner/repo/releases/assets/456"
assert_equal expected, @strategy.send(:asset_url)
end
def test_resolve_asset_id
release_metadata = {
"assets" => [
{
"id" => 123,
"name" => "foo_v0.1.0_linux_amd64.tar.gz",
},
{
"id" => 456,
"name" => "foo_v0.1.0_darwin_amd64.tar.gz",
},
]
}
@strategy.stubs(:fetch_release_metadata).returns(release_metadata)
assert_equal 456, @strategy.send(:resolve_asset_id)
end
def test_release_url
expected = "https://api.github.com/repos/owner/repo/releases/tags/tag"
assert_equal expected, @strategy.send(:release_url)
end
end
class GitDownloadStrategyTests < Homebrew::TestCase
include FileUtils