underway 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0725b18ff63695654ff86cd63ba3d4669971a8ca
4
+ data.tar.gz: 2804c0a2916e847b2a7a53739c1568f94e7fcd99
5
+ SHA512:
6
+ metadata.gz: 19914c5e32911685356e467ea190e73ac62222b939d9234c6e9180e3483d8433b1ddea0bf59fffd1990097eb5c326b5bc26d08c50a8d2fcd55179b6ded028dec
7
+ data.tar.gz: 1601a1f138f9dcf6ea6a709a2d272165deaff6fa1cb4a98b0c5327111a06ab912f62a391542114b6f4b9d5070d445b290577448bb0d34e109a5633d3ec909447
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ config.json
2
+ config.json.localhost
3
+ config.json.prod*
4
+ *.pem
5
+ *.db
6
+ *.gem
7
+ *.rbc
8
+ .bundle
9
+ .config
10
+ .yardoc
11
+ Gemfile.lock
12
+ InstalledFiles
13
+ _yardoc
14
+ coverage
15
+ doc/
16
+ lib/bundler/man
17
+ pkg
18
+ rdoc
19
+ test/tmp
20
+ test/version_tmp
21
+ tmp
22
+ .ruby-*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 James Martin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # Underway
2
+
3
+ Underway is a Ruby gem that helps developers quickly prototype [GitHub
4
+ Apps](https://developer.github.com/apps/).
5
+
6
+ Underway consists of some convenience wrappers for the GitHub REST API, with a
7
+ particular focus on generating credentials for accessing installation resources
8
+ associated with a GitHub App. Access tokens are cached using Sqlite3 for
9
+ convenience.
10
+
11
+ If you like rapid prototyping with [Sinatra](http://sinatrarb.com) you can use
12
+ the included Sinatra routes, which allow you to quickly get access to a JWT or
13
+ access token for your App and its installations. Starting with a Sinatra
14
+ application is a fast way to build a GitHub App prototype and explore how
15
+ GitHub Apps work with the GitHub REST API.
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem "underway", "~> 1.0"
23
+ ```
24
+
25
+ And then run:
26
+
27
+ ```
28
+ bundle
29
+ ```
30
+
31
+ Or install it globally with:
32
+
33
+ ```
34
+ gem install underway
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ First, follow the documentation to [create a GitHub
40
+ App](https://developer.github.com/apps/building-github-apps/creating-a-github-app/).
41
+
42
+ When you're done creating a new App you should have:
43
+
44
+ - Your GitHub App's ID (an integer)
45
+ - A private key file (.pem)
46
+ - A webhook secret (optional)
47
+
48
+ Make a copy of the [config.json.example]() file in a location readable by your
49
+ application and edit the file to match your GitHub App's settings.
50
+
51
+ At its core, Underway is just a Ruby library and can be used in virtually any
52
+ application. To get started quickly and test out your new GitHub App you might
53
+ want to use the included [Sinatra](http://sinatrarb.com) routes. A complete
54
+ [example Sinatra
55
+ application](https://github.com/jamesmartin/underway/blob/master/example/app.rb)
56
+ is included with Underway and shows how to configure the gem and include the
57
+ informational routes.
58
+
59
+
60
+ If you would prefer to configure Underway manually, or are not building a
61
+ Sinatra application, do something like this:
62
+
63
+ ```ruby
64
+ require "underway"
65
+
66
+ # This assumes that your configuration file and private key file is located in
67
+ # the same directory as the current ruby file.
68
+
69
+ Underway::Settings.configure do |config|
70
+ config.app_root = __FILE__
71
+ config.config_filename = "config.json"
72
+ end
73
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task default: %w[test]
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.pattern = "test/**/*_test.rb"
8
+ end
@@ -0,0 +1,8 @@
1
+ {
2
+ "webhook_secret":"secret",
3
+ "private_key_filename":"./path-to.private-key.pem",
4
+ "github_api_host":"http://api.github.localhost",
5
+ "app_id":"1",
6
+ "database_url":"sqlite://gha.db",
7
+ "verbose_logging":false
8
+ }
data/example/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org" do
2
+ local_gem_path = File.expand_path("../../", __FILE__)
3
+ gem "underway", path: local_gem_path
4
+
5
+ gem "sinatra"
6
+ end
data/example/app.rb ADDED
@@ -0,0 +1,30 @@
1
+ require "sinatra"
2
+ require "underway"
3
+
4
+ configure do
5
+ Underway::Settings.configure do |config|
6
+ config.app_root = __FILE__
7
+ config.config_filename = "config.json"
8
+ end
9
+ end
10
+
11
+ include Sinatra::Underway
12
+ include Sinatra::Underway::AppInfo
13
+
14
+ get "/" do
15
+ erb <<~EOS
16
+ <a href="/info">Underway App Information</a>
17
+ EOS
18
+ end
19
+
20
+ post "/user_authz" do
21
+ debug_route(request)
22
+ end
23
+
24
+ get "/setup" do
25
+ debug_route(request)
26
+ end
27
+
28
+ post "/hook" do
29
+ debug_route(request)
30
+ end
@@ -0,0 +1,8 @@
1
+ {
2
+ "webhook_secret":"secret",
3
+ "private_key_filename":"./path-to.private-key.pem",
4
+ "github_api_host":"http://api.github.localhost",
5
+ "app_id":"1",
6
+ "database_url":"sqlite://gha.db",
7
+ "verbose_logging":false
8
+ }
data/lib/underway.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "underway/api"
2
+ require "underway/database"
3
+ require "underway/logger"
4
+ require "underway/sawyer_to_json"
5
+ require "underway/settings"
6
+ require "underway/sinatra"
7
+ require "underway/token_cache"
8
+ require "underway/version"
9
+
10
+ module Underway
11
+ end
@@ -0,0 +1,82 @@
1
+ require "jwt"
2
+ require "octokit"
3
+
4
+ module Underway
5
+ class Api
6
+ # Returns a Sawyer::Resource or PORO from the GitHub REST API
7
+ def self.invoke(route, headers: {}, method: :get)
8
+ debug_octokit! if verbose_logging?
9
+
10
+ Octokit.api_endpoint = Underway::Settings.config.raw["github_api_host"]
11
+
12
+ if !headers[:authorization] && !headers["Authorization"]
13
+ Octokit.bearer_token = generate_jwt
14
+ end
15
+
16
+ final_headers = {
17
+ accept: "application/vnd.github.machine-man-preview+json",
18
+ headers: headers
19
+ }
20
+
21
+ begin
22
+ case method
23
+ when :post then Octokit.post(route, final_headers)
24
+ else Octokit.get(route, final_headers)
25
+ end
26
+ rescue Octokit::Error => e
27
+ { error: e.to_s }
28
+ end
29
+ end
30
+
31
+ def self.generate_jwt
32
+ payload = {
33
+ # Issued at time:
34
+ iat: Time.now.to_i,
35
+ # JWT expiration time (10 minute maximum)
36
+ exp: Time.now.to_i + (10 * 60),
37
+ # GitHub Apps identifier
38
+ iss: Underway::Settings.config.app_issuer
39
+ }
40
+
41
+ JWT.encode(payload, Underway::Settings.config.private_key, "RS256")
42
+ end
43
+
44
+ # Returns a valid auth token for the installation
45
+ def self.installation_token(id:)
46
+ if token = Underway::Settings.config.token_cache.lookup_installation_auth_token(id: id)
47
+ log("token cache: hit")
48
+ return token
49
+ else
50
+ log("token cache: miss")
51
+ res = invoke(
52
+ "/app/installations/#{id}/access_tokens",
53
+ method: :post
54
+ )
55
+
56
+ if error = res[:error]
57
+ raise ArgumentError.new(error)
58
+ end
59
+
60
+ token = res.token
61
+ expires_at = res.expires_at.to_s
62
+ Underway::Settings.config.token_cache.store_installation_auth_token(id: id, token: token, expires_at: expires_at)
63
+ token
64
+ end
65
+ end
66
+
67
+ def self.debug_octokit!
68
+ stack = Faraday::RackBuilder.new do |builder|
69
+ builder.use Octokit::Middleware::FollowRedirects
70
+ builder.use Octokit::Response::RaiseError
71
+ builder.use Octokit::Response::FeedParser
72
+ builder.response :logger
73
+ builder.adapter Faraday.default_adapter
74
+ end
75
+ Octokit.middleware = stack
76
+ end
77
+
78
+ def self.verbose_logging?
79
+ !!Underway::Settings.config.verbose_logging
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,31 @@
1
+ require "sqlite3"
2
+ require "sequel"
3
+ require "singleton"
4
+
5
+ module Underway
6
+ class DB
7
+ include Singleton
8
+
9
+ @@db = nil
10
+
11
+ def self.configure(database_url)
12
+ @@db = Sequel.connect(database_url)
13
+
14
+ Sequel.default_timezone = :utc
15
+
16
+ # TODO: extract to schema migration
17
+ @@db.create_table?(:cached_tokens) do
18
+ primary_key :id
19
+ Fixnum :installation_id, null: false
20
+ String :token, null: false
21
+ DateTime :expires_at, null: false
22
+
23
+ index [:installation_id, :expires_at]
24
+ end
25
+ end
26
+
27
+ def database
28
+ @@db
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ module Underway
2
+ class Logger
3
+ def info(message)
4
+ $stdout.puts(message)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ require "json"
2
+ require "sawyer"
3
+
4
+ module Underway
5
+ class SawyerToJson
6
+ def self.convert(object)
7
+ JSON.generate(unwrap(object))
8
+ end
9
+
10
+ def self.unwrap(object)
11
+ case object
12
+ when Array then object.map { |o| unwrap(o) }
13
+ when Sawyer::Resource then object.to_hash
14
+ else object
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,101 @@
1
+ require "pathname"
2
+ require "json"
3
+
4
+ module Underway
5
+ class Settings
6
+ class Configuration
7
+ attr_reader :config, :logger
8
+
9
+ def initialize
10
+ @logger = Underway::Logger.new
11
+ end
12
+
13
+ def load!
14
+ @config = JSON.parse(
15
+ @app_root.join(@config_filename).read
16
+ )
17
+ end
18
+
19
+ # TODO: deprecate
20
+ def raw
21
+ @config
22
+ end
23
+
24
+ def app_root=(directory)
25
+ @app_root = Pathname.new(directory).dirname
26
+ end
27
+
28
+ def config_filename=(filename)
29
+ @config_filename = filename
30
+ end
31
+
32
+ def logger=(logger)
33
+ @logger = logger
34
+ end
35
+
36
+ def db
37
+ @db ||=
38
+ begin
39
+ Underway::DB.configure(config["database_url"])
40
+ Underway::DB.instance.database
41
+ end
42
+ end
43
+
44
+ # The Integration ID
45
+ # From "About -> ID" at github.com/settings/apps/<app-name>
46
+ def app_issuer
47
+ @app_issuer ||= config["app_id"]
48
+ end
49
+
50
+ # Integration webhook secret (for validating that webhooks come from GitHub)
51
+ def webhook_secret
52
+ @webhook_secret ||= config["webhook_secret"]
53
+ end
54
+
55
+ def private_key_filename
56
+ @app_root.join(config["private_key_filename"])
57
+ end
58
+
59
+ # PEM file for request signing (PKCS#1 RSAPrivateKey format)
60
+ # (Download from github.com/settings/apps/<app-name> "Private key")
61
+ def private_pem
62
+ @private_pem ||= File.read(private_key_filename)
63
+ end
64
+
65
+ # Private Key for the App, generated based on the PEM file
66
+ def private_key
67
+ @private_key ||= OpenSSL::PKey::RSA.new(private_pem)
68
+ end
69
+
70
+ def verbose_logging
71
+ @verbose ||= config["verbose_logging"]
72
+ end
73
+
74
+ def token_cache
75
+ @token_cache ||= Underway::TokenCache.new(db)
76
+ end
77
+ end
78
+
79
+ @configuration = Configuration.new
80
+
81
+ class << self
82
+ attr_reader :configuration
83
+
84
+ def configure
85
+ if block_given?
86
+ yield configuration
87
+ else
88
+ raise ArgumentError.new("Please set configuration by passing a block")
89
+ end
90
+
91
+ configuration.load!
92
+ end
93
+
94
+ # TODO: remove me
95
+ def config
96
+ configuration
97
+ end
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,24 @@
1
+ require "underway/sinatra/app_info"
2
+
3
+ # Repopen Sinatra to add helpers to the app
4
+ module Sinatra
5
+ module Underway
6
+ def debug_route(request)
7
+ log(request.inspect)
8
+ end
9
+
10
+ def verbose_logging?
11
+ !!::Underway::Settings.config.verbose_logging
12
+ end
13
+
14
+ def log(message)
15
+ if verbose_logging?
16
+ ::Underway::Settings.config.logger.info(message)
17
+ end
18
+ end
19
+
20
+ def gh_api(*args)
21
+ ::Underway::Api.invoke(*args)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,81 @@
1
+ require "sinatra/base"
2
+
3
+ # Include this module in your Sinatra app to get access to these helpful
4
+ # routes:
5
+ #
6
+ # /info/app => information about this App
7
+ # /info/app/installations => a list of all installations of this App
8
+ # /info/app/installations/:id => information about the installation
9
+ # /info/app/installations/:id/repositories => the repositories this
10
+ # installation has access to
11
+ # /info/app/installations/:id/access_token => a valid access token for the
12
+ # installation
13
+
14
+ module Sinatra
15
+ module Underway
16
+ module AppInfo
17
+ def self.registered(app)
18
+ app.get "/info" do
19
+ erb <<~EOS
20
+ <h1>Underway</h1>
21
+ <h2>Interesting routes:</h2>
22
+ <pre>
23
+ <li>/info => This page</li>
24
+ <li><a href="/info/app">/info/app</a> => Information about the configured GitHub App</li>
25
+ <li><a href="/info/app/jwt">/info/app/jwt</a> => Generates a JWT for authentication as this App</li>
26
+ <li><a href="/info/app/installations">/info/app/installations</a> => A list of installations associated with this App</li>
27
+ <li><a href="/info/app/installations/1">/info/app/installations/:id</a> => Information about the given installation of this App</li>
28
+ <li><a href="/info/app/installations/1/access_token">/info/app/installations/:id/access_token</a> => A valid access token for accessing the given installation as this App</li>
29
+ <li><a href="/info/app/installations/1/repositories">/info/app/installations/:id/repositories</a> => A list of all repositories accessible to the installation of this App</li>
30
+ </pre>
31
+ <h2>Private PEM file</h2>
32
+ <pre>
33
+ #{::Underway::Settings.config.private_key_filename}
34
+ </pre>
35
+ EOS
36
+ end
37
+
38
+ app.get "/info/app/jwt" do
39
+ content_type :json
40
+ ::Underway::Api.generate_jwt
41
+ end
42
+
43
+ app.get "/info/app" do
44
+ content_type :json
45
+ ::Underway::SawyerToJson.convert(gh_api("/app"))
46
+ end
47
+
48
+ app.get "/info/app/installations" do
49
+ content_type :json
50
+ ::Underway::SawyerToJson.convert(gh_api("/app/installations"))
51
+ end
52
+
53
+ app.get "/info/app/installations/:installation_id" do
54
+ content_type :json
55
+ ::Underway::SawyerToJson.convert(gh_api("/app/installations/#{params["installation_id"]}"))
56
+ end
57
+
58
+ app.get "/info/app/installations/:installation_id/access_token" do
59
+ content_type :json
60
+ ::Underway::SawyerToJson.convert(
61
+ gh_api(
62
+ "/app/installations/#{params["installation_id"]}/access_tokens",
63
+ method: :post
64
+ )
65
+ )
66
+ end
67
+
68
+ app.get "/info/app/installations/:installation_id/repositories" do
69
+ content_type :json
70
+ ::Underway::SawyerToJson.convert(
71
+ gh_api(
72
+ "/installation/repositories",
73
+ headers: { authorization: "token #{::Underway::Api.installation_token(id: params[:installation_id])}" }
74
+ )
75
+ )
76
+ end
77
+ end
78
+ end
79
+ end
80
+ register Underway::AppInfo
81
+ end
@@ -0,0 +1,26 @@
1
+ module Underway
2
+ class TokenCache
3
+ attr_accessor :db
4
+
5
+ def initialize(database)
6
+ @db = database
7
+ end
8
+
9
+ def lookup_installation_auth_token(id:)
10
+ results = db[:cached_tokens].where(installation_id: id)
11
+ .where{expires_at >= DateTime.now.new_offset(0)} # Force UTC Timezone
12
+ .reverse(:expires_at)
13
+ if results.any?
14
+ results.first[:token]
15
+ end
16
+ end
17
+
18
+ def store_installation_auth_token(id:, token:, expires_at:)
19
+ db[:cached_tokens].insert(
20
+ installation_id: id,
21
+ token: token,
22
+ expires_at: DateTime.parse(expires_at)
23
+ )
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Underway
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ require_relative "../test_helper"
2
+
3
+ class SawyerToJsonTest < SequelTestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ def test_can_convert_an_empty_array
9
+ assert_equal "[]", Underway::SawyerToJson.convert([])
10
+ end
11
+
12
+ def test_can_convert_an_array_with_nested_hash
13
+ obj = [ { foo: "bar" } ]
14
+ expected = "[{\"foo\":\"bar\"}]"
15
+ actual = Underway::SawyerToJson.convert(obj)
16
+
17
+ assert_equal expected, actual
18
+ end
19
+
20
+ def test_can_convert_a_nested_sawyer_object
21
+ agent = Sawyer::Agent.new("/irrelevant")
22
+ data = "{\"foo\":\"bar\"}"
23
+ resource = Sawyer::Resource.new(agent, agent.decode_body(data))
24
+ obj = [ resource ]
25
+ expected = "[{\"foo\":\"bar\"}]"
26
+ actual = Underway::SawyerToJson.convert(obj)
27
+
28
+ assert_equal expected, actual
29
+ end
30
+ end
@@ -0,0 +1,66 @@
1
+ require_relative "../test_helper"
2
+
3
+ class TokenCacheTest < SequelTestCase
4
+
5
+ def setup
6
+ @cache = Underway::TokenCache.new(Underway::DB.instance.database)
7
+ end
8
+
9
+ def test_can_store_and_retrieve_a_token
10
+ Timecop.freeze(DateTime.parse("2018-02-12T09:00:00+00:00")) do
11
+ @cache.store_installation_auth_token(
12
+ id: 1,
13
+ token: "some-token",
14
+ expires_at: "2018-02-12T10:00:00Z"
15
+ )
16
+
17
+ assert_equal "some-token", @cache.lookup_installation_auth_token(id: 1)
18
+ end
19
+ end
20
+
21
+ def test_retrieves_the_newest_token
22
+ Timecop.freeze(DateTime.parse("2018-02-12T09:00:00+00:00")) do
23
+ @cache.store_installation_auth_token(
24
+ id: 1,
25
+ token: "first-token",
26
+ expires_at: "2018-02-12T10:00:00Z"
27
+ )
28
+
29
+ @cache.store_installation_auth_token(
30
+ id: 1,
31
+ token: "second-token",
32
+ expires_at: "2018-02-12T11:00:00Z"
33
+ )
34
+
35
+ assert_equal "second-token", @cache.lookup_installation_auth_token(id: 1)
36
+ end
37
+ end
38
+
39
+ def test_returns_nil_when_looking_up_a_token_that_does_not_exist
40
+ assert_nil @cache.lookup_installation_auth_token(id: "non-existent")
41
+ end
42
+
43
+ def test_returns_nil_for_tokens_that_have_expired
44
+ Timecop.freeze(DateTime.parse("2018-02-12T09:00:00+00:00")) do
45
+ @cache.store_installation_auth_token(
46
+ id: 1,
47
+ token: "some-token",
48
+ expires_at: "2018-02-12T08:00:00Z"
49
+ )
50
+
51
+ assert_nil @cache.lookup_installation_auth_token(id: 1)
52
+ end
53
+ end
54
+
55
+ def test_retrieves_a_token_when_the_expiry_is_equal_to_now
56
+ Timecop.freeze(DateTime.parse("2018-02-12T09:00:00+00:00")) do
57
+ @cache.store_installation_auth_token(
58
+ id: 1,
59
+ token: "some-token",
60
+ expires_at: "2018-02-12T09:00:00Z"
61
+ )
62
+
63
+ assert_equal "some-token", @cache.lookup_installation_auth_token(id: 1)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,14 @@
1
+ #require_relative "../lib/database"
2
+
3
+ # http://sequel.jeremyevans.net/rdoc/files/doc/testing_rdoc.html
4
+
5
+ class SequelTestCase < Minitest::Test
6
+ def run(*args, &block)
7
+ Underway::DB.configure("sqlite:/") # Always assume an in-memory database for now
8
+
9
+ Sequel::Model.db.transaction(
10
+ rollback: :always,
11
+ auto_savepoint: true
12
+ ){super}
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ #ENV["RACK_ENV"] = "test"
2
+ require "minitest/autorun"
3
+ require "timecop"
4
+ require "underway"
5
+ require_relative "./sequel_helper.rb"
6
+
7
+ Timecop.safe_mode = true # Never, NEVER, allow arbitrary mutation of time without a block
data/underway.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "underway/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "underway"
8
+ spec.version = Underway::VERSION
9
+ spec.authors = ["James Martin"]
10
+ spec.email = ["underway-gem@jmrtn.com"]
11
+ spec.summary = %q{Quick prototyping helpers for building GitHub Apps.}
12
+ spec.description = %q{Generate tokens and interact with the GitHub Rest API as a GitHub App.}
13
+ spec.homepage = "https://github.com/jamesmartin/underway"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.16"
22
+ spec.add_development_dependency "minitest"
23
+ spec.add_development_dependency "pry"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "sinatra"
26
+ spec.add_development_dependency "timecop"
27
+
28
+ spec.add_runtime_dependency "jwt", "~> 2.1"
29
+ spec.add_runtime_dependency "octokit", "~> 4.0"
30
+ spec.add_runtime_dependency "sequel"
31
+ spec.add_runtime_dependency "sqlite3", "~> 1.3"
32
+ end
metadata ADDED
@@ -0,0 +1,213 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: underway
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - James Martin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sinatra
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: timecop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: jwt
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.1'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.1'
111
+ - !ruby/object:Gem::Dependency
112
+ name: octokit
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sequel
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: sqlite3
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.3'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.3'
153
+ description: Generate tokens and interact with the GitHub Rest API as a GitHub App.
154
+ email:
155
+ - underway-gem@jmrtn.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gitignore"
161
+ - ".ruby-version"
162
+ - Gemfile
163
+ - LICENSE
164
+ - README.md
165
+ - Rakefile
166
+ - config.json.example
167
+ - example/Gemfile
168
+ - example/app.rb
169
+ - example/config.json.example
170
+ - lib/underway.rb
171
+ - lib/underway/api.rb
172
+ - lib/underway/database.rb
173
+ - lib/underway/logger.rb
174
+ - lib/underway/sawyer_to_json.rb
175
+ - lib/underway/settings.rb
176
+ - lib/underway/sinatra.rb
177
+ - lib/underway/sinatra/app_info.rb
178
+ - lib/underway/token_cache.rb
179
+ - lib/underway/version.rb
180
+ - test/lib/sawyer_to_json_test.rb
181
+ - test/lib/token_cache_test.rb
182
+ - test/sequel_helper.rb
183
+ - test/test_helper.rb
184
+ - underway.gemspec
185
+ homepage: https://github.com/jamesmartin/underway
186
+ licenses:
187
+ - MIT
188
+ metadata: {}
189
+ post_install_message:
190
+ rdoc_options: []
191
+ require_paths:
192
+ - lib
193
+ required_ruby_version: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ requirements: []
204
+ rubyforge_project:
205
+ rubygems_version: 2.6.11
206
+ signing_key:
207
+ specification_version: 4
208
+ summary: Quick prototyping helpers for building GitHub Apps.
209
+ test_files:
210
+ - test/lib/sawyer_to_json_test.rb
211
+ - test/lib/token_cache_test.rb
212
+ - test/sequel_helper.rb
213
+ - test/test_helper.rb