tpkg 1.21.1 → 1.22.1

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.
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('ddao-kwalify')
8
- s.version = '1.21.1'
8
+ s.version = '1.22.1'
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/bin/cpan2tpkg CHANGED
@@ -256,29 +256,46 @@ MODULE: foreach my $name ($extinst->modules)
256
256
  }
257
257
  PREREQ: foreach my $dep (keys %prereqs)
258
258
  {
259
- # Skip dependencies on core modules
260
- my $depmod = CPAN::Shell->expand('Module', $dep);
261
- # This is a bit of an indirect way to identify core modules
262
- # but the only way I can figure out. Core stuff gets
263
- # installed with perl into /home/t/perl-version, CPAN
264
- # modules into /home/t/lib/perl5/site_perl.
265
- #
266
- # What seems like it should be the "right" way is that the "D"
267
- # (aka "Development Stage") field in dslip_status has "S" (aka
268
- # "Standard, supplied with Perl 5") as one of its possible values,
269
- # according to the docs (http://perldoc.perl.org/CPAN.html).
270
- # However, that doesn't seem to be set reliably.
271
- if ($depmod->inst_file && $depmod->inst_file =~ /$Config{prefix}/)
259
+ if ($dep eq 'perl')
272
260
  {
273
- print "Prereq $dep is a core module, skipping\n";
274
- next PREREQ;
261
+ # We always add a dependency on perl anyway, so the
262
+ # module's dependency is only relevant if it specifies a
263
+ # newer version than is running.
264
+ if ($prereqs{$dep} ne '0')
265
+ {
266
+ if ($prereqs{$dep} > $])
267
+ {
268
+ die "Module requires perl >= $prereqs{$dep}, this is $]\n";
269
+ }
270
+ }
275
271
  }
276
-
277
- my $deppkgname = module_to_pkg_name($depmod);
278
- $deps{$deppkgname} = {};
279
- if ($prereqs{$dep} ne '0')
272
+ else
280
273
  {
281
- $deps{$deppkgname}{minimum_version} = $prereqs{$dep};
274
+ # Skip dependencies on core modules
275
+ my $depmod = CPAN::Shell->expand('Module', $dep);
276
+ # This is a bit of an indirect way to identify core
277
+ # modules but the only way I can figure out. Core stuff
278
+ # gets installed with perl into /home/t/perl-version,
279
+ # CPAN modules into /home/t/lib/perl5/site_perl.
280
+ #
281
+ # What seems like it should be the "right" way is that
282
+ # the "D" (aka "Development Stage") field in
283
+ # dslip_status has "S" (aka "Standard, supplied with
284
+ # Perl 5") as one of its possible values, according to
285
+ # the docs (http://perldoc.perl.org/CPAN.html).
286
+ # However, that doesn't seem to be set reliably.
287
+ if ($depmod->inst_file && $depmod->inst_file =~ /$Config{prefix}/)
288
+ {
289
+ print "Prereq $dep is a core module, skipping\n";
290
+ next PREREQ;
291
+ }
292
+
293
+ my $deppkgname = module_to_pkg_name($depmod);
294
+ $deps{$deppkgname} = {};
295
+ if ($prereqs{$dep} ne '0')
296
+ {
297
+ $deps{$deppkgname}{minimum_version} = $prereqs{$dep};
298
+ }
282
299
  }
283
300
  }
284
301
  }
data/bin/gem2tpkg CHANGED
@@ -164,7 +164,6 @@ end
164
164
  # Create the directory we want gem to install into
165
165
  @gemdir = tempdir('gem2tpkg')
166
166
  ENV['GEM_HOME'] = @gemdir
167
- ENV['GEM_PATH'] = @gemdir
168
167
 
169
168
  # Install the gem
170
169
  geminst = [@gemcmd, 'install', '--no-rdoc', '--no-ri'] | @gems
@@ -186,6 +185,10 @@ if !r
186
185
  abort('gem install failed')
187
186
  end
188
187
 
188
+ # Now set GEM_PATH so that further operations (particularly `gem list`)
189
+ # only operate against the gems installed in our working directory.
190
+ ENV['GEM_PATH'] = @gemdir
191
+
189
192
  @already_packaged = []
190
193
  def package(gem)
191
194
  pkgfiles = []
@@ -323,8 +326,6 @@ def package(gem)
323
326
  maxver = "#{majorver}.9999.9999"
324
327
  deps[depgem][:maximum_version] = maxver
325
328
  end
326
- # Package the dependency
327
- pkgfiles.concat(package(depgem))
328
329
  end
329
330
  end
330
331
  if !$?.success?
@@ -454,28 +455,16 @@ def package(gem)
454
455
  pkgfiles
455
456
  end
456
457
 
457
- # Count the number of gems installed
458
- gemcount = 0
458
+ # Package each installed gem
459
+ pkgfiles = []
459
460
  IO.popen("#{@gemcmd} list") do |pipe|
460
461
  pipe.each_line do |line|
461
462
  next if line.include?('***') # Skip header line
462
463
  next if line =~ /^\s*$/ # Skip blank lines
463
- gemcount += 1
464
+ gem, version = line.split
465
+ pkgfiles |= package(gem)
464
466
  end
465
467
  end
466
- if gemcount == 0
467
- abort "Zero gems installed according to gem list?"
468
- end
469
-
470
- pkgfiles = []
471
- @gems.each do | gem |
472
- pkgfiles |= package(gem)
473
- end
474
- # Make sure the package method made as many packages as there were gems
475
- # installed
476
- if pkgfiles.length != gemcount
477
- abort "gem count (#{gemcount}) vs pkg count (#{pkgfiles.length}) mismatch"
478
- end
479
468
 
480
469
  # Tell the user what packages were created
481
470
  puts 'The following packages were created:'
data/bin/tpkg CHANGED
@@ -28,8 +28,9 @@ require 'tpkg'
28
28
  @servers = nil
29
29
  @worker_count = 10
30
30
  @rerun_with_sudo = false
31
- @tpkg_options = {}
32
- @init_options = {}
31
+ @tpkg_options = {} # options for instantiating Tpkg object
32
+ @init_options = {} # options for how to run init scripts
33
+ @other_options = {}
33
34
  @compress = nil
34
35
 
35
36
 
@@ -70,8 +71,9 @@ opts.on('--upgrade', '-u', '=PACKAGES', 'Upgrade one or more packages', Array) d
70
71
  @action_value = opt
71
72
  end
72
73
  opts.on('--downgrade', '=PACKAGES', 'Downgrade one or more packages', Array) do |opt|
74
+ @other_options[:downgrade] = true
73
75
  @rerun_with_sudo = true
74
- @action = :downgrade
76
+ @action = :upgrade
75
77
  @action_value = opt
76
78
  end
77
79
  opts.on('--ua', 'Upgrade all packages') do |opt|
@@ -85,12 +87,14 @@ opts.on('--remove', '-r', '=PACKAGES', 'Remove one or more packages', Array) do
85
87
  end
86
88
  opts.on('--rd', '=PACKAGES', 'Similar to -r but also remove depending packages', Array) do |opt|
87
89
  @rerun_with_sudo = true
88
- @action = :remove_all_dep
90
+ @other_options[:remove_all_dep] = true
91
+ @action = :remove
89
92
  @action_value = opt
90
93
  end
91
94
  opts.on('--rp', '=PACKAGES', 'Similar to -r but also remove prerequisites', Array) do |opt|
92
95
  @rerun_with_sudo = true
93
- @action = :remove_all_prereq
96
+ @other_options[:remove_all_prereq] = true
97
+ @action = :remove
94
98
  @action_value = opt
95
99
  end
96
100
  opts.on('--ra', 'Remove all packages') do |opt|
@@ -229,7 +233,10 @@ end
229
233
  opts.on('--lock-force', 'Force the removal of an existing lockfile') do |opt|
230
234
  @lockforce = opt
231
235
  end
232
- opts.on('--force', 'Force the execution of a given task.') do |opt|
236
+ opts.on('--force-replace', 'Replace conflicting pkgs with the new one(s)') do |opt|
237
+ @other_options[:force_replace] = opt
238
+ end
239
+ opts.on('--force', 'Force the execution of a given task') do |opt|
233
240
  @force = opt
234
241
  end
235
242
  opts.on('--use-ssh-key', 'Use ssh key for deploying instead of password') do |opt|
@@ -240,7 +247,7 @@ opts.on('--deploy-as', '=USERNAME', 'What username to use for deploying to remot
240
247
  @deploy_options["deploy-as"] = opt
241
248
  @deploy_params = @deploy_params - ['--deploy-as']
242
249
  end
243
- opts.on('--compress', '=[TYPE]', 'Compress files when making packages.') do |opt|
250
+ opts.on('--compress', '=[TYPE]', 'Compress files when making packages') do |opt|
244
251
  @compress = opt
245
252
  end
246
253
  opts.on('--debug', 'Print lots of messages about what tpkg is doing') do |opt|
@@ -280,7 +287,7 @@ def instantiate_tpkg(options = {})
280
287
  sources = options["sources"] || []
281
288
  report_server = nil
282
289
 
283
- [File.join(Tpkg::CONFIGDIR, 'tpkg.conf'), "#{ENV['HOME']}/.tpkg.conf"].each do |configfile|
290
+ [File.join(Tpkg::CONFIGDIR, 'tpkg.conf'), File.join(ENV['HOME'], ".tpkg.conf")].each do |configfile|
284
291
  if File.exist?(configfile)
285
292
  IO.foreach(configfile) do |line|
286
293
  line.chomp!
@@ -355,23 +362,13 @@ when :extract
355
362
  Tpkg::extract_metadata(@action_value)
356
363
  when :install
357
364
  tpkg = instantiate_tpkg(@tpkg_options)
358
- ret_val = tpkg.install(@action_value, passphrase_callback)
365
+ ret_val = tpkg.install(@action_value, passphrase_callback, @other_options)
359
366
  when :upgrade
360
367
  tpkg = instantiate_tpkg(@tpkg_options)
361
- ret_val = tpkg.upgrade(@action_value, passphrase_callback)
362
- when :downgrade
363
- downgrade = true
364
- tpkg = instantiate_tpkg(@tpkg_options)
365
- ret_val = tpkg.upgrade(@action_value, passphrase_callback, downgrade)
368
+ ret_val = tpkg.upgrade(@action_value, passphrase_callback, @other_options)
366
369
  when :remove
367
370
  tpkg = instantiate_tpkg(@tpkg_options)
368
- ret_val = tpkg.remove(@action_value)
369
- when :remove_all_dep
370
- tpkg = instantiate_tpkg(@tpkg_options)
371
- ret_val = tpkg.remove(@action_value, {:remove_all_dep => true})
372
- when :remove_all_prereq
373
- tpkg = instantiate_tpkg(@tpkg_options)
374
- ret_val = tpkg.remove(@action_value, {:remove_all_prereq => true})
371
+ ret_val = tpkg.remove(@action_value, @other_options)
375
372
  when :verify
376
373
  result = nil
377
374
  # Verify a given .tpkg file
data/lib/tpkg.rb CHANGED
@@ -56,7 +56,7 @@ require 'kwalify' # for validating yaml
56
56
 
57
57
  class Tpkg
58
58
 
59
- VERSION = '1.21.1'
59
+ VERSION = '1.22.1'
60
60
  CONFIGDIR = '/etc'
61
61
 
62
62
  GENERIC_ERR = 1
@@ -131,6 +131,8 @@ class Tpkg
131
131
  end
132
132
  def self.clear_cached_tar
133
133
  @@tar = nil
134
+ @@taroptions = ""
135
+ @@tarinfo = {:version => 'unknown'}
134
136
  end
135
137
 
136
138
  # Encrypts the given file in-place (the plaintext file is replaced by the
@@ -450,47 +452,6 @@ class Tpkg
450
452
  #return FileMetadata.new(YAML::dump(filemetadata),'yml')
451
453
  return FileMetadata.new(Marshal::dump(filemetadata),'bin')
452
454
  end
453
-
454
- def self.get_xml_filemetadata_from_directory(tpkgdir)
455
- filemetadata_xml = REXML::Document.new
456
- filemetadata_xml << REXML::Element.new('files')
457
-
458
- # create file_metadata.xml that stores list of files and their checksum
459
- # will be used later on to check whether installed files have been changed
460
- root_dir = File.join(tpkgdir, "root")
461
- reloc_dir = File.join(tpkgdir, "reloc")
462
- Find.find(root_dir, reloc_dir) do |f|
463
- next if !File.exist?(f)
464
- relocatable = "false"
465
-
466
- # check if it's from root dir or reloc dir
467
- if f =~ /^#{root_dir}/
468
- short_fn = f[root_dir.length ..-1]
469
- else
470
- short_fn = f[reloc_dir.length + 1..-1]
471
- relocatable = "true"
472
- end
473
-
474
- next if short_fn.nil? or short_fn.empty?
475
-
476
- file_ele = filemetadata_xml.root.add_element("file", {"relocatable" => relocatable})
477
- path_ele = file_ele.add_element("path")
478
- path_ele.add_text(short_fn)
479
-
480
- # only do checksum for file
481
- if File.file?(f)
482
- # this doesn't work for older ruby version
483
- #digest = Digest::SHA256.file(f).hexdigest
484
- digest = Digest::SHA256.hexdigest(File.read(f))
485
- chksum_ele = file_ele.add_element("checksum")
486
- alg_ele = chksum_ele.add_element("algorithm")
487
- alg_ele.add_text("SHA256")
488
- digest_ele = chksum_ele.add_element("digest")
489
- digest_ele.add_text(digest)
490
- end
491
- end
492
- return filemetadata_xml
493
- end
494
455
 
495
456
  def self.verify_package_checksum(package_file)
496
457
  topleveldir = package_toplevel_directory(package_file)
@@ -552,12 +513,10 @@ class Tpkg
552
513
  ['yml','xml'].each do |format|
553
514
  file = File.join('tpkg', "tpkg.#{format}")
554
515
 
555
- extract_tpkg_tar_command = cmd_to_extract_tpkg_tar(package_file, topleveldir)
556
-
557
516
  # use popen3 instead of popen because popen display stderr when there's an error such as
558
517
  # tpkg.yml not being there, which is something we want to ignore since old tpkg doesn't
559
518
  # have tpkg.yml file
560
- #stdin, stdout, stderr = Open3.popen3("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{find_tar} -xf - -O #{file}")
519
+ extract_tpkg_tar_command = cmd_to_extract_tpkg_tar(package_file, topleveldir)
561
520
  stdin, stdout, stderr = Open3.popen3("#{extract_tpkg_tar_command} | #{find_tar} -xf - -O #{file}")
562
521
  filecontent = stdout.read
563
522
  if filecontent.nil? or filecontent.empty?
@@ -576,60 +535,6 @@ class Tpkg
576
535
  return metadata
577
536
  end
578
537
 
579
- # TODO: To be deprecated
580
- # Extracts and returns the metadata from a package file
581
- def self.xml_metadata_from_package(package_file)
582
- topleveldir = package_toplevel_directory(package_file)
583
- # Verify checksum
584
- verify_package_checksum(package_file)
585
- # Extract and parse tpkg.xml
586
- tpkg_xml = nil
587
- extract_tpkg_tar_command = cmd_to_extract_tpkg_tar(package_file, topleveldir)
588
- IO.popen("#{extract_tpkg_tar_command} | #{find_tar} -xf - -O #{File.join('tpkg', 'tpkg.xml')}") do |pipe|
589
- tpkg_xml = REXML::Document.new(pipe.read)
590
- end
591
- if !$?.success?
592
- warn "Warning: Extracting tpkg.xml from #{package_file} failed"
593
- return nil
594
- end
595
-
596
- # Insert an attribute on the root element with the package filename
597
- tpkg_xml.root.attributes['filename'] = File.basename(package_file)
598
-
599
- # Return
600
- return tpkg_xml
601
- end
602
-
603
- # TODO: To be deprecated
604
- # Extracts and returns the metadata from a directory of package files
605
- def self.xml_metadata_from_directory(directory)
606
- metadata = []
607
- # if metadata.xml already exists, then go ahead and
608
- # parse it
609
- existing_metadata_file = File.join(directory, 'metadata.xml')
610
- existing_metadata = {}
611
- if File.exists?(existing_metadata_file)
612
- tpkg_metadata_xml = REXML::Document.new(File.open(existing_metadata_file))
613
-
614
- tpkg_metadata_xml.root.elements.each do | metadata_xml |
615
- existing_metadata[metadata_xml.attributes['filename']] = metadata_xml
616
- end
617
- end
618
-
619
- # Populate the metadata array with metadata for all of the packages
620
- # in the given directory. Reuse existing metadata if possible.
621
- Dir.glob(File.join(directory, '*.tpkg')) do |pkg|
622
- if existing_metadata[File.basename(pkg)]
623
- metadata << existing_metadata[File.basename(pkg)]
624
- else
625
- xml = xml_metadata_from_package(pkg)
626
- metadata << xml.root if xml
627
- end
628
- end
629
-
630
- return metadata
631
- end
632
-
633
538
  # Extracts and returns the metadata from a directory of package files
634
539
  def self.metadata_from_directory(directory)
635
540
  metadata = []
@@ -667,26 +572,6 @@ class Tpkg
667
572
  # to metadata.xml in that directory
668
573
  def self.extract_metadata(directory, dest=nil)
669
574
  dest = directory if dest.nil?
670
- # we're no longer generating metadata.xml
671
- backward_compatible = false
672
-
673
- # If we still want to support metadata.xml
674
- if backward_compatible
675
- metadata_xml = xml_metadata_from_directory(directory)
676
- # Combine all of the individual metadata files into one XML document
677
- metadata = REXML::Document.new
678
- metadata << REXML::Element.new('tpkg_metadata')
679
- metadata_xml.each do |md|
680
- metadata.root << md
681
- end
682
- # And write that out to metadata.xml
683
- metadata_tmpfile = Tempfile.new('metadata.xml', dest)
684
- metadata.write(metadata_tmpfile)
685
- metadata_tmpfile.close
686
- File.chmod(0644, metadata_tmpfile.path)
687
- File.rename(metadata_tmpfile.path, File.join(dest, 'metadata.xml'))
688
- end
689
-
690
575
  metadata = metadata_from_directory(directory)
691
576
  # And write that out to metadata.yml
692
577
  metadata_tmpfile = Tempfile.new('metadata.yml', dest)
@@ -1093,7 +978,7 @@ class Tpkg
1093
978
  # deploy_params is an array that holds the list of paramters that is used when invoking tpkg on to the remote
1094
979
  # servers where we want to deploy to.
1095
980
  #
1096
- # servers is an array or a callback that list the remote servers where we want to deploy to
981
+ # servers is an array, a filename or a callback that list the remote servers where we want to deploy to
1097
982
  def self.deploy(deploy_params, deploy_options, servers)
1098
983
  servers.uniq!
1099
984
  deployer = Deployer.new(deploy_options)
@@ -1162,9 +1047,7 @@ class Tpkg
1162
1047
  # 2) the file_defaults settings of the metadata file
1163
1048
  # 3) the explicitly defined settings in the corresponding file section of the metadata file
1164
1049
  def self.predict_file_perms_and_ownership(data)
1165
- perms = nil
1166
- uid = nil
1167
- gid = nil
1050
+ perms = uid = gid = nil
1168
1051
 
1169
1052
  # get current permission and ownership
1170
1053
  if data[:actual_file]
@@ -1193,7 +1076,8 @@ class Tpkg
1193
1076
  return perms, uid, gid
1194
1077
  end
1195
1078
 
1196
- # Given a package file, figure out of tpkg.tar was compressed
1079
+ # Given a package file, figure out if tpkg.tar was compressed
1080
+ # Return what type of compression. If tpkg.tar wasn't compressed, then return nil.
1197
1081
  def self.get_compression(package_file)
1198
1082
  compression = nil
1199
1083
  IO.popen("#{find_tar} -tf #{package_file} #{@@taroptions}") do |pipe|
@@ -1239,6 +1123,16 @@ class Tpkg
1239
1123
  end
1240
1124
  return result
1241
1125
  end
1126
+
1127
+ # Used where we wish to capture an exception and modify the message. This
1128
+ # method returns a new exception with desired message but with the backtrace
1129
+ # from the original exception so that the backtrace info is not lost. This
1130
+ # is necessary because Exception lacks a set_message method.
1131
+ def self.wrap_exception(e, message)
1132
+ eprime = e.exception(message)
1133
+ eprime.set_backtrace(e.backtrace)
1134
+ eprime
1135
+ end
1242
1136
 
1243
1137
  #
1244
1138
  # Instance methods
@@ -1332,6 +1226,7 @@ class Tpkg
1332
1226
  @lock_pid_file = File.join(@lock_directory, 'pid')
1333
1227
  @locks = 0
1334
1228
  @installed_metadata = {}
1229
+ @available_packages_cache = {}
1335
1230
  end
1336
1231
 
1337
1232
  def source_to_local_directory(source)
@@ -1362,30 +1257,24 @@ class Tpkg
1362
1257
  else
1363
1258
  uri = http = localdate = remotedate = localdir = localpath = nil
1364
1259
 
1365
- ['metadata.yml', 'metadata.xml'].each do | metadata_file |
1366
- uri = URI.join(source, metadata_file)
1367
- http = Tpkg::gethttp(uri)
1260
+ uri = URI.join(source, 'metadata.yml')
1261
+ http = Tpkg::gethttp(uri)
1368
1262
 
1369
- # Calculate the path to the local copy of the metadata for this URI
1370
- localdir = source_to_local_directory(source)
1371
- localpath = File.join(localdir, metadata_file)
1372
- localdate = nil
1373
- if File.exist?(localpath)
1374
- localdate = File.mtime(localpath)
1375
- end
1263
+ # Calculate the path to the local copy of the metadata for this URI
1264
+ localdir = source_to_local_directory(source)
1265
+ localpath = File.join(localdir, 'metadata.yml')
1266
+ if File.exist?(localpath)
1267
+ localdate = File.mtime(localpath)
1268
+ end
1376
1269
 
1377
- # For now, we always have to hit the repo once to determine if
1378
- # it has metadata.yml or metadata.xml. In the future,
1379
- # we will only support metadata.yml
1380
- response = http.head(uri.path)
1381
- case response
1382
- when Net::HTTPSuccess
1383
- remotedate = Time.httpdate(response['Date'])
1384
- break
1385
- else
1386
- puts "Error fetching metadata from #{uri}: #{response.body}"
1387
- next
1388
- end
1270
+ # get last modified time of the metadata file from the server
1271
+ response = http.head(uri.path)
1272
+ case response
1273
+ when Net::HTTPSuccess
1274
+ remotedate = Time.httpdate(response['Date'])
1275
+ else
1276
+ puts "Error fetching metadata from #{uri}: #{response.body}"
1277
+ response.error! # Throws an exception
1389
1278
  end
1390
1279
 
1391
1280
  # Fetch the metadata if necessary
@@ -1416,22 +1305,9 @@ class Tpkg
1416
1305
  else
1417
1306
  metadata_contents = IO.read(localpath)
1418
1307
  end
1419
-
1420
- if uri.path =~ /yml/
1421
- Metadata::get_pkgs_metadata_from_yml_doc(metadata_contents, metadata, source)
1422
- else
1423
- # At this stage we just break up the metadata.xml document into
1424
- # per-package chunks and save them for further parsing later.
1425
- # This allows us to parse the whole metadata.xml just once, and
1426
- # saves us from having to further parse and convert the
1427
- # per-package chunks until if/when they are needed.
1428
- tpkg_metadata = REXML::Document.new(metadata_contents)
1429
- tpkg_metadata.elements.each('/tpkg_metadata/tpkg') do |metadata_xml|
1430
- name = metadata_xml.elements['name'].text
1431
- metadata[name] = [] if !metadata[name]
1432
- metadata[name] << Metadata.new(metadata_xml.to_s, 'xml', source)
1433
- end
1434
- end
1308
+ # This method will parse the yml doc and populate the metadata variable
1309
+ # with list of pkgs' metadata
1310
+ Metadata::get_pkgs_metadata_from_yml_doc(metadata_contents, metadata, source)
1435
1311
  end
1436
1312
  end
1437
1313
  @metadata = metadata
@@ -1754,7 +1630,7 @@ class Tpkg
1754
1630
  end
1755
1631
  end
1756
1632
 
1757
- # Returns an array of the tpkg.xml metadata for installed packages
1633
+ # Returns an array of metadata for installed packages
1758
1634
  def metadata_for_installed_packages
1759
1635
  metadata = {}
1760
1636
  if File.directory?(@installed_directory)
@@ -1851,44 +1727,54 @@ class Tpkg
1851
1727
 
1852
1728
  # Returns an array of packages which meet the given requirement
1853
1729
  def available_packages_that_meet_requirement(req=nil)
1854
- pkgs = []
1730
+ pkgs = nil
1855
1731
  puts "avail_pkgs_that_meet_req checking for #{req.inspect}" if @@debug
1856
- if req
1857
- if req[:type] == :native
1858
- load_available_native_packages(req[:name])
1859
- @available_native_packages[req[:name]].each do |pkg|
1860
- if Tpkg::package_meets_requirement?(pkg, req)
1861
- pkgs << pkg
1732
+ if @available_packages_cache[req]
1733
+ puts "avail_pkgs_that_meet_req returning cached result" if @@debug
1734
+ pkgs = @available_packages_cache[req]
1735
+ else
1736
+ pkgs = []
1737
+ if req
1738
+ req = req.clone # we're using req as the key for our cache, so it's important
1739
+ # that we clone it here. Otherwise, req can be changed later on from
1740
+ # the calling method and modify our cache inadvertently
1741
+ if req[:type] == :native
1742
+ load_available_native_packages(req[:name])
1743
+ @available_native_packages[req[:name]].each do |pkg|
1744
+ if Tpkg::package_meets_requirement?(pkg, req)
1745
+ pkgs << pkg
1746
+ end
1862
1747
  end
1863
- end
1864
- else
1865
- load_available_packages(req[:name])
1866
- @available_packages[req[:name]].each do |pkg|
1867
- if Tpkg::package_meets_requirement?(pkg, req)
1868
- pkgs << pkg
1748
+ else
1749
+ load_available_packages(req[:name])
1750
+ @available_packages[req[:name]].each do |pkg|
1751
+ if Tpkg::package_meets_requirement?(pkg, req)
1752
+ pkgs << pkg
1753
+ end
1869
1754
  end
1755
+ # There's a weird dicotomy here where @available_packages contains
1756
+ # available tpkg and native packages, and _installed_ native
1757
+ # packages, but not installed tpkgs. That's somewhat intentional,
1758
+ # as we don't want to cache the installed state since that might
1759
+ # change during a run. We probably should be consistent, and not
1760
+ # cache installed native packages either. However, we do have
1761
+ # some intelligent caching of the installed tpkg state which would
1762
+ # be hard to replicate for native packages, and this method gets
1763
+ # called a lot so re-running the native package query commands
1764
+ # frequently would not be acceptable. So maybe we have the right
1765
+ # design, and this just serves as a note that it is not obvious.
1766
+ pkgs.concat(installed_packages_that_meet_requirement(req))
1870
1767
  end
1871
- # There's a weird dicotomy here where @available_packages contains
1872
- # available tpkg and native packages, and _installed_ native
1873
- # packages, but not installed tpkgs. That's somewhat intentional,
1874
- # as we don't want to cache the installed state since that might
1875
- # change during a run. We probably should be consistent, and not
1876
- # cache installed native packages either. However, we do have
1877
- # some intelligent caching of the installed tpkg state which would
1878
- # be hard to replicate for native packages, and this method gets
1879
- # called a lot so re-running the native package query commands
1880
- # frequently would not be acceptable. So maybe we have the right
1881
- # design, and this just serves as a note that it is not obvious.
1882
- pkgs.concat(installed_packages_that_meet_requirement(req))
1768
+ else
1769
+ # We return everything available if given a nil requirement
1770
+ # We do not include native packages
1771
+ load_available_packages
1772
+ # @available_packages is a hash of pkgname => array of pkgs
1773
+ # Thus m is a 2 element array of [pkgname, array of pkgs]
1774
+ # And thus m[1] is the array of packages
1775
+ pkgs = @available_packages.collect{|m| m[1]}.flatten
1883
1776
  end
1884
- else
1885
- # We return everything available if given a nil requirement
1886
- # We do not include native packages
1887
- load_available_packages
1888
- # @available_packages is a hash of pkgname => array of pkgs
1889
- # Thus m is a 2 element array of [pkgname, array of pkgs]
1890
- # And thus m[1] is the array of packages
1891
- pkgs = @available_packages.collect{|m| m[1]}.flatten
1777
+ @available_packages_cache[req] = pkgs
1892
1778
  end
1893
1779
  pkgs
1894
1780
  end
@@ -1949,6 +1835,16 @@ class Tpkg
1949
1835
  files[:normalized] << File.join(@base, relocfile)
1950
1836
  end
1951
1837
  end
1838
+ def normalize_path(path,root=nil,base=nil)
1839
+ root ||= @file_system_root
1840
+ base ||= @base
1841
+ if path[0,1] == File::SEPARATOR
1842
+ normalized_path = File.join(root, path)
1843
+ else
1844
+ normalized_path = File.join(base, path)
1845
+ end
1846
+ normalized_path
1847
+ end
1952
1848
  def files_for_installed_packages(package_files=nil)
1953
1849
  files = {}
1954
1850
  if !package_files
@@ -2353,12 +2249,7 @@ class Tpkg
2353
2249
  metadata[:files][:files].each do |tpkgfile|
2354
2250
  if tpkgfile[:init]
2355
2251
  tpkg_path = tpkgfile[:path]
2356
- installed_path = nil
2357
- if tpkg_path[0,1] == File::SEPARATOR
2358
- installed_path = File.join(@file_system_root, tpkg_path)
2359
- else
2360
- installed_path = File.join(@base, tpkg_path)
2361
- end
2252
+ installed_path = normalize_path(tpkg_path)
2362
2253
  init_scripts[installed_path] = tpkgfile
2363
2254
  end
2364
2255
  end
@@ -2431,12 +2322,7 @@ class Tpkg
2431
2322
  metadata[:files][:files].each do |tpkgfile|
2432
2323
  if tpkgfile[:crontab]
2433
2324
  tpkg_path = tpkgfile[:path]
2434
- installed_path = nil
2435
- if tpkg_path[0,1] == File::SEPARATOR
2436
- installed_path = File.join(@file_system_root, tpkg_path)
2437
- else
2438
- installed_path = File.join(@base, tpkg_path)
2439
- end
2325
+ installed_path = normalize_path(tpkg_path)
2440
2326
  destinations[installed_path] = {}
2441
2327
 
2442
2328
  # Decide whether we're going to add the file to a per-user
@@ -2494,18 +2380,24 @@ class Tpkg
2494
2380
  IO.popen("#{externalpath} '#{pkgfile}' install", 'w') do |pipe|
2495
2381
  pipe.write(data)
2496
2382
  end
2383
+ if !$?.success?
2384
+ raise "Exit value #{$?.exitstatus}"
2385
+ end
2497
2386
  rescue => e
2498
2387
  # Tell the user which external and package were involved, otherwise
2499
2388
  # failures in externals are very hard to debug
2500
- raise e.exception("External #{name} #{operation} for #{File.basename(pkgfile)}: " + e.message)
2389
+ raise Tpkg.wrap_exception(e, "External #{name} #{operation} for #{File.basename(pkgfile)}: " + e.message)
2501
2390
  end
2502
2391
  when :remove
2503
2392
  begin
2504
2393
  IO.popen("#{externalpath} '#{pkgfile}' remove", 'w') do |pipe|
2505
2394
  pipe.write(data)
2506
2395
  end
2396
+ if !$?.success?
2397
+ raise "Exit value #{$?.exitstatus}"
2398
+ end
2507
2399
  rescue => e
2508
- raise e.exception("External #{name} #{operation} for #{File.basename(pkgfile)}: " + e.message)
2400
+ raise Tpkg.wrap_exception(e, "External #{name} #{operation} for #{File.basename(pkgfile)}: " + e.message)
2509
2401
  end
2510
2402
  else
2511
2403
  raise "Bug, unknown external operation #{operation}"
@@ -2520,6 +2412,14 @@ class Tpkg
2520
2412
  ret_val = 0
2521
2413
  metadata = Tpkg::metadata_from_package(package_file)
2522
2414
 
2415
+ # set env variable to let pre/post install know whether this unpack
2416
+ # is part of an install or upgrade
2417
+ if options[:is_doing_upgrade]
2418
+ ENV['TPKG_ACTION'] = "upgrade"
2419
+ else
2420
+ ENV['TPKG_ACTION'] = "install"
2421
+ end
2422
+
2523
2423
  # Unpack files in a temporary directory
2524
2424
  # I'd prefer to unpack on the fly so that the user doesn't need to
2525
2425
  # have disk space to hold three copies of the package (the package
@@ -2533,22 +2433,19 @@ class Tpkg
2533
2433
  system("#{extract_tpkg_tar_cmd} | #{@tar} #{@@taroptions} -C #{workdir} -xpf -")
2534
2434
  files_info = {} # store perms, uid, gid, etc. for files
2535
2435
  checksums_of_decrypted_files = {}
2536
- root_dir = File.join(workdir, 'tpkg', 'root')
2537
- reloc_dir = File.join(workdir, 'tpkg', 'reloc')
2538
- rel_root_dir = File.join('tpkg', 'root')
2539
- rel_reloc_dir = File.join('tpkg', 'reloc')
2540
-
2436
+
2541
2437
  # Get list of conflicting files/directories & store their perm/ownership. That way, we can
2542
2438
  # set them to the correct values later on in order to preserve them.
2543
- # TODO: verify this command works on all platforms
2439
+ rel_root_dir = File.join('tpkg', 'root')
2440
+ rel_reloc_dir = File.join('tpkg', 'reloc')
2544
2441
  files = `#{extract_tpkg_tar_cmd} | #{@tar} #{@@taroptions} -tf -`
2545
2442
  files = files.split("\n")
2546
2443
  conflicting_files = {}
2547
2444
  files.each do | file |
2548
2445
  if file =~ /^#{rel_root_dir}/
2549
- possible_conflicting_file = "#{@file_system_root}/#{file[rel_root_dir.length ..-1]}"
2446
+ possible_conflicting_file = File.join(@file_system_root, file[rel_root_dir.length ..-1])
2550
2447
  elsif file =~ /^#{rel_reloc_dir}/
2551
- possible_conflicting_file = "#{@base}/#{file[rel_reloc_dir.length + 1..-1]}"
2448
+ possible_conflicting_file = File.join(@base, file[rel_reloc_dir.length + 1..-1])
2552
2449
  end
2553
2450
  if possible_conflicting_file && (File.exists?(possible_conflicting_file) && !File.symlink?(possible_conflicting_file))
2554
2451
  conflicting_files[File.join(workdir, file)] = File.stat(possible_conflicting_file)
@@ -2568,18 +2465,14 @@ class Tpkg
2568
2465
  default_gid = DEFAULT_OWNERSHIP_UID
2569
2466
  default_perms = nil
2570
2467
 
2571
- if metadata[:files] && metadata[:files][:file_defaults]
2572
- if metadata[:files][:file_defaults][:posix]
2573
- if metadata[:files][:file_defaults][:posix][:owner]
2574
- default_uid = Tpkg::lookup_uid(metadata[:files][:file_defaults][:posix][:owner])
2575
- end
2576
- if metadata[:files][:file_defaults][:posix][:group]
2577
- default_gid = Tpkg::lookup_gid(metadata[:files][:file_defaults][:posix][:group])
2578
- end
2579
- if metadata[:files][:file_defaults][:posix][:perms]
2580
- default_perms = metadata[:files][:file_defaults][:posix][:perms]
2581
- end
2582
- end
2468
+ if (metadata[:files][:file_defaults][:posix][:owner] rescue nil)
2469
+ default_uid = Tpkg::lookup_uid(metadata[:files][:file_defaults][:posix][:owner])
2470
+ end
2471
+ if (metadata[:files][:file_defaults][:posix][:group] rescue nil)
2472
+ default_gid = Tpkg::lookup_gid(metadata[:files][:file_defaults][:posix][:group])
2473
+ end
2474
+ if (metadata[:files][:file_defaults][:posix][:perms] rescue nil)
2475
+ default_perms = metadata[:files][:file_defaults][:posix][:perms]
2583
2476
  end
2584
2477
 
2585
2478
  # Set default dir uid/gid to be same as for file.
@@ -2587,20 +2480,18 @@ class Tpkg
2587
2480
  default_dir_gid = default_gid
2588
2481
  default_dir_perms = 0755
2589
2482
 
2590
- if metadata[:files] && metadata[:files][:dir_defaults]
2591
- if metadata[:files][:dir_defaults][:posix]
2592
- if metadata[:files][:dir_defaults][:posix][:owner]
2593
- default_dir_uid = Tpkg::lookup_uid(metadata[:files][:dir_defaults][:posix][:owner])
2594
- end
2595
- if metadata[:files][:dir_defaults][:posix][:group]
2596
- default_dir_gid = Tpkg::lookup_gid(metadata[:files][:dir_defaults][:posix][:group])
2597
- end
2598
- if metadata[:files][:dir_defaults][:posix][:perms]
2599
- default_dir_perms = metadata[:files][:dir_defaults][:posix][:perms]
2600
- end
2601
- end
2483
+ if (metadata[:files][:dir_defaults][:posix][:owner] rescue nil)
2484
+ default_dir_uid = Tpkg::lookup_uid(metadata[:files][:dir_defaults][:posix][:owner])
2485
+ end
2486
+ if (metadata[:files][:dir_defaults][:posix][:group] rescue nil)
2487
+ default_dir_gid = Tpkg::lookup_gid(metadata[:files][:dir_defaults][:posix][:group])
2488
+ end
2489
+ if (metadata[:files][:dir_defaults][:posix][:perms] rescue nil)
2490
+ default_dir_perms = metadata[:files][:dir_defaults][:posix][:perms]
2602
2491
  end
2603
2492
 
2493
+ root_dir = File.join(workdir, 'tpkg', 'root')
2494
+ reloc_dir = File.join(workdir, 'tpkg', 'reloc')
2604
2495
  Find.find(root_dir, reloc_dir) do |f|
2605
2496
  # If the package doesn't contain either of the top level
2606
2497
  # directories we need to skip them, find will pass them to us
@@ -2636,12 +2527,7 @@ class Tpkg
2636
2527
  # Handle any decryption and ownership/permissions on specific files
2637
2528
  metadata[:files][:files].each do |tpkgfile|
2638
2529
  tpkg_path = tpkgfile[:path]
2639
- working_path = nil
2640
- if tpkg_path[0,1] == File::SEPARATOR
2641
- working_path = File.join(workdir, 'tpkg', 'root', tpkg_path)
2642
- else
2643
- working_path = File.join(workdir, 'tpkg', 'reloc', tpkg_path)
2644
- end
2530
+ working_path = normalize_path(tpkg_path, File.join(workdir, 'tpkg', 'root'), File.join(workdir, 'tpkg', 'reloc'))
2645
2531
  if !File.exist?(working_path) && !File.symlink?(working_path)
2646
2532
  raise "tpkg.xml for #{File.basename(package_file)} references file #{tpkg_path} but that file is not in the package"
2647
2533
  end
@@ -2698,7 +2584,7 @@ class Tpkg
2698
2584
  end
2699
2585
 
2700
2586
  digest = Digest::SHA256.hexdigest(File.read(working_path))
2701
- # get checksum for the decrypted file. Will be used for creating file_metadata.xml
2587
+ # get checksum for the decrypted file. Will be used for creating file_metadata
2702
2588
  checksums_of_decrypted_files[File.expand_path(tpkg_path)] = digest
2703
2589
  end
2704
2590
  end
@@ -3048,35 +2934,41 @@ class Tpkg
3048
2934
  r
3049
2935
  end
3050
2936
 
3051
- def run_externals_for_install(metadata, workdir, externals_to_skip)
2937
+ def run_externals_for_install(metadata, workdir, externals_to_skip=[])
3052
2938
  metadata[:externals].each do |external|
3053
- # If the external references a datafile or datascript then read/run it
3054
- # now that we've unpacked the package contents and have the file/script
3055
- # available. This will get us the data for the external.
3056
- if external[:datafile] || external[:datascript]
3057
- pwd = Dir.pwd
3058
- # chdir into the working directory so that the user can specify a
3059
- # relative path to their file/script.
3060
- Dir.chdir(File.join(workdir, 'tpkg'))
3061
- if external[:datafile]
3062
- # Read the file
3063
- external[:data] = IO.read(external[:datafile])
3064
- # Drop the datafile key so that we don't waste time re-reading the
3065
- # datafile again in the future.
3066
- external.delete(:datafile)
3067
- elsif external[:datascript]
3068
- # Run the script
3069
- IO.popen(external[:datascript]) do |pipe|
3070
- external[:data] = pipe.read
2939
+ if !externals_to_skip || !externals_to_skip.include?(external)
2940
+ # If the external references a datafile or datascript then read/run it
2941
+ # now that we've unpacked the package contents and have the file/script
2942
+ # available. This will get us the data for the external.
2943
+ if external[:datafile] || external[:datascript]
2944
+ pwd = Dir.pwd
2945
+ # chdir into the working directory so that the user can specify a
2946
+ # relative path to their file/script.
2947
+ Dir.chdir(File.join(workdir, 'tpkg'))
2948
+ begin
2949
+ if external[:datafile]
2950
+ # Read the file
2951
+ external[:data] = IO.read(external[:datafile])
2952
+ # Drop the datafile key so that we don't waste time re-reading the
2953
+ # datafile again in the future.
2954
+ external.delete(:datafile)
2955
+ elsif external[:datascript]
2956
+ # Run the script
2957
+ IO.popen(external[:datascript]) do |pipe|
2958
+ external[:data] = pipe.read
2959
+ end
2960
+ if !$?.success?
2961
+ raise "Datascript #{external[:datascript]} for package #{File.basename(metadata[:filename])} had exit value #{$?.exitstatus}"
2962
+ end
2963
+ # Drop the datascript key so that we don't waste time re-running the
2964
+ # datascript again in the future.
2965
+ external.delete(:datascript)
2966
+ end
2967
+ ensure
2968
+ # Switch back to our previous directory
2969
+ Dir.chdir(pwd)
3071
2970
  end
3072
- # Drop the datascript key so that we don't waste time re-running the
3073
- # datascript again in the future.
3074
- external.delete(:datascript)
3075
2971
  end
3076
- # Switch back to our previous directory
3077
- Dir.chdir(pwd)
3078
- end
3079
- if !externals_to_skip || !externals_to_skip.include?(external)
3080
2972
  run_external(metadata[:filename], :install, external[:name], external[:data])
3081
2973
  end
3082
2974
  end if metadata[:externals]
@@ -3091,7 +2983,7 @@ class Tpkg
3091
2983
  metadata.write(metadata_file)
3092
2984
  metadata_file.close
3093
2985
 
3094
- # Save file_metadata.yml for this pkg
2986
+ # Save file_metadata for this pkg
3095
2987
  if File.exist?(File.join(workdir, 'tpkg', 'file_metadata.bin'))
3096
2988
  file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.bin')), 'bin')
3097
2989
  elsif File.exist?(File.join(workdir, 'tpkg', 'file_metadata.yml'))
@@ -3102,8 +2994,11 @@ class Tpkg
3102
2994
  if file_metadata
3103
2995
  file_metadata[:package_file] = File.basename(package_file)
3104
2996
  file_metadata[:files].each do |file|
2997
+ # update file_metadata with user/group ownership and permission
3105
2998
  acl = files_info[file[:path]]
3106
2999
  file.merge!(acl) unless acl.nil?
3000
+
3001
+ # update file_metadata with the checksums of decrypted files
3107
3002
  digest = checksums_of_decrypted_files[File.expand_path(file[:path])]
3108
3003
  if digest
3109
3004
  digests = file[:checksum][:digests]
@@ -3329,22 +3224,62 @@ class Tpkg
3329
3224
  conflicts
3330
3225
  end
3331
3226
 
3332
- def check_for_conflicting_pkgs(pkgs_to_check)
3333
- # loop through packages that we're interested in, check for conflict listing,
3334
- # see if there are any conflicts among each other
3335
- pkgs_to_check.each do |pkg1|
3227
+ # This method is called by install and upgrade method to make sure there is
3228
+ # no conflicts between the existing pkgs and the pkgs we're about to install
3229
+ def handle_conflicting_pkgs(installed_pkgs, pkgs_to_install, options ={})
3230
+ conflicting_pkgs = []
3231
+
3232
+ # check if existing pkgs have conflicts with pkgs we're about to install
3233
+ installed_pkgs.each do |pkg1|
3234
+ next if pkg1[:metadata][:conflicts].nil?
3235
+ pkg1[:metadata][:conflicts].each do | conflict |
3236
+ pkgs_to_install.each do |pkg2|
3237
+ if Tpkg::package_meets_requirement?(pkg2, conflict)
3238
+ conflicting_pkgs << pkg1
3239
+ end
3240
+ end
3241
+ end
3242
+ end
3243
+
3244
+ # check if pkgs we're about to install conflict with existing pkgs
3245
+ pkgs_to_install.each do |pkg1|
3246
+ next if pkg1[:metadata][:conflicts].nil?
3247
+ pkg1[:metadata][:conflicts].each do | conflict |
3248
+ conflicting_pkgs |= installed_packages_that_meet_requirement(conflict)
3249
+ end
3250
+ end
3251
+
3252
+ # Check if there are conflicts among the pkgs we're about to install
3253
+ # For these type of conflicts, we can't proceed, so raise exception.
3254
+ pkgs_to_install.each do |pkg1|
3336
3255
  # native package might not have conflicts defined so skip
3337
3256
  next if pkg1[:metadata][:conflicts].nil?
3338
3257
  pkg1[:metadata][:conflicts].each do | conflict |
3339
- pkgs_to_check.each do |pkg2|
3258
+ pkgs_to_install.each do |pkg2|
3340
3259
  if Tpkg::package_meets_requirement?(pkg2, conflict)
3341
- raise "Package conflicts between #{pkg2.inspect} and #{pkg1.inspect}"
3260
+ raise "Package conflicts between #{pkg2[:metadata][:filename]} and #{pkg1[:metadata][:filename]}"
3342
3261
  end
3343
3262
  end
3344
3263
  end
3345
3264
  end
3265
+
3266
+ # Report to the users if there are conflicts
3267
+ unless conflicting_pkgs.empty?
3268
+ puts "The package(s) you're trying to install conflict with the following package(s):"
3269
+ conflicting_pkgs = conflicting_pkgs.collect{|pkg|pkg[:metadata][:filename]}
3270
+ puts conflicting_pkgs.join("\n")
3271
+ if options[:force_replace]
3272
+ puts "Attemping to replace the conflicting packages."
3273
+ success = remove(conflicting_pkgs)
3274
+ return success
3275
+ else
3276
+ puts "Try removing the conflicting package(s) first, or rerun tpkg with the --force-replace option."
3277
+ return false
3278
+ end
3279
+ end
3280
+ return true
3346
3281
  end
3347
-
3282
+
3348
3283
  def prompt_for_conflicting_files(package_file, mode=CHECK_INSTALL)
3349
3284
  if !@@prompt
3350
3285
  return true
@@ -3404,29 +3339,18 @@ class Tpkg
3404
3339
  end
3405
3340
 
3406
3341
  # See parse_requests for format of requests
3407
- def install(requests, passphrase=nil)
3342
+ def install(requests, passphrase=nil, options={})
3408
3343
  ret_val = 0
3409
3344
  requirements = []
3410
3345
  packages = {}
3411
3346
  lock
3412
-
3413
3347
  parse_requests(requests, requirements, packages)
3414
3348
  check_requests(packages)
3349
+
3415
3350
  core_packages = []
3416
- #currently_installed_requirements = []
3417
3351
  requirements.each do |req|
3418
3352
  core_packages << req[:name] if !core_packages.include?(req[:name])
3419
-
3420
- # This was here to ensure that nothing went backwards. But I guess in the
3421
- # install case (as opposed to upgrade) going backwards can't really happen,
3422
- # we may just install an older version alongside a newer version, which is
3423
- # perfectly fine.
3424
- # currently_installed_requirements.concat(
3425
- # requirements_for_currently_installed_package(req[:name]))
3426
3353
  end
3427
- #requirements.concat(currently_installed_requirements).uniq!
3428
-
3429
-
3430
3354
 
3431
3355
  puts "install calling best_solution" if @@debug
3432
3356
  puts "install requirements: #{requirements.inspect}" if @@debug
@@ -3438,7 +3362,8 @@ class Tpkg
3438
3362
  raise "Unable to resolve dependencies"
3439
3363
  end
3440
3364
 
3441
- check_for_conflicting_pkgs(solution_packages | installed_packages)
3365
+ success = handle_conflicting_pkgs(installed_packages, solution_packages, options)
3366
+ return false if !success
3442
3367
 
3443
3368
  if !prompt_for_install(solution_packages, 'installed')
3444
3369
  unlock
@@ -3564,8 +3489,7 @@ class Tpkg
3564
3489
 
3565
3490
  # send udpate back to reporting server
3566
3491
  unless @report_server.nil?
3567
- options = {:newly_installed => newly_installed,
3568
- :currently_installed => currently_installed}
3492
+ options = {:newly_installed => newly_installed, :currently_installed => currently_installed}
3569
3493
  send_update_to_server(options)
3570
3494
  end
3571
3495
  unlock
@@ -3573,7 +3497,8 @@ class Tpkg
3573
3497
  end
3574
3498
 
3575
3499
  # This method can also be used for doing downgrade
3576
- def upgrade(requests=nil, passphrase=nil, downgrade=false)
3500
+ def upgrade(requests=nil, passphrase=nil, options={})
3501
+ downgrade = options[:downgrade] || false
3577
3502
  ret_val = 0
3578
3503
  requirements = []
3579
3504
  packages = {}
@@ -3655,7 +3580,8 @@ class Tpkg
3655
3580
  raise "Unable to find solution for upgrading. Please verify that you specified the correct package(s) for upgrade."
3656
3581
  end
3657
3582
 
3658
- check_for_conflicting_pkgs(solution_packages | installed_packages)
3583
+ success = handle_conflicting_pkgs(installed_packages, solution_packages, options)
3584
+ return false if !success
3659
3585
 
3660
3586
  if downgrade
3661
3587
  prompt_action = 'downgraded'
@@ -3675,7 +3601,6 @@ class Tpkg
3675
3601
  installed_files = files_for_installed_packages
3676
3602
  removed_pkgs = [] # keep track of what we removed so far
3677
3603
  while pkg = solution_packages.shift
3678
- # solution_packages.each do |pkg|
3679
3604
  if pkg[:source] == :currently_installed ||
3680
3605
  pkg[:source] == :native_installed
3681
3606
  # Nothing to do for packages currently installed
@@ -3798,7 +3723,9 @@ class Tpkg
3798
3723
  end
3799
3724
  end if pkg[:metadata][:dependencies]
3800
3725
  if can_unpack
3801
- ret_val |= unpack(pkgfile, :passphrase => passphrase, :externals_to_skip => externals_to_skip)
3726
+ is_doing_upgrade = true if removed_pkgs.include?(pkg[:metadata][:name])
3727
+ ret_val |= unpack(pkgfile, :passphrase => passphrase, :externals_to_skip => externals_to_skip,
3728
+ :is_doing_upgrade => is_doing_upgrade)
3802
3729
  end
3803
3730
 
3804
3731
  has_updates = true
@@ -3816,8 +3743,7 @@ class Tpkg
3816
3743
  if !has_updates
3817
3744
  puts "No updates available"
3818
3745
  elsif !@report_server.nil?
3819
- options = {:newly_installed => newly_installed,
3820
- :removed => removed,
3746
+ options = {:newly_installed => newly_installed, :removed => removed,
3821
3747
  :currently_installed => currently_installed}
3822
3748
  send_update_to_server(options)
3823
3749
  end
@@ -4050,8 +3976,7 @@ class Tpkg
4050
3976
 
4051
3977
  # send update back to reporting server
4052
3978
  unless @report_server.nil? || options[:upgrade]
4053
- options = {:removed => removed,
4054
- :currently_installed => currently_installed}
3979
+ options = {:removed => removed, :currently_installed => currently_installed}
4055
3980
  send_update_to_server(options)
4056
3981
  end
4057
3982
 
@@ -4088,7 +4013,7 @@ class Tpkg
4088
4013
  file_metadata = FileMetadata.new(File.read(file_metadata_xml), 'xml')
4089
4014
  else
4090
4015
  errors = []
4091
- errors << "Can't find file_metadata.xml or file_metadata.yml file. Most likely this is because the package was created before the verify feature was added"
4016
+ errors << "Can't find file metadata. Most likely this is because the package was created before the verify feature was added"
4092
4017
  results[package_file] = errors
4093
4018
  return results
4094
4019
  end
@@ -4138,7 +4063,6 @@ class Tpkg
4138
4063
  errors << "File is missing"
4139
4064
  else
4140
4065
  # get actual values
4141
- #chksum_actual = Digest::SHA256.file(fp).hexdigest if File.file?(fp)
4142
4066
  chksum_actual = Digest::SHA256.hexdigest(File.read(fp)) if File.file?(fp)
4143
4067
  uid_actual = File.stat(fp).uid
4144
4068
  gid_actual = File.stat(fp).gid
@@ -4316,7 +4240,7 @@ class Tpkg
4316
4240
 
4317
4241
  # populate the depencency map
4318
4242
  depended_on.each do | req_pkg |
4319
- dependency_mapping[req_pkg[:metadata][:filename]] = [] if dependency_mapping[req_pkg[:metadata][:filename]].nil?
4243
+ dependency_mapping[req_pkg[:metadata][:filename]] ||= []
4320
4244
  dependency_mapping[req_pkg[:metadata][:filename]] << pkg
4321
4245
  end
4322
4246
  end
@@ -4360,7 +4284,7 @@ class Tpkg
4360
4284
  # log changes of pkgs that were installed/removed
4361
4285
  def log_changes(options={})
4362
4286
  msg = ""
4363
- user = Etc.getlogin
4287
+ user = Etc.getlogin || Etc.getpwuid(Process.uid).name
4364
4288
  newly_installed = removed = []
4365
4289
  newly_installed = options[:newly_installed] if options[:newly_installed]
4366
4290
  removed = options[:removed] if options[:removed]
@@ -4379,7 +4303,7 @@ class Tpkg
4379
4303
 
4380
4304
  def send_update_to_server(options={})
4381
4305
  request = {"client"=>Facter['fqdn'].value}
4382
- request[:user] = Etc.getlogin
4306
+ request[:user] = Etc.getlogin || Etc.getpwuid(Process.uid).name
4383
4307
  request[:tpkg_home] = ENV['TPKG_HOME']
4384
4308
 
4385
4309
  if options[:currently_installed]