tpkg 2.2.2 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/Rakefile +1 -1
  2. data/lib/tpkg.rb +77 -29
  3. data/lib/tpkg/metadata.rb +71 -67
  4. metadata +6 -6
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ spec = Gem::Specification.new do |s|
5
5
  s.add_dependency('facter')
6
6
  s.add_dependency('net-ssh')
7
7
  s.add_dependency('kwalify')
8
- s.version = '2.2.2'
8
+ s.version = '2.2.3'
9
9
  s.authors = ['Darren Dao', 'Jason Heiss']
10
10
  s.email = 'tpkg-users@lists.sourceforge.net'
11
11
  s.homepage = 'http://tpkg.sourceforge.net'
data/lib/tpkg.rb CHANGED
@@ -40,7 +40,7 @@ require 'tpkg/metadata'
40
40
 
41
41
  class Tpkg
42
42
 
43
- VERSION = '2.2.2'
43
+ VERSION = '2.2.3'
44
44
 
45
45
  GENERIC_ERR = 1
46
46
  POSTINSTALL_ERR = 2
@@ -372,10 +372,18 @@ class Tpkg
372
372
  end
373
373
  File.delete(pkgfile)
374
374
  end
375
-
375
+
376
376
  # update metadata file with the tpkg version
377
- metadata.add_tpkg_version(VERSION)
378
-
377
+ begin
378
+ metadata.add_tpkg_version(VERSION)
379
+ rescue Errno::EACCES => e
380
+ # The source directory from which the package is made may not be
381
+ # writeable by the user making the package. It is not critical that
382
+ # the tpkg version get added to the package metadata, so just warn the
383
+ # user if that happens.
384
+ warn "Failed to insert tpkg_version into tpkg.(xml|yml): #{e.message}"
385
+ end
386
+
379
387
  # Tar up the tpkg directory
380
388
  tpkgfile = File.join(package_directory, 'tpkg.tar')
381
389
  system("#{find_tar} -C #{workdir} -cf #{tpkgfile} tpkg") || raise("tpkg.tar creation failed")
@@ -415,23 +423,41 @@ class Tpkg
415
423
  # This assumes the first entry in the tarball is the top level directory.
416
424
  # I think that is a safe assumption.
417
425
  toplevel = nil
418
- # FIXME: This is so lame, to read the whole package to get the
419
- # first filename. Blech.
420
- IO.popen("#{find_tar} -tf #{package_file} #{@@taroptions}") do |pipe|
421
- toplevel = pipe.gets
422
- if toplevel.nil?
423
- raise "Package directory structure of #{package_file} unexpected. Unable to get top level."
424
- end
425
- toplevel.chomp!
426
- # Avoid SIGPIPE, if we don't sink the rest of the output from tar
427
- # then tar ends up getting SIGPIPE when it tries to write to the
428
- # closed pipe and exits with error, which causes us to throw an
429
- # exception down below here when we check the exit status.
430
- pipe.read
426
+ # We need one or more 512 byte tar blocks from the file to get the first
427
+ # filename. In most cases we'll just need one block, but if the top-level
428
+ # directory has an exceptionally long name it may be spread over multiple
429
+ # blocks. The trick is that we don't want any additional blocks because
430
+ # that will confuse tar and it will report that the archive is damaged.
431
+ # So start with one block and go up to an arbitrarily picked limit of 10
432
+ # blocks (I've been unable to make a test tarball that needed more than 3
433
+ # blocks) and see if tar succeeds in listing a file.
434
+ 1.upto(10) do |numblocks|
435
+ tarblocks = File.read(package_file, 512*numblocks)
436
+ # Open3.popen3("#{find_tar} -tf - #{@@taroptions}") do |stdin, stdout, stderr|
437
+ # stdin.write(tarblocks)
438
+ # stdin.close
439
+ # toplevel = stdout.read
440
+ # end
441
+ # Unfortunately popen3 doesn't provide a mechanism for determining the
442
+ # success or failure of the command until ruby 1.9. ($? is never
443
+ # accurately set for popen3, the mechanism in ruby 1.9 for getting the
444
+ # exit status for popen3 is unique to popen3.) So we're left with this,
445
+ # which it rather Unix-specific.
446
+ IO.popen("#{find_tar} -tf - #{@@taroptions} 2> /dev/null", 'r+') do |pipe|
447
+ pipe.write(tarblocks)
448
+ pipe.close_write
449
+ toplevel = pipe.read
450
+ end
451
+ if $?.success?
452
+ break
453
+ else
454
+ toplevel = nil
455
+ end
431
456
  end
432
- if !$?.success?
457
+ if toplevel.nil?
433
458
  raise "Error reading top level directory from #{package_file}"
434
459
  end
460
+ toplevel.chomp!
435
461
  # Strip off the trailing slash
436
462
  toplevel.sub!(Regexp.new("#{File::SEPARATOR}$"), '')
437
463
  if toplevel.include?(File::SEPARATOR)
@@ -1501,6 +1527,7 @@ class Tpkg
1501
1527
  end
1502
1528
  stderr_first_line = stderr.gets
1503
1529
  end
1530
+ # FIXME: popen3 doesn't set $?
1504
1531
  if !$?.success?
1505
1532
  # Ignore 'no matching packages', raise anything else
1506
1533
  if stderr_first_line != "Error: No matching Packages to list\n"
@@ -1538,6 +1565,7 @@ class Tpkg
1538
1565
  end
1539
1566
  stderr_first_line = stderr.gets
1540
1567
  end
1568
+ # FIXME: popen3 doesn't set $?
1541
1569
  if !$?.success?
1542
1570
  # Ignore 'no matching packages', raise anything else
1543
1571
  if stderr_first_line !~ 'No packages found matching'
@@ -2541,6 +2569,10 @@ class Tpkg
2541
2569
  if (metadata[:files][:file_defaults][:posix][:group] rescue nil)
2542
2570
  default_gid = Tpkg::lookup_gid(metadata[:files][:file_defaults][:posix][:group])
2543
2571
  end
2572
+ # FIXME: Default file permissions aren't likely to be generally useful
2573
+ # since different classes of files often require different permissions.
2574
+ # I.e. executables should be 0555, links 0777, everything else 0444.
2575
+ # Something more like a umask would probably be more generally useful.
2544
2576
  if (metadata[:files][:file_defaults][:posix][:perms] rescue nil)
2545
2577
  default_perms = metadata[:files][:file_defaults][:posix][:perms]
2546
2578
  end
@@ -2560,28 +2592,39 @@ class Tpkg
2560
2592
  default_dir_perms = metadata[:files][:dir_defaults][:posix][:perms]
2561
2593
  end
2562
2594
 
2563
- # FIXME: attempt lchown/lchmod on symlinks
2564
2595
  root_dir = File.join(workdir, 'tpkg', 'root')
2565
2596
  reloc_dir = File.join(workdir, 'tpkg', 'reloc')
2566
2597
  Find.find(*Tpkg::get_package_toplevels(File.join(workdir, 'tpkg'))) do |f|
2567
2598
  begin
2568
- if File.file?(f) && !File.symlink?(f)
2599
+ if File.symlink?(f)
2600
+ begin
2601
+ File.lchown(default_uid, default_gid, f)
2602
+ rescue NotImplementedError
2603
+ end
2604
+ elsif File.file?(f)
2569
2605
  File.chown(default_uid, default_gid, f)
2570
- elsif File.directory?(f) && !File.symlink?(f)
2606
+ elsif File.directory?(f)
2571
2607
  File.chown(default_dir_uid, default_dir_gid, f)
2572
2608
  end
2573
2609
  rescue Errno::EPERM
2574
2610
  raise if Process.euid == 0
2575
2611
  end
2576
- if File.file?(f) && !File.symlink?(f)
2612
+ if File.symlink?(f)
2613
+ if default_perms
2614
+ begin
2615
+ File.lchmod(default_perms, f)
2616
+ rescue NotImplementedError
2617
+ end
2618
+ end
2619
+ elsif File.file?(f)
2577
2620
  if default_perms
2578
2621
  File.chmod(default_perms, f)
2579
2622
  end
2580
- elsif File.directory?(f) && !File.symlink?(f)
2623
+ elsif File.directory?(f)
2581
2624
  File.chmod(default_dir_perms, f)
2582
2625
  end
2583
2626
  end
2584
-
2627
+
2585
2628
  # Reset the permission/ownership of the conflicting files as how they were before.
2586
2629
  # This needs to be done after the default permission/ownership is applied, but before
2587
2630
  # the handling of ownership/permissions on specific files
@@ -2619,7 +2662,10 @@ class Tpkg
2619
2662
  if !File.symlink?(working_path)
2620
2663
  File.chown(uid, gid, working_path)
2621
2664
  else
2622
- # FIXME: attempt lchown
2665
+ begin
2666
+ File.lchown(uid, gid, working_path)
2667
+ rescue NotImplementedError
2668
+ end
2623
2669
  end
2624
2670
  rescue Errno::EPERM
2625
2671
  raise if Process.euid == 0
@@ -2630,7 +2676,10 @@ class Tpkg
2630
2676
  if !File.symlink?(working_path)
2631
2677
  File.chmod(perms, working_path)
2632
2678
  else
2633
- # FIXME: attempt lchmod
2679
+ begin
2680
+ File.lchmod(perms, working_path)
2681
+ rescue NotImplementedError
2682
+ end
2634
2683
  end
2635
2684
  end
2636
2685
  end
@@ -4581,8 +4630,7 @@ class Tpkg
4581
4630
  def stub_native_pkg(pkg)
4582
4631
  # gather all of the native dependencies
4583
4632
  native_deps = pkg[:metadata].get_native_deps
4584
-
4585
- return if native_deps.nil? or native_deps.empty?
4633
+ return if native_deps.empty?
4586
4634
 
4587
4635
  if Tpkg::get_os =~ /RedHat|CentOS|Fedora/
4588
4636
  rpm = create_rpm("stub_for_#{pkg[:metadata][:name]}", native_deps)
@@ -4604,7 +4652,7 @@ class Tpkg
4604
4652
  def remove_native_stub_pkg(pkg)
4605
4653
  # Don't have to do anything if this package has no native dependencies
4606
4654
  native_deps = pkg[:metadata].get_native_deps
4607
- return if native_deps.nil? or native_deps.empty?
4655
+ return if native_deps.empty?
4608
4656
 
4609
4657
  # the convention is that stub package is named as "stub_for_pkgname"
4610
4658
  stub_pkg_name = "stub_for_#{pkg[:metadata][:name]}"
data/lib/tpkg/metadata.rb CHANGED
@@ -230,7 +230,8 @@ end
230
230
  # is that you can give it a metadata file of any format, such as yaml or xml,
231
231
  # and it will provide you a uniform interface for accessing/dealing with the metadata.
232
232
  class Metadata
233
- attr_accessor :source, :file_path
233
+ attr_reader :text, :format, :file
234
+ attr_accessor :source
234
235
  REQUIRED_FIELDS = [:name, :version, :maintainer, :description]
235
236
 
236
237
  # Cleans up a string to make it suitable for use in a filename
@@ -247,7 +248,7 @@ class Metadata
247
248
  if metadata_text =~ /^:?name:(.+)/
248
249
  name = $1.strip
249
250
  metadata[name] ||= []
250
- metadata[name] << Metadata.new(metadata_text,'yml', source)
251
+ metadata[name] << Metadata.new(metadata_text,'yml', nil, source)
251
252
  end
252
253
  end
253
254
  return metadata
@@ -258,26 +259,31 @@ class Metadata
258
259
  def self.instantiate_from_dir(dir)
259
260
  metadata = nil
260
261
  if File.exist?(File.join(dir, 'tpkg.yml'))
261
- metadata = Metadata.new(File.read(File.join(dir, 'tpkg.yml')), 'yml')
262
- metadata.file_path = File.join(dir, 'tpkg.yml')
262
+ metadata = Metadata.new(File.read(File.join(dir, 'tpkg.yml')),
263
+ 'yml',
264
+ File.join(dir, 'tpkg.yml'))
263
265
  elsif File.exists?(File.join(dir, 'tpkg.xml'))
264
- metadata = Metadata.new(File.read(File.join(dir, 'tpkg.xml')), 'xml')
265
- metadata.file_path = File.join(dir, 'tpkg.xml')
266
+ metadata = Metadata.new(File.read(File.join(dir, 'tpkg.xml')),
267
+ 'xml',
268
+ File.join(dir, 'tpkg.xml'))
266
269
  end
267
270
  return metadata
268
271
  end
269
272
 
270
- # metadata_text = text representation of the metadata
273
+ # text = text representation of the metadata
271
274
  # format = yml, xml, json, etc.
275
+ # file = Path to the metadata file that was the source of this metadata
272
276
  # source = Source, in the tpkg sense, of the package described by this
273
277
  # metadata. I.e. the filename of an individual package or a directory or
274
278
  # URL containing multiple packages and a metadata.yml file. Used by tpkg to
275
279
  # report on how many packages are available from various sources.
276
- def initialize(metadata_text, format, source=nil)
277
- @hash = nil
278
- @metadata_text = metadata_text
280
+ def initialize(text, format, file=nil, source=nil)
281
+ @text = text
282
+ # FIXME: should define enum of supported formats and reject others
279
283
  @format = format
284
+ @file = file
280
285
  @source = source
286
+ @hash = nil
281
287
  end
282
288
 
283
289
  def [](key)
@@ -287,16 +293,16 @@ class Metadata
287
293
  def []=(key,value)
288
294
  to_hash[key]=value
289
295
  end
290
-
296
+
291
297
  def to_hash
292
298
  if @hash
293
299
  return @hash
294
300
  end
295
-
301
+
296
302
  if @format == 'yml'
297
- hash = YAML::load(@metadata_text)
303
+ hash = YAML::load(@text)
298
304
  @hash = hash.with_indifferent_access
299
-
305
+
300
306
  # We need this for backward compatibility. With xml, we specify
301
307
  # native dependency as type: :native rather then native: true
302
308
  @hash[:dependencies].each do | dep |
@@ -308,7 +314,7 @@ class Metadata
308
314
  end
309
315
  end
310
316
  end if @hash[:dependencies]
311
-
317
+
312
318
  @hash[:files][:files].each do |file|
313
319
  # We need to do this for backward compatibility. In the old yml schema,
314
320
  # the encrypt field can either be "true" or a string value. Now, it is
@@ -325,60 +331,55 @@ class Metadata
325
331
  file[:posix][:perms] = "#{file[:posix][:perms]}".oct
326
332
  end
327
333
  end if @hash[:files] && @hash[:files][:files]
328
- else
334
+ elsif @format == 'xml'
329
335
  @hash = metadata_xml_to_hash.with_indifferent_access
336
+ else
337
+ raise "Unknown metadata format"
330
338
  end
331
- return @hash
339
+ @hash
332
340
  end
333
-
341
+
334
342
  # Write the metadata to a file under the specified directory
335
- # The file will be saved as tpkg.yml or tpkg.xml.
336
- def write(dir, retain_format=false)
337
- file = nil
338
- if retain_format && @format == 'xml'
339
- puts "TODO"
340
- else
341
- file = File.new(File.join(dir, "tpkg.yml"), "w")
343
+ # The file will be saved as tpkg.yml, even if originally loaded as XML.
344
+ def write(dir)
345
+ File.open(File.join(dir, "tpkg.yml"), "w") do |file|
342
346
  # When we convert xml to hash, we store the key as symbol. So when we
343
347
  # write back out to file, we should stringify all the keys for readability.
344
348
  data = to_hash.recursively{|h| h.stringify_keys }
345
349
  YAML::dump(data, file)
346
350
  end
347
- file.close
348
351
  end
349
-
352
+
350
353
  # Add tpkg_version to the existing tpkg.xml or tpkg.yml file
351
354
  def add_tpkg_version(version)
352
- begin
353
- if @format == 'xml'
354
- metadata_xml = REXML::Document.new(@metadata_text)
355
- if metadata_xml.root.elements["tpkg_version"] && (tpkg_version = metadata_xml.root.elements["tpkg_version"].text) != Tpkg::VERSION
356
- warn "Warning: tpkg_version is specified as #{tpkg_version}, which doesn't match with the actual tpkg version being used (#{Tpkg::VERSION})."
357
- elsif !metadata_xml.root.elements["tpkg_version"]
358
- tpkg_version_ele = REXML::Element.new("tpkg_version")
359
- tpkg_version_ele.text = Tpkg::VERSION
355
+ if self[:tpkg_version]
356
+ if self[:tpkg_version] != version
357
+ warn "Warning: tpkg_version is specified as #{self[:tpkg_version]}, which doesn't match with the actual tpkg version being used (#{version})."
358
+ end
359
+ else
360
+ # Add to in-memory data
361
+ self[:tpkg_version] = version
362
+ # Update the metadata source file (if known)
363
+ if @file
364
+ if @format == 'yml'
365
+ File.open(@file, 'a') do |file|
366
+ file.puts "tpkg_version: #{version}"
367
+ end
368
+ elsif @format == 'xml'
369
+ metadata_xml = REXML::Document.new(@text)
370
+ tpkg_version_ele = REXML::Element.new('tpkg_version')
371
+ tpkg_version_ele.text = version
360
372
  metadata_xml.root.add_element(tpkg_version_ele)
361
- File.open(@file_path, 'w') do |file|
373
+ File.open(@file, 'w') do |file|
362
374
  metadata_xml.write(file)
363
375
  end
376
+ else
377
+ raise "Unknown metadata format"
364
378
  end
365
- elsif @format == 'yml'
366
- if (tpkg_version = to_hash[:tpkg_version]) && tpkg_version != Tpkg::VERSION
367
- warn "Warning: tpkg_version is specified as #{tpkg_version}, which doesn't match with the actual tpkg version being used (#{Tpkg::VERSION})."
368
- elsif !tpkg_version
369
- File.open(@file_path, 'a') do |file|
370
- file.puts "tpkg_version: #{Tpkg::VERSION}"
371
- end
372
- end
373
- else
374
- raise "Unknown metadata format"
375
379
  end
376
- rescue Errno::EACCES => e
377
- warn "Warning: Failed to insert tpkg_version into tpkg.(xml|yml)."
378
- puts e
379
- end
380
+ end
380
381
  end
381
-
382
+
382
383
  def generate_package_filename
383
384
  name = to_hash[:name]
384
385
  version = to_hash[:version]
@@ -445,7 +446,7 @@ class Metadata
445
446
  warn "Warning: unable to validate metadata because #{schema_file} does not exist"
446
447
  return
447
448
  end
448
- errors = verify_yaml(schema_file, @metadata_text)
449
+ errors = verify_yaml(schema_file, @text)
449
450
  elsif @format == 'xml'
450
451
  # TODO: use DTD to validate XML
451
452
  errors = verify_required_fields
@@ -464,12 +465,14 @@ class Metadata
464
465
  # Verify the yaml text against the given schema
465
466
  # Return array of errors (if there are any)
466
467
  def verify_yaml(schema, yaml_text)
467
- schema = Kwalify::Yaml.load_file(schema)
468
-
469
- ## create validator
470
- validator = Kwalify::Validator.new(schema.with_indifferent_access)
471
- ## validate
472
- errors = validator.validate(YAML::load(yaml_text).with_indifferent_access)
468
+ errors = nil
469
+ # Kwalify generates lots of warnings, silence it
470
+ Silently.silently do
471
+ schema = Kwalify::Yaml.load_file(schema)
472
+ validator = Kwalify::Validator.new(schema.with_indifferent_access)
473
+ errors = validator.validate(YAML::load(yaml_text).with_indifferent_access)
474
+ end
475
+ errors
473
476
  end
474
477
 
475
478
  # Once we implement validating the XML using the DTD, we won't need
@@ -491,7 +494,7 @@ class Metadata
491
494
  return if @format != "xml"
492
495
 
493
496
  metadata_hash = {}
494
- metadata_xml = REXML::Document.new(@metadata_text)
497
+ metadata_xml = REXML::Document.new(@text)
495
498
 
496
499
  if metadata_xml.root.attributes['filename'] # && !metadata_xml.root.attributes['filename'].empty?
497
500
  metadata_hash[:filename] = metadata_xml.root.attributes['filename']
@@ -719,12 +722,13 @@ class Metadata
719
722
 
720
723
  return metadata_hash
721
724
  end
722
-
725
+
723
726
  def get_native_deps
724
- dependencies = to_hash[:dependencies]
725
- return nil if dependencies.nil? or dependencies.empty?
726
-
727
- return dependencies.select{|dep| dep[:type] == :native}
727
+ native_deps = []
728
+ if self[:dependencies]
729
+ native_deps = self[:dependencies].select{|dep| dep[:type] == :native}
730
+ end
731
+ native_deps
728
732
  end
729
733
  end
730
734
 
@@ -749,10 +753,10 @@ class FileMetadata < Metadata
749
753
  end
750
754
 
751
755
  if @format == 'bin'
752
- hash = Marshal::load(@metadata_text)
756
+ hash = Marshal::load(@text)
753
757
  @hash = hash.with_indifferent_access
754
758
  elsif @format == 'yml'
755
- hash = YAML::load(@metadata_text)
759
+ hash = YAML::load(@text)
756
760
  @hash = hash.with_indifferent_access
757
761
  elsif @format == 'xml'
758
762
  @hash = file_metadata_xml_to_hash
@@ -765,7 +769,7 @@ class FileMetadata < Metadata
765
769
 
766
770
  file_metadata_hash = {}
767
771
  files = []
768
- file_metadata_xml = REXML::Document.new(@metadata_text)
772
+ file_metadata_xml = REXML::Document.new(@text)
769
773
  file_metadata_hash[:package_file] = file_metadata_xml.root.attributes['package_file']
770
774
  file_metadata_xml.elements.each("files/file") do | file_ele |
771
775
  file = {}
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tpkg
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
5
- prerelease:
4
+ hash: 1
5
+ prerelease: false
6
6
  segments:
7
7
  - 2
8
8
  - 2
9
- - 2
10
- version: 2.2.2
9
+ - 3
10
+ version: 2.2.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Darren Dao
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-02-14 00:00:00 -08:00
19
+ date: 2011-02-20 00:00:00 -08:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -133,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
133
  requirements: []
134
134
 
135
135
  rubyforge_project: tpkg
136
- rubygems_version: 1.5.0
136
+ rubygems_version: 1.3.7
137
137
  signing_key:
138
138
  specification_version: 3
139
139
  summary: tpkg Application Packaging & Deployment