gemirro 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +29 -0
  3. data/.rubocop.yml +6 -3
  4. data/Gemfile.lock +126 -0
  5. data/MANIFEST +6 -10
  6. data/README.md +1 -1
  7. data/bin/gemirro +7 -1
  8. data/gemirro.gemspec +7 -2
  9. data/lib/gemirro/cli/index.rb +12 -3
  10. data/lib/gemirro/cli/init.rb +6 -1
  11. data/lib/gemirro/cli/update.rb +6 -0
  12. data/lib/gemirro/configuration.rb +1 -1
  13. data/lib/gemirro/gems_fetcher.rb +5 -10
  14. data/lib/gemirro/indexer.rb +396 -89
  15. data/lib/gemirro/mirror_file.rb +1 -0
  16. data/lib/gemirro/server.rb +73 -166
  17. data/lib/gemirro/utils.rb +123 -68
  18. data/lib/gemirro/version.rb +1 -1
  19. data/lib/gemirro/versions_fetcher.rb +6 -2
  20. data/lib/gemirro.rb +0 -1
  21. data/spec/gemirro/server_spec.rb +76 -47
  22. data/template/config.rb +6 -0
  23. data/template/public/dist/css/gemirro.css +25 -1
  24. data/template/public/latest_specs.4.8 +0 -0
  25. data/template/public/prerelease_specs.4.8 +0 -0
  26. data/template/public/specs.4.8 +0 -0
  27. data/views/gem.erb +46 -37
  28. data/views/index.erb +41 -33
  29. data/views/layout.erb +4 -17
  30. data/views/not_found.erb +4 -4
  31. metadata +82 -16
  32. data/lib/gemirro/cache.rb +0 -115
  33. data/spec/gemirro/cache_spec.rb +0 -32
  34. data/template/public/dist/css/bootstrap.min.css +0 -7
  35. data/template/public/dist/fonts/glyphicons-halflings-regular.eot +0 -0
  36. data/template/public/dist/fonts/glyphicons-halflings-regular.svg +0 -288
  37. data/template/public/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  38. data/template/public/dist/fonts/glyphicons-halflings-regular.woff +0 -0
  39. data/template/public/dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
  40. 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,7 +428,9 @@ 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
435
  if File.empty?(gemfile)
187
436
  Utils.logger.warn("Skipping zero-length gem: #{gemfile}")
@@ -190,12 +439,12 @@ module Gemirro
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)