gemirro 1.4.0 → 1.6.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +29 -0
  3. data/.rubocop.yml +6 -3
  4. data/Gemfile +9 -0
  5. data/Gemfile.lock +126 -0
  6. data/MANIFEST +6 -10
  7. data/README.md +1 -1
  8. data/bin/gemirro +7 -1
  9. data/gemirro.gemspec +9 -9
  10. data/lib/gemirro/cli/index.rb +12 -3
  11. data/lib/gemirro/cli/init.rb +6 -1
  12. data/lib/gemirro/cli/server.rb +2 -4
  13. data/lib/gemirro/cli/update.rb +6 -0
  14. data/lib/gemirro/configuration.rb +1 -1
  15. data/lib/gemirro/gems_fetcher.rb +5 -10
  16. data/lib/gemirro/http.rb +6 -6
  17. data/lib/gemirro/indexer.rb +397 -90
  18. data/lib/gemirro/mirror_file.rb +1 -0
  19. data/lib/gemirro/server.rb +73 -160
  20. data/lib/gemirro/source.rb +2 -2
  21. data/lib/gemirro/utils.rb +123 -68
  22. data/lib/gemirro/version.rb +1 -1
  23. data/lib/gemirro/versions_fetcher.rb +6 -2
  24. data/lib/gemirro.rb +1 -1
  25. data/spec/gemirro/http_spec.rb +83 -24
  26. data/spec/gemirro/indexer_spec.rb +3 -0
  27. data/spec/gemirro/server_spec.rb +76 -47
  28. data/template/config.rb +6 -0
  29. data/template/public/dist/css/gemirro.css +25 -1
  30. data/template/public/latest_specs.4.8 +0 -0
  31. data/template/public/prerelease_specs.4.8 +0 -0
  32. data/template/public/specs.4.8 +0 -0
  33. data/views/gem.erb +46 -37
  34. data/views/index.erb +41 -33
  35. data/views/layout.erb +5 -25
  36. data/views/not_found.erb +4 -4
  37. metadata +72 -89
  38. data/lib/gemirro/cache.rb +0 -115
  39. data/spec/gemirro/cache_spec.rb +0 -32
  40. data/template/public/dist/css/bootstrap.min.css +0 -7
  41. data/template/public/dist/fonts/glyphicons-halflings-regular.eot +0 -0
  42. data/template/public/dist/fonts/glyphicons-halflings-regular.svg +0 -288
  43. data/template/public/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  44. data/template/public/dist/fonts/glyphicons-halflings-regular.woff +0 -0
  45. data/template/public/dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
  46. data/template/public/dist/js/bootstrap.min.js +0 -7
@@ -19,12 +19,14 @@ module Gemirro
19
19
  # @return [Array]
20
20
  #
21
21
  class Indexer < ::Gem::Indexer
22
- attr_accessor(:files,
23
- :quick_marshal_dir,
24
- :directory,
25
- :dest_directory,
26
- :only_origin,
27
- :updated_gems)
22
+ attr_accessor(
23
+ :files,
24
+ :quick_marshal_dir,
25
+ :directory,
26
+ :dest_directory,
27
+ :only_origin,
28
+ :updated_gems
29
+ )
28
30
 
29
31
  ##
30
32
  # Create an indexer that will index the gems in +directory+.
@@ -37,34 +39,38 @@ module Gemirro
37
39
  require 'fileutils'
38
40
  require 'tmpdir'
39
41
  require 'zlib'
42
+ require 'builder/xchar'
43
+ require 'compact_index'
40
44
 
41
- unless defined?(Builder::XChar)
42
- raise 'Gem::Indexer requires that the XML Builder ' \
43
- 'library be installed:' \
44
- "\n\tgem install builder"
45
- end
46
-
47
- options = { build_modern: true }.merge options
45
+ options.merge!({ build_modern: true, build_compact: true })
48
46
 
49
47
  @build_modern = options[:build_modern]
48
+ @build_compact = options[:build_compact]
50
49
 
51
50
  @dest_directory = directory
52
- @directory = File.join(Dir.tmpdir,
53
- "gem_generate_index_#{rand(1_000_000_000)}")
51
+ @directory =
52
+ File.join(Dir.tmpdir, "gem_generate_index_#{rand(1_000_000_000)}")
54
53
 
55
54
  marshal_name = "Marshal.#{::Gem.marshal_version}"
56
55
 
57
- @master_index = File.join @directory, 'yaml'
58
- @marshal_index = File.join @directory, marshal_name
56
+ @master_index =
57
+ File.join(@directory, 'yaml')
58
+ @marshal_index =
59
+ File.join(@directory, marshal_name)
59
60
 
60
- @quick_dir = File.join @directory, 'quick'
61
- @quick_marshal_dir = File.join @quick_dir, marshal_name
62
- @quick_marshal_dir_base = File.join 'quick', marshal_name # FIX: UGH
61
+ @quick_dir = File.join(@directory, 'quick')
62
+ @quick_marshal_dir =
63
+ File.join(@quick_dir, marshal_name)
64
+ @quick_marshal_dir_base =
65
+ File.join(@dest_directory, 'quick', marshal_name) # FIX: UGH
63
66
 
64
- @quick_index = File.join @quick_dir, 'index'
65
- @latest_index = File.join @quick_dir, 'latest_index'
67
+ @quick_index =
68
+ File.join(@quick_dir, 'index')
69
+ @latest_index =
70
+ File.join(@quick_dir, 'latest_index')
66
71
 
67
- @specs_index = File.join @directory, "specs.#{::Gem.marshal_version}"
72
+ @specs_index =
73
+ File.join(@directory, "specs.#{::Gem.marshal_version}")
68
74
  @latest_specs_index =
69
75
  File.join(@directory, "latest_specs.#{::Gem.marshal_version}")
70
76
  @prerelease_specs_index =
@@ -75,6 +81,10 @@ module Gemirro
75
81
  File.join(@dest_directory, "latest_specs.#{::Gem.marshal_version}")
76
82
  @dest_prerelease_specs_index =
77
83
  File.join(@dest_directory, "prerelease_specs.#{::Gem.marshal_version}")
84
+ @infos_dir =
85
+ File.join(@dest_directory, 'info')
86
+ @api_v1_dependencies_dir =
87
+ File.join(@dest_directory, 'api', 'v1', 'dependencies')
78
88
 
79
89
  @files = []
80
90
  end
@@ -100,28 +110,24 @@ module Gemirro
100
110
 
101
111
  if files.include?(@quick_marshal_dir) && !files.include?(@quick_dir)
102
112
  files.delete @quick_marshal_dir
103
- dst_name = File.join(@dest_directory, @quick_marshal_dir_base)
104
- FileUtils.mkdir_p(File.dirname(dst_name), verbose: verbose)
105
- FileUtils.rm_rf(dst_name, verbose: verbose)
106
- FileUtils.mv(@quick_marshal_dir, dst_name,
107
- verbose: verbose, force: true)
113
+ FileUtils.mkdir_p(File.dirname(@quick_marshal_dir_base), verbose: verbose)
114
+ FileUtils.rm_rf(@quick_marshal_dir_base, verbose: verbose)
115
+ FileUtils.mv(@quick_marshal_dir, @quick_marshal_dir_base, verbose: verbose, force: true)
108
116
  end
109
117
 
110
118
  files.each do |path|
111
119
  file = path.sub(%r{^#{Regexp.escape @directory}/?}, '')
112
- src_name = File.join(@directory, file)
113
- dst_name = File.join(@dest_directory, file)
114
120
 
115
121
  if ["#{@specs_index}.gz",
116
122
  "#{@latest_specs_index}.gz",
117
123
  "#{@prerelease_specs_index}.gz"].include?(path)
118
- res = build_zlib_file(file, src_name, dst_name, true)
124
+ res = build_zlib_file(file, File.join(@directory, file), File.join(@dest_directory, file), true)
119
125
  next unless res
120
126
  else
121
127
  source_content = download_from_source(file)
122
128
  next if source_content.nil?
123
129
 
124
- MirrorFile.new(dst_name).write(source_content)
130
+ MirrorFile.new(File.join(@dest_directory, file)).write(source_content)
125
131
  end
126
132
 
127
133
  FileUtils.rm_rf(path)
@@ -129,7 +135,7 @@ module Gemirro
129
135
  end
130
136
 
131
137
  ##
132
- # Download file from source
138
+ # Download file from source (example: rubygems.org)
133
139
  #
134
140
  # @param [String] file File path
135
141
  # @return [String]
@@ -164,14 +170,255 @@ module Gemirro
164
170
  ::Gem::Specification.all = specs
165
171
 
166
172
  if ::Gem::VERSION >= '2.5.0'
167
- build_marshal_gemspecs specs
168
- build_modern_indices specs if @build_modern
173
+ build_marshal_gemspecs(specs)
174
+ build_modern_indices(specs) if @build_modern
169
175
  compress_indices
170
176
  else
171
177
  build_marshal_gemspecs
172
178
  build_modern_indicies if @build_modern
173
179
  compress_indicies
174
180
  end
181
+
182
+ build_api_v1_dependencies(specs)
183
+
184
+ return unless @build_compact
185
+
186
+ build_compact_index_names
187
+ build_compact_index_infos(specs)
188
+ build_compact_index_versions(specs)
189
+ end
190
+
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
+ ##
257
+ # Cache compact_index endpoint /names
258
+ # Report all gems with versions available. Does not require opening spec files.
259
+ #
260
+ # @return nil
261
+ #
262
+ def build_compact_index_names
263
+ Utils.logger.info('[1/1]: Caching /names')
264
+ FileUtils.rm_rf(Dir.glob(File.join(@dest_directory, 'names*.list')))
265
+
266
+ gem_name_list = Dir.glob('*.gem', base: File.join(@dest_directory, 'gems')).collect do |x|
267
+ x.sub(/-\d+(\.\d+)*(\.[a-zA-Z\d]+)*([-_a-zA-Z\d]+)?\.gem/, '')
268
+ end.uniq.sort!
269
+
270
+ Tempfile.create('names.list') do |f|
271
+ f.write CompactIndex.names(gem_name_list).to_s
272
+ f.rewind
273
+ FileUtils.cp(
274
+ f.path,
275
+ File.join(
276
+ @dest_directory,
277
+ "names.#{Digest::MD5.file(f.path).hexdigest}.#{Digest::SHA256.file(f.path).hexdigest}.list"
278
+ ),
279
+ verbose: verbose
280
+ )
281
+ end
282
+
283
+ nil
284
+ end
285
+
286
+ ##
287
+ # Cache compact_index endpoint /versions
288
+ #
289
+ # @param [Array] specs Gems list
290
+ # @param [Boolean] partial Is gem list an update or a full index
291
+ # @return nil
292
+ #
293
+ def build_compact_index_versions(specs, partial = false)
294
+ Utils.logger.info('[1/1]: Caching /versions')
295
+
296
+ cg =
297
+ specs
298
+ .sort_by(&:name)
299
+ .group_by(&:name)
300
+ .collect do |name, gem_versions|
301
+ gem_versions =
302
+ gem_versions.sort do |a, b|
303
+ a.version <=> b.version
304
+ end
305
+
306
+ info_file = Dir.glob(File.join(@infos_dir, "#{name}.*.*.list")).last
307
+
308
+ throw "Info file for #{name} not found" unless info_file
309
+
310
+ info_file_checksum = info_file.split('.', -4)[-3]
311
+
312
+ CompactIndex::Gem.new(
313
+ name,
314
+ gem_versions.collect do |y|
315
+ CompactIndex::GemVersion.new(
316
+ y.version.to_s,
317
+ y.platform,
318
+ nil,
319
+ info_file_checksum
320
+ )
321
+ end
322
+ )
323
+ end
324
+
325
+ Tempfile.create('versions.list') do |f|
326
+ previous_versions_file = Dir.glob(File.join(@dest_directory, 'versions*.list')).last
327
+
328
+ if partial && previous_versions_file
329
+ versions_file = CompactIndex::VersionsFile.new(previous_versions_file)
330
+ else
331
+ versions_file = CompactIndex::VersionsFile.new(f.path)
332
+ f.write format('created_at: %s', Time.now.utc.iso8601)
333
+ f.write "\n---\n"
334
+ end
335
+
336
+ f.write CompactIndex.versions(versions_file, cg)
337
+ f.rewind
338
+
339
+ FileUtils.rm_rf(Dir.glob(File.join(@dest_directory, 'versions*.list')))
340
+
341
+ FileUtils.cp(
342
+ f.path,
343
+ File.join(
344
+ @dest_directory,
345
+ "versions.#{Digest::MD5.file(f.path).hexdigest}.#{Digest::SHA256.file(f.path).hexdigest}.list"
346
+ ),
347
+ verbose: verbose
348
+ )
349
+ end
350
+
351
+ nil
352
+ end
353
+
354
+ ##
355
+ # Cache compact_index endpoint /info/[gemname]
356
+ #
357
+ # @param [Array] specs Gems list
358
+ # @param [Boolean] partial Is gem list an update or a full index
359
+ # @return nil
360
+ #
361
+ def build_compact_index_infos(specs, partial = false)
362
+ FileUtils.mkdir_p(@infos_dir)
363
+
364
+ if partial
365
+ specs.collect(&:name).uniq do |name|
366
+ FileUtils.rm_rf(Dir.glob(File.join(@infos_dir, "#{name}.*.*.list")))
367
+ end
368
+ else
369
+ FileUtils.rm_rf(Dir.glob(File.join(@infos_dir, '*.list')))
370
+ end
371
+
372
+ grouped_specs = specs.sort_by(&:name).group_by(&:name)
373
+ grouped_specs.each_with_index do |(name, gem_versions), index|
374
+ Utils.logger.info("[#{index + 1}/#{grouped_specs.size}]: Caching /info/#{name}")
375
+
376
+ gem_versions =
377
+ gem_versions.sort do |a, b|
378
+ a.version <=> b.version
379
+ end
380
+
381
+ versions =
382
+ Parallel.map(gem_versions, in_threads: Utils.configuration.update_thread_count) do |spec|
383
+ deps =
384
+ spec
385
+ .dependencies
386
+ .select { |d| d.type == :runtime }
387
+ .sort_by(&:name)
388
+ .collect do |dependency|
389
+ CompactIndex::Dependency.new(
390
+ dependency.name,
391
+ dependency.requirement.to_s
392
+ )
393
+ end
394
+
395
+ CompactIndex::GemVersion.new(
396
+ spec.version,
397
+ spec.platform,
398
+ Digest::SHA256.file(spec.loaded_from).hexdigest,
399
+ nil,
400
+ deps,
401
+ spec.required_ruby_version.to_s,
402
+ spec.required_rubygems_version.to_s
403
+ )
404
+ end
405
+
406
+ Tempfile.create("info_#{name}.list") do |f|
407
+ f.write CompactIndex.info(versions).to_s
408
+ f.rewind
409
+
410
+ FileUtils.cp(
411
+ f.path,
412
+ File.join(
413
+ @infos_dir,
414
+ "#{name}.#{Digest::MD5.file(f.path).hexdigest}.#{Digest::SHA256.file(f.path).hexdigest}.list"
415
+ ),
416
+ verbose: verbose
417
+ )
418
+ end
419
+ end
420
+
421
+ nil
175
422
  end
176
423
 
177
424
  ##
@@ -181,21 +428,23 @@ module Gemirro
181
428
  # @return [Array]
182
429
  #
183
430
  def map_gems_to_specs(gems)
184
- gems.map.with_index do |gemfile, index|
431
+ results = []
432
+
433
+ Parallel.each_with_index(gems, in_threads: Utils.configuration.update_thread_count) do |gemfile, index|
185
434
  Utils.logger.info("[#{index + 1}/#{gems.size}]: Processing #{gemfile.split('/')[-1]}")
186
- if File.size(gemfile).zero?
435
+ if File.empty?(gemfile)
187
436
  Utils.logger.warn("Skipping zero-length gem: #{gemfile}")
188
437
  next
189
438
  end
190
439
 
191
440
  begin
192
441
  begin
193
- spec = if ::Gem::Package.respond_to? :open
194
- ::Gem::Package
195
- .open(File.open(gemfile, 'rb'), 'r', &:metadata)
196
- else
197
- ::Gem::Package.new(gemfile).spec
198
- end
442
+ spec =
443
+ if ::Gem::Package.respond_to? :open
444
+ ::Gem::Package.open(File.open(gemfile, 'rb'), 'r', &:metadata)
445
+ else
446
+ ::Gem::Package.new(gemfile).spec
447
+ end
199
448
  rescue NotImplementedError
200
449
  next
201
450
  end
@@ -213,7 +462,7 @@ module Gemirro
213
462
  end
214
463
 
215
464
  version = spec.version.version
216
- unless version =~ /^\d+\.\d+\.\d+.*/
465
+ unless version =~ /^\d+(\.\d+)?(\.\d+)?.*/
217
466
  msg = "Skipping gem #{spec.full_name} - invalid version #{version}"
218
467
  Utils.logger.warn(msg)
219
468
  next
@@ -223,8 +472,8 @@ module Gemirro
223
472
  spec.abbreviate
224
473
  spec.sanitize
225
474
  else
226
- abbreviate spec
227
- sanitize spec
475
+ abbreviate(spec)
476
+ sanitize(spec)
228
477
  end
229
478
 
230
479
  spec
@@ -238,20 +487,48 @@ module Gemirro
238
487
  "\t#{e.backtrace.join "\n\t"}"].join("\n")
239
488
  Utils.logger.debug(msg)
240
489
  end
241
- end.compact
490
+
491
+ results[index] = spec
492
+ end
493
+
494
+ # nils can result from insert by index
495
+ results.compact
242
496
  end
243
497
 
498
+ ##
499
+ # Handle `index --update`, detecting changed files and file lists.
500
+ #
501
+ # @return nil
502
+ #
244
503
  def update_index
245
504
  make_temp_directories
246
505
 
247
- specs_mtime = File.stat(@dest_specs_index).mtime
506
+ present_gemfiles = Dir.glob('*.gem', base: File.join(@dest_directory, 'gems'))
507
+ indexed_gemfiles = Dir.glob('*.gemspec.rz', base: @quick_marshal_dir_base).collect { |x| x.gsub(/spec.rz$/, '') }
508
+
509
+ @updated_gems = []
510
+ # detect files manually added to public/gems
511
+ @updated_gems += (present_gemfiles - indexed_gemfiles).collect { |x| File.join(@dest_directory, 'gems', x) }
512
+ # detect files manually deleted from public/gems
513
+ @updated_gems += (indexed_gemfiles - present_gemfiles).collect { |x| File.join(@dest_directory, 'gems', x) }
514
+
515
+ specs_mtime =
516
+ begin
517
+ File.stat(@dest_specs_index).mtime
518
+ rescue StandardError
519
+ Time.at(0)
520
+ end
248
521
  newest_mtime = Time.at(0)
249
522
 
250
- @updated_gems = gem_file_list.select do |gem|
251
- gem_mtime = File.stat(gem).mtime
252
- newest_mtime = gem_mtime if gem_mtime > newest_mtime
253
- gem_mtime > specs_mtime
254
- end
523
+ # files that have been replaced
524
+ @updated_gems +=
525
+ gem_file_list.select do |gem|
526
+ gem_mtime = File.stat(gem).mtime
527
+ newest_mtime = gem_mtime if gem_mtime > newest_mtime
528
+ gem_mtime > specs_mtime
529
+ end
530
+
531
+ @updated_gems.uniq!
255
532
 
256
533
  if @updated_gems.empty?
257
534
  Utils.logger.info('No new gems')
@@ -259,24 +536,46 @@ module Gemirro
259
536
  end
260
537
 
261
538
  specs = map_gems_to_specs(@updated_gems)
539
+
540
+ # specs only includes latest discovered files.
541
+ # /info/[gemname] and /api/v1/dependencies can not be rebuilt
542
+ # incrementally, so retrive specs for all versions of these gems.
543
+ gem_name_updates = specs.collect(&:name).uniq
544
+ u2 =
545
+ Dir.glob(File.join(File.join(@dest_directory, 'gems'), '*.gem')).select do |possibility|
546
+ gem_name_updates.any? { |updated| File.basename(possibility) =~ /^#{updated}-\d/ }
547
+ end
548
+
549
+ Utils.logger.info('Reloading for /info/[gemname]')
550
+ version_specs = map_gems_to_specs(u2)
551
+
262
552
  prerelease, released = specs.partition { |s| s.version.prerelease? }
263
553
 
264
554
  ::Gem::Specification.dirs = []
265
555
  ::Gem::Specification.all = *specs
266
- files = if ::Gem::VERSION >= '2.5.0'
267
- build_marshal_gemspecs specs
268
- else
269
- build_marshal_gemspecs
270
- end
556
+ files =
557
+ if ::Gem::VERSION >= '2.5.0'
558
+ build_marshal_gemspecs specs
559
+ else
560
+ build_marshal_gemspecs
561
+ end
271
562
 
272
563
  ::Gem.time('Updated indexes') do
273
- update_specs_index(released, @dest_specs_index, @specs_index)
274
- update_specs_index(released,
275
- @dest_latest_specs_index,
276
- @latest_specs_index)
277
- update_specs_index(prerelease,
278
- @dest_prerelease_specs_index,
279
- @prerelease_specs_index)
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
+ )
280
579
  end
281
580
 
282
581
  if ::Gem::VERSION >= '2.5.0'
@@ -285,6 +584,14 @@ module Gemirro
285
584
  compress_indicies
286
585
  end
287
586
 
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
+
288
595
  Utils.logger.info("Updating production dir #{@dest_directory}") if verbose
289
596
  files << @specs_index
290
597
  files << "#{@specs_index}.gz"
@@ -295,36 +602,36 @@ module Gemirro
295
602
 
296
603
  files.each do |path|
297
604
  file = path.sub(%r{^#{Regexp.escape @directory}/?}, '')
298
- src_name = File.join(@directory, file)
299
- dst_name = File.join(@dest_directory, file)
300
605
 
301
- if ["#{@specs_index}.gz",
302
- "#{@latest_specs_index}.gz",
303
- "#{@prerelease_specs_index}.gz"].include?(path)
304
- res = build_zlib_file(file, src_name, dst_name)
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))
305
608
  next unless res
306
609
  else
307
- FileUtils.mv(src_name,
308
- dst_name,
309
- verbose: verbose,
310
- force: true)
610
+ FileUtils.mv(
611
+ File.join(@directory, file),
612
+ File.join(@dest_directory, file),
613
+ verbose: verbose,
614
+ force: true
615
+ )
311
616
  end
312
617
 
313
- File.utime(newest_mtime, newest_mtime, dst_name)
618
+ File.utime(newest_mtime, newest_mtime, File.join(@dest_directory, file))
314
619
  end
315
620
  end
316
621
 
317
622
  def build_zlib_file(file, src_name, dst_name, from_source = false)
318
623
  content = Marshal.load(Zlib::GzipReader.open(src_name).read)
319
- create_zlib_file("#{dst_name}.orig", content)
624
+ create_zlib_file("#{dst_name}.local", content)
320
625
 
321
626
  return false if @only_origin
322
627
 
323
628
  if from_source
324
629
  source_content = download_from_source(file)
325
- source_content = Marshal.load(Zlib::GzipReader
326
- .new(StringIO
327
- .new(source_content)).read)
630
+ source_content = Marshal.load(
631
+ Zlib::GzipReader.new(
632
+ StringIO.new(source_content)
633
+ ).read
634
+ )
328
635
  else
329
636
  source_content = Marshal.load(Zlib::GzipReader.open(dst_name).read)
330
637
  end
@@ -336,17 +643,17 @@ module Gemirro
336
643
  end
337
644
 
338
645
  def create_zlib_file(dst_name, content)
339
- temp_file = Tempfile.new('gemirro')
340
-
341
- Zlib::GzipWriter.open(temp_file.path) do |io|
342
- io.write(Marshal.dump(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
650
+
651
+ FileUtils.cp(
652
+ f.path,
653
+ dst_name,
654
+ verbose: verbose
655
+ )
343
656
  end
344
-
345
- FileUtils.mv(temp_file.path,
346
- dst_name,
347
- verbose: verbose,
348
- force: true)
349
- Utils.cache.flush_key(File.basename(dst_name))
350
657
  end
351
658
 
352
659
  def verbose
@@ -26,6 +26,7 @@ module Gemirro
26
26
  # @param [String] content
27
27
  #
28
28
  def write(content)
29
+ FileUtils.mkdir_p(File.dirname(@path))
29
30
  handle = File.open(@path, 'w')
30
31
 
31
32
  handle.write(content)