tpkg 1.23.3 → 1.25.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/bin/gem2tpkg +11 -2
- data/bin/tpkg +19 -2
- data/lib/tpkg.rb +165 -80
- data/lib/tpkg/metadata.rb +62 -7
- data/schema/schema-1.0.7.yml +91 -0
- data/schema/schema.yml +5 -1
- data/schema/tpkg-1.0.7.dtd +43 -0
- data/schema/tpkg.dtd +1 -0
- metadata +4 -2
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ spec = Gem::Specification.new do |s|
|
|
5
5
|
s.add_dependency('facter')
|
6
6
|
s.add_dependency('net-ssh')
|
7
7
|
s.add_dependency('ddao-kwalify')
|
8
|
-
s.version = '1.
|
8
|
+
s.version = '1.25.1'
|
9
9
|
s.authors = ['Darren Dao', 'Jason Heiss']
|
10
10
|
s.email = 'tpkg-users@lists.sourceforge.net'
|
11
11
|
s.homepage = 'http://tpkg.sourceforge.net'
|
data/bin/gem2tpkg
CHANGED
@@ -35,6 +35,7 @@ end
|
|
35
35
|
@gemcmd = DEFAULT_GEM_COMMAND
|
36
36
|
@rubygemspath = nil
|
37
37
|
@extrapkgname = nil
|
38
|
+
@verbose = false
|
38
39
|
|
39
40
|
opts = OptionParser.new
|
40
41
|
opts.banner = 'Usage: gem2tpkg [options] GEMNAME1 GEMNAME2 ...'
|
@@ -85,6 +86,9 @@ end
|
|
85
86
|
opts.on('--extra-name', '=EXTRANAME', 'Extra string to add to package name') do |opt|
|
86
87
|
@extrapkgname = opt
|
87
88
|
end
|
89
|
+
opts.on('--verbose', 'Provides more info on what is being executed') do |opt|
|
90
|
+
@verbose = opt
|
91
|
+
end
|
88
92
|
opts.on_tail("-h", "--help", "Show this message") do
|
89
93
|
puts opts
|
90
94
|
exit
|
@@ -99,7 +103,9 @@ leftover = opts.parse(ARGV)
|
|
99
103
|
@geminstallpath = nil
|
100
104
|
@gembinpath = nil
|
101
105
|
@rubypath = nil
|
102
|
-
|
106
|
+
cmd = "#{@gemcmd} environment"
|
107
|
+
puts "Executing: #{cmd}" if @verbose
|
108
|
+
IO.popen(cmd) do |pipe|
|
103
109
|
pipe.each_line do |line|
|
104
110
|
if line =~ /INSTALLATION DIRECTORY:\s+(\S+)/
|
105
111
|
@geminstallpath = $1
|
@@ -180,6 +186,7 @@ if !@buildopts.empty?
|
|
180
186
|
geminst.concat(@buildopts)
|
181
187
|
end
|
182
188
|
|
189
|
+
puts "Executing: #{geminst.join(" ")}" if @verbose
|
183
190
|
r = system(*geminst)
|
184
191
|
if !r
|
185
192
|
abort('gem install failed')
|
@@ -240,7 +247,9 @@ def package(gem)
|
|
240
247
|
# We want to exactly match the gem in question. Turns out that if you
|
241
248
|
# pass something that looks like a regex then gem dependency will use
|
242
249
|
# that instead. See rubygems/commands/dependency_command.rb
|
243
|
-
|
250
|
+
cmd = "#{@gemcmd} dependency /^#{Regexp.escape(gem)}$/"
|
251
|
+
puts "Executing: #{cmd}" if @verbose
|
252
|
+
IO.popen(cmd) do |pipe|
|
244
253
|
pipe.each_line do |line|
|
245
254
|
next if line =~ /^Gem / # Skip header line
|
246
255
|
next if line =~ /^\s*$/ # Skip blank lines
|
data/bin/tpkg
CHANGED
@@ -31,7 +31,7 @@ require 'tpkg'
|
|
31
31
|
@tpkg_options = {} # options for instantiating Tpkg object
|
32
32
|
@init_options = {} # options for how to run init scripts
|
33
33
|
@other_options = {}
|
34
|
-
@compress =
|
34
|
+
@compress = "gzip"
|
35
35
|
|
36
36
|
|
37
37
|
def rerun_with_sudo_if_necessary
|
@@ -214,12 +214,19 @@ opts.on('--qX', '=FILENAME', 'Display tpkg.xml or tpkg.yml of the given package'
|
|
214
214
|
@action = :query_tpkg_metadata
|
215
215
|
@action_value = opt
|
216
216
|
end
|
217
|
+
opts.on('--history', 'Display package installation history') do |opt|
|
218
|
+
@action = :query_history
|
219
|
+
end
|
217
220
|
opts.on('--qenv', "Display machine's information") do |opt|
|
218
221
|
@action = :query_env
|
219
222
|
end
|
220
223
|
opts.on('--source', '=NAME', 'Sources where packages are located', Array) do |opt|
|
221
224
|
@tpkg_options["sources"] = opt
|
222
225
|
end
|
226
|
+
opts.on('--download', '=PACKAGES', 'Download one or more packages', Array) do |opt|
|
227
|
+
@action = :download
|
228
|
+
@action_value = opt
|
229
|
+
end
|
223
230
|
opts.on('-n', '--no-prompt', 'No confirmation prompts') do |opt|
|
224
231
|
@prompt = opt
|
225
232
|
Tpkg::set_prompt(@prompt)
|
@@ -252,7 +259,11 @@ opts.on('--deploy-as', '=USERNAME', 'What username to use for deploying to remot
|
|
252
259
|
@deploy_params = @deploy_params - ['--deploy-as']
|
253
260
|
end
|
254
261
|
opts.on('--compress', '=[TYPE]', 'Compress files when making packages') do |opt|
|
255
|
-
|
262
|
+
if opt == "no"
|
263
|
+
@compress= false
|
264
|
+
else
|
265
|
+
@compress = opt
|
266
|
+
end
|
256
267
|
end
|
257
268
|
opts.on('--debug', 'Print lots of messages about what tpkg is doing') do |opt|
|
258
269
|
@debug = opt
|
@@ -392,6 +403,9 @@ when :upgrade
|
|
392
403
|
when :remove
|
393
404
|
tpkg = instantiate_tpkg(@tpkg_options)
|
394
405
|
ret_val = tpkg.remove(@action_value, @other_options)
|
406
|
+
when :download
|
407
|
+
tpkg = instantiate_tpkg(@tpkg_options)
|
408
|
+
ret_val = tpkg.download_pkgs(@action_value, @other_options)
|
395
409
|
when :verify
|
396
410
|
result = nil
|
397
411
|
# Verify a given .tpkg file
|
@@ -614,5 +628,8 @@ when :query_tpkg_metadata
|
|
614
628
|
when :query_env
|
615
629
|
puts "Operating System: #{Tpkg::get_os}"
|
616
630
|
puts "Architecture: #{Tpkg::get_arch}"
|
631
|
+
when :query_history
|
632
|
+
tpkg = instantiate_tpkg(@tpkg_options)
|
633
|
+
tpkg.installation_history
|
617
634
|
end
|
618
635
|
exit ret_val
|
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.25.1'
|
60
60
|
CONFIGDIR = '/etc'
|
61
61
|
|
62
62
|
GENERIC_ERR = 1
|
@@ -153,7 +153,15 @@ class Tpkg
|
|
153
153
|
else
|
154
154
|
pass = passphrase
|
155
155
|
end
|
156
|
-
|
156
|
+
|
157
|
+
# special handling for directory
|
158
|
+
if File.directory?(filename)
|
159
|
+
Find.find(filename) do |f|
|
160
|
+
encrypt(pkgname, f, pass, cipher) if File.file?(f)
|
161
|
+
end
|
162
|
+
return
|
163
|
+
end
|
164
|
+
|
157
165
|
salt = OpenSSL::Random::random_bytes(SALT_LEN)
|
158
166
|
c = OpenSSL::Cipher::Cipher.new(cipher)
|
159
167
|
c.encrypt
|
@@ -173,6 +181,7 @@ class Tpkg
|
|
173
181
|
tmpfile.close
|
174
182
|
File.rename(tmpfile.path, filename)
|
175
183
|
end
|
184
|
+
|
176
185
|
# Decrypt the given file in-place.
|
177
186
|
def self.decrypt(pkgname, filename, passphrase, cipher='aes-256-cbc')
|
178
187
|
# passphrase can be a callback Proc, call it if that's the case
|
@@ -185,6 +194,13 @@ class Tpkg
|
|
185
194
|
else
|
186
195
|
pass = passphrase
|
187
196
|
end
|
197
|
+
|
198
|
+
if File.directory?(filename)
|
199
|
+
Find.find(filename) do |f|
|
200
|
+
decrypt(pkgname, f, pass, cipher) if File.file?(f)
|
201
|
+
end
|
202
|
+
return
|
203
|
+
end
|
188
204
|
|
189
205
|
file = File.open(filename)
|
190
206
|
if (buf = file.read(MAGIC.length)) != MAGIC
|
@@ -250,12 +266,11 @@ class Tpkg
|
|
250
266
|
else
|
251
267
|
workdir = tempdir('tpkg', File.dirname(pkgsrcdir))
|
252
268
|
end
|
253
|
-
|
254
269
|
begin
|
255
270
|
# Make the 'tpkg' directory for storing the package contents
|
256
271
|
tpkgdir = File.join(workdir, 'tpkg')
|
257
272
|
Dir.mkdir(tpkgdir)
|
258
|
-
|
273
|
+
|
259
274
|
# A package really shouldn't be partially relocatable, warn the user if
|
260
275
|
# they're creating such a scourge.
|
261
276
|
if (File.exist?(File.join(pkgsrcdir, 'root')) && File.exist?(File.join(pkgsrcdir, 'reloc')))
|
@@ -267,20 +282,13 @@ class Tpkg
|
|
267
282
|
# And on further reflection it makes sense to only have one chunk of
|
268
283
|
# code (tar) ever touch the user's files.
|
269
284
|
system("#{find_tar} -C #{pkgsrcdir} -cf - . | #{find_tar} -C #{tpkgdir} -xpf -") || raise("Package content copy failed")
|
270
|
-
|
285
|
+
|
271
286
|
# check metadata file
|
272
287
|
errors = []
|
273
|
-
|
274
|
-
|
275
|
-
metadata_format = 'yml'
|
276
|
-
elsif File.exists?(File.join(tpkgdir, 'tpkg.xml'))
|
277
|
-
metadata_file = File.join(tpkgdir, 'tpkg.xml')
|
278
|
-
metadata_format = 'xml'
|
279
|
-
else
|
288
|
+
metadata = Metadata::instantiate_from_dir(tpkgdir)
|
289
|
+
if !metadata
|
280
290
|
raise 'Your source directory does not contain the metadata configuration file.'
|
281
291
|
end
|
282
|
-
metadata_text = File.read(metadata_file)
|
283
|
-
metadata = Metadata.new(metadata_text, metadata_format)
|
284
292
|
|
285
293
|
# This is for when we're in developement mode or when installed as gem
|
286
294
|
if File.exists?(File.join(File.dirname(File.dirname(__FILE__)), "schema"))
|
@@ -306,7 +314,6 @@ class Tpkg
|
|
306
314
|
filemetadata = get_filemetadata_from_directory(tpkgdir)
|
307
315
|
data = filemetadata.to_hash.recursively{|h| h.stringify_keys }
|
308
316
|
Marshal::dump(data, file)
|
309
|
-
# YAML::dump(filemetadata.to_hash, file)
|
310
317
|
end
|
311
318
|
|
312
319
|
# Check all the files are there as specified in the metadata config file
|
@@ -338,13 +345,13 @@ class Tpkg
|
|
338
345
|
|
339
346
|
# Encrypt any files marked for encryption
|
340
347
|
if tpkgfile[:encrypt]
|
341
|
-
if tpkgfile[:encrypt]
|
348
|
+
if tpkgfile[:encrypt][:precrypt]
|
342
349
|
verify_precrypt_file(working_path)
|
343
350
|
else
|
344
351
|
if passphrase.nil?
|
345
352
|
raise "Package requires encryption but supplied passphrase is nil"
|
346
353
|
end
|
347
|
-
encrypt(metadata[:name], working_path, passphrase)
|
354
|
+
encrypt(metadata[:name], working_path, passphrase, *([tpkgfile[:encrypt][:algorithm]].compact))
|
348
355
|
end
|
349
356
|
end
|
350
357
|
end unless metadata[:files].nil? or metadata[:files][:files].nil?
|
@@ -369,7 +376,10 @@ class Tpkg
|
|
369
376
|
end
|
370
377
|
File.delete(pkgfile)
|
371
378
|
end
|
372
|
-
|
379
|
+
|
380
|
+
# update metadata file with the tpkg version
|
381
|
+
# TODO
|
382
|
+
|
373
383
|
# Tar up the tpkg directory
|
374
384
|
tpkgfile = File.join(package_directory, 'tpkg.tar')
|
375
385
|
system("#{find_tar} -C #{workdir} -cf #{tpkgfile} tpkg") || raise("tpkg.tar creation failed")
|
@@ -444,6 +454,11 @@ class Tpkg
|
|
444
454
|
next if !File.exist?(f)
|
445
455
|
relocatable = false
|
446
456
|
|
457
|
+
# Append file separator at the end for directory
|
458
|
+
if File.directory?(f)
|
459
|
+
f += File::SEPARATOR
|
460
|
+
end
|
461
|
+
|
447
462
|
# check if it's from root dir or reloc dir
|
448
463
|
if f =~ /^#{root_dir}/
|
449
464
|
short_fn = f[root_dir.length ..-1]
|
@@ -864,15 +879,42 @@ class Tpkg
|
|
864
879
|
barchlength, acurrentinstallnoprefer]
|
865
880
|
end
|
866
881
|
|
867
|
-
def self.files_in_package(package_file)
|
882
|
+
def self.files_in_package(package_file, options = {})
|
883
|
+
file_lists = []
|
868
884
|
files = {}
|
869
885
|
files[:root] = []
|
870
886
|
files[:reloc] = []
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
887
|
+
|
888
|
+
# If the metadata_directory option is available, it means this package
|
889
|
+
# has been installed and the file_metadata might be available in that directory.
|
890
|
+
# If that's the case, then parse the file_metadata to get the file list. It's
|
891
|
+
# much faster than extracting from the tar file
|
892
|
+
if metadata_directory = options[:metadata_directory]
|
893
|
+
package_name = File.basename(package_file, File.extname(package_file))
|
894
|
+
file_metadata = FileMetadata::instantiate_from_dir(File.join(metadata_directory, package_name))
|
895
|
+
end
|
896
|
+
|
897
|
+
if file_metadata
|
898
|
+
file_metadata[:files].each do |file|
|
899
|
+
if file[:relocatable]
|
900
|
+
files[:reloc] << file[:path]
|
901
|
+
else
|
902
|
+
files[:root] << file[:path]
|
903
|
+
end
|
904
|
+
end
|
905
|
+
else
|
906
|
+
topleveldir = package_toplevel_directory(package_file)
|
907
|
+
extract_tpkg_tar_cmd = cmd_to_extract_tpkg_tar(package_file, topleveldir)
|
908
|
+
IO.popen("#{extract_tpkg_tar_cmd} | #{find_tar} #{@@taroptions} -tf -") do |pipe|
|
909
|
+
pipe.each do |file|
|
910
|
+
file_lists << file.chomp!
|
911
|
+
end
|
912
|
+
end
|
913
|
+
if !$?.success?
|
914
|
+
raise "Extracting file list from #{package_file} failed"
|
915
|
+
end
|
916
|
+
|
917
|
+
file_lists.each do |file|
|
876
918
|
if file =~ Regexp.new(File.join('tpkg', 'root'))
|
877
919
|
files[:root] << file.sub(Regexp.new(File.join('tpkg', 'root')), '')
|
878
920
|
elsif file =~ Regexp.new(File.join('tpkg', 'reloc', '.'))
|
@@ -880,9 +922,7 @@ class Tpkg
|
|
880
922
|
end
|
881
923
|
end
|
882
924
|
end
|
883
|
-
|
884
|
-
raise "Extracting file list from #{package_file} failed"
|
885
|
-
end
|
925
|
+
|
886
926
|
files
|
887
927
|
end
|
888
928
|
|
@@ -1150,7 +1190,7 @@ class Tpkg
|
|
1150
1190
|
eprime.set_backtrace(e.backtrace)
|
1151
1191
|
eprime
|
1152
1192
|
end
|
1153
|
-
|
1193
|
+
|
1154
1194
|
#
|
1155
1195
|
# Instance methods
|
1156
1196
|
#
|
@@ -1675,16 +1715,10 @@ class Tpkg
|
|
1675
1715
|
File.join(@metadata_directory,
|
1676
1716
|
File.basename(entry, File.extname(entry)))
|
1677
1717
|
metadata_file = File.join(package_metadata_dir, "tpkg.yml")
|
1678
|
-
m =
|
1679
|
-
if File.exists?(metadata_file)
|
1680
|
-
metadata_text = File.read(metadata_file)
|
1681
|
-
m = Metadata.new(metadata_text, 'yml')
|
1682
|
-
elsif File.exists?(File.join(package_metadata_dir, "tpkg.xml"))
|
1683
|
-
metadata_text = File.read(File.join(package_metadata_dir, "tpkg.xml"))
|
1684
|
-
m = Metadata.new(metadata_text, 'xml')
|
1718
|
+
m = Metadata::instantiate_from_dir(package_metadata_dir)
|
1685
1719
|
# No cached metadata found, we have to extract it ourselves
|
1686
1720
|
# and save it for next time
|
1687
|
-
|
1721
|
+
if !m
|
1688
1722
|
m = Tpkg::metadata_from_package(
|
1689
1723
|
File.join(@installed_directory, entry))
|
1690
1724
|
begin
|
@@ -1721,22 +1755,18 @@ class Tpkg
|
|
1721
1755
|
end
|
1722
1756
|
|
1723
1757
|
# Returns a hash of file_metadata for installed packages
|
1724
|
-
def file_metadata_for_installed_packages
|
1758
|
+
def file_metadata_for_installed_packages(package_files = nil)
|
1725
1759
|
ret = {}
|
1726
1760
|
|
1761
|
+
if package_files
|
1762
|
+
packages_files.collect!{|package_file| File.basename(package_file, File.extname(package_file))}
|
1763
|
+
end
|
1764
|
+
|
1727
1765
|
if File.directory?(@metadata_directory)
|
1728
1766
|
Dir.foreach(@metadata_directory) do |entry|
|
1729
1767
|
next if entry == '.' || entry == '..'
|
1730
|
-
if
|
1731
|
-
|
1732
|
-
file_metadata = FileMetadata.new(File.read(file), 'bin')
|
1733
|
-
elsif File.exists?(File.join(@metadata_directory, entry, "file_metadata.yml"))
|
1734
|
-
file = File.join(@metadata_directory, entry, "file_metadata.yml")
|
1735
|
-
file_metadata = FileMetadata.new(File.read(file), 'yml')
|
1736
|
-
elsif File.exists?(File.join(@metadata_directory, entry, "file_metadata.xml"))
|
1737
|
-
file = File.join(@metadata_directory, entry, "file_metadata.xml")
|
1738
|
-
file_metadata = FileMetadata.new(File.read(file), 'xml')
|
1739
|
-
end
|
1768
|
+
next if package_files && !package_files.include?(entry)
|
1769
|
+
file_metadata = FileMetadata::instantiate_from_dir(File.join(@metadata_directory, entry))
|
1740
1770
|
ret[file_metadata[:package_file]] = file_metadata
|
1741
1771
|
end
|
1742
1772
|
end
|
@@ -1871,10 +1901,11 @@ class Tpkg
|
|
1871
1901
|
package_files << metadata[:filename]
|
1872
1902
|
end
|
1873
1903
|
end
|
1904
|
+
|
1874
1905
|
metadata_for_installed_packages.each do |metadata|
|
1875
1906
|
package_file = metadata[:filename]
|
1876
1907
|
if package_files.include?(package_file)
|
1877
|
-
fip = Tpkg::files_in_package(File.join(@installed_directory, package_file))
|
1908
|
+
fip = Tpkg::files_in_package(File.join(@installed_directory, package_file), {:metadata_directory => @metadata_directory})
|
1878
1909
|
normalize_paths(fip)
|
1879
1910
|
fip[:metadata] = metadata
|
1880
1911
|
files[package_file] = fip
|
@@ -2206,14 +2237,13 @@ class Tpkg
|
|
2206
2237
|
return {:number_of_possible_solutions_checked => number_of_possible_solutions_checked}
|
2207
2238
|
end
|
2208
2239
|
|
2209
|
-
def download(source, path, downloaddir = nil)
|
2240
|
+
def download(source, path, downloaddir = nil, use_cache = true)
|
2210
2241
|
http = Tpkg::gethttp(URI.parse(source))
|
2211
2242
|
localdir = source_to_local_directory(source)
|
2212
2243
|
localpath = File.join(localdir, File.basename(path))
|
2213
|
-
|
2214
2244
|
# Don't download again if file is already there from previous installation
|
2215
2245
|
# and still has valid checksum
|
2216
|
-
if File.file?(localpath)
|
2246
|
+
if File.file?(localpath) && use_cache
|
2217
2247
|
begin
|
2218
2248
|
Tpkg::verify_package_checksum(localpath)
|
2219
2249
|
return localpath
|
@@ -2521,7 +2551,7 @@ class Tpkg
|
|
2521
2551
|
if File.directory?(f)
|
2522
2552
|
File.chown(default_dir_uid, default_dir_gid, f)
|
2523
2553
|
else
|
2524
|
-
File.chown(default_uid, default_gid, f)
|
2554
|
+
File.chown(default_uid, default_gid, f)
|
2525
2555
|
end
|
2526
2556
|
rescue Errno::EPERM
|
2527
2557
|
raise if Process.euid == 0
|
@@ -2590,7 +2620,7 @@ class Tpkg
|
|
2590
2620
|
else
|
2591
2621
|
(1..3).each do | i |
|
2592
2622
|
begin
|
2593
|
-
Tpkg::decrypt(metadata[:name], working_path, options[:passphrase])
|
2623
|
+
Tpkg::decrypt(metadata[:name], working_path, options[:passphrase], *([tpkgfile[:encrypt][:algorithm]].compact))
|
2594
2624
|
break
|
2595
2625
|
rescue OpenSSL::CipherError
|
2596
2626
|
@@passphrase = nil
|
@@ -2601,10 +2631,12 @@ class Tpkg
|
|
2601
2631
|
end
|
2602
2632
|
end
|
2603
2633
|
end
|
2604
|
-
|
2605
|
-
|
2606
|
-
|
2607
|
-
|
2634
|
+
|
2635
|
+
if File.file?(working_path)
|
2636
|
+
digest = Digest::SHA256.hexdigest(File.read(working_path))
|
2637
|
+
# get checksum for the decrypted file. Will be used for creating file_metadata
|
2638
|
+
checksums_of_decrypted_files[File.expand_path(tpkg_path)] = digest
|
2639
|
+
end
|
2608
2640
|
end
|
2609
2641
|
end
|
2610
2642
|
end if metadata[:files] && metadata[:files][:files]
|
@@ -2998,18 +3030,10 @@ class Tpkg
|
|
2998
3030
|
package_name = File.basename(package_file, File.extname(package_file))
|
2999
3031
|
package_metadata_dir = File.join(@metadata_directory, package_name)
|
3000
3032
|
FileUtils.mkdir_p(package_metadata_dir)
|
3001
|
-
|
3002
|
-
metadata.write(metadata_file)
|
3003
|
-
metadata_file.close
|
3033
|
+
metadata.write(package_metadata_dir)
|
3004
3034
|
|
3005
3035
|
# Save file_metadata for this pkg
|
3006
|
-
|
3007
|
-
file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.bin')), 'bin')
|
3008
|
-
elsif File.exist?(File.join(workdir, 'tpkg', 'file_metadata.yml'))
|
3009
|
-
file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.yml')), 'yml')
|
3010
|
-
elsif File.exists?(File.join(workdir, 'tpkg', 'file_metadata.xml'))
|
3011
|
-
file_metadata = FileMetadata.new(File.read(File.join(workdir, 'tpkg', 'file_metadata.xml')), 'xml')
|
3012
|
-
end
|
3036
|
+
file_metadata = FileMetadata::instantiate_from_dir(File.join(workdir, 'tpkg'))
|
3013
3037
|
if file_metadata
|
3014
3038
|
file_metadata[:package_file] = File.basename(package_file)
|
3015
3039
|
file_metadata[:files].each do |file|
|
@@ -3230,7 +3254,6 @@ class Tpkg
|
|
3230
3254
|
end
|
3231
3255
|
end
|
3232
3256
|
end
|
3233
|
-
|
3234
3257
|
# The remove method actually needs !conflicts, so invert in that case
|
3235
3258
|
if mode == CHECK_REMOVE
|
3236
3259
|
# Flatten conflicts to an array
|
@@ -3617,7 +3640,6 @@ class Tpkg
|
|
3617
3640
|
# in order to report back to the server
|
3618
3641
|
already_installed_pkgs = metadata_for_installed_packages.collect{|metadata| metadata.to_hash}
|
3619
3642
|
|
3620
|
-
installed_files = files_for_installed_packages
|
3621
3643
|
removed_pkgs = [] # keep track of what we removed so far
|
3622
3644
|
while pkg = solution_packages.shift
|
3623
3645
|
if pkg[:source] == :currently_installed ||
|
@@ -4020,17 +4042,9 @@ class Tpkg
|
|
4020
4042
|
# Extract checksum.xml from the package
|
4021
4043
|
checksum_xml = nil
|
4022
4044
|
|
4023
|
-
# get file_metadata
|
4024
|
-
|
4025
|
-
|
4026
|
-
file_metadata_xml = File.join(@metadata_directory, package_full_name, 'file_metadata.xml')
|
4027
|
-
if File.exist?(file_metadata_bin)
|
4028
|
-
file_metadata = FileMetadata.new(File.read(file_metadata_bin), 'bin')
|
4029
|
-
elsif File.exist?(file_metadata_yml)
|
4030
|
-
file_metadata = FileMetadata.new(File.read(file_metadata_yml), 'yml')
|
4031
|
-
elsif File.exist?(file_metadata_xml)
|
4032
|
-
file_metadata = FileMetadata.new(File.read(file_metadata_xml), 'xml')
|
4033
|
-
else
|
4045
|
+
# get file_metadata from the installed package
|
4046
|
+
file_metadata = FileMetadata::instantiate_from_dir(File.join(@metadata_directory, package_full_name))
|
4047
|
+
if !file_metadata
|
4034
4048
|
errors = []
|
4035
4049
|
errors << "Can't find file metadata. Most likely this is because the package was created before the verify feature was added"
|
4036
4050
|
results[package_file] = errors
|
@@ -4302,6 +4316,77 @@ class Tpkg
|
|
4302
4316
|
return pre_reqs
|
4303
4317
|
end
|
4304
4318
|
|
4319
|
+
# print out history packages installation/remove
|
4320
|
+
def installation_history
|
4321
|
+
if !File.exists?(File.join(@log_directory,'changes.log'))
|
4322
|
+
puts "Tpkg history log does not exist."
|
4323
|
+
return GENERIC_ERR
|
4324
|
+
end
|
4325
|
+
IO.foreach(File.join(@log_directory,'changes.log')) do |line|
|
4326
|
+
puts line
|
4327
|
+
end
|
4328
|
+
end
|
4329
|
+
|
4330
|
+
# Download packages that meet the requests specified by the user.
|
4331
|
+
# Packages are downloaded into the current directory or into the directory
|
4332
|
+
# specified in options[:out]
|
4333
|
+
def download_pkgs(requests, options={})
|
4334
|
+
if options[:out]
|
4335
|
+
if !File.exists?(options[:out])
|
4336
|
+
FileUtils.mkdir_p(options[:out])
|
4337
|
+
elsif !File.directory?(options[:out])
|
4338
|
+
puts "#{options[:out]} is not a valid directory."
|
4339
|
+
return GENERIC_ERR
|
4340
|
+
end
|
4341
|
+
end
|
4342
|
+
output_dir = options[:out] || Dir.pwd
|
4343
|
+
|
4344
|
+
requirements = []
|
4345
|
+
packages = {}
|
4346
|
+
original_dir = Dir.pwd
|
4347
|
+
|
4348
|
+
workdir = Tpkg::tempdir("tpkg_download")
|
4349
|
+
Dir.chdir(workdir)
|
4350
|
+
parse_requests(requests, requirements, packages)
|
4351
|
+
packages = packages.values.flatten
|
4352
|
+
if packages.size < 1
|
4353
|
+
puts "Unable to find any packages that satisfy your request."
|
4354
|
+
Dir.chdir(original_dir)
|
4355
|
+
return GENERIC_ERR
|
4356
|
+
end
|
4357
|
+
|
4358
|
+
# Confirm with user what packages will be downloaded
|
4359
|
+
packages.delete_if{|pkg|pkg[:source] !~ /^http/}
|
4360
|
+
puts "The following packages will be downloaded:"
|
4361
|
+
packages.each do |pkg|
|
4362
|
+
puts "#{pkg[:metadata][:filename]} (source: #{pkg[:source]})"
|
4363
|
+
end
|
4364
|
+
if @@prompt && !Tpkg::confirm
|
4365
|
+
Dir.chdir(original_dir)
|
4366
|
+
return 0
|
4367
|
+
end
|
4368
|
+
|
4369
|
+
err_code = 0
|
4370
|
+
puts "Downloading to #{output_dir}"
|
4371
|
+
packages.each do |pkg|
|
4372
|
+
puts "Downloading #{pkg[:metadata][:filename]}"
|
4373
|
+
begin
|
4374
|
+
downloaded_file = download(pkg[:source], pkg[:metadata][:filename], Dir.pwd, false)
|
4375
|
+
# copy downloaded files over to destination
|
4376
|
+
FileUtils.cp(downloaded_file, output_dir)
|
4377
|
+
rescue
|
4378
|
+
warn "Warning: unable to download #{pkg[:metadata][:filename]} to #{output_dir}"
|
4379
|
+
err_code = GENERIC_ERR
|
4380
|
+
end
|
4381
|
+
end
|
4382
|
+
|
4383
|
+
# clean up working directory
|
4384
|
+
FileUtils.rm_rf(workdir)
|
4385
|
+
|
4386
|
+
Dir.chdir(original_dir)
|
4387
|
+
return err_code
|
4388
|
+
end
|
4389
|
+
|
4305
4390
|
# TODO: figure out what other methods above can be turned into protected methods
|
4306
4391
|
protected
|
4307
4392
|
# log changes of pkgs that were installed/removed
|
data/lib/tpkg/metadata.rb
CHANGED
@@ -242,6 +242,18 @@ class Metadata
|
|
242
242
|
return metadata
|
243
243
|
end
|
244
244
|
|
245
|
+
# Given the directory from which the metadata is saved, returns a Metadata
|
246
|
+
# object. The metadata file can be in yml or xml format
|
247
|
+
def self.instantiate_from_dir(dir)
|
248
|
+
metadata = nil
|
249
|
+
if File.exist?(File.join(dir, 'tpkg.yml'))
|
250
|
+
metadata = Metadata.new(File.read(File.join(dir, 'tpkg.yml')), 'yml')
|
251
|
+
elsif File.exists?(File.join(dir, 'tpkg.xml'))
|
252
|
+
metadata = Metadata.new(File.read(File.join(dir, 'tpkg.xml')), 'xml')
|
253
|
+
end
|
254
|
+
return metadata
|
255
|
+
end
|
256
|
+
|
245
257
|
# metadata_text = text representation of the metadata
|
246
258
|
# format = yml, xml, json, etc.
|
247
259
|
def initialize(metadata_text, format, source=nil)
|
@@ -279,17 +291,43 @@ class Metadata
|
|
279
291
|
end
|
280
292
|
end
|
281
293
|
end if @hash[:dependencies]
|
294
|
+
|
295
|
+
@hash[:files][:files].each do |file|
|
296
|
+
# We need to do this for backward compatibility. In the old yml schema,
|
297
|
+
# the encrypt field can either be "true" or a string value. Now, it is
|
298
|
+
# a hash. We need to use a hash because we need to store info like the
|
299
|
+
# encryption algorithm.
|
300
|
+
if file[:encrypt] && !file[:encrypt].is_a?(Hash)
|
301
|
+
precrypt = true if file[:encrypt] == 'precrypt'
|
302
|
+
file[:encrypt] = {:precrypt => precrypt}
|
303
|
+
end
|
304
|
+
# perms value are octal, but kwalify might treat it as decimal if it's something like 4550
|
305
|
+
# the user might also use string instead of number
|
306
|
+
if file[:posix] && file[:posix][:perms] &&
|
307
|
+
(file[:posix][:perms].is_a?(String) or file[:posix][:perms] >= 1000)
|
308
|
+
file[:posix][:perms] = "#{file[:posix][:perms]}".oct
|
309
|
+
end
|
310
|
+
end if @hash[:files] && @hash[:files][:files]
|
282
311
|
else
|
283
312
|
@hash = metadata_xml_to_hash.with_indifferent_access
|
284
313
|
end
|
285
314
|
return @hash
|
286
315
|
end
|
287
316
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
317
|
+
# Write the metadata to a file under the specified directory
|
318
|
+
# The file will be saved as tpkg.yml or tpkg.xml.
|
319
|
+
def write(dir, retain_format=false)
|
320
|
+
file = nil
|
321
|
+
if retain_format && @format == 'xml'
|
322
|
+
puts "TODO"
|
323
|
+
else
|
324
|
+
file = File.new(File.join(dir, "tpkg.yml"), "w")
|
325
|
+
# When we convert xml to hash, we store the key as symbol. So when we
|
326
|
+
# write back out to file, we should stringify all the keys for readability.
|
327
|
+
data = to_hash.recursively{|h| h.stringify_keys }
|
328
|
+
YAML::dump(data, file)
|
329
|
+
end
|
330
|
+
file.close
|
293
331
|
end
|
294
332
|
|
295
333
|
def generate_package_filename
|
@@ -559,10 +597,13 @@ class Metadata
|
|
559
597
|
file = {}
|
560
598
|
file[:path] = filexml.elements['path'].text
|
561
599
|
if filexml.elements['encrypt']
|
562
|
-
encrypt =
|
600
|
+
encrypt = {}
|
563
601
|
if filexml.elements['encrypt'].attribute('precrypt') &&
|
564
602
|
filexml.elements['encrypt'].attribute('precrypt').value == 'true'
|
565
|
-
encrypt =
|
603
|
+
encrypt['precrypt'] = true
|
604
|
+
end
|
605
|
+
if filexml.elements['encrypt'].attribute('algorithm')
|
606
|
+
encrypt['algorithm'] = filexml.elements['encrypt'].attribute('algorithm').value
|
566
607
|
end
|
567
608
|
file[:encrypt] = encrypt
|
568
609
|
end
|
@@ -618,6 +659,20 @@ class Metadata
|
|
618
659
|
end
|
619
660
|
|
620
661
|
class FileMetadata < Metadata
|
662
|
+
# Given the directory from which the file_metadata is saved, returns a FileMetadata
|
663
|
+
# object. The file_metadata file can be in binary, yaml or xml.
|
664
|
+
def self.instantiate_from_dir(dir)
|
665
|
+
file_metadata = nil
|
666
|
+
if File.exist?(File.join(dir, 'file_metadata.bin'))
|
667
|
+
file_metadata = FileMetadata.new(File.read(File.join(dir, 'file_metadata.bin')), 'bin')
|
668
|
+
elsif File.exist?(File.join(dir, 'file_metadata.yml'))
|
669
|
+
file_metadata = FileMetadata.new(File.read(File.join(dir, 'file_metadata.yml')), 'yml')
|
670
|
+
elsif File.exists?(File.join(dir, 'file_metadata.xml'))
|
671
|
+
file_metadata = FileMetadata.new(File.read(File.join(dir, 'file_metadata.xml')), 'xml')
|
672
|
+
end
|
673
|
+
return file_metadata
|
674
|
+
end
|
675
|
+
|
621
676
|
def to_hash
|
622
677
|
if @hash
|
623
678
|
return @hash
|
@@ -0,0 +1,91 @@
|
|
1
|
+
type: map
|
2
|
+
mapping:
|
3
|
+
"schema_file": { type: text }
|
4
|
+
"name": { type: str, required: yes }
|
5
|
+
"version": { type: text, required: yes }
|
6
|
+
"package_version": { type: text }
|
7
|
+
"maintainer": { type: str, required: yes }
|
8
|
+
"operatingsystem": { type: seq, sequence: [ {type: str} ] }
|
9
|
+
"architecture": { type: seq, sequence: [ {type: str} ] }
|
10
|
+
"description": { type: str }
|
11
|
+
"bugreporting": { type: str }
|
12
|
+
"dependencies":
|
13
|
+
type: seq
|
14
|
+
sequence:
|
15
|
+
- type: map
|
16
|
+
mapping:
|
17
|
+
"name": { type: str, required: yes }
|
18
|
+
"minimum_version": { type: text }
|
19
|
+
"maximum_version": { type: text }
|
20
|
+
"minimum_package_version": { type: text }
|
21
|
+
"maximum_package_version": { type: text }
|
22
|
+
"allowed_versions": { type: text }
|
23
|
+
"native": { type: bool }
|
24
|
+
"type": { type: any, pattern: /(native|tpkg)$/ }
|
25
|
+
"conflicts":
|
26
|
+
type: seq
|
27
|
+
sequence:
|
28
|
+
- type: map
|
29
|
+
mapping:
|
30
|
+
"name": { type: str, required: yes }
|
31
|
+
"minimum_version": { type: text }
|
32
|
+
"maximum_version": { type: text }
|
33
|
+
"minimum_package_version": { type: text }
|
34
|
+
"maximum_package_version": { type: text }
|
35
|
+
"native": { type: bool }
|
36
|
+
"type": { type: any, pattern: /(native|tpkg)$/ }
|
37
|
+
"externals":
|
38
|
+
type: seq
|
39
|
+
sequence:
|
40
|
+
- type: map
|
41
|
+
mapping:
|
42
|
+
"name": { type: text, required: yes }
|
43
|
+
"data": { type: text }
|
44
|
+
"datascript": { type: text }
|
45
|
+
"datafile": { type: text }
|
46
|
+
"files":
|
47
|
+
type: map
|
48
|
+
mapping:
|
49
|
+
"file_defaults":
|
50
|
+
type: map
|
51
|
+
mapping:
|
52
|
+
"posix":
|
53
|
+
type: map
|
54
|
+
mapping:
|
55
|
+
"owner": { type: text }
|
56
|
+
"group": { type: text }
|
57
|
+
"perms": { type: text }
|
58
|
+
"dirs_defaults":
|
59
|
+
type: map
|
60
|
+
mapping:
|
61
|
+
"posix":
|
62
|
+
type: map
|
63
|
+
mapping:
|
64
|
+
"owner": { type: text }
|
65
|
+
"group": { type: text }
|
66
|
+
"perms": { type: text }
|
67
|
+
"files":
|
68
|
+
type: seq
|
69
|
+
sequence:
|
70
|
+
- type: map
|
71
|
+
mapping:
|
72
|
+
"path": { type: text, required: yes }
|
73
|
+
"encrypt":
|
74
|
+
type: map
|
75
|
+
mapping:
|
76
|
+
"algorithm": { type: text }
|
77
|
+
"precrypt": { type: any, pattern: /^true$|^false$/ }
|
78
|
+
"init":
|
79
|
+
type: map
|
80
|
+
mapping:
|
81
|
+
"start": { type: int }
|
82
|
+
"levels": { type: seq, sequence: [ { type: int } ] }
|
83
|
+
"crontab":
|
84
|
+
type: map
|
85
|
+
mapping: { "user": { type: str } }
|
86
|
+
"posix":
|
87
|
+
type: map
|
88
|
+
mapping: { "owner": { type: text },
|
89
|
+
"group": { type: text },
|
90
|
+
"perms": { type: text } }
|
91
|
+
|
data/schema/schema.yml
CHANGED
@@ -70,7 +70,11 @@ mapping:
|
|
70
70
|
- type: map
|
71
71
|
mapping:
|
72
72
|
"path": { type: text, required: yes }
|
73
|
-
"encrypt":
|
73
|
+
"encrypt":
|
74
|
+
type: map
|
75
|
+
mapping:
|
76
|
+
"algorithm": { type: text }
|
77
|
+
"precrypt": { type: any, pattern: /^true$|^false$/ }
|
74
78
|
"init":
|
75
79
|
type: map
|
76
80
|
mapping:
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<!ELEMENT tpkg (name, version, package_version?, maintainer+, operatingsystem*, architecture*, description?, bugreporting?, dependencies?, externals?, files?)>
|
2
|
+
|
3
|
+
<!ELEMENT name (#PCDATA)>
|
4
|
+
<!ELEMENT version (#PCDATA)>
|
5
|
+
<!ELEMENT package_version (#PCDATA)>
|
6
|
+
<!ELEMENT maintainer (#PCDATA)>
|
7
|
+
<!ELEMENT operatingsystem (#PCDATA)>
|
8
|
+
<!ELEMENT architecture (#PCDATA)>
|
9
|
+
<!ELEMENT description (#PCDATA)>
|
10
|
+
<!ELEMENT bugreporting (#PCDATA)>
|
11
|
+
|
12
|
+
<!ELEMENT dependencies (dependency*)>
|
13
|
+
<!ELEMENT dependency (name, minimum_version?, maximum_version?, minimum_package_version?, maximum_package_version?, allowed_versions?, native?)>
|
14
|
+
<!ELEMENT minimum_version (#PCDATA)>
|
15
|
+
<!ELEMENT maximum_version (#PCDATA)>
|
16
|
+
<!ELEMENT minimum_package_version (#PCDATA)>
|
17
|
+
<!ELEMENT maximum_package_version (#PCDATA)>
|
18
|
+
<!ELEMENT allowed_versions (#PCDATA)>
|
19
|
+
<!ELEMENT native EMPTY>
|
20
|
+
|
21
|
+
<!ELEMENT externals (external*)>
|
22
|
+
<!ELEMENT external (name, (data|datafile|datascript))>
|
23
|
+
<!ELEMENT data (#PCDATA)>
|
24
|
+
<!ELEMENT datafile (#PCDATA)>
|
25
|
+
<!ELEMENT datascript (#PCDATA)>
|
26
|
+
|
27
|
+
<!ELEMENT files (file_defaults?, file*)>
|
28
|
+
<!ELEMENT file_defaults (posix?)>
|
29
|
+
<!ELEMENT posix (owner?, group?, perms?)>
|
30
|
+
<!ELEMENT owner (#PCDATA)>
|
31
|
+
<!ELEMENT group (#PCDATA)>
|
32
|
+
<!ELEMENT perms (#PCDATA)>
|
33
|
+
<!ELEMENT file (path, encrypt?, init?, crontab?, posix?)>
|
34
|
+
<!ELEMENT path (#PCDATA)>
|
35
|
+
<!ELEMENT encrypt EMPTY>
|
36
|
+
<!ATTLIST encrypt precrypt (true|false) #IMPLIED>
|
37
|
+
<!ATTLIST encrypt algorithm CDATA #IMPLIED>
|
38
|
+
<!ELEMENT init (start?, levels?)>
|
39
|
+
<!ELEMENT start (#PCDATA)>
|
40
|
+
<!ELEMENT levels (#PCDATA)>
|
41
|
+
<!ELEMENT crontab (user?)>
|
42
|
+
<!ELEMENT user (#PCDATA)>
|
43
|
+
|
data/schema/tpkg.dtd
CHANGED
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.25.1
|
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-
|
13
|
+
date: 2010-07-14 00:00:00 +00:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -63,8 +63,10 @@ files:
|
|
63
63
|
- schema/tpkg-1.0.4.dtd
|
64
64
|
- schema/tpkg-1.0.0.dtd
|
65
65
|
- schema/tpkg-1.0.5.dtd
|
66
|
+
- schema/schema-1.0.7.yml
|
66
67
|
- schema/tpkg.dtd
|
67
68
|
- schema/tpkg-1.0.1.dtd
|
69
|
+
- schema/tpkg-1.0.7.dtd
|
68
70
|
- schema/tpkg-1.0.3.dtd
|
69
71
|
- schema/schema.yml
|
70
72
|
- schema/tpkg-1.0.2.dtd
|