aboutsummaryrefslogtreecommitdiffstats
path: root/Library
diff options
context:
space:
mode:
authorMike McQuaid2017-01-16 18:52:47 +0000
committerGitHub2017-01-16 18:52:47 +0000
commit133e59724a365b9095c4e6f51809fcac48ff1b44 (patch)
tree20192edb9985e8f88a34dfb51503bd8238be7764 /Library
parentc6d7c85d4f7e6f7963222956638d62165a9c8742 (diff)
parent12b9cb7f4c5e0927cb1049db4f1aa53fbe371a6d (diff)
downloadbrew-133e59724a365b9095c4e6f51809fcac48ff1b44.tar.bz2
Merge pull request #1763 from minamijoyo/add-feature-github-release-download-strategy
New feature: Allow download from private GitHub repository
Diffstat (limited to 'Library')
-rw-r--r--Library/Homebrew/download_strategy.rb104
-rw-r--r--Library/Homebrew/test/download_strategies_test.rb76
2 files changed, 179 insertions, 1 deletions
diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb
index 9f9b2abab..bd036067d 100644
--- a/Library/Homebrew/download_strategy.rb
+++ b/Library/Homebrew/download_strategy.rb
@@ -532,6 +532,110 @@ class S3DownloadStrategy < CurlDownloadStrategy
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
def initialize(name, resource)
super
diff --git a/Library/Homebrew/test/download_strategies_test.rb b/Library/Homebrew/test/download_strategies_test.rb
index 87218fb12..2b64abbf9 100644
--- a/Library/Homebrew/test/download_strategies_test.rb
+++ b/Library/Homebrew/test/download_strategies_test.rb
@@ -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,79 @@ class VCSDownloadStrategyTests < Homebrew::TestCase
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
include FileUtils