tpkg 1.19.2 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. data/Rakefile +2 -2
  2. data/bin/gem2tpkg +1 -1
  3. data/bin/tpkg +11 -2
  4. data/lib/tpkg.rb +241 -141
  5. data/lib/tpkg/deployer.rb +22 -48
  6. metadata +16 -16
data/Rakefile CHANGED
@@ -4,8 +4,8 @@ spec = Gem::Specification.new do |s|
4
4
  s.summary = 'tpkg Application Packaging & Deployment'
5
5
  s.add_dependency('facter')
6
6
  s.add_dependency('net-ssh')
7
- s.add_dependency('kwalify')
8
- s.version = '1.19.2'
7
+ s.add_dependency('ddao-kwalify')
8
+ s.version = '1.20.0'
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'
@@ -12,7 +12,7 @@ require 'facter'
12
12
 
13
13
  # We don't want to just use the first gem command in the user's PATH by
14
14
  # default, as that may not be a tpkg gem. I.e. /usr/bin/gem on Mac OS X.
15
- DEFAULT_GEM_COMMAND = "#{Tpkg::DEFAULT_BASE}/ruby-1.8/bin/gem"
15
+ DEFAULT_GEM_COMMAND = "#{Tpkg::DEFAULT_BASE}/ruby-ypc/bin/gem"
16
16
 
17
17
  # Haven't found a Ruby method for creating temporary directories,
18
18
  # so create a temporary file and replace it with a directory.
data/bin/tpkg CHANGED
@@ -30,11 +30,17 @@ require 'tpkg'
30
30
  @rerun_with_sudo = false
31
31
  @tpkg_options = {}
32
32
  @init_options = {}
33
+ @compress = nil
34
+
33
35
 
34
36
  def rerun_with_sudo_if_necessary
35
37
  if Process.euid != 0 && @sudo
36
38
  warn "Executing with sudo"
37
- exec('sudo', $0, *ARGV)
39
+ if ENV['TPKG_HOME']
40
+ exec('sudo', 'env', "TPKG_HOME=#{ENV['TPKG_HOME']}", $0, *ARGV)
41
+ else
42
+ exec('sudo', $0, *ARGV)
43
+ end
38
44
  end
39
45
  end
40
46
 
@@ -234,6 +240,9 @@ opts.on('--deploy-as', '=USERNAME', 'What username to use for deploying to remot
234
240
  @deploy_options["deploy-as"] = opt
235
241
  @deploy_params = @deploy_params - ['--deploy-as']
236
242
  end
243
+ opts.on('--compress', '=[TYPE]', 'Compress files when making packages.') do |opt|
244
+ @compress = opt
245
+ end
237
246
  opts.on('--debug', 'Print lots of messages about what tpkg is doing') do |opt|
238
247
  @debug = opt
239
248
  Tpkg::set_debug(@debug)
@@ -336,7 +345,7 @@ end
336
345
  ret_val = 0
337
346
  case @action
338
347
  when :make
339
- pkgfile = Tpkg::make_package(@action_value, passphrase_callback, {:force => @force})
348
+ pkgfile = Tpkg::make_package(@action_value, passphrase_callback, {:force => @force, :compress => @compress})
340
349
  if pkgfile
341
350
  puts "Package is #{pkgfile}"
342
351
  else
@@ -56,7 +56,7 @@ require 'kwalify' # for validating yaml
56
56
 
57
57
  class Tpkg
58
58
 
59
- VERSION = '1.19.2'
59
+ VERSION = '1.20.0'
60
60
  CONFIGDIR = '/etc'
61
61
 
62
62
  GENERIC_ERR = 1
@@ -86,7 +86,7 @@ class Tpkg
86
86
  # Raises an exception if a suitable tar cannot be found
87
87
  @@tar = nil
88
88
  @@taroptions = ""
89
- @@tartype = nil
89
+ @@tarinfo = {:version => 'unknown'}
90
90
  TARNAMES = ['tar', 'gtar', 'gnutar', 'bsdtar']
91
91
  def self.find_tar
92
92
  if !@@tar
@@ -96,15 +96,17 @@ class Tpkg
96
96
  if File.executable?(File.join(path, tarname))
97
97
  IO.popen("#{File.join(path, tarname)} --version 2>/dev/null") do |pipe|
98
98
  pipe.each_line do |line|
99
- if line.include?('GNU tar')
100
- @@tartype = 'gnu'
99
+ if line.include?('GNU tar')
100
+ @@tarinfo[:type] = 'gnu'
101
101
  @@tar = File.join(path, tarname)
102
- throw :tar_found
103
102
  elsif line.include?('bsdtar')
104
- @@tartype = 'bsd'
103
+ @@tarinfo[:type] = 'bsd'
105
104
  @@tar = File.join(path, tarname)
106
- throw :tar_found
107
105
  end
106
+ if line =~ /(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)/
107
+ @@tarinfo[:version] = [$1, $2, $3].compact.join(".")
108
+ end
109
+ throw :tar_found if @@tar
108
110
  end
109
111
  end
110
112
  end
@@ -120,7 +122,7 @@ class Tpkg
120
122
  # gnu tar should just ignore them and gives a warning. This is what the latest gnu tar
121
123
  # will do. However, on older gnu tar, it only threw an error at the end. The work
122
124
  # around is to explicitly tell gnu tar to ignore those extensions.
123
- if @@tartype == 'gnu'
125
+ if @@tarinfo[:type] == 'gnu' && @@tarinfo[:version] != 'unknown' && @@tarinfo[:version] >= '1.15.1'
124
126
  @@taroptions = "--pax-option='delete=SCHILY.*'"
125
127
  end
126
128
  @@tar.dup
@@ -265,14 +267,18 @@ class Tpkg
265
267
  metadata_text = File.read(metadata_file)
266
268
  metadata = Metadata.new(metadata_text, metadata_format)
267
269
 
270
+ # This is for when we're in developement mode or when installed as gem
271
+ if File.exists?(File.join(File.dirname(File.dirname(__FILE__)), "schema"))
272
+ schema_dir = File.join(File.dirname(File.dirname(__FILE__)), "schema")
268
273
  # This is the directory where we put our dtd/schema for validating
269
274
  # the metadata file
270
- if File.exist?(File.join(CONFIGDIR, 'tpkg', 'schema'))
275
+ elsif File.exist?(File.join(CONFIGDIR, 'tpkg', 'schema'))
271
276
  schema_dir = File.join(CONFIGDIR, 'tpkg', 'schema')
272
- else # This is for when we're in developement mode or when installed as gem
273
- schema_dir = File.join(File.dirname(File.dirname(__FILE__)), "schema")
277
+ else
278
+ warn "Warning: unable to find schema for tpkg.yml"
274
279
  end
275
- errors = metadata.validate(schema_dir)
280
+
281
+ errors = metadata.validate(schema_dir) if schema_dir
276
282
  if errors && !errors.empty?
277
283
  puts "Bad metadata file. Possible error(s):"
278
284
  errors.each {|e| puts e }
@@ -362,6 +368,11 @@ class Tpkg
362
368
  csx.puts('</tpkg_checksums>')
363
369
  end
364
370
 
371
+ # compress if needed
372
+ if options[:compress]
373
+ tpkgfile = compress_file(tpkgfile, options[:compress])
374
+ end
375
+
365
376
  # Tar up checksum.xml and the main tarball
366
377
  system("#{find_tar} -C #{workdir} -cf #{pkgfile} #{package_filename}") || raise("Final package creation failed")
367
378
  ensure
@@ -508,7 +519,9 @@ class Tpkg
508
519
  raise("Unrecognized checksum algorithm #{checksum.elements['algorithm']}")
509
520
  end
510
521
  # Extract tpkg.tar from the package and digest it
511
- IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions}") do |pipe|
522
+ extract_tpkg_tar_command = cmd_to_extract_tpkg_tar(package_file, topleveldir)
523
+ IO.popen(extract_tpkg_tar_command) do |pipe|
524
+ #IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions}") do |pipe|
512
525
  # Package files can be quite large, so we digest the package in
513
526
  # chunks. A survey of the Internet turns up someone who tested
514
527
  # various chunk sizes on various platforms and found 4k to be
@@ -537,10 +550,13 @@ class Tpkg
537
550
  ['yml','xml'].each do |format|
538
551
  file = File.join('tpkg', "tpkg.#{format}")
539
552
 
553
+ extract_tpkg_tar_command = cmd_to_extract_tpkg_tar(package_file, topleveldir)
554
+
540
555
  # use popen3 instead of popen because popen display stderr when there's an error such as
541
556
  # tpkg.yml not being there, which is something we want to ignore since old tpkg doesn't
542
557
  # have tpkg.yml file
543
- stdin, stdout, stderr = Open3.popen3("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{find_tar} -xf - -O #{file}")
558
+ #stdin, stdout, stderr = Open3.popen3("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{find_tar} -xf - -O #{file}")
559
+ stdin, stdout, stderr = Open3.popen3("#{extract_tpkg_tar_command} | #{find_tar} -xf - -O #{file}")
544
560
  filecontent = stdout.read
545
561
  if filecontent.nil? or filecontent.empty?
546
562
  next
@@ -566,7 +582,8 @@ class Tpkg
566
582
  verify_package_checksum(package_file)
567
583
  # Extract and parse tpkg.xml
568
584
  tpkg_xml = nil
569
- IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{find_tar} -xf - -O #{File.join('tpkg', 'tpkg.xml')}") do |pipe|
585
+ extract_tpkg_tar_command = cmd_to_extract_tpkg_tar(package_file, topleveldir)
586
+ IO.popen("#{extract_tpkg_tar_command} | #{find_tar} -xf - -O #{File.join('tpkg', 'tpkg.xml')}") do |pipe|
570
587
  tpkg_xml = REXML::Document.new(pipe.read)
571
588
  end
572
589
  if !$?.success?
@@ -671,7 +688,8 @@ class Tpkg
671
688
  # And write that out to metadata.yml
672
689
  metadata_tmpfile = Tempfile.new('metadata.yml', dest)
673
690
  metadata.each do | metadata |
674
- YAML::dump(metadata.to_hash, metadata_tmpfile)
691
+ YAML::dump(metadata.to_hash.recursively{|h| h.stringify_keys }, metadata_tmpfile)
692
+ #YAML::dump(metadata.to_hash, metadata_tmpfile)
675
693
  end
676
694
  metadata_tmpfile.close
677
695
  File.chmod(0644, metadata_tmpfile.path)
@@ -718,6 +736,12 @@ class Tpkg
718
736
  Facter['lsbmajdistrelease'].value &&
719
737
  !Facter['lsbmajdistrelease'].value.empty?
720
738
  osver = Facter['lsbmajdistrelease'].value
739
+ elsif operatingsystem == 'Ubuntu'
740
+ # Work around lack of lsbmajdistrelease on older versions of Ubuntu
741
+ # due to older version of facter. Support for lsbmajdistrelease on
742
+ # Ubuntu was added in facter 1.5.3, but there's no good way to force
743
+ # older Ubuntu systems to a newer version of facter.
744
+ osver = Facter['lsbdistrelease'].value.split('.').first
721
745
  elsif Facter['kernel'] &&
722
746
  Facter['kernel'].value == 'Darwin' &&
723
747
  Facter['macosx_productversion'] &&
@@ -940,7 +964,8 @@ class Tpkg
940
964
  files[:root] = []
941
965
  files[:reloc] = []
942
966
  topleveldir = package_toplevel_directory(package_file)
943
- IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions}| #{find_tar} #{@@taroptions} -tf -") do |pipe|
967
+ extract_tpkg_tar_cmd = cmd_to_extract_tpkg_tar(package_file, topleveldir)
968
+ IO.popen("#{extract_tpkg_tar_cmd} | #{find_tar} #{@@taroptions} -tf -") do |pipe|
944
969
  pipe.each do |file|
945
970
  file.chomp!
946
971
  if file =~ Regexp.new(File.join('tpkg', 'root'))
@@ -1104,7 +1129,8 @@ class Tpkg
1104
1129
  begin
1105
1130
  topleveldir = Tpkg::package_toplevel_directory(package_file)
1106
1131
  workdir = Tpkg::tempdir(topleveldir)
1107
- system("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions}| #{find_tar} #{@@taroptions} -C #{workdir} -xpf -")
1132
+ extract_tpkg_tar_command = cmd_to_extract_tpkg_tar(package_file, topleveldir)
1133
+ system("#{extract_tpkg_tar_command} | #{find_tar} #{@@taroptions} -C #{workdir} -xpf -")
1108
1134
 
1109
1135
  if File.exist?(File.join(workdir,"tpkg", "tpkg.yml"))
1110
1136
  metadata_file = File.join(workdir,"tpkg", "tpkg.yml")
@@ -1162,6 +1188,53 @@ class Tpkg
1162
1188
  end
1163
1189
  return perms, uid, gid
1164
1190
  end
1191
+
1192
+ # Given a package file, figure out of tpkg.tar was compressed
1193
+ def self.get_compression(package_file)
1194
+ compression = nil
1195
+ IO.popen("#{find_tar} -tf #{package_file} #{@@taroptions}") do |pipe|
1196
+ pipe.each do |file|
1197
+ if file =~ /tpkg.tar.gz$/
1198
+ compression = "gzip"
1199
+ elsif file =~ /tpkg.tar.bz2$/
1200
+ compression = "bz2"
1201
+ end
1202
+ end
1203
+ end
1204
+ return compression
1205
+ end
1206
+
1207
+ # Given a .tpkg file and the topleveldir, generate the command for
1208
+ # extracting tpkg.tar
1209
+ def self.cmd_to_extract_tpkg_tar(package_file, topleveldir)
1210
+ compression = get_compression(package_file)
1211
+ if compression == "gzip"
1212
+ cmd = "#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar.gz')} #{@@taroptions} | gunzip -c"
1213
+ elsif compression == "bz2"
1214
+ cmd = "#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar.bz2')} #{@@taroptions} | bunzip2 -c"
1215
+ else
1216
+ cmd = "#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions}"
1217
+ end
1218
+ end
1219
+
1220
+ # Compresses the file using the compression type
1221
+ # specified by the compress flag
1222
+ # Returns the compressed file
1223
+ def self.compress_file(file, compress)
1224
+ if compress == true or compress == "gzip"
1225
+ result = "#{file}.gz"
1226
+ system("gzip #{file}")
1227
+ elsif compress == "bz2"
1228
+ result = "#{file}.bz2"
1229
+ system("bzip2 #{file}")
1230
+ else
1231
+ raise "Compression #{compress} is not supported"
1232
+ end
1233
+ if !$?.success? or !File.exists?(result)
1234
+ raise "Failed to compress the package"
1235
+ end
1236
+ return result
1237
+ end
1165
1238
 
1166
1239
  #
1167
1240
  # Instance methods
@@ -1512,7 +1585,11 @@ class Tpkg
1512
1585
  else
1513
1586
  version = debversion
1514
1587
  end
1515
- if status =~ /installed/
1588
+ # We want packages with a state of "installed". However,
1589
+ # there's also a state of "not-installed", and the state
1590
+ # field contains several space-seperated values, so we have
1591
+ # to be somewhat careful to pick out "installed".
1592
+ if status.split(' ').include?('installed')
1516
1593
  pkg = pkg_for_native_package(name, version, package_version, :native_installed)
1517
1594
  native_packages << pkg
1518
1595
  end
@@ -2457,7 +2534,7 @@ class Tpkg
2457
2534
  # permissions and ownership, etc. Does not check for conflicting
2458
2535
  # files or packages, etc. Those checks (if desired) must be done before
2459
2536
  # calling this method.
2460
- def unpack(package_file, passphrase=nil, options={})
2537
+ def unpack(package_file, options={})
2461
2538
  ret_val = 0
2462
2539
  metadata = Tpkg::metadata_from_package(package_file)
2463
2540
 
@@ -2470,7 +2547,8 @@ class Tpkg
2470
2547
  # directory structure in the package.
2471
2548
  topleveldir = Tpkg::package_toplevel_directory(package_file)
2472
2549
  workdir = Tpkg::tempdir(topleveldir, @tmp_directory)
2473
- system("#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions} | #{@tar} #{@@taroptions} -C #{workdir} -xpf -")
2550
+ extract_tpkg_tar_cmd = Tpkg::cmd_to_extract_tpkg_tar(package_file, topleveldir)
2551
+ system("#{extract_tpkg_tar_cmd} | #{@tar} #{@@taroptions} -C #{workdir} -xpf -")
2474
2552
  files_info = {} # store perms, uid, gid, etc. for files
2475
2553
  checksums_of_decrypted_files = {}
2476
2554
  root_dir = File.join(workdir, 'tpkg', 'root')
@@ -2481,7 +2559,7 @@ class Tpkg
2481
2559
  # Get list of conflicting files/directories & store their perm/ownership. That way, we can
2482
2560
  # set them to the correct values later on in order to preserve them.
2483
2561
  # TODO: verify this command works on all platforms
2484
- files = `#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions} | #{@tar} #{@@taroptions} -tf -`
2562
+ files = `#{extract_tpkg_tar_cmd} | #{@tar} #{@@taroptions} -tf -`
2485
2563
  files = files.split("\n")
2486
2564
  conflicting_files = {}
2487
2565
  files.each do | file |
@@ -2495,59 +2573,9 @@ class Tpkg
2495
2573
  end
2496
2574
  end
2497
2575
 
2498
- # Run preinstall script
2499
- if File.exist?(File.join(workdir, 'tpkg', 'preinstall'))
2500
- pwd = Dir.pwd
2501
- # chdir into the working directory so that the user can specify a
2502
- # relative path to their file/script.
2503
- Dir.chdir(File.join(workdir, 'tpkg'))
2504
-
2505
- # Warn the user about non-executable files, as system will just
2506
- # silently fail and exit if that's the case.
2507
- if !File.executable?(File.join(workdir, 'tpkg', 'preinstall'))
2508
- warn "Warning: preinstall script for #{File.basename(package_file)} is not executable, execution will likely fail"
2509
- end
2510
- if @force
2511
- system(File.join(workdir, 'tpkg', 'preinstall')) || warn("Warning: preinstall for #{File.basename(package_file)} failed with exit value #{$?.exitstatus}")
2512
- else
2513
- system(File.join(workdir, 'tpkg', 'preinstall')) || raise("Error: preinstall for #{File.basename(package_file)} failed with exit value #{$?.exitstatus}")
2514
- end
2515
- # Switch back to our previous directory
2516
- Dir.chdir(pwd)
2517
- end
2576
+ run_preinstall(package_file, workdir)
2518
2577
 
2519
- # Run any externals
2520
- metadata[:externals].each do |external|
2521
- # If the external references a datafile or datascript then read/run it
2522
- # now that we've unpacked the package contents and have the file/script
2523
- # available. This will get us the data for the external.
2524
- if external[:datafile] || external[:datascript]
2525
- pwd = Dir.pwd
2526
- # chdir into the working directory so that the user can specify a
2527
- # relative path to their file/script.
2528
- Dir.chdir(File.join(workdir, 'tpkg'))
2529
- if external[:datafile]
2530
- # Read the file
2531
- external[:data] = IO.read(external[:datafile])
2532
- # Drop the datafile key so that we don't waste time re-reading the
2533
- # datafile again in the future.
2534
- external.delete(:datafile)
2535
- elsif external[:datascript]
2536
- # Run the script
2537
- IO.popen(external[:datascript]) do |pipe|
2538
- external[:data] = pipe.read
2539
- end
2540
- # Drop the datascript key so that we don't waste time re-running the
2541
- # datascript again in the future.
2542
- external.delete(:datascript)
2543
- end
2544
- # Switch back to our previous directory
2545
- Dir.chdir(pwd)
2546
- end
2547
- if !options[:externals_to_skip] || !options[:externals_to_skip].include?(external)
2548
- run_external(metadata[:filename], :install, external[:name], external[:data])
2549
- end
2550
- end if metadata[:externals]
2578
+ run_externals_for_install(metadata, workdir, options[:externals_to_skip])
2551
2579
 
2552
2580
  # Since we're stuck with unpacking to a temporary folder take
2553
2581
  # advantage of that to handle permissions, ownership and decryption
@@ -2665,7 +2693,7 @@ class Tpkg
2665
2693
 
2666
2694
  # Decrypt any files marked for decryption
2667
2695
  if tpkgfile[:encrypt]
2668
- if passphrase.nil?
2696
+ if !options[:passphrase]
2669
2697
  # If the user didn't supply a passphrase then just remove the
2670
2698
  # encrypted file. This allows users to install packages that
2671
2699
  # contain encrypted files for which they don't have the
@@ -2675,7 +2703,7 @@ class Tpkg
2675
2703
  else
2676
2704
  (1..3).each do | i |
2677
2705
  begin
2678
- Tpkg::decrypt(metadata[:name], working_path, passphrase)
2706
+ Tpkg::decrypt(metadata[:name], working_path, options[:passphrase])
2679
2707
  break
2680
2708
  rescue OpenSSL::CipherError
2681
2709
  @@passphrase = nil
@@ -2686,8 +2714,7 @@ class Tpkg
2686
2714
  end
2687
2715
  end
2688
2716
  end
2689
-
2690
- #digest = Digest::SHA256.file(working_path).hexdigest
2717
+
2691
2718
  digest = Digest::SHA256.hexdigest(File.read(working_path))
2692
2719
  # get checksum for the decrypted file. Will be used for creating file_metadata.xml
2693
2720
  checksums_of_decrypted_files[File.expand_path(tpkg_path)] = digest
@@ -2733,67 +2760,10 @@ class Tpkg
2733
2760
  install_init_scripts(metadata)
2734
2761
  install_crontabs(metadata)
2735
2762
 
2736
- # Run postinstall script
2737
- if File.exist?(File.join(workdir, 'tpkg', 'postinstall'))
2738
- pwd = Dir.pwd
2739
- # chdir into the working directory so that the user can specify a
2740
- # relative path to their file/script.
2741
- Dir.chdir(File.join(workdir, 'tpkg'))
2742
-
2743
- # Warn the user about non-executable files, as system will just
2744
- # silently fail and exit if that's the case.
2745
- if !File.executable?(File.join(workdir, 'tpkg', 'postinstall'))
2746
- warn "Warning: postinstall script for #{File.basename(package_file)} is not executable, execution will likely fail"
2747
- end
2748
- # Note this only warns the user if the postinstall fails, it does
2749
- # not raise an exception like we do if preinstall fails. Raising
2750
- # an exception would leave the package's files installed but the
2751
- # package not registered as installed, which does not seem
2752
- # desirable. We could remove the package's files and raise an
2753
- # exception, but this seems the best approach to me.
2754
- system(File.join(workdir, 'tpkg', 'postinstall')) || warn("Warning: postinstall for #{File.basename(package_file)} failed with exit value #{$?.exitstatus}")
2755
- ret_val = POSTINSTALL_ERR if $?.exitstatus > 0
2756
-
2757
- # Switch back to our previous directory
2758
- Dir.chdir(pwd)
2759
- end
2763
+ ret_val = run_postinstall(package_file, workdir)
2764
+
2765
+ save_package_metadata(package_file, workdir, metadata, files_info, checksums_of_decrypted_files)
2760
2766
 
2761
- # Save metadata for this pkg
2762
- package_name = File.basename(package_file, File.extname(package_file))
2763
- package_metadata_dir = File.join(@metadata_directory, package_name)
2764
- FileUtils.mkdir_p(package_metadata_dir)
2765
- metadata_file = File.new(File.join(package_metadata_dir, "tpkg.yml"), "w")
2766
- metadata.write(metadata_file)
2767
- metadata_file.close
2768
-
2769
- # Save file_metadata.yml for this pkg
2770
- if File.exist?(File.join(workdir, 'tpkg', 'file_metadata.bin'))
2771
- file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.bin')), 'bin')
2772
- elsif File.exist?(File.join(workdir, 'tpkg', 'file_metadata.yml'))
2773
- file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.yml')), 'yml')
2774
- elsif File.exists?(File.join(workdir, 'tpkg', 'file_metadata.xml'))
2775
- file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.xml')), 'xml')
2776
- end
2777
- if file_metadata
2778
- file_metadata[:package_file] = File.basename(package_file)
2779
- file_metadata[:files].each do |file|
2780
- acl = files_info[file[:path]]
2781
- file.merge!(acl) unless acl.nil?
2782
- digest = checksums_of_decrypted_files[File.expand_path(file[:path])]
2783
- if digest
2784
- digests = file[:checksum][:digests]
2785
- digests[0][:encrypted] = true
2786
- digests[1] = {:decrypted => true, :value => digest}
2787
- end
2788
- end
2789
-
2790
- file = File.open(File.join(package_metadata_dir, "file_metadata.bin"), "w")
2791
- Marshal.dump(file_metadata.to_hash, file)
2792
- file.close
2793
- else
2794
- warn "Warning: package #{File.basename(package_file)} does not include file_metadata information."
2795
- end
2796
-
2797
2767
  # Copy the package file to the directory for installed packages
2798
2768
  FileUtils.cp(package_file, @installed_directory)
2799
2769
 
@@ -3039,6 +3009,135 @@ class Tpkg
3039
3009
  end
3040
3010
  end
3041
3011
 
3012
+ def run_preinstall(package_file, workdir)
3013
+ if File.exist?(File.join(workdir, 'tpkg', 'preinstall'))
3014
+ pwd = Dir.pwd
3015
+ # chdir into the working directory so that the user can specify
3016
+ # relative paths to other files in the package.
3017
+ Dir.chdir(File.join(workdir, 'tpkg'))
3018
+
3019
+ begin
3020
+ # Warn the user about non-executable files, as system will just
3021
+ # silently fail and exit if that's the case.
3022
+ if !File.executable?(File.join(workdir, 'tpkg', 'preinstall'))
3023
+ warn "Warning: preinstall script for #{File.basename(package_file)} is not executable, execution will likely fail"
3024
+ end
3025
+ if @force
3026
+ system(File.join(workdir, 'tpkg', 'preinstall')) || warn("Warning: preinstall for #{File.basename(package_file)} failed with exit value #{$?.exitstatus}")
3027
+ else
3028
+ system(File.join(workdir, 'tpkg', 'preinstall')) || raise("Error: preinstall for #{File.basename(package_file)} failed with exit value #{$?.exitstatus}")
3029
+ end
3030
+ ensure
3031
+ # Switch back to our previous directory
3032
+ Dir.chdir(pwd)
3033
+ end
3034
+ end
3035
+ end
3036
+ def run_postinstall(package_file, workdir)
3037
+ r = 0
3038
+ if File.exist?(File.join(workdir, 'tpkg', 'postinstall'))
3039
+ pwd = Dir.pwd
3040
+ # chdir into the working directory so that the user can specify
3041
+ # relative paths to other files in the package.
3042
+ Dir.chdir(File.join(workdir, 'tpkg'))
3043
+
3044
+ begin
3045
+ # Warn the user about non-executable files, as system will just
3046
+ # silently fail and exit if that's the case.
3047
+ if !File.executable?(File.join(workdir, 'tpkg', 'postinstall'))
3048
+ warn "Warning: postinstall script for #{File.basename(package_file)} is not executable, execution will likely fail"
3049
+ end
3050
+ # Note this only warns the user if the postinstall fails, it does
3051
+ # not raise an exception like we do if preinstall fails. Raising
3052
+ # an exception would leave the package's files installed but the
3053
+ # package not registered as installed, which does not seem
3054
+ # desirable. We could remove the package's files and raise an
3055
+ # exception, but this seems the best approach to me.
3056
+ system(File.join(workdir, 'tpkg', 'postinstall'))
3057
+ if !$?.success?
3058
+ warn("Warning: postinstall for #{File.basename(package_file)} failed with exit value #{$?.exitstatus}")
3059
+ r = POSTINSTALL_ERR
3060
+ end
3061
+ ensure
3062
+ # Switch back to our previous directory
3063
+ Dir.chdir(pwd)
3064
+ end
3065
+ end
3066
+ r
3067
+ end
3068
+
3069
+ def run_externals_for_install(metadata, workdir, externals_to_skip)
3070
+ metadata[:externals].each do |external|
3071
+ # If the external references a datafile or datascript then read/run it
3072
+ # now that we've unpacked the package contents and have the file/script
3073
+ # available. This will get us the data for the external.
3074
+ if external[:datafile] || external[:datascript]
3075
+ pwd = Dir.pwd
3076
+ # chdir into the working directory so that the user can specify a
3077
+ # relative path to their file/script.
3078
+ Dir.chdir(File.join(workdir, 'tpkg'))
3079
+ if external[:datafile]
3080
+ # Read the file
3081
+ external[:data] = IO.read(external[:datafile])
3082
+ # Drop the datafile key so that we don't waste time re-reading the
3083
+ # datafile again in the future.
3084
+ external.delete(:datafile)
3085
+ elsif external[:datascript]
3086
+ # Run the script
3087
+ IO.popen(external[:datascript]) do |pipe|
3088
+ external[:data] = pipe.read
3089
+ end
3090
+ # Drop the datascript key so that we don't waste time re-running the
3091
+ # datascript again in the future.
3092
+ external.delete(:datascript)
3093
+ end
3094
+ # Switch back to our previous directory
3095
+ Dir.chdir(pwd)
3096
+ end
3097
+ if !externals_to_skip || !externals_to_skip.include?(external)
3098
+ run_external(metadata[:filename], :install, external[:name], external[:data])
3099
+ end
3100
+ end if metadata[:externals]
3101
+ end
3102
+
3103
+ def save_package_metadata(package_file, workdir, metadata, files_info, checksums_of_decrypted_files)
3104
+ # Save metadata for this pkg
3105
+ package_name = File.basename(package_file, File.extname(package_file))
3106
+ package_metadata_dir = File.join(@metadata_directory, package_name)
3107
+ FileUtils.mkdir_p(package_metadata_dir)
3108
+ metadata_file = File.new(File.join(package_metadata_dir, "tpkg.yml"), "w")
3109
+ metadata.write(metadata_file)
3110
+ metadata_file.close
3111
+
3112
+ # Save file_metadata.yml for this pkg
3113
+ if File.exist?(File.join(workdir, 'tpkg', 'file_metadata.bin'))
3114
+ file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.bin')), 'bin')
3115
+ elsif File.exist?(File.join(workdir, 'tpkg', 'file_metadata.yml'))
3116
+ file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.yml')), 'yml')
3117
+ elsif File.exists?(File.join(workdir, 'tpkg', 'file_metadata.xml'))
3118
+ file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.xml')), 'xml')
3119
+ end
3120
+ if file_metadata
3121
+ file_metadata[:package_file] = File.basename(package_file)
3122
+ file_metadata[:files].each do |file|
3123
+ acl = files_info[file[:path]]
3124
+ file.merge!(acl) unless acl.nil?
3125
+ digest = checksums_of_decrypted_files[File.expand_path(file[:path])]
3126
+ if digest
3127
+ digests = file[:checksum][:digests]
3128
+ digests[0][:encrypted] = true
3129
+ digests[1] = {:decrypted => true, :value => digest}
3130
+ end
3131
+ end
3132
+
3133
+ file = File.open(File.join(package_metadata_dir, "file_metadata.bin"), "w")
3134
+ Marshal.dump(file_metadata.to_hash, file)
3135
+ file.close
3136
+ else
3137
+ warn "Warning: package #{File.basename(package_file)} does not include file_metadata information."
3138
+ end
3139
+ end
3140
+
3042
3141
  def requirements_for_currently_installed_package(pkgname=nil)
3043
3142
  requirements = []
3044
3143
  metadata_for_installed_packages.each do |metadata|
@@ -3461,7 +3560,7 @@ class Tpkg
3461
3560
  warn "Skipping #{File.basename(pkgfile)}, already installed"
3462
3561
  else
3463
3562
  if prompt_for_conflicting_files(pkgfile)
3464
- ret_val |= unpack(pkgfile, passphrase)
3563
+ ret_val |= unpack(pkgfile, :passphrase => passphrase)
3465
3564
  end
3466
3565
  end
3467
3566
  end
@@ -3697,7 +3796,7 @@ class Tpkg
3697
3796
  end
3698
3797
  end if pkg[:metadata][:dependencies]
3699
3798
  if can_unpack
3700
- ret_val |= unpack(pkgfile, passphrase, :externals_to_skip => externals_to_skip)
3799
+ ret_val |= unpack(pkgfile, :passphrase => passphrase, :externals_to_skip => externals_to_skip)
3701
3800
  end
3702
3801
 
3703
3802
  has_updates = true
@@ -3829,7 +3928,8 @@ class Tpkg
3829
3928
 
3830
3929
  topleveldir = Tpkg::package_toplevel_directory(package_file)
3831
3930
  workdir = Tpkg::tempdir(topleveldir, @tmp_directory)
3832
- system("#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions} | #{@tar} #{@@taroptions} -C #{workdir} -xpf -")
3931
+ extract_tpkg_tar_command = Tpkg::cmd_to_extract_tpkg_tar(package_file, topleveldir)
3932
+ system("#{extract_tpkg_tar_command} | #{@tar} #{@@taroptions} -C #{workdir} -xpf -")
3833
3933
 
3834
3934
  # Run preremove script
3835
3935
  if File.exist?(File.join(workdir, 'tpkg', 'preremove'))
@@ -16,22 +16,12 @@ rescue LoadError
16
16
  require 'rubygems'
17
17
  require 'net/ssh'
18
18
  end
19
- #require 'highline/import'
20
19
 
21
20
  class Deployer
22
- # def self.new
23
- # begin
24
- # require 'rubygems'
25
- # require 'net/ssh'
26
- # require 'highline/import'
27
- # rescue LoadError
28
- # raise LoadError, "In order to use the deployment feature, you must have rubygems installed. Additionally, you need to install the following gems: net-ssh, highline"
29
- # else
30
- # super
31
- # end
32
- # end
33
-
21
+
34
22
  def initialize(options = nil)
23
+ @sudo_pw = nil
24
+ @pw_prompts = {}
35
25
  @mutex = Mutex.new
36
26
  @max_worker = 4
37
27
  @abort_on_failure = false
@@ -47,21 +37,12 @@ class Deployer
47
37
  end
48
38
  end
49
39
 
50
- def prompt
51
- user = prompt_username
52
- password = prompt_password
53
- return user, password
54
- end
55
-
56
40
  def prompt_username
57
- print "Username: "
58
- user = $stdin.gets.chomp
59
- return user
41
+ ask("Username: ")
60
42
  end
61
43
 
62
44
  def prompt_password
63
- password = ask("SSH Password (leave blank if using ssh key): ", true)
64
- return password
45
+ ask("SSH Password (leave blank if using ssh key): ", true)
65
46
  end
66
47
 
67
48
  def ask(str,mask=false)
@@ -75,31 +56,29 @@ class Deployer
75
56
  return input
76
57
  end
77
58
 
78
- $sudo_pw = nil
79
59
  def get_sudo_pw
80
60
  @mutex.synchronize {
81
- if $sudo_pw.nil?
82
- $sudo_pw = ask("Sudo password: ", true)
61
+ if @sudo_pw.nil?
62
+ @sudo_pw = ask("Sudo password: ", true)
83
63
  else
84
- return $sudo_pw
64
+ return @sudo_pw
85
65
  end
86
66
  }
87
67
  end
88
68
 
89
- $passphrases = {}
90
- def get_passphrase(package)
69
+ # Prompt user for input and cache it. If in the future, we see
70
+ # the same prompt again, we can reuse the existing inputs. This saves
71
+ # the users from having to type in a bunch of inputs (such as password)
72
+ def get_input_for_pw_prompt(prompt)
91
73
  @mutex.synchronize {
92
- if $passphrases[package].nil?
93
- # $stdout.write package
94
- # $stdout.flush
95
- # $passphrases[package] = $stdin.gets.chomp
96
- $passphrases[package] = ask(package, true)
97
- else
98
- return $passphrases[package]
99
- end
74
+ if @pw_prompts[prompt].nil?
75
+ @pw_prompts[prompt] = ask(prompt, true)
76
+ end
77
+ return @pw_prompts[prompt]
100
78
  }
101
79
  end
102
80
 
81
+ # Return a block that can be used for executing a cmd on the remote server
103
82
  def ssh_execute(server, username, password, cmd)
104
83
  return lambda {
105
84
  exit_status = 0
@@ -130,19 +109,14 @@ class Deployer
130
109
  # for; now we can check to see if it's a password prompt, and
131
110
  # interactively return data if so (see request_pty above).
132
111
  channel.on_data do |ch, data|
133
- if data =~ /Password/
134
- #sudo_password = (!password.nil && password != "" && password) || get_sudo_pw
112
+ if data =~ /Password:/
135
113
  password = get_sudo_pw unless !password.nil? && password != ""
136
114
  channel.send_data "#{password}\n"
137
- elsif data =~ /Passphrase/ or data =~ /pass phrase/ or data =~ /incorrect passphrase/i
138
- passphrase = get_passphrase(data)
139
- channel.send_data "#{passphrase}\n"
115
+ elsif data =~ /password/i or data =~ /passphrase/i or
116
+ data =~ /pass phrase/i or data =~ /incorrect passphrase/i
117
+ input = get_input_for_pw_prompt(data)
118
+ channel.send_data "#{input}\n"
140
119
  else
141
- # print "#{server}: #{data}" if $debug
142
- # ssh channels can be treated as a hash for the specific purpose of
143
- # getting values out of the block later
144
- # channel[:result] ||= ""
145
- # channel[:result] << data
146
120
  result << data unless data.nil? or data.empty?
147
121
  end
148
122
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tpkg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.2
4
+ version: 1.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Darren Dao
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2010-02-16 00:00:00 -08:00
13
+ date: 2010-02-27 00:00:00 +00:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -34,7 +34,7 @@ dependencies:
34
34
  version: "0"
35
35
  version:
36
36
  - !ruby/object:Gem::Dependency
37
- name: kwalify
37
+ name: ddao-kwalify
38
38
  type: :runtime
39
39
  version_requirement:
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -55,24 +55,24 @@ extensions: []
55
55
  extra_rdoc_files: []
56
56
 
57
57
  files:
58
- - bin/cpan2tpkg
59
- - bin/tpkg_xml_to_yml
60
- - bin/tpkg
61
- - bin/gem2tpkg
62
- - schema/tpkg.dtd
63
- - schema/schema-1.0.yml
64
- - schema/tpkg-1.0.0.dtd
65
- - schema/tpkg-1.0.4.dtd
66
- - schema/tpkg-1.0.3.dtd
67
- - schema/schema.yml
68
- - schema/tpkg-1.0.5.dtd
69
- - schema/tpkg-1.0.2.dtd
70
- - schema/tpkg-1.0.1.dtd
71
58
  - lib/tpkg/versiontype.rb
72
59
  - lib/tpkg/deployer.rb
73
60
  - lib/tpkg/thread_pool.rb
74
61
  - lib/tpkg/metadata.rb
75
62
  - lib/tpkg.rb
63
+ - schema/tpkg-1.0.4.dtd
64
+ - schema/tpkg-1.0.0.dtd
65
+ - schema/tpkg-1.0.5.dtd
66
+ - schema/tpkg.dtd
67
+ - schema/tpkg-1.0.1.dtd
68
+ - schema/tpkg-1.0.3.dtd
69
+ - schema/schema.yml
70
+ - schema/tpkg-1.0.2.dtd
71
+ - schema/schema-1.0.yml
72
+ - bin/tpkg
73
+ - bin/cpan2tpkg
74
+ - bin/gem2tpkg
75
+ - bin/tpkg_xml_to_yml
76
76
  - Rakefile
77
77
  has_rdoc: true
78
78
  homepage: http://tpkg.sourceforge.net