gemirro 1.6.0 → 2.0.0

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: 23b3ba616d8007252b6be8b5a3edd675246bb89839e8ae13fd2b01dcf9dff4d4
4
- data.tar.gz: a82c036b510ce0da4f1c6475a7b6baac5afbcc065ce911bec8a6e789b84ad9ee
3
+ metadata.gz: e54b02e5575feb8b7a4fcbb40f5fd91764fb0f589368e4a9207f6a5074fe2f05
4
+ data.tar.gz: 14449b2b15d5477216adaecea6e36aff26dae8b30104e3db627bdcdbdf7c6b7d
5
5
  SHA512:
6
- metadata.gz: 6f31d4f862a961c03b097b9eff5bff4a5d8b9e02816346a9dcf581c393213b4f8754be127115374895dd3613179a550b4e1e63bb26fdaa13e50ebe1646693998
7
- data.tar.gz: fc8957ef3a2231aca1c9c386cc27316b252e46267b360948b8cfe6fd0f1300b6a788edf6fbe3553d2a6889536216bf9921f06ec121f3cebb68a50a7111a636ed
6
+ metadata.gz: ba26ee7c502dd1bbfc02fd55e1263097dedad78670281d8f775bd9c5940be1c46032130cd76be02fbdee3b8a4c2f24504f39b38c7e5901497c7eff28d13a0307
7
+ data.tar.gz: ce425c173bcfc46ea6298cc0911ddec8a838f321749f9ec782da8323afdaf3268f6071d629be5fa6b129fbe29d2198e930b6202225d23f17ac582fac71fc1533
data/.gitignore CHANGED
@@ -4,3 +4,4 @@
4
4
  /.ruby-version
5
5
  /*.gem
6
6
  /.bundle
7
+ .DS_Store
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gemirro (1.6.0)
4
+ gemirro (2.0.0)
5
5
  addressable (~> 2.8)
6
6
  builder (~> 3.2)
7
7
  compact_index (~> 0.15)
@@ -50,7 +50,7 @@ GEM
50
50
  racc
51
51
  public_suffix (6.0.1)
52
52
  racc (1.8.1)
53
- rack (2.2.10)
53
+ rack (2.2.14)
54
54
  rack-protection (3.2.0)
55
55
  base64 (>= 0.1.0)
56
56
  rack (~> 2.2, >= 2.2.4)
data/MANIFEST CHANGED
@@ -55,9 +55,6 @@ template/config.rb
55
55
  template/logs/.gitkeep
56
56
  template/public/dist/css/gemirro.css
57
57
  template/public/gems/.gitkeep
58
- template/public/latest_specs.4.8
59
- template/public/prerelease_specs.4.8
60
- template/public/specs.4.8
61
58
  views/gem.erb
62
59
  views/index.erb
63
60
  views/layout.erb
@@ -19,10 +19,11 @@ Gemirro::CLI.options.command 'index' do
19
19
  abort
20
20
  end
21
21
 
22
- indexer = Gemirro::Indexer.new(config.destination)
22
+ indexer = Gemirro::Indexer.new
23
23
  indexer.ui = Gem::SilentUI.new
24
24
 
25
- if File.exist?(File.join(config.destination, "specs.#{Gem.marshal_version}.gz"))
25
+ if File.exist?(File.join(config.versions_file))
26
+ indexer.download_source_versions
26
27
  if opts[:u]
27
28
  config.logger.info('Generating index updates')
28
29
  indexer.update_index
@@ -31,7 +32,7 @@ Gemirro::CLI.options.command 'index' do
31
32
  indexer.generate_index
32
33
  end
33
34
  else
34
- config.logger.error("/public/specs.#{Gem.marshal_version}.gz file is missing.")
35
+ config.logger.error("#{File.basename(config.versions_file)} file is missing.")
35
36
  config.logger.error('Run "gemirro update" before running index.')
36
37
  end
37
38
  end
@@ -26,11 +26,6 @@ Gemirro::CLI.options.command 'init' do
26
26
  end
27
27
  end
28
28
 
29
- # make sure index updates blank local specs
30
- ['specs.4.8', 'latest_specs.4.8', 'prerelease_specs.4.8'].each do |s|
31
- File.utime(Time.at(0), Time.at(0), File.join(directory, 'public', s))
32
- end
33
-
34
29
  puts "Initialized empty mirror in #{directory}"
35
30
  end
36
31
  end
data/lib/gemirro/cli.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require File.expand_path('../version', __FILE__)
4
+
3
5
  module Gemirro
4
6
  # CLI mode
5
7
  module CLI
@@ -58,7 +60,7 @@ module Gemirro
58
60
  # @return [String]
59
61
  #
60
62
  def self.version_information
61
- "gemirro v#{VERSION} on #{RUBY_DESCRIPTION}"
63
+ "gemirro v#{Gemirro::VERSION} on #{RUBY_DESCRIPTION}"
62
64
  end
63
65
  end
64
66
  end
@@ -99,18 +99,10 @@ module Gemirro
99
99
  #
100
100
  # @return [String]
101
101
  #
102
- def self.versions_file
103
- "specs.#{marshal_version}.gz"
104
- end
102
+ def versions_file
103
+ return unless @source
105
104
 
106
- ##
107
- # Returns the name of the file that contains an index
108
- # of all the prerelease versions.
109
- #
110
- # @return [String]
111
- #
112
- def self.prerelease_versions_file
113
- "prerelease_specs.#{marshal_version}.gz"
105
+ File.expand_path("#{URI.parse(@source.host).host.gsub('.', '_')}_versions", destination.to_s)
114
106
  end
115
107
 
116
108
  ##
@@ -55,6 +55,8 @@ module Gemirro
55
55
  # @return [Array]
56
56
  #
57
57
  def versions_for(gem)
58
+ return [] unless @versions_file
59
+
58
60
  available = @versions_file.versions_for(gem.name)
59
61
  return [available.last] if gem.only_latest?
60
62
 
@@ -35,19 +35,16 @@ module Gemirro
35
35
  # @param [Hash] options Indexer options
36
36
  # @return [Array]
37
37
  ##
38
- def initialize(directory, options = {})
38
+ def initialize(options = {})
39
39
  require 'fileutils'
40
40
  require 'tmpdir'
41
41
  require 'zlib'
42
42
  require 'builder/xchar'
43
43
  require 'compact_index'
44
44
 
45
- options.merge!({ build_modern: true, build_compact: true })
45
+ options.merge!({ build_modern: false })
46
46
 
47
- @build_modern = options[:build_modern]
48
- @build_compact = options[:build_compact]
49
-
50
- @dest_directory = directory
47
+ @dest_directory = Gemirro.configuration.destination
51
48
  @directory =
52
49
  File.join(Dir.tmpdir, "gem_generate_index_#{rand(1_000_000_000)}")
53
50
 
@@ -69,22 +66,12 @@ module Gemirro
69
66
  @latest_index =
70
67
  File.join(@quick_dir, 'latest_index')
71
68
 
72
- @specs_index =
73
- File.join(@directory, "specs.#{::Gem.marshal_version}")
74
69
  @latest_specs_index =
75
70
  File.join(@directory, "latest_specs.#{::Gem.marshal_version}")
76
- @prerelease_specs_index =
77
- File.join(@directory, "prerelease_specs.#{::Gem.marshal_version}")
78
- @dest_specs_index =
79
- File.join(@dest_directory, "specs.#{::Gem.marshal_version}")
80
71
  @dest_latest_specs_index =
81
72
  File.join(@dest_directory, "latest_specs.#{::Gem.marshal_version}")
82
- @dest_prerelease_specs_index =
83
- File.join(@dest_directory, "prerelease_specs.#{::Gem.marshal_version}")
84
73
  @infos_dir =
85
74
  File.join(@dest_directory, 'info')
86
- @api_v1_dependencies_dir =
87
- File.join(@dest_directory, 'api', 'v1', 'dependencies')
88
75
 
89
76
  @files = []
90
77
  end
@@ -92,16 +79,9 @@ module Gemirro
92
79
  ##
93
80
  # Generate indices on the destination directory
94
81
  #
95
- def install_indices
96
- install_indicies
97
- end
98
-
99
- ##
100
- # Generate indicies on the destination directory
101
- #
102
82
  # @return [Array]
103
83
  #
104
- def install_indicies
84
+ def install_indices
105
85
  Utils.logger
106
86
  .debug("Downloading index into production dir #{@dest_directory}")
107
87
 
@@ -111,24 +91,19 @@ module Gemirro
111
91
  if files.include?(@quick_marshal_dir) && !files.include?(@quick_dir)
112
92
  files.delete @quick_marshal_dir
113
93
  FileUtils.mkdir_p(File.dirname(@quick_marshal_dir_base), verbose: verbose)
114
- FileUtils.rm_rf(@quick_marshal_dir_base, verbose: verbose)
94
+ if @quick_marshal_dir_base && File.exist?(@quick_marshal_dir_base)
95
+ FileUtils.rm_rf(@quick_marshal_dir_base, verbose: verbose)
96
+ end
115
97
  FileUtils.mv(@quick_marshal_dir, @quick_marshal_dir_base, verbose: verbose, force: true)
116
98
  end
117
99
 
118
100
  files.each do |path|
119
101
  file = path.sub(%r{^#{Regexp.escape @directory}/?}, '')
120
102
 
121
- if ["#{@specs_index}.gz",
122
- "#{@latest_specs_index}.gz",
123
- "#{@prerelease_specs_index}.gz"].include?(path)
124
- res = build_zlib_file(file, File.join(@directory, file), File.join(@dest_directory, file), true)
125
- next unless res
126
- else
127
- source_content = download_from_source(file)
128
- next if source_content.nil?
103
+ source_content = download_from_source(file)
104
+ next if source_content.nil?
129
105
 
130
- MirrorFile.new(File.join(@dest_directory, file)).write(source_content)
131
- end
106
+ MirrorFile.new(File.join(@dest_directory, file)).write(source_content)
132
107
 
133
108
  FileUtils.rm_rf(path)
134
109
  end
@@ -142,7 +117,7 @@ module Gemirro
142
117
  #
143
118
  def download_from_source(file)
144
119
  source_host = Gemirro.configuration.source.host
145
- Utils.logger.info("Download from source: #{file}")
120
+ Utils.logger.info("Download from source #{source_host}/#{file}")
146
121
  resp = Http.get("#{source_host}/#{File.basename(file)}")
147
122
  return unless resp.code == 200
148
123
 
@@ -155,104 +130,18 @@ module Gemirro
155
130
  # @return [Array]
156
131
  #
157
132
  def build_indices
158
- build_indicies
159
- end
160
-
161
- ##
162
- # Build indicies
163
- #
164
- # @return [Array]
165
- #
166
- def build_indicies
167
133
  specs = *map_gems_to_specs(gem_file_list)
168
134
  specs.select! { |s| s.instance_of?(::Gem::Specification) }
169
135
  ::Gem::Specification.dirs = []
170
136
  ::Gem::Specification.all = specs
171
137
 
172
- if ::Gem::VERSION >= '2.5.0'
173
- build_marshal_gemspecs(specs)
174
- build_modern_indices(specs) if @build_modern
175
- compress_indices
176
- else
177
- build_marshal_gemspecs
178
- build_modern_indicies if @build_modern
179
- compress_indicies
180
- end
181
-
182
- build_api_v1_dependencies(specs)
183
-
184
- return unless @build_compact
138
+ build_marshal_gemspecs(specs)
185
139
 
186
140
  build_compact_index_names
187
141
  build_compact_index_infos(specs)
188
142
  build_compact_index_versions(specs)
189
143
  end
190
144
 
191
- ##
192
- # Cache Modern Index endpoints /api/v1/dependencies?gems= and /api/v1/dependencies.json?gems=
193
- # This single request may include many fragments. server.rb determines which are required per request.
194
- #
195
- # @return nil
196
- #
197
- def build_api_v1_dependencies(specs, partial = false)
198
- FileUtils.mkdir_p(@api_v1_dependencies_dir)
199
-
200
- if partial
201
- specs.collect(&:name).uniq do |name|
202
- FileUtils.rm_rf(Dir.glob(File.join(@api_v1_dependencies_dir, "#{name}.*.*.list")))
203
- end
204
- else
205
- FileUtils.rm_rf(Dir.glob(File.join(@api_v1_dependencies_dir, '*.list')))
206
- end
207
-
208
- grouped_specs = specs.sort_by(&:name).group_by(&:name)
209
- grouped_specs.each_with_index do |(name, gem_versions), index|
210
- Utils.logger.info("[#{index + 1}/#{grouped_specs.size}]: Caching /api/v1/dependencies/#{name}")
211
-
212
- gem_versions =
213
- gem_versions.sort do |a, b|
214
- a.version <=> b.version
215
- end
216
-
217
- cg = []
218
- Parallel.each_with_index(
219
- gem_versions,
220
- in_threads: Utils.configuration.update_thread_count
221
- ) do |spec, index2|
222
- next if spec.nil?
223
-
224
- dependencies = spec.dependencies.select do |d|
225
- d.type == :runtime
226
- end
227
-
228
- dependencies = dependencies.collect do |d|
229
- [d.name.is_a?(Array) ? d.name.first : d.name, d.requirement.to_s]
230
- end
231
-
232
- cg[index2] =
233
- {
234
- name: spec.name,
235
- number: spec.version.to_s,
236
- platform: spec.platform,
237
- dependencies: dependencies
238
- }
239
- end
240
-
241
- Tempfile.create("api_v1_dependencies_#{name}.list") do |f|
242
- f.write Marshal.dump(cg)
243
- f.rewind
244
-
245
- FileUtils.cp(
246
- f.path,
247
- File.join(
248
- @api_v1_dependencies_dir,
249
- "#{name}.#{Digest::MD5.file(f.path).hexdigest}.#{Digest::SHA256.file(f.path).hexdigest}.list"
250
- )
251
- )
252
- end
253
- end
254
- end
255
-
256
145
  ##
257
146
  # Cache compact_index endpoint /names
258
147
  # Report all gems with versions available. Does not require opening spec files.
@@ -323,7 +212,7 @@ module Gemirro
323
212
  end
324
213
 
325
214
  Tempfile.create('versions.list') do |f|
326
- previous_versions_file = Dir.glob(File.join(@dest_directory, 'versions*.list')).last
215
+ previous_versions_file = Dir.glob(File.join(@dest_directory, 'versions.*.*.list')).last
327
216
 
328
217
  if partial && previous_versions_file
329
218
  versions_file = CompactIndex::VersionsFile.new(previous_versions_file)
@@ -336,7 +225,7 @@ module Gemirro
336
225
  f.write CompactIndex.versions(versions_file, cg)
337
226
  f.rewind
338
227
 
339
- FileUtils.rm_rf(Dir.glob(File.join(@dest_directory, 'versions*.list')))
228
+ FileUtils.rm_rf(Dir.glob(File.join(@dest_directory, 'versions.*.*.list')))
340
229
 
341
230
  FileUtils.cp(
342
231
  f.path,
@@ -359,7 +248,7 @@ module Gemirro
359
248
  # @return nil
360
249
  #
361
250
  def build_compact_index_infos(specs, partial = false)
362
- FileUtils.mkdir_p(@infos_dir)
251
+ FileUtils.mkdir_p(@infos_dir, verbose: verbose)
363
252
 
364
253
  if partial
365
254
  specs.collect(&:name).uniq do |name|
@@ -468,13 +357,8 @@ module Gemirro
468
357
  next
469
358
  end
470
359
 
471
- if ::Gem::VERSION >= '2.5.0'
472
- spec.abbreviate
473
- spec.sanitize
474
- else
475
- abbreviate(spec)
476
- sanitize(spec)
477
- end
360
+ spec.abbreviate
361
+ spec.sanitize
478
362
 
479
363
  spec
480
364
  rescue SignalException
@@ -507,14 +391,15 @@ module Gemirro
507
391
  indexed_gemfiles = Dir.glob('*.gemspec.rz', base: @quick_marshal_dir_base).collect { |x| x.gsub(/spec.rz$/, '') }
508
392
 
509
393
  @updated_gems = []
394
+
510
395
  # detect files manually added to public/gems
511
396
  @updated_gems += (present_gemfiles - indexed_gemfiles).collect { |x| File.join(@dest_directory, 'gems', x) }
512
397
  # detect files manually deleted from public/gems
513
398
  @updated_gems += (indexed_gemfiles - present_gemfiles).collect { |x| File.join(@dest_directory, 'gems', x) }
514
399
 
515
- specs_mtime =
400
+ versions_mtime =
516
401
  begin
517
- File.stat(@dest_specs_index).mtime
402
+ File.stat(Dir.glob(File.join(@dest_directory, 'versions*.list')).last).mtime
518
403
  rescue StandardError
519
404
  Time.at(0)
520
405
  end
@@ -525,7 +410,7 @@ module Gemirro
525
410
  gem_file_list.select do |gem|
526
411
  gem_mtime = File.stat(gem).mtime
527
412
  newest_mtime = gem_mtime if gem_mtime > newest_mtime
528
- gem_mtime > specs_mtime
413
+ gem_mtime > versions_mtime
529
414
  end
530
415
 
531
416
  @updated_gems.uniq!
@@ -538,7 +423,7 @@ module Gemirro
538
423
  specs = map_gems_to_specs(@updated_gems)
539
424
 
540
425
  # specs only includes latest discovered files.
541
- # /info/[gemname] and /api/v1/dependencies can not be rebuilt
426
+ # /info/[gemname] can not be rebuilt
542
427
  # incrementally, so retrive specs for all versions of these gems.
543
428
  gem_name_updates = specs.collect(&:name).uniq
544
429
  u2 =
@@ -549,108 +434,24 @@ module Gemirro
549
434
  Utils.logger.info('Reloading for /info/[gemname]')
550
435
  version_specs = map_gems_to_specs(u2)
551
436
 
552
- prerelease, released = specs.partition { |s| s.version.prerelease? }
553
-
554
437
  ::Gem::Specification.dirs = []
555
438
  ::Gem::Specification.all = *specs
556
- files =
557
- if ::Gem::VERSION >= '2.5.0'
558
- build_marshal_gemspecs specs
559
- else
560
- build_marshal_gemspecs
561
- end
562
-
563
- ::Gem.time('Updated indexes') do
564
- update_specs_index(
565
- released,
566
- @dest_specs_index,
567
- @specs_index
568
- )
569
- update_specs_index(
570
- released,
571
- @dest_latest_specs_index,
572
- @latest_specs_index
573
- )
574
- update_specs_index(
575
- prerelease,
576
- @dest_prerelease_specs_index,
577
- @prerelease_specs_index
578
- )
579
- end
580
-
581
- if ::Gem::VERSION >= '2.5.0'
582
- compress_indices
583
- else
584
- compress_indicies
585
- end
439
+ build_marshal_gemspecs specs
586
440
 
587
- build_api_v1_dependencies(version_specs, true)
588
-
589
- if @build_compact
590
- build_compact_index_names
591
- build_compact_index_infos(version_specs, true)
592
- build_compact_index_versions(specs, true)
593
- end
594
-
595
- Utils.logger.info("Updating production dir #{@dest_directory}") if verbose
596
- files << @specs_index
597
- files << "#{@specs_index}.gz"
598
- files << @latest_specs_index
599
- files << "#{@latest_specs_index}.gz"
600
- files << @prerelease_specs_index
601
- files << "#{@prerelease_specs_index}.gz"
602
-
603
- files.each do |path|
604
- file = path.sub(%r{^#{Regexp.escape @directory}/?}, '')
605
-
606
- if ["#{@specs_index}.gz", "#{@latest_specs_index}.gz", "#{@prerelease_specs_index}.gz"].include?(path)
607
- res = build_zlib_file(file, File.join(@directory, file), File.join(@dest_directory, file))
608
- next unless res
609
- else
610
- FileUtils.mv(
611
- File.join(@directory, file),
612
- File.join(@dest_directory, file),
613
- verbose: verbose,
614
- force: true
615
- )
616
- end
617
-
618
- File.utime(newest_mtime, newest_mtime, File.join(@dest_directory, file))
619
- end
620
- end
621
-
622
- def build_zlib_file(file, src_name, dst_name, from_source = false)
623
- content = Marshal.load(Zlib::GzipReader.open(src_name).read)
624
- create_zlib_file("#{dst_name}.local", content)
625
-
626
- return false if @only_origin
627
-
628
- if from_source
629
- source_content = download_from_source(file)
630
- source_content = Marshal.load(
631
- Zlib::GzipReader.new(
632
- StringIO.new(source_content)
633
- ).read
634
- )
635
- else
636
- source_content = Marshal.load(Zlib::GzipReader.open(dst_name).read)
637
- end
638
-
639
- return false if source_content.nil?
640
-
641
- new_content = source_content.concat(content).uniq
642
- create_zlib_file(dst_name, new_content)
441
+ build_compact_index_infos(version_specs, true)
442
+ build_compact_index_versions(specs, true)
443
+ build_compact_index_names
643
444
  end
644
445
 
645
- def create_zlib_file(dst_name, content)
646
- Tempfile.create(File.basename(dst_name)) do |f|
647
- gzf = Zlib::GzipWriter.new(f)
648
- gzf.write(Marshal.dump(content))
649
- gzf.close
446
+ def download_source_versions
447
+ Tempfile.create(File.basename(Gemirro.configuration.versions_file)) do |f|
448
+ f.write(download_from_source('versions'))
449
+ f.close
650
450
 
451
+ FileUtils.rm(Gemirro.configuration.versions_file, verbose: verbose)
651
452
  FileUtils.cp(
652
453
  f.path,
653
- dst_name,
454
+ Gemirro.configuration.versions_file,
654
455
  verbose: verbose
655
456
  )
656
457
  end
@@ -4,6 +4,7 @@ require 'sinatra/base'
4
4
  require 'thin'
5
5
  require 'uri'
6
6
  require 'addressable/uri'
7
+ require 'base64'
7
8
 
8
9
  module Gemirro
9
10
  ##
@@ -78,39 +79,6 @@ module Gemirro
78
79
  erb(:index, {}, gems: Utils.gems_collection)
79
80
  end
80
81
 
81
- ##
82
- # Return gem dependencies as marshaled binary
83
- #
84
- # @return [nil]
85
- #
86
- get '/api/v1/dependencies' do
87
- content_type 'application/octet-stream'
88
- if params[:gems].to_s != '' && params[:gems].to_s.split(',').any?
89
- Marshal.dump(dependencies_loader(params[:gems].to_s.split(',')))
90
- else
91
- 200
92
- end
93
- end
94
-
95
- ##
96
- # Return gem dependencies as json
97
- #
98
- # @return [nil]
99
- #
100
- get '/api/v1/dependencies.json' do
101
- content_type 'application/json'
102
-
103
- return '[]' unless params[:gems]
104
-
105
- gem_names = params[:gems].to_s
106
- .split(',')
107
- .map(&:strip)
108
- .reject(&:empty?)
109
- return '[]' if gem_names.empty?
110
-
111
- JSON.dump(dependencies_loader(gem_names))
112
- end
113
-
114
82
  ##
115
83
  # compact_index, Return list of available gem names
116
84
  #
@@ -120,10 +88,10 @@ module Gemirro
120
88
  content_type 'text/plain'
121
89
 
122
90
  content_path = Dir.glob(File.join(Gemirro.configuration.destination, 'names.*.*.list')).last
123
- _, etag, repr_digest, _ = content_path.split('.', -4)
91
+ _, etag, repr_digest, _ = File.basename(content_path).split('.')
124
92
 
125
- headers 'etag' => etag
126
- headers 'repr-digest' => %(sha-256="#{repr_digest}")
93
+ headers 'etag' => %("#{etag}")
94
+ headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
127
95
  send_file content_path
128
96
  end
129
97
 
@@ -136,10 +104,10 @@ module Gemirro
136
104
  content_type 'text/plain'
137
105
 
138
106
  content_path = Dir.glob(File.join(Utils.configuration.destination, 'versions.*.*.list')).last
139
- _, etag, repr_digest, _ = content_path.split('.', -4)
107
+ _, etag, repr_digest, _ = File.basename(content_path).split('.')
140
108
 
141
- headers 'etag' => etag
142
- headers 'repr-digest' => %(sha-256="#{repr_digest}")
109
+ headers 'etag' => %("#{etag}")
110
+ headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
143
111
  send_file content_path
144
112
  end
145
113
 
@@ -155,10 +123,10 @@ module Gemirro
155
123
  content_type 'text/plain'
156
124
 
157
125
  content_path = Dir.glob(File.join(Utils.configuration.destination, 'info', "#{params[:gemname]}.*.*.list")).last
158
- _, etag, repr_digest, _ = content_path.split('.', -4)
126
+ _, etag, repr_digest, _ = File.basename(content_path).split('.')
159
127
 
160
- headers 'etag' => etag
161
- headers 'repr-digest' => %(sha-256="#{repr_digest}")
128
+ headers 'etag' => %("#{etag}")
129
+ headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
162
130
  send_file content_path
163
131
  end
164
132
 
@@ -33,23 +33,10 @@ module Gemirro
33
33
  #
34
34
  def fetch_versions
35
35
  Utils.logger.info(
36
- "Fetching #{Configuration.versions_file} on #{@name} (#{@host})"
36
+ "Fetching versions on #{@name} (#{@host})"
37
37
  )
38
38
 
39
- Http.get("#{host}/#{Configuration.versions_file}").body
40
- end
41
-
42
- ##
43
- # Fetches a list of all the available Gems and their versions.
44
- #
45
- # @return [String]
46
- #
47
- def fetch_prerelease_versions
48
- Utils.logger.info(
49
- "Fetching #{Configuration.prerelease_versions_file} " \
50
- "on #{@name} (#{@host})"
51
- )
52
- Http.get("#{host}/#{Configuration.prerelease_versions_file}").body
39
+ Http.get("#{host}/versions").body
53
40
  end
54
41
 
55
42
  ##
data/lib/gemirro/utils.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'gemirro/gem_version'
4
+
3
5
  module Gemirro
4
6
  ##
5
7
  # The Utils class is responsible for executing specific traitments
@@ -33,12 +35,10 @@ module Gemirro
33
35
  @gems_collection ||= { files: {}, values: nil }
34
36
 
35
37
  file_paths =
36
- %i[specs prerelease_specs].collect do |specs_file_type|
37
- File.join(
38
- configuration.destination,
39
- "#{specs_file_type}.#{Gemirro::Configuration.marshal_version}.gz.local"
40
- )
41
- end
38
+ Dir.glob(File.join(
39
+ Gemirro.configuration.destination,
40
+ 'versions.*.*.list'
41
+ ))
42
42
 
43
43
  has_file_changed =
44
44
  @gems_collection[:files] != file_paths.each_with_object({}) do |f, r|
@@ -50,12 +50,22 @@ module Gemirro
50
50
 
51
51
  gems = []
52
52
 
53
- # parallel is not for mtime, it's for the Marshal.
54
- Parallel.map(file_paths, in_threads: Utils.configuration.update_thread_count) do |file_path|
55
- next unless File.exist?(file_path)
53
+ CompactIndex::VersionsFile.new(file_paths.last).contents.each_line.with_index do |line, index|
54
+ next if index < 2
55
+
56
+ gem_name = line.split[0]
57
+ versions = line.split[1..-2].collect { |x| x.split(',') }.flatten # All except first and last
56
58
 
57
- gems.concat(Marshal.load(Zlib::GzipReader.open(file_path).read))
58
- @gems_collection[:files][file_path] = File.mtime(file_path)
59
+ versions.each do |ver|
60
+ version, platform =
61
+ if ver.include?('-')
62
+ ver.split('-', 2)
63
+ else
64
+ [ver, 'ruby']
65
+ end
66
+
67
+ gems << Gemirro::GemVersion.new(gem_name, version, platform)
68
+ end
59
69
  end
60
70
 
61
71
  @gems_collection[:values] = GemVersionCollection.new(gems)
@@ -187,7 +197,7 @@ module Gemirro
187
197
  # @return [Indexer]
188
198
  #
189
199
  def self.update_indexes
190
- indexer = Gemirro::Indexer.new(Utils.configuration.destination)
200
+ indexer = Gemirro::Indexer.new
191
201
  indexer.only_origin = true
192
202
  indexer.ui = ::Gem::SilentUI.new
193
203
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Gemirro Version
4
4
  module Gemirro
5
- VERSION = '1.6.0'
5
+ VERSION = '2.0.0'
6
6
  end
@@ -22,29 +22,24 @@ module Gemirro
22
22
  # @return [Gemirro::VersionsFile]
23
23
  #
24
24
  def fetch
25
- VersionsFile.load(
26
- read_file(Configuration.versions_file),
27
- read_file(Configuration.prerelease_versions_file, true)
28
- )
25
+ return unless Gemirro.configuration.versions_file
26
+
27
+ VersionsFile.new(read_file(Gemirro.configuration.versions_file))
29
28
  end
30
29
 
31
30
  ##
32
31
  # Read file if exists otherwise download its from source
33
32
  #
34
33
  # @param [String] file name
35
- # @param [TrueClass|FalseClass] prerelease Is prerelease or not
36
34
  #
37
- def read_file(file, prerelease = false)
38
- destination = Gemirro.configuration.destination
39
- file_dst = File.join(destination, file)
40
- unless File.exist?(file_dst)
35
+ def read_file(file)
36
+ unless File.exist?(file)
41
37
  throw 'No source defined' unless @source
42
38
 
43
- File.write(file_dst, @source.fetch_versions) unless prerelease
44
- File.write(file_dst, @source.fetch_prerelease_versions) if prerelease
39
+ File.write(file, @source.fetch_versions)
45
40
  end
46
41
 
47
- File.read(file_dst)
42
+ File.read(file)
48
43
  end
49
44
  end
50
45
  end
@@ -11,36 +11,24 @@ module Gemirro
11
11
  # @return [Hash]
12
12
  #
13
13
  class VersionsFile
14
- attr_reader :versions, :versions_hash
14
+ attr_reader :versions_string, :versions_hash
15
15
 
16
16
  ##
17
17
  # Reads the versions file from the specified String.
18
18
  #
19
- # @param [String] spec_content
20
- # @param [String] prerelease_content
19
+ # @param [String] versions_content
21
20
  # @return [Gemirro::VersionsFile]
22
21
  #
23
- def self.load(spec_content, prerelease_content)
24
- buffer = StringIO.new(spec_content)
25
- reader = Zlib::GzipReader.new(buffer)
26
- versions = Marshal.load(reader.read)
27
-
28
- buffer = StringIO.new(prerelease_content)
29
- reader = Zlib::GzipReader.new(buffer)
30
- versions.concat(Marshal.load(reader.read))
31
-
32
- instance = new(versions)
33
-
34
- reader.close
35
-
36
- instance
37
- end
38
22
 
39
23
  ##
40
- # @param [Array] versions
24
+ # @param [String] versions
41
25
  #
42
- def initialize(versions)
43
- @versions = versions
26
+ def initialize(versions_string)
27
+ unless versions_string.is_a? String
28
+ throw "#{versions_string.class} is wrong format, expect String; #{versions_string.inspect}"
29
+ end
30
+
31
+ @versions_string = versions_string
44
32
  @versions_hash = create_versions_hash
45
33
  end
46
34
 
@@ -53,10 +41,24 @@ module Gemirro
53
41
  def create_versions_hash
54
42
  hash = Hash.new { |h, k| h[k] = [] }
55
43
 
56
- versions.each do |version|
57
- hash[version[0]] << version
58
- end
44
+ versions_string.each_line.with_index do |line, index|
45
+ next if index < 2
59
46
 
47
+ parts = line.split
48
+ gem_name = parts[0]
49
+ parts[-1]
50
+ versions = parts[1..-2].collect { |x| x.split(',') }.flatten # All except first and last
51
+
52
+ versions.each do |ver|
53
+ version, platform =
54
+ if ver.include?('-')
55
+ ver.split('-', 2)
56
+ else
57
+ [ver, 'ruby']
58
+ end
59
+ hash[gem_name] << [gem_name, ::Gem::Version.new(version), platform]
60
+ end
61
+ end
60
62
  hash
61
63
  end
62
64
 
@@ -39,8 +39,8 @@ module Gemirro
39
39
  expect(Configuration.marshal_identifier).to match(/Marshal\.(\d+)\.(\d+)/)
40
40
  end
41
41
 
42
- it 'should return versions file' do
43
- expect(Configuration.versions_file).to match(/specs\.(\d+)\.(\d+).gz/)
42
+ it 'should error on version since no source is defined' do
43
+ expect(Gemirro.configuration.versions_file).to match(nil)
44
44
  end
45
45
 
46
46
  it 'should return marshal file' do
@@ -84,5 +84,12 @@ module Gemirro
84
84
  expect(result.host).to eq('https://rubygems.org')
85
85
  expect(result.name).to eq('rubygems')
86
86
  end
87
+
88
+
89
+ it 'should return versions file with source' do
90
+ result = @config.define_source('RubyGems', 'https://rubygems.org') do
91
+ end
92
+ expect(@config.versions_file).to match(/rubygems_org_versions$/)
93
+ end
87
94
  end
88
95
  end
@@ -12,7 +12,7 @@ module Gemirro
12
12
 
13
13
  before(:each) do
14
14
  @source = Source.new('RubyGems', 'https://rubygems.org')
15
- @versions_file = VersionsFile.new(['0.0.1', '0.0.2'])
15
+ @versions_file = VersionsFile.new(%(created_at: 2025-04-24T03:46:59Z\n---\nrack 3.0.0,3.0.1 d545a45462d63b1b4865bbb89a109366))
16
16
  @fetcher = GemsFetcher.new(@source, @versions_file)
17
17
  Gemirro.configuration.ignored_gems.clear
18
18
  end
@@ -24,7 +24,7 @@ module Gemirro
24
24
 
25
25
  dir = MirrorDirectory.new('/tmp')
26
26
  dir.add_directory('test')
27
- indexer = Indexer.new('/tmp/test')
27
+ indexer = Indexer.new
28
28
 
29
29
  Struct.new('HttpGet', :code, :body)
30
30
  http_get = Struct::HttpGet.new(200, 'bad')
@@ -43,7 +43,7 @@ module Gemirro
43
43
  allow(::Gem.configuration).to receive(:really_verbose)
44
44
  .once.and_return(true)
45
45
 
46
- indexer = Indexer.new('/tmp/test')
46
+ indexer = Indexer.new
47
47
  indexer.quick_marshal_dir = '/tmp/gem_generate_index/quick/Marshal.4.8'
48
48
  indexer.dest_directory = '/tmp/test'
49
49
  indexer.directory = '/tmp/gem_generate_index'
@@ -97,7 +97,7 @@ module Gemirro
97
97
  end
98
98
 
99
99
  it 'should build indices' do
100
- indexer = Indexer.new('/')
100
+ indexer = Indexer.new
101
101
  dir = MirrorDirectory.new('/')
102
102
  dir.add_directory('gems')
103
103
  dir.add_directory('quick')
@@ -140,7 +140,7 @@ module Gemirro
140
140
  end
141
141
 
142
142
  it 'should update index and exit ruby gems' do
143
- indexer = Indexer.new('/')
143
+ indexer = Indexer.new
144
144
  MirrorDirectory.new('/')
145
145
  MirrorFile.new('/specs.4.8').write('')
146
146
  expect { indexer.update_index }.to raise_error(::Gem::SystemExitException)
@@ -152,7 +152,7 @@ module Gemirro
152
152
  dir.add_directory('test/gems')
153
153
  dir.add_directory('test/quick')
154
154
 
155
- indexer = Indexer.new('/tmp/test')
155
+ indexer = Indexer.new
156
156
  indexer.quick_marshal_dir = '/tmp/gem_generate_index/quick/Marshal.4.8'
157
157
  indexer.dest_directory = '/tmp/test'
158
158
  indexer.directory = '/tmp/gem_generate_index'
@@ -1,6 +1,9 @@
1
1
  require 'rack/test'
2
2
  require 'json'
3
3
  require 'parallel'
4
+ require 'sinatra/base'
5
+ require 'thin'
6
+ require 'base64'
4
7
  require 'gemirro/utils'
5
8
  require 'gemirro/mirror_directory'
6
9
  require 'gemirro/mirror_file'
@@ -37,7 +40,6 @@ module Gemirro
37
40
  Utils.instance_eval('@gems_source_collection = nil')
38
41
  FakeFS::FileSystem.clone(Gemirro::Configuration.views_directory)
39
42
  allow_any_instance_of(Indexer).to receive(:compress_indices)
40
- allow_any_instance_of(Indexer).to receive(:compress_indicies)
41
43
  allow_any_instance_of(Indexer).to receive(:rand).and_return('0')
42
44
 
43
45
  source = Source.new('Rubygems', 'https://rubygems.org')
@@ -51,6 +53,9 @@ module Gemirro
51
53
  .and_return(nil)
52
54
  .and_yield(@fake_logger)
53
55
 
56
+
57
+ MirrorFile.new('/var/www/gemirro/versions.md5.aaa256.list').write('created_at: 2025-01-01T00:00:00Z\n---\nvolay 0.1.0\n')
58
+
54
59
  get '/'
55
60
  expect(last_response).to be_ok
56
61
  end
@@ -68,14 +73,7 @@ module Gemirro
68
73
  end
69
74
 
70
75
  it 'should display gem specifications' do
71
- marshal_dump = Marshal.dump([['volay',
72
- ::Gem::Version.create('0.1.0'),
73
- 'ruby']])
74
-
75
- MirrorFile.new('/var/www/gemirro/specs.4.8.gz.local').write(Marshal.dump({}))
76
-
77
-
78
- allow(Zlib::GzipReader).to receive(:open).and_return(double(read: marshal_dump))
76
+ MirrorFile.new('/var/www/gemirro/versions.md5.aaa256.list').write(%(created_at: 2025-01-01T00:00:00Z\n---\nvolay 0.1.0 checksum))
79
77
 
80
78
  get '/gem/volay'
81
79
 
@@ -84,48 +82,41 @@ module Gemirro
84
82
  end
85
83
 
86
84
  it 'responds to compact_index /names' do
87
- MirrorFile.new('/var/www/gemirro/names.md5.sha256.list').write('---\n- volay\n')
85
+ MirrorFile.new('/var/www/gemirro/names.md5.aaa256.list').write(%(---\nvolay))
88
86
 
89
87
  get '/names'
90
88
  expect(last_response.status).to eq(200)
91
89
  expect(last_response).to be_ok
92
- expect(last_response.body).to eq('---\n- volay\n')
93
- expect(last_response.headers['etag']).to eq("md5")
94
- expect(last_response.headers['repr-digest']).to eq('sha-256="sha256"')
90
+
91
+
92
+ expect(last_response.body).to eq(%(---\nvolay))
93
+ expect(last_response.headers['etag']).to eq('"md5"')
94
+ expect(last_response.headers['repr-digest']).to eq(%(sha-256=#{Base64.strict_encode64(['aaa256'].pack('H*'))}))
95
95
  end
96
96
 
97
97
  it 'responds to compact_index /info/[gemname]' do
98
- marshal_dump = Marshal.dump([['volay',
99
- ::Gem::Version.create('0.1.0'),
100
- 'ruby']])
101
-
102
- MirrorFile.new('/var/www/gemirro/specs.4.8.gz.local').write(Marshal.dump({}))
103
-
104
- allow(Zlib::GzipReader).to receive(:open).and_return(double(read: marshal_dump))
105
-
106
-
107
98
  MirrorDirectory.new('/var/www/gemirro/info')
108
- MirrorFile.new('/var/www/gemirro/info/volay.md5.sha256.list').write('---\n 0.1.0 |checksum:sha256\n')
99
+ MirrorFile.new('/var/www/gemirro/info/volay.md5.aaa256.list').write('---\n 0.1.0 |checksum:sha256\n')
109
100
 
110
101
 
111
102
  get '/info/volay'
112
103
  expect(last_response.status).to eq(200)
113
104
  expect(last_response).to be_ok
114
105
  expect(last_response.body).to eq('---\n 0.1.0 |checksum:sha256\n')
115
- expect(last_response.headers['etag']).to eq("md5")
116
- expect(last_response.headers['repr-digest']).to eq('sha-256="sha256"')
106
+ expect(last_response.headers['etag']).to eq('"md5"')
107
+ expect(last_response.headers['repr-digest']).to eq(%(sha-256=#{Base64.strict_encode64(['aaa256'].pack('H*'))}))
117
108
  end
118
109
 
119
110
 
120
111
  it 'responds to compact_index /versions' do
121
- MirrorFile.new('/var/www/gemirro/versions.md5.sha256.list').write('created_at: 2025-01-01T00:00:00Z\m---\nvolay 0.1.0\n')
112
+ MirrorFile.new('/var/www/gemirro/versions.md5.aaa256.list').write(%(created_at: 2025-01-01T00:00:00Z\n---\nvolay 0.1.0))
122
113
 
123
114
  get '/versions'
124
115
  expect(last_response.status).to eq(200)
125
116
  expect(last_response).to be_ok
126
- expect(last_response.body).to eq('created_at: 2025-01-01T00:00:00Z\m---\nvolay 0.1.0\n')
127
- expect(last_response.headers['etag']).to eq("md5")
128
- expect(last_response.headers['repr-digest']).to eq('sha-256="sha256"')
117
+ expect(last_response.body).to eq(%(created_at: 2025-01-01T00:00:00Z\n---\nvolay 0.1.0))
118
+ expect(last_response.headers['etag']).to eq('"md5"')
119
+ expect(last_response.headers['repr-digest']).to eq(%(sha-256=#{Base64.strict_encode64(['aaa256'].pack('H*'))}))
129
120
  end
130
121
 
131
122
 
@@ -214,31 +205,20 @@ module Gemirro
214
205
  end
215
206
 
216
207
  context 'dependencies' do
217
- it 'should retrieve nothing' do
208
+ it 'should retrieve nothing and give 404' do
218
209
  get '/api/v1/dependencies'
219
- expect(last_response.headers['Content-Type'])
220
- .to eq('application/octet-stream')
221
- expect(last_response.body).to eq('')
222
- expect(last_response).to be_ok
210
+ expect(last_response.status).to eq(404)
211
+ expect(last_response).to_not be_ok
223
212
  end
224
213
 
225
- it 'should retrieve empty json' do
226
- get '/api/v1/dependencies.json'
227
- expect(last_response.headers['Content-Type'])
228
- .to eq('application/json')
229
- expect(last_response.body).to eq('[]')
230
- expect(last_response).to be_ok
231
- end
232
214
 
233
- it 'should retrieve empty json when gem was not found' do
215
+ it 'should retrieve nothing and give 404' do
234
216
  get '/api/v1/dependencies.json?gems=gemirro'
235
- expect(last_response.headers['Content-Type'])
236
- .to eq('application/json')
237
- expect(last_response.body).to eq('[]')
238
- expect(last_response).to be_ok
217
+ expect(last_response.status).to eq(404)
218
+ expect(last_response).to_not be_ok
239
219
  end
240
220
 
241
- it 'should retrieve json when gem was found' do
221
+ it 'should retrieve nothing and give 404' do
242
222
  MirrorDirectory.new('/var/www/gemirro')
243
223
  .add_directory('quick/Marshal.4.8')
244
224
  # rubocop:disable Metrics/LineLength
@@ -295,11 +275,8 @@ module Gemirro
295
275
  allow(Utils).to receive(:gems_collection)
296
276
  .and_return(collection)
297
277
  get '/api/v1/dependencies.json?gems=volay'
298
- expect(last_response.headers['Content-Type'])
299
- .to eq('application/json')
300
-
301
- expect(last_response.body).to match(/"name":"volay"/)
302
- expect(last_response).to be_ok
278
+ expect(last_response.status).to eq(404)
279
+ expect(last_response).to_not be_ok
303
280
  end
304
281
  end
305
282
  end
@@ -21,20 +21,11 @@ module Gemirro
21
21
  Struct.new('FetchVersions', :body)
22
22
  result = Struct::FetchVersions.new(true)
23
23
  allow(Http).to receive(:get).once.with(
24
- "https://rubygems.org/#{Configuration.versions_file}"
24
+ "https://rubygems.org/versions"
25
25
  ).and_return(result)
26
26
  expect(@source.fetch_versions).to be_truthy
27
27
  end
28
28
 
29
- it 'should fetch prereleases versions' do
30
- Struct.new('FetchPrereleaseVersions', :body)
31
- result = Struct::FetchPrereleaseVersions.new(true)
32
- allow(Http).to receive(:get).once.with(
33
- "https://rubygems.org/#{Configuration.prerelease_versions_file}"
34
- ).and_return(result)
35
- expect(@source.fetch_prerelease_versions).to be_truthy
36
- end
37
-
38
29
  it 'should fetch gem' do
39
30
  Struct.new('FetchGem', :body)
40
31
  result = Struct::FetchGem.new(true)
@@ -17,10 +17,9 @@ module Gemirro
17
17
 
18
18
  it 'should fetch versions' do
19
19
  allow(@source).to receive(:fetch_versions).once.and_return([])
20
- allow(@source).to receive(:fetch_prerelease_versions).once.and_return([])
21
- allow(VersionsFile).to receive(:load).with('nothing', 'nothing')
22
- allow(File).to receive(:write).twice
23
- allow(File).to receive(:read).twice.and_return('nothing')
20
+ allow(VersionsFile).to receive(:load).with('nothing')
21
+ allow(File).to receive(:write).once
22
+ allow(File).to receive(:read).once.and_return('nothing')
24
23
  expect(@fetcher.fetch).to be_nil
25
24
  end
26
25
  end
@@ -7,44 +7,20 @@ module Gemirro
7
7
  describe 'VersionsFile' do
8
8
  include FakeFS::SpecHelpers
9
9
 
10
- it 'should be initialized' do
11
- @versions_file = VersionsFile.new([['gemirro', '0.0.1'],
12
- ['gemirro', '0.0.2']])
13
- expect(@versions_file.versions).to eq([['gemirro', '0.0.1'],
14
- ['gemirro', '0.0.2']])
15
- expect(@versions_file.versions_hash).to eq(
16
- 'gemirro' => [
17
- ['gemirro', '0.0.1'],
18
- ['gemirro', '0.0.2']
19
- ]
20
- )
21
- end
22
-
23
10
  it 'should load versions file' do
24
- spec = StringIO.new('w')
25
- w_gz = Zlib::GzipWriter.new(spec)
26
- w_gz.write(Marshal.dump([['gemirro', '0.0.1'],
27
- ['gemirro', '0.0.2']]))
28
- w_gz.close
29
- prerelease = StringIO.new('w')
30
- w_gz = Zlib::GzipWriter.new(prerelease)
31
- w_gz.write(Marshal.dump([['gemirro', '0.0.1.alpha1'],
32
- ['gemirro', '0.0.2.alpha2']]))
33
- w_gz.close
11
+ spec = %(created_at: 2025-01-01T00:00:00Z\n---\ngemirro 0.0.1.alpha1,0.0.1,0.0.2.alpha2,0.0.2 checksum)
34
12
 
35
- result = VersionsFile.load(spec.string, prerelease.string)
13
+ result = VersionsFile.new(spec)
36
14
  expect(result).to be_a(VersionsFile)
37
15
 
38
- expect(result.versions).to eq([['gemirro', '0.0.1'],
39
- ['gemirro', '0.0.2'],
40
- ['gemirro', '0.0.1.alpha1'],
41
- ['gemirro', '0.0.2.alpha2']])
16
+ expect(result.versions_string).to eq(%(created_at: 2025-01-01T00:00:00Z\n---\ngemirro 0.0.1.alpha1,0.0.1,0.0.2.alpha2,0.0.2 checksum))
17
+
42
18
  expect(result.versions_hash).to eq(
43
19
  'gemirro' => [
44
- ['gemirro', '0.0.1'],
45
- ['gemirro', '0.0.2'],
46
- ['gemirro', '0.0.1.alpha1'],
47
- ['gemirro', '0.0.2.alpha2']
20
+ ['gemirro', ::Gem::Version.new('0.0.1.alpha1'), 'ruby'],
21
+ ['gemirro', ::Gem::Version.new('0.0.1'), 'ruby'],
22
+ ['gemirro', ::Gem::Version.new('0.0.2.alpha2'), 'ruby'],
23
+ ['gemirro', ::Gem::Version.new('0.0.2'), 'ruby']
48
24
  ]
49
25
  )
50
26
  end
data/template/config.rb CHANGED
@@ -60,6 +60,6 @@ Gemirro.configuration.configure do
60
60
  # All gem in the block will be downloaded with the update command.
61
61
  # Other gems will be downloaded with the server.
62
62
  define_source 'rubygems', 'https://rubygems.org' do
63
- gem 'rack', '>= 1.0.0'
63
+ gem 'rack', '>= 3.0.0'
64
64
  end
65
65
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gemirro
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre Rambaud
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-04 00:00:00.000000000 Z
11
+ date: 2025-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -290,9 +290,6 @@ files:
290
290
  - template/logs/.gitkeep
291
291
  - template/public/dist/css/gemirro.css
292
292
  - template/public/gems/.gitkeep
293
- - template/public/latest_specs.4.8
294
- - template/public/prerelease_specs.4.8
295
- - template/public/specs.4.8
296
293
  - views/gem.erb
297
294
  - views/index.erb
298
295
  - views/layout.erb
Binary file
Binary file
Binary file