gemgate 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.env.sample +5 -0
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/Procfile +1 -0
- data/README.md +46 -0
- data/Rakefile +8 -0
- data/config.ru +3 -0
- data/gemgate.gemspec +25 -0
- data/lib/gemgate.rb +11 -0
- data/lib/gemgate/gem_files.rb +9 -0
- data/lib/gemgate/gem_wrapper.rb +42 -0
- data/lib/gemgate/index.rb +40 -0
- data/lib/gemgate/quick_marshal_specs.rb +31 -0
- data/lib/gemgate/repository.rb +30 -0
- data/lib/gemgate/specs_index.rb +62 -0
- data/lib/gemgate/storage/s3.rb +48 -0
- data/lib/gemgate/version.rb +3 -0
- data/lib/gemgate/web.rb +40 -0
- data/spec/acceptance/push_spec.rb +96 -0
- data/spec/fixtures/foobar-0.0.1.gem +0 -0
- data/spec/fixtures/foobar-0.0.1.pre.gem +0 -0
- data/spec/gem_files_spec.rb +16 -0
- data/spec/gem_wrapper_spec.rb +51 -0
- data/spec/index_spec.rb +83 -0
- data/spec/quick_marshal_specs_spec.rb +16 -0
- data/spec/repository_spec.rb +52 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/specs_index_spec.rb +92 -0
- data/spec/storage/s3_spec.rb +56 -0
- data/spec/web_spec.rb +42 -0
- metadata +167 -0
data/.env.sample
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Procfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
web: bundle exec rackup -I lib -p $PORT
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# gemgate
|
2
|
+
|
3
|
+
gemgate receives built gems and makes them available via S3.
|
4
|
+
|
5
|
+
Example use:
|
6
|
+
|
7
|
+
```bash
|
8
|
+
$ gem build foobar.gemspec #=> produces foobar-0.0.1.gem
|
9
|
+
$ curl -F file=@foobar-0.0.1.gem -u foo:bar https://gemgate.herokuapp.com/
|
10
|
+
$ gem sources -a https://gemgate.s3.amazonaws.com/deadbeef/
|
11
|
+
$ gem install foobar -v 0.0.1
|
12
|
+
```
|
13
|
+
|
14
|
+
## Running locally
|
15
|
+
|
16
|
+
```bash
|
17
|
+
$ cp .env.sample .env
|
18
|
+
# edit .env
|
19
|
+
$ foreman start
|
20
|
+
$ curl -F file=@foobar-0.0.1.gem -u $GEMGATE_AUTH http://localhost:5000/
|
21
|
+
$ gem sources -a https://gemgate-development.s3.amazonaws.com/$S3_KEY_PREFIX
|
22
|
+
$ gem install foobar -v 0.0.1
|
23
|
+
```
|
24
|
+
|
25
|
+
## Deployment on Heroku
|
26
|
+
|
27
|
+
```bash
|
28
|
+
$ heroku create gemgate-production -s cedar
|
29
|
+
$ heroku config:add -a gemgate-production \
|
30
|
+
AWS_ACCESS_KEY_ID=... \
|
31
|
+
AWS_SECRET_ACCESS_KEY=... \
|
32
|
+
GEMGATE_AUTH=foo:bar \
|
33
|
+
S3_BUCKET=... \
|
34
|
+
S3_KEY_PREFIX=...
|
35
|
+
$ git push heroku master
|
36
|
+
```
|
37
|
+
|
38
|
+
## Configuration
|
39
|
+
|
40
|
+
* `GEMGATE_AUTH`: User and password to require for basic auth, joined by `:`
|
41
|
+
* `AWS_ACCESS_KEY_ID`: The AWS access key to use when communicating with S3
|
42
|
+
* `AWS_SECRET_ACCESS_KEY`: The AWS secret access key to use when communicating with S3
|
43
|
+
* `S3_BUCKET`: Name of the S3 bucket to use
|
44
|
+
* `S3_KEY_PREFIX`: Probably random string (eg created with `openssl rand -hex 32`) to prefix file/directory keys with. Mainly for security.
|
45
|
+
|
46
|
+
It's recommended you use [IAM credentials](https://gist.github.com/34e08aaf5e5e87814c72) with bucket-specific access for S3.
|
data/Rakefile
ADDED
data/config.ru
ADDED
data/gemgate.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path("../lib/gemgate/version", __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ["Dan Peterson"]
|
7
|
+
gem.email = ["dpiddy@gmail.com"]
|
8
|
+
gem.description = %q{Host a private gem repository at S3}
|
9
|
+
gem.summary = %q{Host a private gem repository at S3}
|
10
|
+
gem.homepage = "https://github.com/dpiddy/gemgate"
|
11
|
+
|
12
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
13
|
+
gem.files = `git ls-files`.split("\n")
|
14
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
gem.name = "gemgate"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = Gemgate::VERSION
|
18
|
+
|
19
|
+
gem.add_dependency "sinatra", "~> 1.3.2"
|
20
|
+
gem.add_dependency "fog", "~> 1.5.0"
|
21
|
+
|
22
|
+
gem.add_development_dependency "rack-test"
|
23
|
+
gem.add_development_dependency "rake"
|
24
|
+
gem.add_development_dependency "rspec"
|
25
|
+
end
|
data/lib/gemgate.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "fog"
|
2
|
+
|
3
|
+
require "gemgate/gem_files"
|
4
|
+
require "gemgate/gem_wrapper"
|
5
|
+
require "gemgate/index"
|
6
|
+
require "gemgate/specs_index"
|
7
|
+
require "gemgate/quick_marshal_specs"
|
8
|
+
require "gemgate/repository"
|
9
|
+
require "gemgate/web"
|
10
|
+
|
11
|
+
require "gemgate/storage/s3"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Gemgate
|
2
|
+
class GemWrapper
|
3
|
+
def self.from_path(path)
|
4
|
+
new.tap do |gem_wrapper|
|
5
|
+
gem_wrapper.spec = Gem::Package.open(File.open(path)) {|p| p.metadata }
|
6
|
+
gem_wrapper.path = path
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :spec, :path
|
11
|
+
|
12
|
+
def filename
|
13
|
+
spec.file_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def spec_filename
|
17
|
+
spec.spec_name
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
spec.name
|
22
|
+
end
|
23
|
+
|
24
|
+
def version
|
25
|
+
spec.version
|
26
|
+
end
|
27
|
+
|
28
|
+
def prerelease?
|
29
|
+
spec.version.prerelease?
|
30
|
+
end
|
31
|
+
|
32
|
+
def platform
|
33
|
+
platform = spec.original_platform
|
34
|
+
platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
|
35
|
+
platform
|
36
|
+
end
|
37
|
+
|
38
|
+
def data
|
39
|
+
File.open(path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Gemgate
|
2
|
+
class Index
|
3
|
+
attr_accessor :storage
|
4
|
+
attr_writer :specs, :latest_specs, :prerelease_specs, :quick_marshal_specs
|
5
|
+
|
6
|
+
def add(gem)
|
7
|
+
specs.add(gem)
|
8
|
+
latest_specs.add(gem)
|
9
|
+
prerelease_specs.add(gem)
|
10
|
+
quick_marshal_specs.add(gem)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def specs
|
16
|
+
@specs ||= SpecsIndex.new("specs.4.8.gz").tap do |s|
|
17
|
+
s.storage = storage
|
18
|
+
s.conditions << lambda {|g| !g.prerelease? }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def latest_specs
|
23
|
+
@latest_specs ||= SpecsIndex.new("latest_specs.4.8.gz").tap do |s|
|
24
|
+
s.storage = storage
|
25
|
+
s.conditions << lambda {|g| !g.prerelease? }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def prerelease_specs
|
30
|
+
@prerelease_specs ||= SpecsIndex.new("prerelease_specs.4.8.gz").tap do |s|
|
31
|
+
s.storage = storage
|
32
|
+
s.conditions << lambda {|g| g.prerelease? }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def quick_marshal_specs
|
37
|
+
@quick_marshal_specs || QuickMarshalSpecs.new.tap {|l| l.storage = storage }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gemgate
|
2
|
+
class QuickMarshalSpecs
|
3
|
+
class Entry
|
4
|
+
def initialize(gem)
|
5
|
+
@gem = gem
|
6
|
+
end
|
7
|
+
|
8
|
+
def filename
|
9
|
+
"quick/Marshal.4.8/#{@gem.spec_filename}.rz"
|
10
|
+
end
|
11
|
+
|
12
|
+
def spec
|
13
|
+
@gem.spec
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :storage
|
18
|
+
|
19
|
+
def add(gem)
|
20
|
+
entry = Entry.new(gem)
|
21
|
+
|
22
|
+
storage.create(entry.filename, to_deflated_marshal(entry.spec))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def to_deflated_marshal(data)
|
28
|
+
Gem.deflate(Marshal.dump(data))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Gemgate
|
2
|
+
class Repository
|
3
|
+
attr_writer :realizer, :gem_files, :index
|
4
|
+
|
5
|
+
def add_gem(path)
|
6
|
+
gem = realizer.call(path)
|
7
|
+
|
8
|
+
gem_files.add(gem)
|
9
|
+
index.add(gem)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def realizer
|
15
|
+
@realizer || GemWrapper.public_method(:from_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def gem_files
|
19
|
+
@gem_files ||= GemFiles.new.tap {|f| f.storage = storage }
|
20
|
+
end
|
21
|
+
|
22
|
+
def storage
|
23
|
+
@storage ||= Storage::S3.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def index
|
27
|
+
@index ||= Index.new.tap {|i| i.storage = storage }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Gemgate
|
2
|
+
class SpecsIndex
|
3
|
+
attr_accessor :storage
|
4
|
+
attr_reader :conditions
|
5
|
+
|
6
|
+
def initialize(filename)
|
7
|
+
@filename = filename
|
8
|
+
@conditions = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(gem)
|
12
|
+
if conditions.all? {|c| c.call(gem) }
|
13
|
+
update_with(gem)
|
14
|
+
else
|
15
|
+
ensure_exists
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def ensure_exists
|
22
|
+
unless existing_file_data
|
23
|
+
store([])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_with(gem)
|
28
|
+
# create our own instance of Gem::Version for the incoming gem version.
|
29
|
+
# this works around an issue where the @hash instance variable is already set
|
30
|
+
# in the YAML-dumped metadata in the uploaded gem, which breaks our uniq'ing of the
|
31
|
+
# index.
|
32
|
+
store(existing_data + [[gem.name, Gem::Version.create(gem.version.version), gem.platform]])
|
33
|
+
end
|
34
|
+
|
35
|
+
def existing_file_data
|
36
|
+
@existing_file_data ||= storage.get(@filename)
|
37
|
+
end
|
38
|
+
|
39
|
+
def existing_data
|
40
|
+
if existing_file_data
|
41
|
+
from_gzipped_marshal(existing_file_data).map do |(name, version, platform)|
|
42
|
+
# create our own Gem::Version here, too
|
43
|
+
[name, Gem::Version.create(version.version), platform]
|
44
|
+
end
|
45
|
+
else
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def store(data)
|
51
|
+
storage.update(@filename, to_gzipped_marshal(data.uniq))
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_gzipped_marshal(data)
|
55
|
+
Gem.gzip(Marshal.dump(data))
|
56
|
+
end
|
57
|
+
|
58
|
+
def from_gzipped_marshal(data)
|
59
|
+
Marshal.load(Gem.gunzip(data))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Gemgate
|
2
|
+
module Storage
|
3
|
+
class S3
|
4
|
+
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
def initialize(env = ENV)
|
8
|
+
@env = env
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(path)
|
12
|
+
if file = remote_directory.files.get(prefix(path))
|
13
|
+
file.body
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(path, body)
|
18
|
+
remote_directory.files.create(:key => prefix(path), :body => body, :acl => "public-read")
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(path, body)
|
22
|
+
create(path, body)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def env!(name)
|
28
|
+
@env[name] or raise "ENV[#{name}] must be set"
|
29
|
+
end
|
30
|
+
|
31
|
+
def prefix(path)
|
32
|
+
File.join(env!("S3_KEY_PREFIX"), path)
|
33
|
+
end
|
34
|
+
|
35
|
+
def remote_directory
|
36
|
+
fog.directories.get(remote_directory_name) or raise Error, "Bucket `#{remote_directory_name}` doesn't exist"
|
37
|
+
end
|
38
|
+
|
39
|
+
def remote_directory_name
|
40
|
+
env!("S3_BUCKET")
|
41
|
+
end
|
42
|
+
|
43
|
+
def fog
|
44
|
+
Fog::Storage.new(:provider => "AWS", :aws_access_key_id => env!("AWS_ACCESS_KEY_ID"), :aws_secret_access_key => env!("AWS_SECRET_ACCESS_KEY"))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/gemgate/web.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "sinatra/base"
|
2
|
+
|
3
|
+
module Gemgate
|
4
|
+
class Web < Sinatra::Base
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :repository
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.env!(name)
|
11
|
+
ENV[name] or raise "ENV[#{name}] must be set"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.auth
|
15
|
+
env!("GEMGATE_AUTH").split(":")
|
16
|
+
end
|
17
|
+
|
18
|
+
use Rack::Auth::Basic, "gemgate" do |username, password|
|
19
|
+
[username, password] == auth
|
20
|
+
end
|
21
|
+
|
22
|
+
configure :test do
|
23
|
+
enable :raise_errors
|
24
|
+
end
|
25
|
+
|
26
|
+
disable :show_exceptions
|
27
|
+
|
28
|
+
post "/" do
|
29
|
+
repository.add_gem(params[:file][:tempfile].path)
|
30
|
+
|
31
|
+
status 200
|
32
|
+
end
|
33
|
+
|
34
|
+
helpers do
|
35
|
+
def repository
|
36
|
+
@repository ||= self.class.repository || Repository.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "acceptance: push" do
|
4
|
+
it "basically works" do
|
5
|
+
post "/", :file => Rack::Test::UploadedFile.new(fixture("foobar-0.0.1.gem"), "application/octet-stream")
|
6
|
+
|
7
|
+
last_response.status.should == 200
|
8
|
+
|
9
|
+
file = directory.files.get("deadbeef/gems/foobar-0.0.1.gem")
|
10
|
+
file.should_not be_nil, "gems/foobar-0.0.1.gem should exist"
|
11
|
+
file.public_url.should_not be_nil, "gems/foobar-0.0.1.gem should be public"
|
12
|
+
file.body.tap {|b| b.force_encoding("binary") }.should == fixture_read("foobar-0.0.1.gem")
|
13
|
+
|
14
|
+
file = directory.files.get("deadbeef/latest_specs.4.8.gz")
|
15
|
+
file.should_not be_nil, "latest_specs.4.8.gz should exist"
|
16
|
+
file.public_url.should_not be_nil, "latest_specs.4.8.gz should be public"
|
17
|
+
|
18
|
+
latest_specs = Marshal.load(Gem.gunzip(file.body))
|
19
|
+
latest_specs.should == [["foobar", Gem::Version.new("0.0.1"), "ruby"]]
|
20
|
+
|
21
|
+
file = directory.files.get("deadbeef/specs.4.8.gz")
|
22
|
+
file.should_not be_nil, "specs.4.8.gz should exist"
|
23
|
+
file.public_url.should_not be_nil, "specs.4.8.gz should be public"
|
24
|
+
|
25
|
+
specs = Marshal.load(Gem.gunzip(file.body))
|
26
|
+
specs.should == latest_specs
|
27
|
+
|
28
|
+
file = directory.files.get("deadbeef/prerelease_specs.4.8.gz")
|
29
|
+
file.should_not be_nil, "prerelease_specs.4.8.gz should exist"
|
30
|
+
file.public_url.should_not be_nil, "prerelease_specs.4.8.gz should be public"
|
31
|
+
|
32
|
+
prerelease_specs = Marshal.load(Gem.gunzip(file.body))
|
33
|
+
prerelease_specs.should == []
|
34
|
+
|
35
|
+
file = directory.files.get("deadbeef/quick/Marshal.4.8/foobar-0.0.1.gemspec.rz")
|
36
|
+
file.should_not be_nil, "quick spec should exist"
|
37
|
+
file.public_url.should_not be_nil, "quick spec should be public"
|
38
|
+
|
39
|
+
quick_spec = Marshal.load(Gem.inflate(file.body))
|
40
|
+
quick_spec.should == Gemgate::GemWrapper.from_path(fixture("foobar-0.0.1.gem")).spec
|
41
|
+
end
|
42
|
+
|
43
|
+
it "basically works for a prerelease gem" do
|
44
|
+
post "/", :file => Rack::Test::UploadedFile.new(fixture("foobar-0.0.1.pre.gem"), "application/octet-stream")
|
45
|
+
|
46
|
+
last_response.status.should == 200
|
47
|
+
|
48
|
+
file = directory.files.get("deadbeef/gems/foobar-0.0.1.pre.gem")
|
49
|
+
file.should_not be_nil, "gems/foobar-0.0.1.pre.gem should exist"
|
50
|
+
file.public_url.should_not be_nil, "gems/foobar-0.0.1.pre.gem should be public"
|
51
|
+
file.body.tap {|b| b.force_encoding("binary") }.should == fixture_read("foobar-0.0.1.pre.gem")
|
52
|
+
|
53
|
+
file = directory.files.get("deadbeef/latest_specs.4.8.gz")
|
54
|
+
file.should_not be_nil, "latest_specs.4.8.gz should exist"
|
55
|
+
file.public_url.should_not be_nil, "latest_specs.4.8.gz should be public"
|
56
|
+
|
57
|
+
latest_specs = Marshal.load(Gem.gunzip(file.body))
|
58
|
+
latest_specs.should == []
|
59
|
+
|
60
|
+
file = directory.files.get("deadbeef/prerelease_specs.4.8.gz")
|
61
|
+
file.should_not be_nil, "prerelease_specs.4.8.gz should exist"
|
62
|
+
file.public_url.should_not be_nil, "prerelease_specs.4.8.gz should be public"
|
63
|
+
|
64
|
+
prerelease_specs = Marshal.load(Gem.gunzip(file.body))
|
65
|
+
prerelease_specs.should == [["foobar", Gem::Version.new("0.0.1.pre"), "ruby"]]
|
66
|
+
|
67
|
+
file = directory.files.get("deadbeef/quick/Marshal.4.8/foobar-0.0.1.pre.gemspec.rz")
|
68
|
+
file.should_not be_nil, "quick spec should exist"
|
69
|
+
file.public_url.should_not be_nil, "quick spec should be public"
|
70
|
+
|
71
|
+
quick_spec = Marshal.load(Gem.inflate(file.body))
|
72
|
+
quick_spec.should == Gemgate::GemWrapper.from_path(fixture("foobar-0.0.1.pre.gem")).spec
|
73
|
+
end
|
74
|
+
|
75
|
+
it "does nothing if no authentication info is provded" do
|
76
|
+
header "Authorization", nil
|
77
|
+
|
78
|
+
post "/", :file => Rack::Test::UploadedFile.new(fixture("foobar-0.0.1.pre.gem"), "application/octet-stream")
|
79
|
+
|
80
|
+
last_response.status.should == 401
|
81
|
+
|
82
|
+
file = directory.files.get("deadbeef/gems/foobar-0.0.1.pre.gem")
|
83
|
+
file.should be_nil, "gems/foobar-0.0.1.pre.gem should not exist"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "does nothing if no authentication info is provded but is incorrect" do
|
87
|
+
basic_authorize "in", "correct"
|
88
|
+
|
89
|
+
post "/", :file => Rack::Test::UploadedFile.new(fixture("foobar-0.0.1.pre.gem"), "application/octet-stream")
|
90
|
+
|
91
|
+
last_response.status.should == 401
|
92
|
+
|
93
|
+
file = directory.files.get("deadbeef/gems/foobar-0.0.1.pre.gem")
|
94
|
+
file.should be_nil, "gems/foobar-0.0.1.pre.gem should not exist"
|
95
|
+
end
|
96
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gemgate::GemFiles do
|
4
|
+
it "adds a gem using storage" do
|
5
|
+
gem = mock("gem")
|
6
|
+
gem.should_receive(:filename) { "foobar-0.0.1.gem" }
|
7
|
+
gem.should_receive(:data) { "data" }
|
8
|
+
|
9
|
+
storage = mock("storage")
|
10
|
+
storage.should_receive(:create).with("gems/foobar-0.0.1.gem", "data")
|
11
|
+
|
12
|
+
subject.storage = storage
|
13
|
+
|
14
|
+
subject.add(gem)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gemgate::GemWrapper do
|
4
|
+
describe ".from_path" do
|
5
|
+
it "creates a new instance from the provided path" do
|
6
|
+
described_class.from_path(fixture("foobar-0.0.1.gem")).should be_a(described_class)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "sets the spec on the new instance" do
|
10
|
+
gem_wrapper = described_class.from_path(fixture("foobar-0.0.1.gem"))
|
11
|
+
|
12
|
+
gem_wrapper.spec.should == Gem::Package.open(File.open(fixture("foobar-0.0.1.gem"))) {|p| p.metadata }
|
13
|
+
end
|
14
|
+
|
15
|
+
it "sets the path on the new instance" do
|
16
|
+
gem_wrapper = described_class.from_path(fixture("foobar-0.0.1.gem"))
|
17
|
+
|
18
|
+
gem_wrapper.path.should == fixture("foobar-0.0.1.gem")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { described_class.from_path(fixture("foobar-0.0.1.gem")) }
|
23
|
+
|
24
|
+
it "returns its filename" do
|
25
|
+
subject.filename.should == "foobar-0.0.1.gem"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns its spec filename" do
|
29
|
+
subject.spec_filename.should == "foobar-0.0.1.gemspec"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns its prerelease status" do
|
33
|
+
subject.prerelease?.should be_false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns an io with its data" do
|
37
|
+
subject.data.should be_a(IO)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns its name" do
|
41
|
+
subject.name.should == "foobar"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns its version" do
|
45
|
+
subject.version.should == Gem::Version.new("0.0.1")
|
46
|
+
end
|
47
|
+
|
48
|
+
it "returns its platform" do
|
49
|
+
subject.platform.should == "ruby"
|
50
|
+
end
|
51
|
+
end
|
data/spec/index_spec.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gemgate::Index do
|
4
|
+
it "adds a gem to latest specs" do
|
5
|
+
gem = stub("gem")
|
6
|
+
|
7
|
+
specs = stub("specs").as_null_object
|
8
|
+
|
9
|
+
latest_specs = mock("latest specs")
|
10
|
+
latest_specs.should_receive(:add).with(gem)
|
11
|
+
|
12
|
+
prerelease_specs = stub("latest specs").as_null_object
|
13
|
+
|
14
|
+
quick_marshal_specs = stub("quick marshal specs").as_null_object
|
15
|
+
|
16
|
+
subject.specs = specs
|
17
|
+
subject.latest_specs = latest_specs
|
18
|
+
subject.prerelease_specs = prerelease_specs
|
19
|
+
subject.quick_marshal_specs = quick_marshal_specs
|
20
|
+
|
21
|
+
subject.add(gem)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "adds a gem to specs" do
|
25
|
+
gem = stub("gem")
|
26
|
+
|
27
|
+
specs = mock("specs")
|
28
|
+
specs.should_receive(:add).with(gem)
|
29
|
+
|
30
|
+
latest_specs = stub("latest specs").as_null_object
|
31
|
+
|
32
|
+
prerelease_specs = stub("latest specs").as_null_object
|
33
|
+
|
34
|
+
quick_marshal_specs = stub("quick marshal specs").as_null_object
|
35
|
+
|
36
|
+
subject.specs = specs
|
37
|
+
subject.prerelease_specs = prerelease_specs
|
38
|
+
subject.latest_specs = latest_specs
|
39
|
+
subject.quick_marshal_specs = quick_marshal_specs
|
40
|
+
|
41
|
+
subject.add(gem)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "adds a gem to the prerelease specs" do
|
45
|
+
gem = stub("gem")
|
46
|
+
|
47
|
+
specs = stub("specs").as_null_object
|
48
|
+
|
49
|
+
latest_specs = stub("latest specs").as_null_object
|
50
|
+
|
51
|
+
prerelease_specs = mock("prerelease specs")
|
52
|
+
prerelease_specs.should_receive(:add).with(gem)
|
53
|
+
|
54
|
+
quick_marshal_specs = stub("quick marshal specs").as_null_object
|
55
|
+
|
56
|
+
subject.specs = specs
|
57
|
+
subject.latest_specs = latest_specs
|
58
|
+
subject.prerelease_specs = prerelease_specs
|
59
|
+
subject.quick_marshal_specs = quick_marshal_specs
|
60
|
+
|
61
|
+
subject.add(gem)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "adds a gem to the quick marshal specs" do
|
65
|
+
gem = stub("gem")
|
66
|
+
|
67
|
+
specs = stub("specs").as_null_object
|
68
|
+
|
69
|
+
latest_specs = stub("latest specs").as_null_object
|
70
|
+
|
71
|
+
prerelease_specs = stub("latest specs").as_null_object
|
72
|
+
|
73
|
+
quick_marshal_specs = mock("quick marshal specs")
|
74
|
+
quick_marshal_specs.should_receive(:add).with(gem)
|
75
|
+
|
76
|
+
subject.specs = specs
|
77
|
+
subject.latest_specs = latest_specs
|
78
|
+
subject.prerelease_specs = prerelease_specs
|
79
|
+
subject.quick_marshal_specs = quick_marshal_specs
|
80
|
+
|
81
|
+
subject.add(gem)
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gemgate::QuickMarshalSpecs do
|
4
|
+
it "adds a quick marshal spec to storage for a gem" do
|
5
|
+
storage = mock("storage")
|
6
|
+
storage.should_receive(:create).with("quick/Marshal.4.8/foobar-0.0.1.gemspec.rz", Gem.deflate(Marshal.dump("spec")))
|
7
|
+
|
8
|
+
gem = mock("gem")
|
9
|
+
gem.should_receive(:spec_filename) { "foobar-0.0.1.gemspec" }
|
10
|
+
gem.should_receive(:spec) { "spec" }
|
11
|
+
|
12
|
+
subject.storage = storage
|
13
|
+
|
14
|
+
subject.add(gem)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gemgate::Repository do
|
4
|
+
it "uses the gem realizer to realize a gem binary blob" do
|
5
|
+
realizer = mock("realizer")
|
6
|
+
realizer.should_receive(:call).with("hi")
|
7
|
+
|
8
|
+
gem_files = stub("gem_files").as_null_object
|
9
|
+
|
10
|
+
index = stub("index").as_null_object
|
11
|
+
|
12
|
+
subject.realizer = realizer
|
13
|
+
subject.gem_files = gem_files
|
14
|
+
subject.index = index
|
15
|
+
|
16
|
+
subject.add_gem("hi")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "adds the realized gem to the gem files" do
|
20
|
+
gem = stub("gem")
|
21
|
+
|
22
|
+
realizer = stub("realizer", :call => gem)
|
23
|
+
|
24
|
+
gem_files = mock("gem_files")
|
25
|
+
gem_files.should_receive(:add).with(gem)
|
26
|
+
|
27
|
+
index = stub("index").as_null_object
|
28
|
+
|
29
|
+
subject.realizer = realizer
|
30
|
+
subject.gem_files = gem_files
|
31
|
+
subject.index = index
|
32
|
+
|
33
|
+
subject.add_gem("hi")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "adds the realized gem to the index" do
|
37
|
+
gem = stub("gem")
|
38
|
+
|
39
|
+
realizer = stub("realizer", :call => gem)
|
40
|
+
|
41
|
+
gem_files = stub("gem_files").as_null_object
|
42
|
+
|
43
|
+
index = mock("index")
|
44
|
+
index.should_receive(:add).with(gem)
|
45
|
+
|
46
|
+
subject.realizer = realizer
|
47
|
+
subject.gem_files = gem_files
|
48
|
+
subject.index = index
|
49
|
+
|
50
|
+
subject.add_gem("hi")
|
51
|
+
end
|
52
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
ENV["RACK_ENV"] = "test"
|
2
|
+
|
3
|
+
ENV["AWS_ACCESS_KEY_ID"] = "foobar"
|
4
|
+
ENV["AWS_SECRET_ACCESS_KEY"] = "foobar"
|
5
|
+
ENV["GEMGATE_AUTH"] = "foo:bar"
|
6
|
+
ENV["S3_BUCKET"] = "gemgate-test"
|
7
|
+
ENV["S3_KEY_PREFIX"] = "deadbeef"
|
8
|
+
|
9
|
+
require "gemgate"
|
10
|
+
|
11
|
+
require "rack/test"
|
12
|
+
|
13
|
+
RSpec.configure do |c|
|
14
|
+
c.include Rack::Test::Methods
|
15
|
+
|
16
|
+
c.before do
|
17
|
+
Fog.mock!
|
18
|
+
Fog::Mock.reset
|
19
|
+
|
20
|
+
Gemgate::Web.repository = nil
|
21
|
+
|
22
|
+
directory.save
|
23
|
+
|
24
|
+
basic_authorize *ENV["GEMGATE_AUTH"].split(":")
|
25
|
+
end
|
26
|
+
|
27
|
+
def app
|
28
|
+
Gemgate::Web
|
29
|
+
end
|
30
|
+
|
31
|
+
def fixture(name)
|
32
|
+
File.join(File.expand_path("../fixtures", __FILE__), name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def fixture_read(name)
|
36
|
+
File.read(fixture(name), encoding: "binary")
|
37
|
+
end
|
38
|
+
|
39
|
+
def directory
|
40
|
+
@directory ||= fog.directories.new(:key => ENV["S3_BUCKET"])
|
41
|
+
end
|
42
|
+
|
43
|
+
def fog
|
44
|
+
@fog ||= Fog::Storage.new(
|
45
|
+
:provider => "AWS",
|
46
|
+
:aws_access_key_id => ENV["AWS_ACCESS_KEY_ID"],
|
47
|
+
:aws_secret_access_key => ENV["AWS_SECRET_ACCESS_KEY"]
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gemgate::SpecsIndex do
|
4
|
+
subject { described_class.new("foobar_specs.4.8.gz") }
|
5
|
+
|
6
|
+
it "adds a gem using storage when there is no existing file" do
|
7
|
+
gem = mock("gem")
|
8
|
+
gem.should_receive(:name) { "foobar" }
|
9
|
+
gem.should_receive(:version) { Gem::Version.create("0.0.1") }
|
10
|
+
gem.should_receive(:platform) { "ruby" }
|
11
|
+
|
12
|
+
storage = mock("storage")
|
13
|
+
storage.should_receive(:get).with("foobar_specs.4.8.gz") { nil }
|
14
|
+
storage.should_receive(:update).with("foobar_specs.4.8.gz", Gem.gzip(Marshal.dump([["foobar", Gem::Version.create("0.0.1"), "ruby"]])))
|
15
|
+
|
16
|
+
subject.storage = storage
|
17
|
+
|
18
|
+
subject.add(gem)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "adds a gem using storage when there is an existing file" do
|
22
|
+
gem = mock("gem")
|
23
|
+
gem.should_receive(:name) { "foobar" }
|
24
|
+
gem.should_receive(:version) { Gem::Version.create("0.0.1") }
|
25
|
+
gem.should_receive(:platform) { "ruby" }
|
26
|
+
|
27
|
+
storage = mock("storage")
|
28
|
+
storage.should_receive(:get).with("foobar_specs.4.8.gz") { Gem.gzip(Marshal.dump([["something", Gem::Version.create("0.0.2"), "ruby"]])) }
|
29
|
+
storage.should_receive(:update).with("foobar_specs.4.8.gz", Gem.gzip(Marshal.dump([["something", Gem::Version.create("0.0.2"), "ruby"], ["foobar", Gem::Version.create("0.0.1"), "ruby"]])))
|
30
|
+
|
31
|
+
subject.storage = storage
|
32
|
+
|
33
|
+
subject.add(gem)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "adds if all provided conditions are true" do
|
37
|
+
gem = stub("gem", :name => "foobar", :version => Gem::Version.create("0.0.1"), :platform => "ruby")
|
38
|
+
|
39
|
+
storage = mock("storage")
|
40
|
+
storage.should_receive(:get).with("foobar_specs.4.8.gz") { nil }
|
41
|
+
storage.should_receive(:update).with("foobar_specs.4.8.gz", Gem.gzip(Marshal.dump([["foobar", Gem::Version.create("0.0.1"), "ruby"]])))
|
42
|
+
|
43
|
+
subject.storage = storage
|
44
|
+
|
45
|
+
subject.conditions << lambda {|g| g.name == "foobar" } << lambda {|g| g.version == Gem::Version.create("0.0.1") }
|
46
|
+
|
47
|
+
subject.add(gem)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "skips adding if not all provided conditions are true" do
|
51
|
+
gem = stub("gem", :name => "foobar", :version => Gem::Version.create("0.0.1"), :platform => "ruby")
|
52
|
+
|
53
|
+
storage = mock("storage")
|
54
|
+
storage.should_receive(:get).with("foobar_specs.4.8.gz") { Gem.gzip(Marshal.dump([])) }
|
55
|
+
storage.should_not_receive(:update)
|
56
|
+
|
57
|
+
subject.storage = storage
|
58
|
+
|
59
|
+
subject.conditions << lambda {|g| g.name == "foobar" } << lambda {|g| g.version == "0.0.2" }
|
60
|
+
|
61
|
+
subject.add(gem)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "ensures the file exists even when not adding" do
|
65
|
+
gem = stub("gem", :name => "foobar", :version => Gem::Version.create("0.0.1"), :platform => "ruby")
|
66
|
+
|
67
|
+
storage = mock("storage")
|
68
|
+
storage.should_receive(:get).with("foobar_specs.4.8.gz") { nil }
|
69
|
+
storage.should_receive(:update).with("foobar_specs.4.8.gz", Gem.gzip(Marshal.dump([])))
|
70
|
+
|
71
|
+
subject.storage = storage
|
72
|
+
|
73
|
+
subject.conditions << lambda {|g| g.name == "foobar" } << lambda {|g| g.version == Gem::Version.create("0.0.2") }
|
74
|
+
|
75
|
+
subject.add(gem)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "uniqs the added data" do
|
79
|
+
gem = mock("gem")
|
80
|
+
gem.should_receive(:name) { "foobar" }
|
81
|
+
gem.should_receive(:version) { Gem::Version.create("0.0.1") }
|
82
|
+
gem.should_receive(:platform) { "ruby" }
|
83
|
+
|
84
|
+
storage = mock("storage")
|
85
|
+
storage.should_receive(:get).with("foobar_specs.4.8.gz") { Gem.gzip(Marshal.dump([["foobar", Gem::Version.create("0.0.1"), "ruby"]])) }
|
86
|
+
storage.should_receive(:update).with("foobar_specs.4.8.gz", Gem.gzip(Marshal.dump([["foobar", Gem::Version.create("0.0.1"), "ruby"]])))
|
87
|
+
|
88
|
+
subject.storage = storage
|
89
|
+
|
90
|
+
subject.add(gem)
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gemgate::Storage::S3 do
|
4
|
+
subject { described_class.new("AWS_ACCESS_KEY_ID" => "foobar", "AWS_SECRET_ACCESS_KEY" => "foobar", "S3_BUCKET" => "gemgate-test", "S3_KEY_PREFIX" => "ultrasecure") }
|
5
|
+
|
6
|
+
it "creates a public file with the given path and body" do
|
7
|
+
subject.create("foobar", "hello")
|
8
|
+
|
9
|
+
remote_file = remote_directory.files.detect {|f| f.key == "ultrasecure/foobar" }
|
10
|
+
remote_file.should_not be_nil
|
11
|
+
|
12
|
+
remote_file.body.should == "hello"
|
13
|
+
|
14
|
+
remote_file.public_url.should_not be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "updates a public file that already exists" do
|
18
|
+
remote_directory.files.create(:key => "ultrasecure/foobar", :body => "first")
|
19
|
+
|
20
|
+
subject.update("foobar", "second")
|
21
|
+
|
22
|
+
remote_file = remote_directory.files.detect {|f| f.key == "ultrasecure/foobar" }
|
23
|
+
remote_file.body.should == "second"
|
24
|
+
|
25
|
+
remote_file.public_url.should_not be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "creates a public file that doesn't exist when updating" do
|
29
|
+
subject.update("foobar", "created")
|
30
|
+
|
31
|
+
remote_file = remote_directory.files.detect {|f| f.key == "ultrasecure/foobar" }
|
32
|
+
remote_file.body.should == "created"
|
33
|
+
|
34
|
+
remote_file.public_url.should_not be_nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it "gets an existing file's data" do
|
38
|
+
remote_directory.files.create(:key => "ultrasecure/foobar", :body => "hello")
|
39
|
+
|
40
|
+
subject.get("foobar").should == "hello"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns nil when getting a file that doesn't exist" do
|
44
|
+
subject.get("foobar").should be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it "errors if the bucket doesn't exist" do
|
48
|
+
remote_directory.destroy
|
49
|
+
|
50
|
+
expect { subject.get("foobar") }.to raise_error(described_class::Error, "Bucket `gemgate-test` doesn't exist")
|
51
|
+
end
|
52
|
+
|
53
|
+
def remote_directory
|
54
|
+
fog.directories.get("gemgate-test")
|
55
|
+
end
|
56
|
+
end
|
data/spec/web_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gemgate::Web do
|
4
|
+
describe "POST /" do
|
5
|
+
it "adds the gem to the repository" do
|
6
|
+
uploaded_file = Rack::Test::UploadedFile.new(fixture("foobar-0.0.1.gem"), "application/octet-stream")
|
7
|
+
|
8
|
+
repository = mock("repository")
|
9
|
+
repository.should_receive(:add_gem).with(kind_of(String))
|
10
|
+
|
11
|
+
described_class.repository = repository
|
12
|
+
|
13
|
+
post "/", :file => uploaded_file
|
14
|
+
end
|
15
|
+
|
16
|
+
it "does not add the gem to the repository if authentication fails" do
|
17
|
+
uploaded_file = Rack::Test::UploadedFile.new(fixture("foobar-0.0.1.gem"), "application/octet-stream")
|
18
|
+
|
19
|
+
repository = mock("repository")
|
20
|
+
repository.should_not_receive(:add_gem)
|
21
|
+
|
22
|
+
described_class.repository = repository
|
23
|
+
|
24
|
+
basic_authorize "in", "valid"
|
25
|
+
|
26
|
+
post "/", :file => uploaded_file
|
27
|
+
end
|
28
|
+
|
29
|
+
it "does not add the gem to the repository if authentication info is not provided" do
|
30
|
+
uploaded_file = Rack::Test::UploadedFile.new(fixture("foobar-0.0.1.gem"), "application/octet-stream")
|
31
|
+
|
32
|
+
repository = mock("repository")
|
33
|
+
repository.should_not_receive(:add_gem)
|
34
|
+
|
35
|
+
described_class.repository = repository
|
36
|
+
|
37
|
+
header "Authorization", nil
|
38
|
+
|
39
|
+
post "/", :file => uploaded_file
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gemgate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dan Peterson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sinatra
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.3.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.3.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: fog
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.5.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.5.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rack-test
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Host a private gem repository at S3
|
95
|
+
email:
|
96
|
+
- dpiddy@gmail.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .env.sample
|
102
|
+
- .gitignore
|
103
|
+
- Gemfile
|
104
|
+
- Procfile
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- config.ru
|
108
|
+
- gemgate.gemspec
|
109
|
+
- lib/gemgate.rb
|
110
|
+
- lib/gemgate/gem_files.rb
|
111
|
+
- lib/gemgate/gem_wrapper.rb
|
112
|
+
- lib/gemgate/index.rb
|
113
|
+
- lib/gemgate/quick_marshal_specs.rb
|
114
|
+
- lib/gemgate/repository.rb
|
115
|
+
- lib/gemgate/specs_index.rb
|
116
|
+
- lib/gemgate/storage/s3.rb
|
117
|
+
- lib/gemgate/version.rb
|
118
|
+
- lib/gemgate/web.rb
|
119
|
+
- spec/acceptance/push_spec.rb
|
120
|
+
- spec/fixtures/foobar-0.0.1.gem
|
121
|
+
- spec/fixtures/foobar-0.0.1.pre.gem
|
122
|
+
- spec/gem_files_spec.rb
|
123
|
+
- spec/gem_wrapper_spec.rb
|
124
|
+
- spec/index_spec.rb
|
125
|
+
- spec/quick_marshal_specs_spec.rb
|
126
|
+
- spec/repository_spec.rb
|
127
|
+
- spec/spec_helper.rb
|
128
|
+
- spec/specs_index_spec.rb
|
129
|
+
- spec/storage/s3_spec.rb
|
130
|
+
- spec/web_spec.rb
|
131
|
+
homepage: https://github.com/dpiddy/gemgate
|
132
|
+
licenses: []
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ! '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 1.8.23
|
152
|
+
signing_key:
|
153
|
+
specification_version: 3
|
154
|
+
summary: Host a private gem repository at S3
|
155
|
+
test_files:
|
156
|
+
- spec/acceptance/push_spec.rb
|
157
|
+
- spec/fixtures/foobar-0.0.1.gem
|
158
|
+
- spec/fixtures/foobar-0.0.1.pre.gem
|
159
|
+
- spec/gem_files_spec.rb
|
160
|
+
- spec/gem_wrapper_spec.rb
|
161
|
+
- spec/index_spec.rb
|
162
|
+
- spec/quick_marshal_specs_spec.rb
|
163
|
+
- spec/repository_spec.rb
|
164
|
+
- spec/spec_helper.rb
|
165
|
+
- spec/specs_index_spec.rb
|
166
|
+
- spec/storage/s3_spec.rb
|
167
|
+
- spec/web_spec.rb
|