gemstash 1.0.0.pre.1-java → 2.6.0-java

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.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +303 -0
  3. data/exe/gemstash +3 -0
  4. data/lib/gemstash/api_key_authorization.rb +32 -0
  5. data/lib/gemstash/authorization.rb +15 -8
  6. data/lib/gemstash/cache.rb +42 -2
  7. data/lib/gemstash/cli/authorize.rb +52 -9
  8. data/lib/gemstash/cli/base.rb +14 -6
  9. data/lib/gemstash/cli/setup.rb +67 -39
  10. data/lib/gemstash/cli/start.rb +6 -2
  11. data/lib/gemstash/cli/status.rb +3 -1
  12. data/lib/gemstash/cli/stop.rb +4 -1
  13. data/lib/gemstash/cli.rb +59 -1
  14. data/lib/gemstash/config.ru +4 -3
  15. data/lib/gemstash/configuration.rb +61 -8
  16. data/lib/gemstash/db/authorization.rb +5 -3
  17. data/lib/gemstash/db/cached_rubygem.rb +20 -0
  18. data/lib/gemstash/db/dependency.rb +2 -0
  19. data/lib/gemstash/db/rubygem.rb +3 -0
  20. data/lib/gemstash/db/upstream.rb +15 -0
  21. data/lib/gemstash/db/version.rb +25 -2
  22. data/lib/gemstash/db.rb +5 -0
  23. data/lib/gemstash/dependencies.rb +6 -2
  24. data/lib/gemstash/env.rb +44 -13
  25. data/lib/gemstash/gem_fetcher.rb +5 -3
  26. data/lib/gemstash/gem_pusher.rb +25 -18
  27. data/lib/gemstash/gem_source/dependency_caching.rb +4 -4
  28. data/lib/gemstash/gem_source/private_source.rb +34 -50
  29. data/lib/gemstash/gem_source/rack_middleware.rb +3 -0
  30. data/lib/gemstash/gem_source/upstream_source.rb +71 -27
  31. data/lib/gemstash/gem_source.rb +4 -2
  32. data/lib/gemstash/gem_yanker.rb +14 -4
  33. data/lib/gemstash/health.rb +55 -0
  34. data/lib/gemstash/http_client.rb +15 -5
  35. data/lib/gemstash/logging.rb +19 -7
  36. data/lib/gemstash/man/gemstash-authorize.1 +54 -0
  37. data/lib/gemstash/man/gemstash-authorize.1.txt +52 -0
  38. data/lib/gemstash/man/gemstash-configuration.5 +186 -0
  39. data/lib/gemstash/man/gemstash-configuration.5.txt +208 -0
  40. data/lib/gemstash/man/gemstash-customize.7 +273 -0
  41. data/lib/gemstash/man/gemstash-customize.7.txt +184 -0
  42. data/lib/gemstash/man/gemstash-debugging.7 +30 -0
  43. data/lib/gemstash/man/gemstash-debugging.7.txt +27 -0
  44. data/lib/gemstash/man/gemstash-deploy.7 +63 -0
  45. data/lib/gemstash/man/gemstash-deploy.7.txt +57 -0
  46. data/lib/gemstash/man/gemstash-mirror.7 +34 -0
  47. data/lib/gemstash/man/gemstash-mirror.7.txt +31 -0
  48. data/lib/gemstash/man/gemstash-multiple-sources.7 +131 -0
  49. data/lib/gemstash/man/gemstash-multiple-sources.7.txt +116 -0
  50. data/lib/gemstash/man/gemstash-private-gems.7 +191 -0
  51. data/lib/gemstash/man/gemstash-private-gems.7.txt +154 -0
  52. data/lib/gemstash/man/gemstash-readme.7 +199 -0
  53. data/lib/gemstash/man/gemstash-readme.7.txt +177 -0
  54. data/lib/gemstash/man/gemstash-setup.1 +38 -0
  55. data/lib/gemstash/man/gemstash-setup.1.txt +38 -0
  56. data/lib/gemstash/man/gemstash-start.1 +23 -0
  57. data/lib/gemstash/man/gemstash-start.1.txt +24 -0
  58. data/lib/gemstash/man/gemstash-status.1 +17 -0
  59. data/lib/gemstash/man/gemstash-status.1.txt +20 -0
  60. data/lib/gemstash/man/gemstash-stop.1 +17 -0
  61. data/lib/gemstash/man/gemstash-stop.1.txt +20 -0
  62. data/lib/gemstash/man/gemstash-version.1 +17 -0
  63. data/lib/gemstash/man/gemstash-version.1.txt +19 -0
  64. data/lib/gemstash/migrations/01_gem_dependencies.rb +11 -9
  65. data/lib/gemstash/migrations/02_authorizations.rb +4 -2
  66. data/lib/gemstash/migrations/03_cached_gems.rb +26 -0
  67. data/lib/gemstash/migrations/04_health_tests.rb +10 -0
  68. data/lib/gemstash/migrations/05_authorization_names.rb +10 -0
  69. data/lib/gemstash/puma.rb +5 -3
  70. data/lib/gemstash/rack_env_rewriter.rb +11 -2
  71. data/lib/gemstash/specs_builder.rb +25 -15
  72. data/lib/gemstash/storage.rb +175 -32
  73. data/lib/gemstash/upstream.rb +43 -8
  74. data/lib/gemstash/version.rb +4 -2
  75. data/lib/gemstash/web.rb +13 -8
  76. data/lib/gemstash.rb +6 -2
  77. metadata +135 -110
  78. data/.gitignore +0 -10
  79. data/.rspec +0 -2
  80. data/.rubocop-bundler.yml +0 -92
  81. data/.rubocop-relax.yml +0 -11
  82. data/.rubocop.yml +0 -8
  83. data/.travis.yml +0 -20
  84. data/Gemfile +0 -4
  85. data/README.md +0 -139
  86. data/Rakefile +0 -35
  87. data/bin/console +0 -14
  88. data/bin/gemstash +0 -3
  89. data/bin/setup +0 -5
  90. data/docs/config.md +0 -136
  91. data/docs/debug.md +0 -24
  92. data/docs/deploy.md +0 -30
  93. data/docs/mirror.md +0 -30
  94. data/docs/multiple_sources.md +0 -68
  95. data/docs/private_gems.md +0 -140
  96. data/docs/reference.md +0 -308
  97. data/gemstash.gemspec +0 -47
  98. data/gemstash.png +0 -0
  99. data/lib/gemstash/gem_unyanker.rb +0 -61
data/lib/gemstash/env.rb CHANGED
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "gemstash"
4
+ require "active_support/core_ext/file/atomic"
2
5
  require "dalli"
3
6
  require "fileutils"
4
7
  require "sequel"
5
8
  require "uri"
9
+ require "pathname"
6
10
 
7
11
  module Gemstash
8
12
  # Storage for application-wide variables and configuration.
@@ -14,7 +18,7 @@ module Gemstash
14
18
 
15
19
  # Little module to provide easy access to the current Gemstash::Env.
16
20
  module Helper
17
- private
21
+ private # rubocop:disable Layout/AccessModifierIndentation
18
22
 
19
23
  def gemstash_env
20
24
  Gemstash::Env.current
@@ -30,6 +34,7 @@ module Gemstash
30
34
 
31
35
  def call(env)
32
36
  env["gemstash.env"] = @gemstash_env
37
+ Gemstash::Env.current = @gemstash_env
33
38
  @app.call(env)
34
39
  end
35
40
  end
@@ -46,6 +51,7 @@ module Gemstash
46
51
 
47
52
  def self.current
48
53
  raise EnvNotSetError unless Thread.current[:gemstash_env]
54
+
49
55
  Thread.current[:gemstash_env]
50
56
  end
51
57
 
@@ -54,11 +60,9 @@ module Gemstash
54
60
  end
55
61
 
56
62
  def self.daemonized?
57
- if @daemonized.nil?
58
- raise "Daemonized hasn't been set yet!"
59
- else
60
- @daemonized
61
- end
63
+ raise "Daemonized hasn't been set yet!" if @daemonized.nil?
64
+
65
+ @daemonized
62
66
  end
63
67
 
64
68
  def self.daemonized=(value)
@@ -98,8 +102,33 @@ module Gemstash
98
102
  File.join(base_path, path)
99
103
  end
100
104
 
105
+ def log_file
106
+ if config[:log_file] == :stdout
107
+ $stdout
108
+ else
109
+ base_file(config[:log_file] || "server.log")
110
+ end
111
+ end
112
+
113
+ def pidfile
114
+ if config[:pidfile]
115
+ pathname = Pathname.new(config[:pidfile])
116
+ if pathname.relative?
117
+ base_file(pathname.to_s)
118
+ else
119
+ pathname.to_s
120
+ end
121
+ else
122
+ base_file("puma.pid")
123
+ end
124
+ end
125
+
126
+ def atomic_write(file, &block)
127
+ File.atomic_write(file, File.dirname(file), &block)
128
+ end
129
+
101
130
  def rackup
102
- File.expand_path("../config.ru", __FILE__)
131
+ File.expand_path("config.ru", __dir__)
103
132
  end
104
133
 
105
134
  def db
@@ -108,13 +137,13 @@ module Gemstash
108
137
  when "sqlite3"
109
138
  db_path = base_file("gemstash.db")
110
139
 
111
- if RUBY_PLATFORM == "java"
112
- db = Sequel.connect("jdbc:sqlite:#{db_path}", max_connections: 1)
140
+ db = if RUBY_PLATFORM == "java"
141
+ Sequel.connect("jdbc:sqlite:#{db_path}", config.database_connection_config)
113
142
  else
114
- db = Sequel.connect("sqlite://#{URI.escape(db_path)}", max_connections: 1)
143
+ Sequel.connect("sqlite://#{CGI.escape(db_path)}", config.database_connection_config)
115
144
  end
116
- when "postgres"
117
- db = Sequel.connect(config[:db_url])
145
+ when "postgres", "mysql", "mysql2"
146
+ db = Sequel.connect(config[:db_url], config.database_connection_config)
118
147
  else
119
148
  raise "Unsupported DB adapter: '#{config[:db_adapter]}'"
120
149
  end
@@ -126,7 +155,7 @@ module Gemstash
126
155
 
127
156
  def self.migrate(db)
128
157
  Sequel.extension :migration
129
- migrations_dir = File.expand_path("../migrations", __FILE__)
158
+ migrations_dir = File.expand_path("migrations", __dir__)
130
159
  Sequel::Migrator.run(db, migrations_dir, :use_transactions => true)
131
160
  end
132
161
 
@@ -141,6 +170,8 @@ module Gemstash
141
170
  Gemstash::LruReduxClient.new
142
171
  when "memcached"
143
172
  Dalli::Client.new(config[:memcached_servers])
173
+ when "redis"
174
+ Gemstash::RedisClient.new(config[:redis_servers])
144
175
  else
145
176
  raise "Invalid cache client: '#{config[:cache_type]}'"
146
177
  end
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "gemstash"
2
4
  require "set"
3
5
 
4
6
  module Gemstash
5
- #:nodoc:
7
+ # :nodoc:
6
8
  class GemFetcher
7
9
  def initialize(http_client)
8
10
  @http_client = http_client
9
- @valid_headers = Set.new(["etag", "content-type", "content-length", "last-modified"])
11
+ @valid_headers = Set.new(%w[etag content-type content-length last-modified])
10
12
  end
11
13
 
12
14
  def fetch(gem_id, type, &block)
@@ -31,7 +33,7 @@ module Gemstash
31
33
  end
32
34
 
33
35
  def filter_headers(headers)
34
- headers.inject({}) do|properties, (key, value)|
36
+ headers.inject({}) do |properties, (key, value)|
35
37
  properties[key.downcase] = value if @valid_headers.include?(key.downcase)
36
38
  properties
37
39
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "gemstash"
2
4
  require "rubygems/package"
3
5
  require "stringio"
4
6
 
5
- #:nodoc:
7
+ # :nodoc:
6
8
  module Gemstash
7
9
  # Class that supports pushing a new gem to the private repository of gems.
8
10
  class GemPusher
@@ -16,12 +18,17 @@ module Gemstash
16
18
  class YankedVersionError < ExistingVersionError
17
19
  end
18
20
 
19
- def initialize(auth_key, content)
20
- @auth_key = auth_key
21
+ def self.serve(app)
22
+ gem = app.request.body.read
23
+ new(app.auth, gem).serve
24
+ end
25
+
26
+ def initialize(auth, content)
27
+ @auth = auth
21
28
  @content = content
22
29
  end
23
30
 
24
- def push
31
+ def serve
25
32
  check_auth
26
33
  store_gem
27
34
  store_gemspec
@@ -44,10 +51,15 @@ module Gemstash
44
51
  end
45
52
 
46
53
  def check_auth
47
- Gemstash::Authorization.check(@auth_key, "push")
54
+ @auth.check("push")
48
55
  end
49
56
 
50
57
  def store_gem
58
+ resource_exist = storage.resource(full_name).exist?
59
+ resource_is_indexed = storage.resource(full_name).properties[:indexed] if resource_exist
60
+
61
+ raise ExistingVersionError, "Cannot push to an existing version!" if resource_exist && resource_is_indexed
62
+
51
63
  storage.resource(full_name).save({ gem: @content }, indexed: true)
52
64
  end
53
65
 
@@ -64,14 +76,8 @@ module Gemstash
64
76
  gemstash_env.db.transaction do
65
77
  gem_id = Gemstash::DB::Rubygem.find_or_insert(spec)
66
78
  existing = Gemstash::DB::Version.find_by_spec(gem_id, spec)
67
-
68
- if existing
69
- if existing.indexed
70
- raise ExistingVersionError, "Cannot push to an existing version!"
71
- else
72
- raise YankedVersionError, "Cannot push to a yanked version!"
73
- end
74
- end
79
+ raise ExistingVersionError, "Cannot push to an existing version!" if existing && existing.indexed
80
+ raise YankedVersionError, "Cannot push to a yanked version!" if existing && !existing.indexed
75
81
 
76
82
  version_id = Gemstash::DB::Version.insert_by_spec(gem_id, spec)
77
83
  Gemstash::DB::Dependency.insert_by_spec(version_id, spec)
@@ -90,14 +96,14 @@ module Gemstash
90
96
  module LegacyRubyGemsSupport
91
97
  def self.included(base)
92
98
  base.class_eval do
93
- alias_method :push_without_cleanup, :push
94
- remove_method :push
99
+ alias_method :serve_without_cleanup, :serve
100
+ remove_method :serve
95
101
  remove_method :gem
96
102
  end
97
103
  end
98
104
 
99
- def push
100
- push_without_cleanup
105
+ def serve
106
+ serve_without_cleanup
101
107
  ensure
102
108
  cleanup
103
109
  end
@@ -115,11 +121,12 @@ module Gemstash
115
121
 
116
122
  def cleanup
117
123
  return unless @tempfile
124
+
118
125
  @tempfile.close
119
126
  @tempfile.unlink
120
127
  end
121
128
  end
122
129
 
123
- GemPusher.send(:include, LegacyRubyGemsSupport)
130
+ GemPusher.include LegacyRubyGemsSupport
124
131
  end
125
132
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gemstash
2
4
  module GemSource
3
5
  # Module for caching dependencies in a GemSource.
@@ -7,9 +9,7 @@ module Gemstash
7
9
  def serve_dependencies
8
10
  gems = gems_from_params
9
11
 
10
- if gems.length > API_REQUEST_LIMIT
11
- halt 422, "Too many gems (use --full-index instead)"
12
- end
12
+ halt 422, "Too many gems (use --full-index instead)" if gems.length > API_REQUEST_LIMIT
13
13
 
14
14
  content_type "application/octet-stream"
15
15
  Marshal.dump dependencies.fetch(gems)
@@ -21,7 +21,7 @@ module Gemstash
21
21
  if gems.length > API_REQUEST_LIMIT
22
22
  halt 422, {
23
23
  "error" => "Too many gems (use --full-index instead)",
24
- "code" => 422
24
+ "code" => 422
25
25
  }.to_json
26
26
  end
27
27
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "gemstash"
2
4
 
3
5
  module Gemstash
@@ -6,6 +8,7 @@ module Gemstash
6
8
  class PrivateSource < Gemstash::GemSource::Base
7
9
  include Gemstash::GemSource::DependencyCaching
8
10
  include Gemstash::Env::Helper
11
+ attr_accessor :auth
9
12
 
10
13
  def self.rack_env_rewriter
11
14
  @rack_env_rewriter ||= Gemstash::RackEnvRewriter.new(%r{\A/private})
@@ -14,6 +17,7 @@ module Gemstash
14
17
  def self.matches?(env)
15
18
  rewriter = rack_env_rewriter.for(env)
16
19
  return false unless rewriter.matches?
20
+
17
21
  rewriter.rewrite
18
22
  true
19
23
  end
@@ -23,27 +27,11 @@ module Gemstash
23
27
  end
24
28
 
25
29
  def serve_add_gem
26
- authenticated("Gemstash Private Gems") do
27
- auth = request.env["HTTP_AUTHORIZATION"]
28
- gem = request.body.read
29
- Gemstash::GemPusher.new(auth, gem).push
30
- end
30
+ protected(Gemstash::GemPusher)
31
31
  end
32
32
 
33
33
  def serve_yank
34
- authenticated("Gemstash Private Gems") do
35
- auth = request.env["HTTP_AUTHORIZATION"]
36
- gem_name = params[:gem_name]
37
- Gemstash::GemYanker.new(auth, gem_name, slug_param).yank
38
- end
39
- end
40
-
41
- def serve_unyank
42
- authenticated("Gemstash Private Gems") do
43
- auth = request.env["HTTP_AUTHORIZATION"]
44
- gem_name = params[:gem_name]
45
- Gemstash::GemUnyanker.new(auth, gem_name, slug_param).unyank
46
- end
34
+ protected(Gemstash::GemYanker)
47
35
  end
48
36
 
49
37
  def serve_add_spec_json
@@ -59,7 +47,7 @@ module Gemstash
59
47
  end
60
48
 
61
49
  def serve_versions
62
- halt 403, "Not yet supported"
50
+ halt 404, "Not yet supported"
63
51
  end
64
52
 
65
53
  def serve_info(name)
@@ -67,11 +55,14 @@ module Gemstash
67
55
  end
68
56
 
69
57
  def serve_marshal(id)
70
- gem_full_name = id.sub(/\.gemspec\.rz\z/, "")
71
- gem = fetch_gem(gem_full_name)
72
- halt 404 unless gem.exist?(:spec)
73
- content_type "application/octet-stream"
74
- gem.load(:spec).content(:spec)
58
+ authorization.protect(self) do
59
+ auth.check("fetch") if gemstash_env.config[:protected_fetch]
60
+ gem_full_name = id.delete_suffix(".gemspec.rz")
61
+ gem = fetch_gem(gem_full_name)
62
+ halt 404 unless gem.exist?(:spec)
63
+ content_type "application/octet-stream"
64
+ gem.content(:spec)
65
+ end
75
66
  end
76
67
 
77
68
  def serve_actual_gem(id)
@@ -79,44 +70,38 @@ module Gemstash
79
70
  end
80
71
 
81
72
  def serve_gem(id)
82
- gem_full_name = id.sub(/\.gem\z/, "")
83
- gem = fetch_gem(gem_full_name)
84
- content_type "application/octet-stream"
85
- gem.content(:gem)
73
+ authorization.protect(self) do
74
+ auth.check("fetch") if gemstash_env.config[:protected_fetch]
75
+ gem_full_name = id.delete_suffix(".gem")
76
+ gem = fetch_gem(gem_full_name)
77
+ content_type "application/octet-stream"
78
+ gem.content(:gem)
79
+ end
86
80
  end
87
81
 
88
- def serve_latest_specs
89
- halt 403, "Not yet supported"
82
+ def serve_specs
83
+ params[:prerelease] = false
84
+ protected(Gemstash::SpecsBuilder)
90
85
  end
91
86
 
92
- def serve_specs
93
- content_type "application/octet-stream"
94
- Gemstash::SpecsBuilder.all
87
+ def serve_latest_specs
88
+ params[:latest] = true
89
+ protected(Gemstash::SpecsBuilder)
95
90
  end
96
91
 
97
92
  def serve_prerelease_specs
98
- content_type "application/octet-stream"
99
- Gemstash::SpecsBuilder.prerelease
93
+ params[:prerelease] = true
94
+ protected(Gemstash::SpecsBuilder)
100
95
  end
101
96
 
102
97
  private
103
98
 
104
- def slug_param
105
- version = params[:version]
106
- platform = params[:platform]
107
-
108
- if platform.to_s.empty?
109
- version
110
- else
111
- "#{version}-#{platform}"
112
- end
99
+ def protected(servable)
100
+ authorization.protect(self) { servable.serve(self) }
113
101
  end
114
102
 
115
- def authenticated(realm)
116
- yield
117
- rescue Gemstash::NotAuthorizedError => e
118
- headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
119
- halt 401, e.message
103
+ def authorization
104
+ Gemstash::ApiKeyAuthorization
120
105
  end
121
106
 
122
107
  def dependencies
@@ -130,7 +115,6 @@ module Gemstash
130
115
  def fetch_gem(gem_full_name)
131
116
  gem = storage.resource(gem_full_name)
132
117
  halt 404 unless gem.exist?(:gem)
133
- gem.load(:gem)
134
118
  halt 403, "That gem has been yanked" unless gem.properties[:indexed]
135
119
  gem
136
120
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "gemstash"
2
4
 
3
5
  module Gemstash
@@ -11,6 +13,7 @@ module Gemstash
11
13
  def call(env)
12
14
  Gemstash::GemSource.sources.each do |source|
13
15
  next unless source.matches?(env)
16
+
14
17
  env["gemstash.gem_source"] = source
15
18
  break
16
19
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "gemstash"
2
4
  require "cgi"
3
5
 
@@ -12,6 +14,7 @@ module Gemstash
12
14
  def self.matches?(env)
13
15
  rewriter = rack_env_rewriter.for(env)
14
16
  return false unless rewriter.matches?
17
+
15
18
  rewriter.rewrite
16
19
  env["gemstash.upstream"] = rewriter.captures["upstream_url"]
17
20
  capture_user_agent(env)
@@ -35,10 +38,6 @@ module Gemstash
35
38
  halt 403, "Cannot yank from an upstream server!"
36
39
  end
37
40
 
38
- def serve_unyank
39
- halt 403, "Cannot unyank from an upstream server!"
40
- end
41
-
42
41
  def serve_add_spec_json
43
42
  halt 403, "Cannot add spec to an upstream server!"
44
43
  end
@@ -60,11 +59,11 @@ module Gemstash
60
59
  end
61
60
 
62
61
  def serve_versions
63
- redirect upstream.url("versions", request.query_string)
62
+ redirect index_upstream.url("versions", request.query_string)
64
63
  end
65
64
 
66
65
  def serve_info(name)
67
- redirect upstream.url("info/#{name}", request.query_string)
66
+ redirect index_upstream.url("info/#{name}", request.query_string)
68
67
  end
69
68
 
70
69
  def serve_marshal(id)
@@ -94,8 +93,21 @@ module Gemstash
94
93
  private
95
94
 
96
95
  def upstream
97
- @upstream ||= Gemstash::Upstream.new(env["gemstash.upstream"],
98
- user_agent: env["gemstash.user-agent"])
96
+ @upstream ||= Gemstash::Upstream.new(
97
+ env["gemstash.upstream"],
98
+ user_agent: env["gemstash.user-agent"]
99
+ )
100
+ end
101
+
102
+ def index_upstream
103
+ @index_upstream ||=
104
+ if upstream.uri.host == "rubygems.org"
105
+ uri = upstream.uri.dup
106
+ uri.host = "index.rubygems.org"
107
+ Gemstash::Upstream.new(uri, user_agent: upstream.user_agent)
108
+ else
109
+ upstream
110
+ end
99
111
  end
100
112
  end
101
113
 
@@ -116,16 +128,40 @@ module Gemstash
116
128
  serve_cached(id, :gem)
117
129
  end
118
130
 
131
+ def serve_latest_specs
132
+ http_client = http_client_for(upstream)
133
+ http_client.get("latest_specs.4.8.gz")
134
+ end
135
+
136
+ def serve_prerelease_specs
137
+ http_client = http_client_for(upstream)
138
+ http_client.get("prerelease_specs.4.8.gz")
139
+ end
140
+
141
+ def serve_specs
142
+ http_client = http_client_for(upstream)
143
+ http_client.get("specs.4.8.gz")
144
+ end
145
+
119
146
  private
120
147
 
121
- def serve_cached(id, key)
122
- gem = fetch_gem(id, key)
123
- headers.update(gem.properties[:headers][key]) if gem.properties[:headers] && gem.properties[:headers][key]
124
- gem.content(key)
148
+ def serve_cached(id, resource_type)
149
+ gem = fetch_gem(id, resource_type)
150
+ set_gem_headers(gem, resource_type)
151
+ gem.content(resource_type)
125
152
  rescue Gemstash::WebError => e
126
153
  halt e.code
127
154
  end
128
155
 
156
+ def set_gem_headers(gem, resource_type)
157
+ return unless gem.property?(:headers, resource_type)
158
+
159
+ gem_headers = gem.properties[:headers][resource_type]
160
+ headers["Content-Type"] = gem_headers["content-type"] if gem_headers.include?("content-type")
161
+ headers["Last-Modified"] = gem_headers["last-modified"] if gem_headers.include?("last-modified")
162
+ headers["ETag"] = gem_headers["etag"] if gem_headers.include?("etag")
163
+ end
164
+
129
165
  def dependencies
130
166
  @dependencies ||= begin
131
167
  http_client = http_client_for(upstream)
@@ -142,25 +178,33 @@ module Gemstash
142
178
  @gem_fetcher ||= Gemstash::GemFetcher.new(http_client_for(upstream))
143
179
  end
144
180
 
145
- def fetch_gem(id, key)
181
+ def fetch_gem(id, resource_type)
146
182
  gem_name = Gemstash::Upstream::GemName.new(upstream, id)
147
183
  gem_resource = storage.resource(gem_name.name)
148
- if gem_resource.exist?(key)
149
- fetch_local_gem(gem_name, gem_resource, key)
184
+ if gem_resource.exist?(resource_type)
185
+ fetch_local_gem(gem_name, gem_resource, resource_type)
150
186
  else
151
- fetch_remote_gem(gem_name, gem_resource, key)
187
+ fetch_remote_gem(gem_name, gem_resource, resource_type)
152
188
  end
153
189
  end
154
190
 
155
- def fetch_local_gem(gem_name, gem_resource, key)
156
- log.info "Gem #{gem_name.name} exists, returning cached #{key}"
157
- gem_resource.load(key)
191
+ def fetch_local_gem(gem_name, gem_resource, resource_type)
192
+ log.info "Gem #{gem_name.name} exists, returning cached #{resource_type}"
193
+ gem_resource
158
194
  end
159
195
 
160
- def fetch_remote_gem(gem_name, gem_resource, key)
161
- log.info "Gem #{gem_name.name} is not cached, fetching #{key}"
162
- gem_fetcher.fetch(gem_name.id, key) do |content, properties|
163
- gem_resource.save({ key => content }, headers: { key => properties })
196
+ def fetch_remote_gem(gem_name, gem_resource, resource_type)
197
+ log.info "Gem #{gem_name.name} is not cached, fetching #{resource_type}"
198
+ gem_fetcher.fetch(gem_name.id, resource_type) do |content, properties|
199
+ resource_properties = {
200
+ upstream: upstream.to_s,
201
+ gem_name: gem_name.name,
202
+ headers: { resource_type => properties }
203
+ }
204
+
205
+ gem = gem_resource.save({ resource_type => content }, resource_properties)
206
+ Gemstash::DB::CachedRubygem.store(upstream, gem_name, resource_type)
207
+ gem
164
208
  end
165
209
  end
166
210
  end
@@ -169,13 +213,13 @@ module Gemstash
169
213
  # default upstream).
170
214
  class RubygemsSource < Gemstash::GemSource::UpstreamSource
171
215
  def self.matches?(env)
172
- if env["HTTP_X_GEMFILE_SOURCE"].to_s.empty?
173
- env["gemstash.upstream"] = env["gemstash.env"].config[:rubygems_url]
216
+ env["gemstash.upstream"] = if env["gemstash.env"].config[:ignore_gemfile_source] || env["HTTP_X_GEMFILE_SOURCE"].to_s.empty?
217
+ env["gemstash.env"].config[:rubygems_url]
174
218
  else
175
- env["gemstash.upstream"] = env["HTTP_X_GEMFILE_SOURCE"]
219
+ env["HTTP_X_GEMFILE_SOURCE"]
176
220
  end
177
- capture_user_agent(env)
178
221
 
222
+ capture_user_agent(env)
179
223
  true
180
224
  end
181
225
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "gemstash"
2
4
  require "forwardable"
3
5
 
4
6
  module Gemstash
5
- #:nodoc:
7
+ # :nodoc:
6
8
  module GemSource
7
9
  autoload :DependencyCaching, "gemstash/gem_source/dependency_caching"
8
10
  autoload :PrivateSource, "gemstash/gem_source/private_source"
@@ -27,7 +29,7 @@ module Gemstash
27
29
  include Gemstash::Logging
28
30
 
29
31
  def_delegators :@app, :cache_control, :content_type, :env, :halt,
30
- :headers, :http_client_for, :params, :redirect, :request
32
+ :headers, :http_client_for, :params, :redirect, :request
31
33
 
32
34
  def initialize(app)
33
35
  @app = app
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "gemstash"
2
4
 
3
5
  module Gemstash
@@ -17,13 +19,19 @@ module Gemstash
17
19
  class YankedVersionError < StandardError
18
20
  end
19
21
 
20
- def initialize(auth_key, gem_name, slug)
21
- @auth_key = auth_key
22
+ def self.serve(app)
23
+ gem_name = app.params[:gem_name]
24
+ slug = Gemstash::DB::Version.slug(app.params)
25
+ new(app.auth, gem_name, slug).serve
26
+ end
27
+
28
+ def initialize(auth, gem_name, slug)
29
+ @auth = auth
22
30
  @gem_name = gem_name
23
31
  @slug = slug
24
32
  end
25
33
 
26
- def yank
34
+ def serve
27
35
  check_auth
28
36
  update_database
29
37
  invalidate_cache
@@ -40,15 +48,17 @@ module Gemstash
40
48
  end
41
49
 
42
50
  def check_auth
43
- Gemstash::Authorization.check(@auth_key, "yank")
51
+ @auth.check("yank")
44
52
  end
45
53
 
46
54
  def update_database
47
55
  gemstash_env.db.transaction do
48
56
  raise UnknownGemError, "Cannot yank an unknown gem!" unless Gemstash::DB::Rubygem[name: @gem_name]
57
+
49
58
  version = Gemstash::DB::Version.find_by_full_name(full_name)
50
59
  raise UnknownVersionError, "Cannot yank an unknown version!" unless version
51
60
  raise YankedVersionError, "Cannot yank an already yanked version!" unless version.indexed
61
+
52
62
  version.deindex
53
63
  storage.resource(version.storage_id).update_properties(indexed: false)
54
64
  end