gemstash 1.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +13 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +139 -0
  6. data/Rakefile +35 -0
  7. data/bin/console +14 -0
  8. data/bin/gemstash +3 -0
  9. data/bin/setup +5 -0
  10. data/docs/config.md +136 -0
  11. data/docs/debug.md +24 -0
  12. data/docs/deploy.md +30 -0
  13. data/docs/mirror.md +30 -0
  14. data/docs/multiple_sources.md +68 -0
  15. data/docs/private_gems.md +140 -0
  16. data/docs/reference.md +308 -0
  17. data/exe/gemstash +3 -0
  18. data/gemstash.gemspec +47 -0
  19. data/gemstash.png +0 -0
  20. data/lib/gemstash.rb +26 -0
  21. data/lib/gemstash/authorization.rb +87 -0
  22. data/lib/gemstash/cache.rb +79 -0
  23. data/lib/gemstash/cli.rb +71 -0
  24. data/lib/gemstash/cli/authorize.rb +69 -0
  25. data/lib/gemstash/cli/base.rb +46 -0
  26. data/lib/gemstash/cli/setup.rb +173 -0
  27. data/lib/gemstash/cli/start.rb +52 -0
  28. data/lib/gemstash/cli/status.rb +21 -0
  29. data/lib/gemstash/cli/stop.rb +21 -0
  30. data/lib/gemstash/config.ru +13 -0
  31. data/lib/gemstash/configuration.rb +41 -0
  32. data/lib/gemstash/db.rb +15 -0
  33. data/lib/gemstash/db/authorization.rb +20 -0
  34. data/lib/gemstash/db/dependency.rb +50 -0
  35. data/lib/gemstash/db/rubygem.rb +14 -0
  36. data/lib/gemstash/db/version.rb +51 -0
  37. data/lib/gemstash/dependencies.rb +93 -0
  38. data/lib/gemstash/env.rb +150 -0
  39. data/lib/gemstash/gem_fetcher.rb +50 -0
  40. data/lib/gemstash/gem_pusher.rb +125 -0
  41. data/lib/gemstash/gem_source.rb +37 -0
  42. data/lib/gemstash/gem_source/dependency_caching.rb +40 -0
  43. data/lib/gemstash/gem_source/private_source.rb +139 -0
  44. data/lib/gemstash/gem_source/rack_middleware.rb +22 -0
  45. data/lib/gemstash/gem_source/upstream_source.rb +183 -0
  46. data/lib/gemstash/gem_unyanker.rb +61 -0
  47. data/lib/gemstash/gem_yanker.rb +61 -0
  48. data/lib/gemstash/http_client.rb +77 -0
  49. data/lib/gemstash/logging.rb +93 -0
  50. data/lib/gemstash/migrations/01_gem_dependencies.rb +41 -0
  51. data/lib/gemstash/migrations/02_authorizations.rb +12 -0
  52. data/lib/gemstash/puma.rb +6 -0
  53. data/lib/gemstash/rack_env_rewriter.rb +66 -0
  54. data/lib/gemstash/specs_builder.rb +93 -0
  55. data/lib/gemstash/storage.rb +207 -0
  56. data/lib/gemstash/upstream.rb +65 -0
  57. data/lib/gemstash/version.rb +4 -0
  58. data/lib/gemstash/web.rb +97 -0
  59. metadata +306 -0
@@ -0,0 +1,207 @@
1
+ require "gemstash"
2
+ require "digest"
3
+ require "pathname"
4
+ require "fileutils"
5
+ require "yaml"
6
+
7
+ module Gemstash
8
+ #:nodoc:
9
+ class Storage
10
+ extend Gemstash::Env::Helper
11
+ VERSION = 1
12
+
13
+ # If the storage engine detects something that was stored with a newer
14
+ # version of the storage engine, this error will be thrown.
15
+ class VersionTooNew < StandardError
16
+ end
17
+
18
+ def initialize(folder, root: true)
19
+ check_engine if root
20
+ @folder = folder
21
+ FileUtils.mkpath(@folder) unless Dir.exist?(@folder)
22
+ end
23
+
24
+ def resource(id)
25
+ Resource.new(@folder, id)
26
+ end
27
+
28
+ def for(child)
29
+ Storage.new(File.join(@folder, child), root: false)
30
+ end
31
+
32
+ def self.for(name)
33
+ new(gemstash_env.base_file(name))
34
+ end
35
+
36
+ def self.metadata
37
+ file = gemstash_env.base_file("metadata.yml")
38
+
39
+ unless File.exist?(file)
40
+ File.write(file, { storage_version: Gemstash::Storage::VERSION,
41
+ gemstash_version: Gemstash::VERSION }.to_yaml)
42
+ end
43
+
44
+ YAML.load_file(file)
45
+ end
46
+
47
+ private
48
+
49
+ def check_engine
50
+ version = Gemstash::Storage.metadata[:storage_version]
51
+ return if version <= Gemstash::Storage::VERSION
52
+ raise Gemstash::Storage::VersionTooNew, "Storage engine is out of date: #{version}"
53
+ end
54
+
55
+ def path_valid?(path)
56
+ return false if path.nil?
57
+ return false unless File.writable?(path)
58
+ true
59
+ end
60
+ end
61
+
62
+ #:nodoc:
63
+ class Resource
64
+ include Gemstash::Logging
65
+ attr_reader :name, :folder
66
+ def initialize(folder, name)
67
+ @base_path = folder
68
+ @name = name
69
+ # Avoid odd characters in paths, in case of issues with the file system
70
+ safe_name = sanitize(@name)
71
+ # Use a trie structure to avoid file system limits causing too many files in 1 folder
72
+ # Downcase to avoid issues with case insensitive file systems
73
+ trie_parents = safe_name[0...3].downcase.split("")
74
+ # The digest is included in case the name differs only by case
75
+ # Some file systems are case insensitive, so such collisions will be a problem
76
+ digest = Digest::MD5.hexdigest(@name)
77
+ child_folder = "#{safe_name}-#{digest}"
78
+ @folder = File.join(@base_path, *trie_parents, child_folder)
79
+ end
80
+
81
+ def exist?(key = nil)
82
+ if key
83
+ File.exist?(properties_filename) && File.exist?(content_filename(key))
84
+ else
85
+ File.exist?(properties_filename) && content?
86
+ end
87
+ end
88
+
89
+ def save(content, properties = nil)
90
+ content.each do |key, value|
91
+ save_content(key, value)
92
+ end
93
+
94
+ update_properties(properties)
95
+ self
96
+ end
97
+
98
+ def content(key)
99
+ @content[key]
100
+ end
101
+
102
+ def properties
103
+ @properties || {}
104
+ end
105
+
106
+ def update_properties(props)
107
+ load_properties
108
+ save_properties(properties.merge(props || {}))
109
+ self
110
+ end
111
+
112
+ def load(key)
113
+ raise "Resource #{@name} has no content to load" unless exist?(key)
114
+ load_properties
115
+ @content ||= {}
116
+ @content[key] = read_file(content_filename(key))
117
+ self
118
+ end
119
+
120
+ def delete(key)
121
+ return self unless exist?(key)
122
+
123
+ begin
124
+ File.delete(content_filename(key))
125
+ rescue => e
126
+ log_error "Failed to delete stored content at #{content_filename(key)}", e, level: :warn
127
+ end
128
+
129
+ begin
130
+ File.delete(properties_filename) unless content?
131
+ rescue => e
132
+ log_error "Failed to delete stored properties at #{properties_filename}", e, level: :warn
133
+ end
134
+
135
+ return self
136
+ ensure
137
+ reset
138
+ end
139
+
140
+ private
141
+
142
+ def load_properties
143
+ return unless File.exist?(properties_filename)
144
+ @properties = YAML.load_file(properties_filename)
145
+ check_version
146
+ end
147
+
148
+ def check_version
149
+ version = @properties[:gemstash_storage_version]
150
+ return if version <= Gemstash::Storage::VERSION
151
+ reset
152
+ raise Gemstash::Storage::VersionTooNew, "Resource was stored with a newer storage: #{version}"
153
+ end
154
+
155
+ def reset
156
+ @content = nil
157
+ @properties = nil
158
+ end
159
+
160
+ def content?
161
+ return false unless Dir.exist?(@folder)
162
+ entries = Dir.entries(@folder).reject {|file| file =~ /\A\.\.?\z/ }
163
+ !entries.empty? && entries != %w(properties.yaml)
164
+ end
165
+
166
+ def sanitize(name)
167
+ name.gsub(/[^a-zA-Z0-9_]/, "_")
168
+ end
169
+
170
+ def save_content(key, content)
171
+ store(content_filename(key), content)
172
+ @content ||= {}
173
+ @content[key] = content
174
+ end
175
+
176
+ def save_properties(props)
177
+ props ||= {}
178
+ props = { gemstash_storage_version: Gemstash::Storage::VERSION }.merge(props)
179
+ store(properties_filename, props.to_yaml)
180
+ @properties = props
181
+ end
182
+
183
+ def store(filename, content)
184
+ FileUtils.mkpath(@folder) unless Dir.exist?(@folder)
185
+ save_file(filename) { content }
186
+ end
187
+
188
+ def save_file(filename)
189
+ content = yield
190
+ File.open(filename, "wb") {|f| f.write(content) }
191
+ end
192
+
193
+ def read_file(filename)
194
+ File.open(filename, "rb", &:read)
195
+ end
196
+
197
+ def content_filename(key)
198
+ name = sanitize(key.to_s)
199
+ raise "Invalid content key #{key.inspect}" if name.empty?
200
+ File.join(@folder, name)
201
+ end
202
+
203
+ def properties_filename
204
+ File.join(@folder, "properties.yaml")
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,65 @@
1
+ require "digest"
2
+
3
+ module Gemstash
4
+ #:nodoc:
5
+ class Upstream
6
+ extend Forwardable
7
+
8
+ attr_reader :user_agent
9
+
10
+ def_delegators :@uri, :scheme, :host, :user, :password, :to_s
11
+
12
+ def initialize(upstream, user_agent: nil)
13
+ @uri = URI(URI.decode(upstream.to_s))
14
+ @user_agent = user_agent
15
+ raise "URL '#{@uri}' is not valid!" unless @uri.to_s =~ URI.regexp
16
+ end
17
+
18
+ def url(path = nil, params = nil)
19
+ base = to_s
20
+
21
+ unless path.to_s.empty?
22
+ base = "#{base}/" unless base.end_with?("/")
23
+ path = path[1..-1] if path.to_s.start_with?("/")
24
+ end
25
+
26
+ params = "?#{params}" if !params.nil? && !params.empty?
27
+ "#{base}#{path}#{params}"
28
+ end
29
+
30
+ def auth?
31
+ !user.to_s.empty? && !password.to_s.empty?
32
+ end
33
+
34
+ # Utilized as the parent directory for cached gems
35
+ def host_id
36
+ @host_id ||= "#{host}_#{hash}"
37
+ end
38
+
39
+ private
40
+
41
+ def hash
42
+ Digest::MD5.hexdigest(to_s)
43
+ end
44
+
45
+ #:nodoc:
46
+ class GemName
47
+ def initialize(upstream, gem_name)
48
+ @upstream = upstream
49
+ @id = gem_name
50
+ end
51
+
52
+ def to_s
53
+ name
54
+ end
55
+
56
+ def id
57
+ @id
58
+ end
59
+
60
+ def name
61
+ @name ||= @id.gsub(/\.gem(spec\.rz)?$/i, "")
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,4 @@
1
+ #:nodoc:
2
+ module Gemstash
3
+ VERSION = "1.0.0.pre.1"
4
+ end
@@ -0,0 +1,97 @@
1
+ require "sinatra/base"
2
+ require "json"
3
+ require "gemstash"
4
+
5
+ module Gemstash
6
+ #:nodoc:
7
+ class Web < Sinatra::Base
8
+ def initialize(gemstash_env: nil, http_client_builder: nil)
9
+ @gemstash_env = gemstash_env || Gemstash::Env.new
10
+ @http_client_builder = http_client_builder || Gemstash::HTTPClient
11
+ Gemstash::Env.current = @gemstash_env
12
+ super()
13
+ end
14
+
15
+ before do
16
+ Gemstash::Env.current = @gemstash_env
17
+ @gem_source = env["gemstash.gem_source"].new(self)
18
+ end
19
+
20
+ def http_client_for(server_url)
21
+ @http_client_builder.for(server_url)
22
+ end
23
+
24
+ not_found do
25
+ status 404
26
+ body JSON.dump("error" => "Not found", "code" => 404)
27
+ end
28
+
29
+ get "/" do
30
+ @gem_source.serve_root
31
+ end
32
+
33
+ get "/api/v1/dependencies" do
34
+ @gem_source.serve_dependencies
35
+ end
36
+
37
+ get "/api/v1/dependencies.json" do
38
+ @gem_source.serve_dependencies_json
39
+ end
40
+
41
+ post "/api/v1/gems" do
42
+ @gem_source.serve_add_gem
43
+ end
44
+
45
+ delete "/api/v1/gems/yank" do
46
+ @gem_source.serve_yank
47
+ end
48
+
49
+ put "/api/v1/gems/unyank" do
50
+ @gem_source.serve_unyank
51
+ end
52
+
53
+ post "/api/v1/add_spec.json" do
54
+ @gem_source.serve_add_spec_json
55
+ end
56
+
57
+ post "/api/v1/remove_spec.json" do
58
+ @gem_source.serve_remove_spec_json
59
+ end
60
+
61
+ get "/names" do
62
+ @gem_source.serve_names
63
+ end
64
+
65
+ get "/versions" do
66
+ @gem_source.serve_versions
67
+ end
68
+
69
+ get "/info/:name" do
70
+ @gem_source.serve_info(params[:name])
71
+ end
72
+
73
+ get "/quick/Marshal.4.8/:id" do
74
+ @gem_source.serve_marshal(params[:id])
75
+ end
76
+
77
+ get "/fetch/actual/gem/:id" do
78
+ @gem_source.serve_actual_gem(params[:id])
79
+ end
80
+
81
+ get "/gems/:id" do
82
+ @gem_source.serve_gem(params[:id])
83
+ end
84
+
85
+ get "/latest_specs.4.8.gz" do
86
+ @gem_source.serve_latest_specs
87
+ end
88
+
89
+ get "/specs.4.8.gz" do
90
+ @gem_source.serve_specs
91
+ end
92
+
93
+ get "/prerelease_specs.4.8.gz" do
94
+ @gem_source.serve_prerelease_specs
95
+ end
96
+ end
97
+ end
metadata ADDED
@@ -0,0 +1,306 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gemstash
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre.1
5
+ platform: ruby
6
+ authors:
7
+ - Andre Arko
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-11-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dalli
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: lru_redux
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: puma
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sequel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.26'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.26'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sinatra
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: thor
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.19'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.19'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faraday
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.9'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: faraday_middleware
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.10'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.10'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sqlite3
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.3'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.3'
139
+ - !ruby/object:Gem::Dependency
140
+ name: bundler
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.10'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.10'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rack-test
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.6'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.6'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rake
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '10.0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '10.0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rspec
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '3.3'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '3.3'
195
+ - !ruby/object:Gem::Dependency
196
+ name: rubocop
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '0.34'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '0.34'
209
+ description: Gemstash acts as a local RubyGems server, caching copies of gems from
210
+ RubyGems.org automatically, and eventually letting you push your own private gems
211
+ as well.
212
+ email:
213
+ - andre@arko.net
214
+ executables:
215
+ - gemstash
216
+ extensions: []
217
+ extra_rdoc_files: []
218
+ files:
219
+ - ".gitignore"
220
+ - ".rspec"
221
+ - ".rubocop-bundler.yml"
222
+ - ".rubocop-relax.yml"
223
+ - ".rubocop.yml"
224
+ - ".travis.yml"
225
+ - CODE_OF_CONDUCT.md
226
+ - Gemfile
227
+ - LICENSE.txt
228
+ - README.md
229
+ - Rakefile
230
+ - bin/console
231
+ - bin/gemstash
232
+ - bin/setup
233
+ - docs/config.md
234
+ - docs/debug.md
235
+ - docs/deploy.md
236
+ - docs/mirror.md
237
+ - docs/multiple_sources.md
238
+ - docs/private_gems.md
239
+ - docs/reference.md
240
+ - exe/gemstash
241
+ - gemstash.gemspec
242
+ - gemstash.png
243
+ - lib/gemstash.rb
244
+ - lib/gemstash/authorization.rb
245
+ - lib/gemstash/cache.rb
246
+ - lib/gemstash/cli.rb
247
+ - lib/gemstash/cli/authorize.rb
248
+ - lib/gemstash/cli/base.rb
249
+ - lib/gemstash/cli/setup.rb
250
+ - lib/gemstash/cli/start.rb
251
+ - lib/gemstash/cli/status.rb
252
+ - lib/gemstash/cli/stop.rb
253
+ - lib/gemstash/config.ru
254
+ - lib/gemstash/configuration.rb
255
+ - lib/gemstash/db.rb
256
+ - lib/gemstash/db/authorization.rb
257
+ - lib/gemstash/db/dependency.rb
258
+ - lib/gemstash/db/rubygem.rb
259
+ - lib/gemstash/db/version.rb
260
+ - lib/gemstash/dependencies.rb
261
+ - lib/gemstash/env.rb
262
+ - lib/gemstash/gem_fetcher.rb
263
+ - lib/gemstash/gem_pusher.rb
264
+ - lib/gemstash/gem_source.rb
265
+ - lib/gemstash/gem_source/dependency_caching.rb
266
+ - lib/gemstash/gem_source/private_source.rb
267
+ - lib/gemstash/gem_source/rack_middleware.rb
268
+ - lib/gemstash/gem_source/upstream_source.rb
269
+ - lib/gemstash/gem_unyanker.rb
270
+ - lib/gemstash/gem_yanker.rb
271
+ - lib/gemstash/http_client.rb
272
+ - lib/gemstash/logging.rb
273
+ - lib/gemstash/migrations/01_gem_dependencies.rb
274
+ - lib/gemstash/migrations/02_authorizations.rb
275
+ - lib/gemstash/puma.rb
276
+ - lib/gemstash/rack_env_rewriter.rb
277
+ - lib/gemstash/specs_builder.rb
278
+ - lib/gemstash/storage.rb
279
+ - lib/gemstash/upstream.rb
280
+ - lib/gemstash/version.rb
281
+ - lib/gemstash/web.rb
282
+ homepage: https://github.com/bundler/gemstash
283
+ licenses:
284
+ - MIT
285
+ metadata: {}
286
+ post_install_message:
287
+ rdoc_options: []
288
+ require_paths:
289
+ - lib
290
+ required_ruby_version: !ruby/object:Gem::Requirement
291
+ requirements:
292
+ - - ">="
293
+ - !ruby/object:Gem::Version
294
+ version: '0'
295
+ required_rubygems_version: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - ">"
298
+ - !ruby/object:Gem::Version
299
+ version: 1.3.1
300
+ requirements: []
301
+ rubyforge_project:
302
+ rubygems_version: 2.4.8
303
+ signing_key:
304
+ specification_version: 4
305
+ summary: A place to stash gems you'll need
306
+ test_files: []