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.
- data/Rakefile +2 -2
- data/bin/gem2tpkg +1 -1
- data/bin/tpkg +11 -2
- data/lib/tpkg.rb +241 -141
- data/lib/tpkg/deployer.rb +22 -48
- 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.
|
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'
|
data/bin/gem2tpkg
CHANGED
@@ -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-
|
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
|
-
|
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
|
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.
|
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
|
-
@@
|
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
|
-
@@
|
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
|
-
@@
|
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 @@
|
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
|
-
|
275
|
+
elsif File.exist?(File.join(CONFIGDIR, 'tpkg', 'schema'))
|
271
276
|
schema_dir = File.join(CONFIGDIR, 'tpkg', 'schema')
|
272
|
-
else
|
273
|
-
|
277
|
+
else
|
278
|
+
warn "Warning: unable to find schema for tpkg.yml"
|
274
279
|
end
|
275
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
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 = `#{
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
2737
|
-
|
2738
|
-
|
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
|
-
|
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'))
|
data/lib/tpkg/deployer.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
58
|
-
user = $stdin.gets.chomp
|
59
|
-
return user
|
41
|
+
ask("Username: ")
|
60
42
|
end
|
61
43
|
|
62
44
|
def prompt_password
|
63
|
-
|
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
|
82
|
-
|
61
|
+
if @sudo_pw.nil?
|
62
|
+
@sudo_pw = ask("Sudo password: ", true)
|
83
63
|
else
|
84
|
-
return
|
64
|
+
return @sudo_pw
|
85
65
|
end
|
86
66
|
}
|
87
67
|
end
|
88
68
|
|
89
|
-
|
90
|
-
|
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
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
138
|
-
|
139
|
-
|
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.
|
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-
|
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
|