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 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.23.3'
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'
@@ -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
- IO.popen("#{@gemcmd} environment") do |pipe|
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
- IO.popen("#{@gemcmd} dependency /^#{Regexp.escape(gem)}$/") do |pipe|
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 = nil
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
- @compress = opt
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
@@ -56,7 +56,7 @@ require 'kwalify' # for validating yaml
56
56
 
57
57
  class Tpkg
58
58
 
59
- VERSION = '1.23.3'
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
- if File.exists?(File.join(tpkgdir, 'tpkg.yml'))
274
- metadata_file = File.join(tpkgdir, 'tpkg.yml')
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] == 'precrypt'
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
- topleveldir = package_toplevel_directory(package_file)
872
- extract_tpkg_tar_cmd = cmd_to_extract_tpkg_tar(package_file, topleveldir)
873
- IO.popen("#{extract_tpkg_tar_cmd} | #{find_tar} #{@@taroptions} -tf -") do |pipe|
874
- pipe.each do |file|
875
- file.chomp!
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
- if !$?.success?
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 = nil
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
- else
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 File.exists?(File.join(@metadata_directory, entry, "file_metadata.bin"))
1731
- file = File.join(@metadata_directory, entry, "file_metadata.bin")
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
- digest = Digest::SHA256.hexdigest(File.read(working_path))
2606
- # get checksum for the decrypted file. Will be used for creating file_metadata
2607
- checksums_of_decrypted_files[File.expand_path(tpkg_path)] = digest
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
- metadata_file = File.new(File.join(package_metadata_dir, "tpkg.yml"), "w")
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
- if File.exist?(File.join(workdir, 'tpkg', 'file_metadata.bin'))
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.xml from the installed package
4024
- file_metadata_bin = File.join(@metadata_directory, package_full_name, 'file_metadata.bin')
4025
- file_metadata_yml = File.join(@metadata_directory, package_full_name, 'file_metadata.yml')
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
@@ -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
- def write(file)
289
- # When we convert xml to hash, we store the key as symbol. So when we
290
- # write back out to file, we should stringify all the keys for readability.
291
- data = to_hash.recursively{|h| h.stringify_keys }
292
- YAML::dump(data, file)
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 = true
600
+ encrypt = {}
563
601
  if filexml.elements['encrypt'].attribute('precrypt') &&
564
602
  filexml.elements['encrypt'].attribute('precrypt').value == 'true'
565
- encrypt = "precrypt"
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
+
@@ -70,7 +70,11 @@ mapping:
70
70
  - type: map
71
71
  mapping:
72
72
  "path": { type: text, required: yes }
73
- "encrypt": { type: any, pattern: /^true$|^precrypt$/ }
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
+
@@ -34,6 +34,7 @@
34
34
  <!ELEMENT path (#PCDATA)>
35
35
  <!ELEMENT encrypt EMPTY>
36
36
  <!ATTLIST encrypt precrypt (true|false) #IMPLIED>
37
+ <!ATTLIST encrypt algorithm CDATA #IMPLIED>
37
38
  <!ELEMENT init (start?, levels?)>
38
39
  <!ELEMENT start (#PCDATA)>
39
40
  <!ELEMENT levels (#PCDATA)>
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.23.3
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-05-20 00:00:00 +00:00
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