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.
@@ -0,0 +1,5 @@
1
+ AWS_ACCESS_KEY_ID=changeme
2
+ AWS_SECRET_ACCESS_KEY=changeme
3
+ GEMGATE_AUTH=foo:bar
4
+ S3_BUCKET=gemgate-development
5
+ S3_KEY_PREFIX=deadbeef
@@ -0,0 +1,2 @@
1
+ .env
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1 @@
1
+ web: bundle exec rackup -I lib -p $PORT
@@ -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.
@@ -0,0 +1,8 @@
1
+ begin
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+ rescue LoadError
8
+ end
@@ -0,0 +1,3 @@
1
+ require "gemgate"
2
+
3
+ run Gemgate::Web
@@ -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
@@ -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,9 @@
1
+ module Gemgate
2
+ class GemFiles
3
+ attr_accessor :storage
4
+
5
+ def add(gem)
6
+ storage.create("gems/#{gem.filename}", gem.data)
7
+ end
8
+ end
9
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Gemgate
2
+ VERSION = "0.0.1"
3
+ end
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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