tpkg 2.2.2 → 2.2.3

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 (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