Skip to content
This repository has been archived by the owner on May 16, 2021. It is now read-only.

Commit

Permalink
Refactor GitHub impl and add GH Enterprise support
Browse files Browse the repository at this point in the history
  • Loading branch information
mattbrictson committed Oct 31, 2016
1 parent 61ae182 commit 8e6dcab
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 106 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ gem install chandler

### 2. Configure .netrc or set ENV vars

In order to access the GitHub API on your behalf, you must provide chandler with your GitHub credentials.
In order to access the GitHub API on your behalf, you must provide chandler with your GitHub credentials.

Do this by creating a `~/.netrc` file with your GitHub username and password, like this:

Expand Down Expand Up @@ -80,6 +80,21 @@ Other command-line options:
* `--changelog=History.md` – location of the CHANGELOG (defaults to `CHANGELOG.md`)
* `--tag-prefix=myapp-` – specify Git version tags are in the format `myapp-1.0.0` instead of `1.0.0`

## GitHub Enterprise

Chandler supports GitHub Enterprise as well as public GitHub repositories. It will make an educated guess as to where your GitHub Enterprise installation is located based on the `origin` git remote. You can also specify your GitHub Enterprise repository using the `--github` option like this:

```
--github=git@github.mycompany.com:organization/project.git
```

Or like this:

```
--github=https://github.mycompany.com/organization/project
```

To authenticate, Chandler relies on your `~/.netrc`, as explained above. Replace `api.github.com` with the hostname of your GitHub Enterprise installation (`github.mycompany.com` in the example).

## Rakefile integration

Expand Down
17 changes: 0 additions & 17 deletions lib/chandler/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,6 @@ def git
@git ||= Chandler::Git.new(:path => git_path, :tag_mapper => tag_mapper)
end

def octokit
@octokit ||= Octokit::Client.new(octokit_options)
end

def octokit_options
chandler_token_key = "CHANDLER_GITHUB_API_TOKEN"
if environment[chandler_token_key]
{ :access_token => environment[chandler_token_key] }
else
{ :netrc => true }
end
end

def environment
@environment ||= ENV
end

def github
@github ||= Chandler::GitHub.new(
:repository => github_repository,
Expand Down
26 changes: 10 additions & 16 deletions lib/chandler/github.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "octokit"
require "chandler/github/client"
require "chandler/github/errors"
require "chandler/github/remote"

module Chandler
# A facade for performing GitHub API operations on a given GitHub repository
Expand All @@ -11,7 +12,8 @@ class GitHub
attr_reader :repository, :config

def initialize(repository:, config:)
@repository = parse_repository(repository)
@repository = repository
@remote = Remote.parse(repository)
@config = config
end

Expand All @@ -28,12 +30,10 @@ def create_or_update_release(tag:, title:, description:)

private

def parse_repository(repo)
repo[%r{(git@github.com:|://github.com/)(.*)\.git}, 2] || repo
end
attr_reader :remote

def existing_release(tag)
release = client.release_for_tag(repository, tag)
release = client.release_for_tag(remote.repository, tag)
release.id.nil? ? nil : release
rescue Octokit::NotFound
nil
Expand All @@ -49,19 +49,13 @@ def release_unchanged?(release, title, desc)
end

def create_release(tag, title, desc)
client.create_release(repository, tag, :name => title, :body => desc)
client.create_release(
remote.repository, tag, :name => title, :body => desc
)
end

def client
@client ||= begin
octokit = config.octokit
octokit.login ? octokit : fail_missing_credentials
end
end

def fail_missing_credentials
netrc = config.octokit.netrc
raise netrc ? NetrcAuthenticationFailure : TokenAuthenticationFailure
@client ||= Client.new(:host => remote.host).tap(&:login!)
end
end
end
40 changes: 40 additions & 0 deletions lib/chandler/github/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "chandler/github/errors"
require "delegate"
require "octokit"
require "uri"

module Chandler
class GitHub
# A thin wrapper around Octokit::Client that adds support for automatic
# GitHub Enterprise, .netrc, and ENV token-based authentication.
#
class Client < SimpleDelegator
def initialize(host: "github.com",
environment: ENV,
octokit_client: Octokit::Client)
super(octokit_client.new(detect_auth_option(environment)))
assign_enterprise_endpoint(host)
end

def login!
return if login
raise netrc ? NetrcAuthenticationFailure : TokenAuthenticationFailure
end

private

def detect_auth_option(env)
if (token = env["CHANDLER_GITHUB_API_TOKEN"])
{ :access_token => token }
else
{ :netrc => true }
end
end

def assign_enterprise_endpoint(host)
return if host.downcase == "github.com"
self.api_endpoint = "https://#{host}/api/v3/"
end
end
end
end
37 changes: 37 additions & 0 deletions lib/chandler/github/remote.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require "uri"

module Chandler
class GitHub
# Assuming a git remote points to a public GitHub or a GitHub Enterprise
# repository, this class parses the remote to obtain the host and repository
# path. Supports SSH and HTTPS style git remotes.
#
# This class also handles parsing values passed into the `--github` command
# line option, which may be a public GitHub repository name, like
# "mattbrictson/chandler".
#
class Remote
def self.parse(url)
if (match = url.match(/@([^:]+):(.+)$/))
new(match[1], match[2])
else
parsed_uri = URI(url)
host = parsed_uri.host || "github.com"
path = parsed_uri.path.sub(%r{^/+}, "")
new(host, path)
end
end

attr_reader :host, :path

def initialize(host, path)
@host = host.downcase
@path = path
end

def repository
path.sub(/\.git$/, "")
end
end
end
end
10 changes: 0 additions & 10 deletions test/chandler/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,4 @@ def test_changelog
assert_instance_of(Chandler::Changelog, changelog)
assert_equal("../test/history.md", changelog.path)
end

def test_octokit_options_as_netrc
@config.environment = { "ANYTHING" => "1234" }
assert_equal({ :netrc => true }, @config.octokit_options)
end

def test_octokit_options_as_access_token
@config.environment = { "CHANDLER_GITHUB_API_TOKEN" => "1234" }
assert_equal({ :access_token => "1234" }, @config.octokit_options)
end
end
72 changes: 72 additions & 0 deletions test/chandler/github/client_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require "minitest_helper"
require "chandler/github/client"

class Chandler::GitHub::ClientTest < Minitest::Test
class FakeOctokitClient
attr_reader :netrc, :access_token
attr_accessor :api_endpoint

def initialize(auth_options)
@netrc = auth_options.fetch(:netrc, false)
@access_token = auth_options[:access_token]
end

def login
"successful"
end
end

class FakeOctokitClientWithError < FakeOctokitClient
def login
nil
end
end

def test_uses_netrc_by_default
client = Chandler::GitHub::Client.new(:octokit_client => FakeOctokitClient)
assert(client.netrc)
assert_nil(client.access_token)
end

def test_uses_access_token_from_env
client = Chandler::GitHub::Client.new(
:environment => { "CHANDLER_GITHUB_API_TOKEN" => "foo" },
:octokit_client => FakeOctokitClient
)
assert_equal("foo", client.access_token)
refute(client.netrc)
end

def test_doesnt_change_default_endpoint_for_public_github
client = Chandler::GitHub::Client.new(
:host => "github.com",
:octokit_client => FakeOctokitClient
)
assert_nil(client.api_endpoint)
end

def test_assigns_enterprise_endpoint
client = Chandler::GitHub::Client.new(
:host => "github.example.com",
:octokit_client => FakeOctokitClient
)
assert_equal("https://github.example.com/api/v3/", client.api_endpoint)
end

def test_raises_exception_if_netrc_fails
assert_raises(Chandler::GitHub::NetrcAuthenticationFailure) do
Chandler::GitHub::Client.new(
:octokit_client => FakeOctokitClientWithError
).login!
end
end

def test_raises_exception_if_access_token_fails
assert_raises(Chandler::GitHub::TokenAuthenticationFailure) do
Chandler::GitHub::Client.new(
:environment => { "CHANDLER_GITHUB_API_TOKEN" => "foo" },
:octokit_client => FakeOctokitClientWithError
).login!
end
end
end
34 changes: 34 additions & 0 deletions test/chandler/github/remote_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require "minitest_helper"
require "chandler/github/remote"

class Chandler::GitHub::RemoteTest < Minitest::Test
def test_bare_repository_name
repo = parse("mattbrictson/chandler")
assert_equal("github.com", repo.host)
assert_equal("mattbrictson/chandler", repo.repository)
end

def test_ssh_style_url
repo = parse("git@github.com:mattbrictson/chandler.git")
assert_equal("github.com", repo.host)
assert_equal("mattbrictson/chandler", repo.repository)
end

def test_https_url
repo = parse("https://github.com/mattbrictson/chandler.git")
assert_equal("github.com", repo.host)
assert_equal("mattbrictson/chandler", repo.repository)
end

def test_enterprise_ssh_style_url
repo = parse("git@github.example.com:org/project.git")
assert_equal("github.example.com", repo.host)
assert_equal("org/project", repo.repository)
end

private

def parse(url)
Chandler::GitHub::Remote.parse(url)
end
end
Loading

0 comments on commit 8e6dcab

Please sign in to comment.