Merge pull request #1763 from minamijoyo/add-feature-github-release-download-strategy
New feature: Allow download from private GitHub repository
This commit is contained in:
commit
133e59724a
@ -532,6 +532,110 @@ class S3DownloadStrategy < CurlDownloadStrategy
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GitHubPrivateRepositoryDownloadStrategy downloads contents from GitHub
|
||||||
|
# Private Repository. To use it, add
|
||||||
|
# ":using => GitHubPrivateRepositoryDownloadStrategy" to the URL section of
|
||||||
|
# your formula. This download strategy uses GitHub access tokens (in the
|
||||||
|
# environment variables HOMEBREW_GITHUB_API_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 GitHubPrivateRepositoryDownloadStrategy < CurlDownloadStrategy
|
||||||
|
require "utils/formatter"
|
||||||
|
require "utils/github"
|
||||||
|
|
||||||
|
def initialize(name, resource)
|
||||||
|
super
|
||||||
|
parse_url_pattern
|
||||||
|
set_github_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_url_pattern
|
||||||
|
url_pattern = %r{https://github.com/([^/]+)/([^/]+)/(\S+)}
|
||||||
|
unless @url =~ url_pattern
|
||||||
|
raise CurlDownloadStrategyError, "Invalid url pattern for GitHub Repository."
|
||||||
|
end
|
||||||
|
|
||||||
|
_, @owner, @repo, @filepath = *@url.match(url_pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_url
|
||||||
|
"https://#{@github_token}@github.com/#{@owner}/#{@repo}/#{@filepath}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def _fetch
|
||||||
|
curl download_url, "-C", downloaded_size, "-o", temporary_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_github_token
|
||||||
|
@github_token = ENV["HOMEBREW_GITHUB_API_TOKEN"]
|
||||||
|
unless @github_token
|
||||||
|
raise CurlDownloadStrategyError, "Environmental variable HOMEBREW_GITHUB_API_TOKEN is required."
|
||||||
|
end
|
||||||
|
validate_github_repository_access!
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_github_repository_access!
|
||||||
|
# Test access to the repository
|
||||||
|
GitHub.repository(@owner, @repo)
|
||||||
|
rescue GitHub::HTTPNotFoundError
|
||||||
|
# We only handle HTTPNotFoundError here,
|
||||||
|
# becase AuthenticationFailedError is handled within util/github.
|
||||||
|
message = <<-EOS.undent
|
||||||
|
HOMEBREW_GITHUB_API_TOKEN can not access the repository: #{@owner}/#{@repo}
|
||||||
|
This token may not have permission to access the repository or the url of formula may be incorrect.
|
||||||
|
EOS
|
||||||
|
raise CurlDownloadStrategyError, message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GitHubPrivateRepositoryReleaseDownloadStrategy downloads tarballs from GitHub
|
||||||
|
# Release assets. To use it, add
|
||||||
|
# ":using => GitHubPrivateRepositoryReleaseDownloadStrategy" to the URL section
|
||||||
|
# of your formula. This download strategy uses GitHub access tokens (in the
|
||||||
|
# environment variables HOMEBREW_GITHUB_API_TOKEN) to sign the request.
|
||||||
|
class GitHubPrivateRepositoryReleaseDownloadStrategy < GitHubPrivateRepositoryDownloadStrategy
|
||||||
|
def parse_url_pattern
|
||||||
|
url_pattern = %r{https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(\S+)}
|
||||||
|
unless @url =~ url_pattern
|
||||||
|
raise CurlDownloadStrategyError, "Invalid url pattern for GitHub Release."
|
||||||
|
end
|
||||||
|
|
||||||
|
_, @owner, @repo, @tag, @filename = *@url.match(url_pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_url
|
||||||
|
"https://#{@github_token}@api.github.com/repos/#{@owner}/#{@repo}/releases/assets/#{asset_id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
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"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
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 }
|
||||||
|
raise CurlDownloadStrategyError, "Asset file not found." if assets.empty?
|
||||||
|
|
||||||
|
assets.first["id"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_release_metadata
|
||||||
|
release_url = "https://api.github.com/repos/#{@owner}/#{@repo}/releases/tags/#{@tag}"
|
||||||
|
GitHub.open(release_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class SubversionDownloadStrategy < VCSDownloadStrategy
|
class SubversionDownloadStrategy < VCSDownloadStrategy
|
||||||
def initialize(name, resource)
|
def initialize(name, resource)
|
||||||
super
|
super
|
||||||
|
|||||||
@ -2,11 +2,12 @@ require "testing_env"
|
|||||||
require "download_strategy"
|
require "download_strategy"
|
||||||
|
|
||||||
class ResourceDouble
|
class ResourceDouble
|
||||||
attr_reader :url, :specs, :version
|
attr_reader :url, :specs, :version, :mirrors
|
||||||
|
|
||||||
def initialize(url = "http://example.com/foo.tar.gz", specs = {})
|
def initialize(url = "http://example.com/foo.tar.gz", specs = {})
|
||||||
@url = url
|
@url = url
|
||||||
@specs = specs
|
@specs = specs
|
||||||
|
@mirrors = []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -60,6 +61,79 @@ class VCSDownloadStrategyTests < Homebrew::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class GitHubPrivateRepositoryDownloadStrategyTests < Homebrew::TestCase
|
||||||
|
def setup
|
||||||
|
resource = ResourceDouble.new("https://github.com/owner/repo/archive/1.1.5.tar.gz")
|
||||||
|
ENV["HOMEBREW_GITHUB_API_TOKEN"] = "token"
|
||||||
|
GitHub.stubs(:repository).returns {}
|
||||||
|
@strategy = GitHubPrivateRepositoryDownloadStrategy.new("foo", resource)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_set_github_token
|
||||||
|
assert_equal "token", @strategy.instance_variable_get(:@github_token)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_parse_url_pattern
|
||||||
|
assert_equal "owner", @strategy.instance_variable_get(:@owner)
|
||||||
|
assert_equal "repo", @strategy.instance_variable_get(:@repo)
|
||||||
|
assert_equal "archive/1.1.5.tar.gz", @strategy.instance_variable_get(:@filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_download_url
|
||||||
|
expected = "https://token@github.com/owner/repo/archive/1.1.5.tar.gz"
|
||||||
|
assert_equal expected, @strategy.download_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class GitHubPrivateRepositoryReleaseDownloadStrategyTests < Homebrew::TestCase
|
||||||
|
def setup
|
||||||
|
resource = ResourceDouble.new("https://github.com/owner/repo/releases/download/tag/foo_v0.1.0_darwin_amd64.tar.gz")
|
||||||
|
ENV["HOMEBREW_GITHUB_API_TOKEN"] = "token"
|
||||||
|
GitHub.stubs(:repository).returns {}
|
||||||
|
@strategy = GitHubPrivateRepositoryReleaseDownloadStrategy.new("foo", resource)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_parse_url_pattern
|
||||||
|
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_download_url
|
||||||
|
@strategy.stubs(:resolve_asset_id).returns(456)
|
||||||
|
expected = "https://token@api.github.com/repos/owner/repo/releases/assets/456"
|
||||||
|
assert_equal expected, @strategy.download_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_fetch_release_metadata
|
||||||
|
expected_release_url = "https://api.github.com/repos/owner/repo/releases/tags/tag"
|
||||||
|
github_mock = MiniTest::Mock.new
|
||||||
|
github_mock.expect :call, {}, [expected_release_url]
|
||||||
|
GitHub.stub :open, github_mock do
|
||||||
|
@strategy.send(:fetch_release_metadata)
|
||||||
|
end
|
||||||
|
github_mock.verify
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class GitDownloadStrategyTests < Homebrew::TestCase
|
class GitDownloadStrategyTests < Homebrew::TestCase
|
||||||
include FileUtils
|
include FileUtils
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user