geminabox 1.0.1 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 989dda9f55d23070e5c33746b9dc21d32e63e1a336bf3d82aa8ac935f7571385
4
- data.tar.gz: bd529519469468e0d51a74bccfe3509f7038fed8238c1431c344755ea380ba59
3
+ metadata.gz: ccd8f5577f0719a552b32d62c4291929c6e544738c4eaef8c3e2d10fab373d92
4
+ data.tar.gz: e6d33dd7903533d09dde5e8ab8528a427b5b20b5d66097f41ba953df99563b15
5
5
  SHA512:
6
- metadata.gz: 9ae826eedc94f20eabb4a8a581aab83a01f68708271c93fbdd4f405690bd61633ef5f523ce4d831ecfdcd79d0cb72feb37f2cce7a9b7fc421b78b0a84e7fef2b
7
- data.tar.gz: f3a64cbf0e89de75122d3de47cd22198ed2205ffdcfc05153adcab2bce114dc120782562c569edbba6759c66378de43070434f3ab5401c7a04b317978fb9abba
6
+ metadata.gz: aa8030ff620fbd6843a336f2a93f009e70b2d934fe17681c3fe51a41282544a6f7ff4619250724283ea632e80399a804ecb1a07d2060215727be24a67ac6d2ac
7
+ data.tar.gz: 8e8a36a99eb8cf115d7b503e4dd9716fcc80138f660c9f8c76746fc5d81554d37e8f2a40f8394be6badc480edb237914b45967b7986fb7ab996bd5d887217e24
data/README.md CHANGED
@@ -92,6 +92,24 @@ local systems SSL certificates.
92
92
 
93
93
  TemplateFaradayAdapter is provided as an example of an alternative HTTPAdapter.
94
94
 
95
+ ## Hooks
96
+
97
+ You can add a hook (anything callable) which will be called when a gem is
98
+ successfully received.
99
+
100
+ ```ruby
101
+ Geminabox.on_gem_received = Proc.new do |gem|
102
+ puts "Gem received: #{gem.spec.name} #{gem.spec.version}"
103
+ end
104
+ ```
105
+
106
+ Typically you might use this to push a notification to your team chat. Any
107
+ exceptions which occur within the hook is silently ignored, so please ensure they
108
+ are handled properly if this is not desirable.
109
+
110
+ Also, please note that this hook blocks `POST /upload` and `POST /api/v1/gems` APIs processing.
111
+ Hook authors are responsible to perform any action non-blocking/async to avoid HTTP timeout.
112
+
95
113
  ## Client Usage
96
114
 
97
115
  Since version 0.10, Geminabox supports the standard gemcutter push API:
@@ -104,6 +122,10 @@ You can also use the gem plugin:
104
122
 
105
123
  gem inabox pkg/my-awesome-gem-1.0.gem
106
124
 
125
+ And since version 1.2.0, Geminabox supports the standard gemcutter yank API:
126
+
127
+ gem yank my-awesome-gem -v 1.0 --host HOST
128
+
107
129
  Configure Gem in a box (interactive prompt to specify where to upload to):
108
130
 
109
131
  gem inabox -c
data/lib/geminabox.rb CHANGED
@@ -46,6 +46,7 @@ module Geminabox
46
46
  :gem_permissions,
47
47
  :allow_delete,
48
48
  :rubygems_proxy,
49
+ :rubygems_proxy_merge_strategy,
49
50
  :http_adapter,
50
51
  :lockfile,
51
52
  :retry_interval,
@@ -53,6 +54,7 @@ module Geminabox
53
54
  :ruby_gems_url,
54
55
  :bundler_ruby_gems_url,
55
56
  :allow_upload,
57
+ :on_gem_received
56
58
  )
57
59
 
58
60
  def set_defaults(defaults)
@@ -72,22 +74,24 @@ module Geminabox
72
74
  end
73
75
 
74
76
  set_defaults(
75
- data: File.join(File.dirname(__FILE__), *%w[.. data]),
76
- public_folder: File.join(File.dirname(__FILE__), *%w[.. public]),
77
- build_legacy: false,
78
- incremental_updates: true,
79
- views: File.join(File.dirname(__FILE__), *%w[.. views]),
80
- allow_replace: false,
81
- gem_permissions: 0644,
82
- rubygems_proxy: (ENV['RUBYGEMS_PROXY'] == 'true'),
83
- allow_delete: true,
84
- http_adapter: HttpClientAdapter.new,
85
- lockfile: '/tmp/geminabox.lockfile',
86
- retry_interval: 60,
87
- allow_remote_failure: false,
88
- ruby_gems_url: 'https://rubygems.org/',
89
- bundler_ruby_gems_url: 'https://bundler.rubygems.org/',
90
- allow_upload: true,
77
+ data: File.join(File.dirname(__FILE__), *%w[.. data]),
78
+ public_folder: File.join(File.dirname(__FILE__), *%w[.. public]),
79
+ build_legacy: false,
80
+ incremental_updates: true,
81
+ views: File.join(File.dirname(__FILE__), *%w[.. views]),
82
+ allow_replace: false,
83
+ gem_permissions: 0644,
84
+ rubygems_proxy: (ENV['RUBYGEMS_PROXY'] == 'true'),
85
+ rubygems_proxy_merge_strategy: ENV.fetch('RUBYGEMS_PROXY_MERGE_STRATEGY') { :local_gems_take_precedence_over_remote_gems }.to_sym,
86
+ allow_delete: true,
87
+ http_adapter: HttpClientAdapter.new,
88
+ lockfile: '/tmp/geminabox.lockfile',
89
+ retry_interval: 60,
90
+ allow_remote_failure: false,
91
+ ruby_gems_url: 'https://rubygems.org/',
92
+ bundler_ruby_gems_url: 'https://bundler.rubygems.org/',
93
+ allow_upload: true,
94
+ on_gem_received: nil
91
95
  )
92
96
 
93
97
  end
@@ -1,23 +1,38 @@
1
- module Geminabox
2
- class GemListMerge
3
- attr_accessor :list
4
-
5
- IGNORE_DEPENDENCIES = 0..-2
1
+ require "set"
6
2
 
7
- def self.from(*lists)
8
- lists.map{|list| new(list)}.inject(:merge)
3
+ module Geminabox
4
+ module GemListMerge
5
+ def self.merge(local_gem_list, remote_gem_list, strategy:)
6
+ strategy_for(strategy).merge(local_gem_list, remote_gem_list)
9
7
  end
10
8
 
11
- def initialize(list)
12
- @list = list
9
+ def self.strategy_for(strategy)
10
+ case strategy
11
+ when :local_gems_take_precedence_over_remote_gems
12
+ LocalGemsTakePrecedenceOverRemoteGems
13
+ when :combine_local_and_remote_gem_versions
14
+ CombineLocalAndRemoteGemVersions
15
+ else
16
+ raise ArgumentError, "Merge strategy must be :local_gems_take_precedence_over_remote_gems (default) or :merge_local_and_remote_gem_versions"
17
+ end
13
18
  end
14
19
 
15
- def merge(other)
16
- merged = (list + other.list)
17
- merged.uniq! {|val| val.values[IGNORE_DEPENDENCIES] }
18
- merged.sort_by! {|x| x.values[IGNORE_DEPENDENCIES] }
19
- merged
20
+ module LocalGemsTakePrecedenceOverRemoteGems
21
+ def self.merge(local_gem_list, remote_gem_list)
22
+ names = Set.new(local_gem_list.map { |gem| gem[:name] })
23
+ local_gem_list + remote_gem_list.reject { |gem| names.include? gem[:name] }
24
+ end
20
25
  end
21
26
 
27
+ module CombineLocalAndRemoteGemVersions
28
+ IGNORE_DEPENDENCIES = 0..-2
29
+
30
+ def self.merge(local_gem_list, remote_gem_list)
31
+ merged = local_gem_list + remote_gem_list
32
+ merged.uniq! {|val| val.values[IGNORE_DEPENDENCIES] }
33
+ merged.sort_by! {|x| x.values[IGNORE_DEPENDENCIES] }
34
+ merged
35
+ end
36
+ end
22
37
  end
23
38
  end
@@ -1,5 +1,6 @@
1
1
  require 'tempfile'
2
2
  require 'fileutils'
3
+ require 'rubygems/util'
3
4
 
4
5
  module Geminabox
5
6
  module Proxy
@@ -65,11 +66,11 @@ module Geminabox
65
66
  end
66
67
 
67
68
  def unpackage(content)
68
- Marshal.load(Gem.gunzip(content))
69
+ Marshal.load(Gem::Util.gunzip(content))
69
70
  end
70
71
 
71
72
  def package(content)
72
- Gem.gzip(Marshal.dump(content))
73
+ Gem::Util.gzip(Marshal.dump(content))
73
74
  end
74
75
 
75
76
  def merge_text_content
@@ -1,4 +1,5 @@
1
1
  require 'reentrant_flock'
2
+ require 'rubygems/util'
2
3
 
3
4
  module Geminabox
4
5
 
@@ -130,6 +131,7 @@ module Geminabox
130
131
  get '/gems/:gemname' do
131
132
  gems = Hash[load_gems.by_name]
132
133
  @gem = gems[params[:gemname]]
134
+ @allow_delete = self.class.allow_delete?
133
135
  halt 404 unless @gem
134
136
  erb :gem
135
137
  end
@@ -147,6 +149,27 @@ module Geminabox
147
149
 
148
150
  end
149
151
 
152
+ delete '/api/v1/gems/yank' do
153
+ unless self.class.allow_delete?
154
+ error_response(403, 'Gem deletion is disabled')
155
+ end
156
+
157
+ halt 400 unless request.form_data?
158
+
159
+ serialize_update do
160
+ gems = load_gems.select { |gem| request['gem_name'] == gem.name and
161
+ request['version'] == gem.number.version }
162
+ halt 404, 'Gem not found' if gems.size == 0
163
+ gems.each do |gem|
164
+ gem_path = File.expand_path(File.join(Geminabox.data, 'gems',
165
+ "#{gem.gemfile_name}.gem"))
166
+ File.delete gem_path if File.exists? gem_path
167
+ end
168
+ self.class.reindex(:force_rebuild)
169
+ return 200, 'Yanked gem and reindexed'
170
+ end
171
+ end
172
+
150
173
  post '/upload' do
151
174
  unless self.class.allow_upload?
152
175
  error_response(403, 'Gem uploading is disabled')
@@ -197,6 +220,12 @@ module Geminabox
197
220
  error_response error.code, error.reason
198
221
  end
199
222
 
223
+ begin
224
+ Geminabox.on_gem_received.call(gem) if Geminabox.on_gem_received
225
+ rescue
226
+ # ignore errors which occur within the hook
227
+ end
228
+
200
229
  if api_request?
201
230
  "Gem #{gem.name} received and indexed."
202
231
  else
@@ -237,7 +266,7 @@ HTML
237
266
  def all_gems_with_duplicates
238
267
  specs_files_paths.map do |specs_file_path|
239
268
  if File.exist?(specs_file_path)
240
- Marshal.load(Gem.gunzip(Gem.read_binary(specs_file_path)))
269
+ Marshal.load(Gem::Util.gunzip(Gem.read_binary(specs_file_path)))
241
270
  else
242
271
  []
243
272
  end
@@ -283,7 +312,7 @@ HTML
283
312
  end
284
313
 
285
314
  def combined_gem_list
286
- GemListMerge.from(local_gem_list, remote_gem_list)
315
+ GemListMerge.merge(local_gem_list, remote_gem_list, strategy: Geminabox.rubygems_proxy_merge_strategy)
287
316
  end
288
317
 
289
318
  helpers do
@@ -305,7 +334,7 @@ HTML
305
334
  spec_file = File.join(Geminabox.data, "quick", "Marshal.#{Gem.marshal_version}", "#{filename.join("-")}.gemspec.rz")
306
335
  File::open(spec_file, 'r') do |unzipped_spec_file|
307
336
  unzipped_spec_file.binmode
308
- Marshal.load(Gem.inflate(unzipped_spec_file.read))
337
+ Marshal.load(Gem::Util.inflate(unzipped_spec_file.read))
309
338
  end if File.exist? spec_file
310
339
  end
311
340
 
@@ -1,3 +1,3 @@
1
1
  module Geminabox
2
- VERSION = '1.0.1' unless defined? VERSION
2
+ VERSION = '1.3.1' unless defined? VERSION
3
3
  end
data/views/atom.erb CHANGED
@@ -10,12 +10,12 @@
10
10
  <% newest_gem = versions.newest %>
11
11
  <% spec = spec_for(name, newest_gem.number, newest_gem.platform) %>
12
12
  <entry>
13
- <id><%= name %></id>
13
+ <id><%= h(name) %></id>
14
14
  <updated><%= spec.date.w3cdtf %></updated>
15
- <title><%= name %> (<%= versions.size == 1 ? versions.oldest.number : "#{versions.oldest.number} - #{versions.newest.number}" %>)</title>
16
- <author><name><%= spec.authors.join(", ") %></name></author>
15
+ <title><%= h(name) %> (<%= h(versions.size == 1 ? versions.oldest.number : "#{versions.oldest.number} - #{versions.newest.number}") %>)</title>
16
+ <author><name><%= h(spec.authors.join(", ")) %></name></author>
17
17
  <% versions.each do |version| %>
18
- <link href="<%= url "/gems/#{version.gemfile_name}.gem" %>" />
18
+ <link href="<%= h(url "/gems/#{version.gemfile_name}.gem") %>" />
19
19
  <% end %>
20
20
  </entry>
21
21
  <% end %>
data/views/gem.erb CHANGED
@@ -5,18 +5,18 @@
5
5
  <ul class="gemlist">
6
6
  <% @gem.by_name do |name, versions| %>
7
7
  <li class="gem-version">
8
- <h2><%= name %> (<%= versions.count == 1 ? versions.first.number : "#{versions.oldest.number} - #{versions.newest.number}" %>)</h2>
8
+ <h2><%= h(name) %> (<%= h(versions.count == 1 ? versions.first.number : "#{versions.oldest.number} - #{versions.newest.number}") %>)</h2>
9
9
  <% versions.each.reverse_each do |version| %>
10
10
  <p>
11
- <code>gem install <%= version.name %> <%= "--prerelease" if version.number.to_s.match(/[a-z]/i) %> -v "<%= version.number %>"</code>
11
+ <code>gem install <%= h(version.name) %> <%= "--prerelease" if version.number.to_s.match(/[a-z]/i) %> -v "<%= h(version.number) %>"</code>
12
12
  <% unless version.platform =~ /^ruby/i %>
13
- <small class="platform"><%= version.platform %></small>
13
+ <small class="platform"><%= h(version.platform) %></small>
14
14
  <% end %>
15
15
  </p>
16
16
  <div class="delete-form">
17
- <a class="download" href="<%= url("/gems/#{version.gemfile_name}.gem") %>">download</a>
17
+ <a class="download" href="<%= h(url("/gems/#{version.gemfile_name}.gem")) %>">download</a>
18
18
  <% if @allow_delete %>
19
- <form method="post" action="<%= url("/gems/#{version.gemfile_name}.gem") %>">
19
+ <form method="post" action="<%= h(url("/gems/#{version.gemfile_name}.gem")) %>">
20
20
  <input type="hidden" name="_method" value="DELETE" />
21
21
  <button type="submit">delete</button>
22
22
  </form>
@@ -28,10 +28,10 @@
28
28
  <p>
29
29
  <% newest_gem = versions.newest %>
30
30
  <% if spec = spec_for(name, newest_gem.number, newest_gem.platform) %>
31
- <%= spec.description %>
31
+ <%= h(spec.description) %>
32
32
  <br/>
33
33
  <span class="author">– <%= spec.authors.map do |author|
34
- "<a href='#{href(spec.homepage)}'>#{author}</a>"
34
+ "<a href='#{href(spec.homepage)}'>#{h(author)}</a>"
35
35
  end.join(', ') %></span>
36
36
  <% end %>
37
37
  </p>
data/views/index.erb CHANGED
@@ -18,16 +18,16 @@
18
18
 
19
19
  <% @gems.by_name do |name, versions| %>
20
20
  <li <%= %{id="jump_#{name[0..0].downcase}"} if @index_gems.delete(name[0..0].downcase) %> class="gem-version">
21
- <h2><%= name %> (<%= versions.count == 1 ? versions.first.number : "#{versions.oldest.number} - #{versions.newest.number}" %>)</h2>
21
+ <h2><%= h(name) %> (<%= h(versions.count == 1 ? versions.first.number : "#{versions.oldest.number} - #{versions.newest.number}") %>)</h2>
22
22
  <% versions.each.reverse_each.first(5).each do |version| %>
23
23
  <p>
24
- <code>gem install <%= version.name %> <%= "--prerelease" if version.number.to_s.match(/[a-z]/i) %> -v "<%= version.number %>"</code>
24
+ <code>gem install <%= h(version.name) %> <%= "--prerelease" if version.number.to_s.match(/[a-z]/i) %> -v "<%= h(version.number) %>"</code>
25
25
  <% unless version.platform =~ /^ruby/i %>
26
- <small class="platform"><%= version.platform %></small>
26
+ <small class="platform"><%= h(version.platform) %></small>
27
27
  <% end %>
28
28
  </p>
29
- <form class="delete-form" method="post" action="<%= url("/gems/#{version.gemfile_name}.gem") %>">
30
- <a class="download" href="<%= url("/gems/#{version.gemfile_name}.gem") %>">download</a>
29
+ <form class="delete-form" method="post" action="<%= h(url("/gems/#{version.gemfile_name}.gem")) %>">
30
+ <a class="download" href="<%= h(url("/gems/#{version.gemfile_name}.gem")) %>">download</a>
31
31
  <% if @allow_delete %>
32
32
  <input type="hidden" name="_method" value="DELETE" />
33
33
  <button type="submit">delete</button>
@@ -36,17 +36,17 @@
36
36
  <% end %>
37
37
 
38
38
  <% if versions.count > 5 %>
39
- <a href="<%= url("/gems/#{name}") %>" id="more_link">Older versions...</a>
39
+ <a href="<%= h(url("/gems/#{name}")) %>" id="more_link">Older versions...</a>
40
40
  <% end %>
41
41
 
42
42
  <div class="details">
43
43
  <p>
44
44
  <% newest_gem = versions.newest %>
45
45
  <% if spec = spec_for(name, newest_gem.number, newest_gem.platform) %>
46
- <%= spec.description %>
46
+ <%= h(spec.description) %>
47
47
  <br/>
48
48
  <span class="author">– <%= spec.authors.map do |author|
49
- "<a href='#{href(spec.homepage)}'>#{author}</a>"
49
+ "<a href='#{href(spec.homepage)}'>#{h(author)}</a>"
50
50
  end.join(', ') %></span>
51
51
  <% end %>
52
52
  </p>
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geminabox
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Lea
8
8
  - Jack Foy
9
9
  - Rob Nichols
10
10
  - Naotoshi Seo
11
- autorequire:
11
+ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2018-03-13 00:00:00.000000000 Z
14
+ date: 2021-05-29 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: sinatra
@@ -149,7 +149,7 @@ licenses:
149
149
  - MIT-LICENSE
150
150
  metadata:
151
151
  source_code_uri: https://github.com/geminabox/geminabox
152
- post_install_message:
152
+ post_install_message:
153
153
  rdoc_options:
154
154
  - "--main"
155
155
  - README.md
@@ -166,9 +166,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
166
166
  - !ruby/object:Gem::Version
167
167
  version: '0'
168
168
  requirements: []
169
- rubyforge_project:
170
- rubygems_version: 2.7.4
171
- signing_key:
169
+ rubygems_version: 3.1.2
170
+ signing_key:
172
171
  specification_version: 4
173
172
  summary: Really simple rubygem hosting
174
173
  test_files: []