tpkg 1.19.2 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.
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