tpkg 1.23.3 → 1.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +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
|