tpkg 2.2.4 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,19 +1,19 @@
1
1
  require 'rake/gempackagetask'
2
2
  spec = Gem::Specification.new do |s|
3
- s.name = 'tpkg'
4
- s.summary = 'tpkg Application Packaging & Deployment'
5
- s.add_dependency('facter')
6
- s.add_dependency('net-ssh')
7
- s.add_dependency('kwalify')
8
- s.version = '2.2.4'
3
+ s.name = 'tpkg'
4
+ s.summary = 'tpkg Application Packaging & Deployment'
5
+ s.version = '2.3.0'
9
6
  s.authors = ['Darren Dao', 'Jason Heiss']
10
7
  s.email = 'tpkg-users@lists.sourceforge.net'
11
8
  s.homepage = 'http://tpkg.sourceforge.net'
12
9
  s.rubyforge_project = 'tpkg'
13
10
  s.platform = Gem::Platform::RUBY
14
11
  s.required_ruby_version = '>=1.8'
15
- s.files = Dir['**/**']
12
+ s.files = Dir['**/**']
16
13
  s.executables = [ 'tpkg', 'cpan2tpkg', 'gem2tpkg', 'tpkg_xml_to_yml' ]
14
+ s.add_dependency('facter')
15
+ s.add_dependency('net-ssh')
16
+ s.add_dependency('kwalify')
17
17
  end
18
18
  Rake::GemPackageTask.new(spec).define
19
19
 
@@ -1,7 +1,6 @@
1
1
  #!/opt/tpkg/bin/perl
2
2
  ##############################################################################
3
3
  # tpkg package management system
4
- # Copyright 2009, 2010, 2011 AT&T Interactive
5
4
  # License: MIT (http://www.opensource.org/licenses/mit-license.php)
6
5
  ##############################################################################
7
6
 
@@ -78,7 +77,7 @@ while (scalar @extradepsopts)
78
77
  {
79
78
  my $dep = shift @extradepsopts;
80
79
  # These are optional, shift will return undef if they aren't present
81
- # and we'll handle that properly when creating tpkg.xml
80
+ # and we'll handle that properly when creating tpkg.yml
82
81
  my $depminver = shift @extradepsopts;
83
82
  my $depmaxver = shift @extradepsopts;
84
83
  $extradeps{$dep} = {};
@@ -90,7 +89,7 @@ while (scalar @nativedepsopts)
90
89
  {
91
90
  my $dep = shift @nativedepsopts;
92
91
  # These are optional, shift will return undef if they aren't present
93
- # and we'll handle that properly when creating tpkg.xml
92
+ # and we'll handle that properly when creating tpkg.yml
94
93
  my $depminver = shift @nativedepsopts;
95
94
  my $depmaxver = shift @nativedepsopts;
96
95
  $nativedeps{$dep} = {};
@@ -315,23 +314,29 @@ MODULE: foreach my $name ($extinst->modules)
315
314
  $packlistfile =~ s/^$workdir//;
316
315
  # Make directory tree in $tpkgdir
317
316
  mkpath("$tpkgdir/root/" . dirname($packlistfile));
317
+
318
318
  # Copy file
319
- copy("$workdir/$packlistfile", "$tpkgdir/root/$packlistfile");
319
+ my $src = "$workdir/$packlistfile";
320
+ my $dst = "$tpkgdir/root/$packlistfile";
321
+ copy($src, $dst);
322
+
323
+ # preserve permissions
324
+ # (avoiding dependency on File::Copy::Recursive::fcopy)
325
+ my $mode = (stat($src))[2];
326
+ chmod($mode & 07777, $dst);
327
+
320
328
  if ($packlistfile =~ quotemeta($Config{archname}))
321
329
  {
322
330
  $nativefile = 1;
323
331
  }
324
332
  }
325
- # Create tpkg.xml
326
- open(my $xmlfh, '>', "$tpkgdir/tpkg.xml") or die;
327
- print $xmlfh '<?xml version="1.0" encoding="UTF-8"?>', "\n";
328
- print $xmlfh '<!DOCTYPE tpkg SYSTEM "http://tpkg.sourceforge.net/tpkg-1.0.dtd">', "\n";
329
- print $xmlfh '<tpkg>', "\n";
330
- print $xmlfh " <name>cpan-perl$majorminor-$pkgname</name>", "\n";
331
- print $xmlfh " <version>$ver</version>", "\n";
332
- print $xmlfh " <package_version>$pkgver</package_version>", "\n";
333
- print $xmlfh " <description>cpan package for $pkgname</description>", "\n";
334
- print $xmlfh ' <maintainer>cpan2tpkg</maintainer>', "\n";
333
+ # Create tpkg.yml
334
+ open(my $ymlfh, '>', "$tpkgdir/tpkg.yml") or die;
335
+ print $ymlfh "name: cpan-perl$majorminor-$pkgname", "\n";
336
+ print $ymlfh "version: $ver", "\n";
337
+ print $ymlfh "package_version: $pkgver", "\n";
338
+ print $ymlfh "description: cpan package for $pkgname", "\n";
339
+ print $ymlfh 'maintainer: cpan2tpkg', "\n";
335
340
  # If the package has native code then it needs to be flagged as
336
341
  # specific to the OS and architecture
337
342
  if ($nativefile)
@@ -360,68 +365,58 @@ MODULE: foreach my $name ($extinst->modules)
360
365
  # vice-versa
361
366
  if ($os =~ /RedHat-(.*)/)
362
367
  {
363
- $os = $os . ",CentOS-$1";
368
+ $os = $os . ", CentOS-$1";
364
369
  }
365
370
  elsif ($os =~ /CentOS-(.*)/)
366
371
  {
367
- $os = $os . ",RedHat-$1";
372
+ $os = $os . ", RedHat-$1";
368
373
  }
369
- print $xmlfh " <operatingsystem>$os</operatingsystem>", "\n";
370
- print $xmlfh " <architecture>$arch</architecture>", "\n";
374
+ print $ymlfh "operatingsystem: [$os]", "\n";
375
+ print $ymlfh "architecture: [$arch]", "\n";
371
376
  }
372
377
  # Insert appropriate dependencies into the package
373
- print $xmlfh ' <dependencies>', "\n";
378
+ print $ymlfh 'dependencies:', "\n";
374
379
  foreach my $dep (keys %deps)
375
380
  {
376
- print $xmlfh ' <dependency>', "\n";
377
- print $xmlfh " <name>cpan-perl$majorminor-$dep</name>", "\n";
381
+ print $ymlfh " - name: cpan-perl$majorminor-$dep", "\n";
378
382
  if ($deps{$dep}{minimum_version})
379
383
  {
380
- print $xmlfh " <minimum_version>$deps{$dep}{minimum_version}</minimum_version>", "\n";
384
+ print $ymlfh " minimum_version: $deps{$dep}{minimum_version}", "\n";
381
385
  }
382
386
  if ($deps{$dep}{maximum_version})
383
387
  {
384
- print $xmlfh " <maximum_version>$deps{$dep}{maximum_version}</maximum_version>", "\n";
388
+ print $ymlfh " maximum_version: $deps{$dep}{maximum_version}", "\n";
385
389
  }
386
- print $xmlfh ' </dependency>', "\n";
387
390
  }
388
391
  foreach my $extradep (keys %extradeps)
389
392
  {
390
- print $xmlfh ' <dependency>', "\n";
391
- print $xmlfh " <name>$extradep</name>", "\n";
393
+ print $ymlfh " - name: $extradep", "\n";
392
394
  if ($extradeps{$extradep}{minimum_version})
393
395
  {
394
- print $xmlfh " <minimum_version>$extradeps{$extradep}{minimum_version}</minimum_version>", "\n";
396
+ print $ymlfh " minimum_version: $extradeps{$extradep}{minimum_version}", "\n";
395
397
  }
396
398
  if ($extradeps{$extradep}{maximum_version})
397
399
  {
398
- print $xmlfh " <maximum_version>$extradeps{$extradep}{maximum_version}</maximum_version>", "\n";
400
+ print $ymlfh " maximum_version: $extradeps{$extradep}{maximum_version}", "\n";
399
401
  }
400
- print $xmlfh ' </dependency>', "\n";
401
402
  }
402
403
  foreach my $nativedep (keys %nativedeps)
403
404
  {
404
- print $xmlfh ' <dependency>', "\n";
405
- print $xmlfh " <name>$nativedep</name>", "\n";
405
+ print $ymlfh " - name: $nativedep", "\n";
406
406
  if ($nativedeps{$nativedep}{minimum_version})
407
407
  {
408
- print $xmlfh " <minimum_version>$nativedeps{$nativedep}{minimum_version}</minimum_version>", "\n";
408
+ print $ymlfh " minimum_version: $nativedeps{$nativedep}{minimum_version}", "\n";
409
409
  }
410
410
  if ($nativedeps{$nativedep}{maximum_version})
411
411
  {
412
- print $xmlfh " <maximum_version>$nativedeps{$nativedep}{maximum_version}</maximum_version>", "\n";
412
+ print $ymlfh " maximum_version: $nativedeps{$nativedep}{maximum_version}", "\n";
413
413
  }
414
- print $xmlfh ' <native/>', "\n";
415
- print $xmlfh ' </dependency>', "\n";
414
+ print $ymlfh ' native: true', "\n";
416
415
  }
417
416
  # Insert an appropriate dependency on Perl itself
418
- print $xmlfh ' <dependency>', "\n";
419
- print $xmlfh " <name>perl</name>", "\n";
420
- print $xmlfh " <minimum_version>", sprintf("%vd", $^V), "</minimum_version>", "\n";
421
- print $xmlfh " <maximum_version>$majordotminor.9999</maximum_version>", "\n";
422
- print $xmlfh ' </dependency>', "\n";
423
- print $xmlfh ' </dependencies>', "\n";
424
- print $xmlfh '</tpkg>', "\n";
417
+ print $ymlfh ' - name: perl', "\n";
418
+ print $ymlfh ' minimum_version: ', sprintf("%vd", $^V), "\n";
419
+ print $ymlfh " maximum_version: $majordotminor.9999", "\n";
425
420
  # Build package
426
421
  system("tpkg --make $tpkgdir");
427
422
  }
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/ruby -w
2
2
  ##############################################################################
3
3
  # tpkg package management system
4
- # Copyright 2009, 2010, 2011 AT&T Interactive
5
4
  # License: MIT (http://www.opensource.org/licenses/mit-license.php)
6
5
  ##############################################################################
7
6
 
8
- # Ensure we can find tpkg.rb
9
- $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
7
+ # When run from the source repository or from an unpacked copy of the
8
+ # distribution we want to find the local library, even if there's a copy
9
+ # installed on the system. The installed copy is likely older, and the API
10
+ # may be out of sync with this executable.
11
+ $:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
10
12
 
11
13
  require 'fileutils' # FileUtils.cp, rm, etc.
12
14
  require 'tempfile' # Tempfile
@@ -56,7 +58,7 @@ opts.on('--extra-deps', '=EXTRADEPS', Array, 'Extra dependencies to add to the p
56
58
  while !opt.empty?
57
59
  dep = opt.shift
58
60
  # These are optional, shift will return nil if they aren't present
59
- # and we'll handle that properly when creating tpkg.xml
61
+ # and we'll handle that properly when creating tpkg.yml
60
62
  depminver = opt.shift
61
63
  depmaxver = opt.shift
62
64
  @extradeps[dep] = {}
@@ -69,7 +71,7 @@ opts.on('--native-deps', '=NATIVEDEPS', Array, 'Native dependencies to add to th
69
71
  while !opt.empty?
70
72
  dep = opt.shift
71
73
  # These are optional, shift will return nil if they aren't present
72
- # and we'll handle that properly when creating tpkg.xml
74
+ # and we'll handle that properly when creating tpkg.yml
73
75
  depminver = opt.shift
74
76
  depmaxver = opt.shift
75
77
  @nativedeps[dep] = {}
@@ -273,7 +275,7 @@ def package(gem)
273
275
  operator.sub!(/^\(/, '')
274
276
  depver.sub!(/\)$/, '')
275
277
  depver.sub!(/,$/, '')
276
- # Save the dependency info for tpkg.xml
278
+ # Save the dependency info for tpkg.yml
277
279
  # http://rubygems.org/read/chapter/16
278
280
  deps[depgem] = {}
279
281
  case operator
@@ -393,77 +395,83 @@ def package(gem)
393
395
  pkgnamesuffix = '-' + @gemdep.first.sub(/\W/, '')
394
396
  end
395
397
 
396
- # Add tpkg.xml
398
+ # Determine if we're packaging a gem the user requested or a dependency gem.
399
+ # If we're packaging a dependency gem we don't want to add @extradeps or
400
+ # @nativedeps. Say you run gem2tpkg for gem foo. You specify some extra
401
+ # dependencies with --extra-deps and/or --native-deps. Gem foo depends on
402
+ # gems bar and baz. The generated tpkgs for bar and baz will include those
403
+ # extra dependencies. I don't think they should. Bar and baz may be very
404
+ # general gems with no close relationship to foo. It seems to me if the user
405
+ # needs bar and baz to have the same extra dependencies they should gem2tpkg
406
+ # those separately. We shouldn't assume everything in the dependency chain
407
+ # needs those same dependencies.
408
+ packaging_requested_gem = false
409
+ if @gems.include?(gem)
410
+ packaging_requested_gem = true
411
+ end
412
+
413
+ # Add tpkg.yml
397
414
  os = nil
398
415
  arch = nil
399
- File.open(File.join(pkgdir, 'tpkg.xml'), 'w') do |file|
400
- file.puts '<?xml version="1.0" encoding="UTF-8"?>'
401
- file.puts '<!DOCTYPE tpkg SYSTEM "http://tpkg.sourceforge.net/tpkg-1.0.dtd">'
402
- file.puts '<tpkg>'
403
- file.puts " <name>gem-#{gem}#{pkgnamesuffix}</name>"
404
- file.puts " <version>#{gemspec.version.to_s}</version>"
405
- file.puts " <package_version>#{@pkgver}</package_version>"
406
- file.puts " <description>Ruby gem for #{gem}</description>"
407
- file.puts ' <maintainer>gem2tpkg</maintainer>'
416
+ File.open(File.join(pkgdir, 'tpkg.yml'), 'w') do |file|
417
+
418
+ file.puts "name: gem-#{gem}#{pkgnamesuffix}"
419
+ file.puts "version: #{gemspec.version.to_s}"
420
+ file.puts "package_version: #{@pkgver}"
421
+ file.puts "description: Ruby gem for #{gem}"
422
+ file.puts 'maintainer: gem2tpkg'
408
423
  # If the gemspec lists any extensions then the package has native
409
424
  # code and needs to be flagged as specific to the OS and architecture
410
425
  if gemspec.extensions && !gemspec.extensions.empty?
411
426
  os = Tpkg::get_os
412
427
  if os =~ /RedHat-(.*)/
413
- os = os + ",CentOS-#{$1}"
428
+ os = os + ", CentOS-#{$1}"
414
429
  elsif os =~ /CentOS-(.*)/
415
- os = os + ",RedHat-#{$1}"
430
+ os = os + ", RedHat-#{$1}"
416
431
  end
417
432
  arch = Facter['hardwaremodel'].value
418
- file.puts " <operatingsystem>#{os}</operatingsystem>"
419
- file.puts " <architecture>#{arch}</architecture>"
433
+ file.puts "operatingsystem: [#{os}]"
434
+ file.puts "architecture: [#{arch}]"
420
435
  end
421
436
  if !deps.empty? ||
422
- !@extradeps.empty? || !@nativedeps.empty? ||
437
+ (!@extradeps.empty? && packaging_requested_gem) ||
438
+ (!@nativedeps.empty? && packaging_requested_gem) ||
423
439
  !@gemdep.empty?
424
- file.puts ' <dependencies>'
440
+ file.puts 'dependencies:'
425
441
  deps.each do |depgem, depvers|
426
- file.puts ' <dependency>'
427
- file.puts " <name>gem-#{depgem}#{pkgnamesuffix}</name>"
428
- if depvers[:minimum_version]
429
- file.puts " <minimum_version>#{depvers[:minimum_version]}</minimum_version>"
430
- end
431
- if depvers[:maximum_version]
432
- file.puts " <maximum_version>#{depvers[:maximum_version]}</maximum_version>"
433
- end
434
- file.puts ' </dependency>'
435
- end
436
- @extradeps.each do |extradep, depvers|
437
- file.puts ' <dependency>'
438
- file.puts " <name>#{extradep}</name>"
442
+ file.puts " - name: gem-#{depgem}#{pkgnamesuffix}"
439
443
  if depvers[:minimum_version]
440
- file.puts " <minimum_version>#{depvers[:minimum_version]}</minimum_version>"
444
+ file.puts " minimum_version: #{depvers[:minimum_version]}"
441
445
  end
442
446
  if depvers[:maximum_version]
443
- file.puts " <maximum_version>#{depvers[:maximum_version]}</maximum_version>"
447
+ file.puts " maximum_version: #{depvers[:maximum_version]}"
444
448
  end
445
- file.puts ' </dependency>'
446
449
  end
447
- @nativedeps.each do |nativedep, depvers|
448
- file.puts ' <dependency>'
449
- file.puts " <name>#{nativedep}</name>"
450
- if depvers[:minimum_version]
451
- file.puts " <minimum_version>#{depvers[:minimum_version]}</minimum_version>"
450
+ if packaging_requested_gem
451
+ @extradeps.each do |extradep, depvers|
452
+ file.puts " - name: #{extradep}"
453
+ if depvers[:minimum_version]
454
+ file.puts " minimum_version: #{depvers[:minimum_version]}"
455
+ end
456
+ if depvers[:maximum_version]
457
+ file.puts " maximum_version: #{depvers[:maximum_version]}"
458
+ end
452
459
  end
453
- if depvers[:maximum_version]
454
- file.puts " <maximum_version>#{depvers[:maximum_version]}</maximum_version>"
460
+ @nativedeps.each do |nativedep, depvers|
461
+ file.puts " - name: #{nativedep}"
462
+ if depvers[:minimum_version]
463
+ file.puts " minimum_version: #{depvers[:minimum_version]}"
464
+ end
465
+ if depvers[:maximum_version]
466
+ file.puts " maximum_version: #{depvers[:maximum_version]}"
467
+ end
468
+ file.puts ' native: true'
455
469
  end
456
- file.puts ' <native/>'
457
- file.puts ' </dependency>'
458
470
  end
459
471
  @gemdep.each do |gemdep|
460
- file.puts ' <dependency>'
461
- file.puts " <name>#{gemdep}</name>"
462
- file.puts ' </dependency>'
472
+ file.puts " - name: #{gemdep}"
463
473
  end
464
- file.puts ' </dependencies>'
465
474
  end
466
- file.puts '</tpkg>'
467
475
  end
468
476
 
469
477
  # Make package
data/bin/tpkg CHANGED
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/ruby -w
2
2
  ##############################################################################
3
3
  # tpkg package management system
4
- # Copyright 2009, 2010, 2011 AT&T Interactive
5
4
  # License: MIT (http://www.opensource.org/licenses/mit-license.php)
6
5
  ##############################################################################
7
6
 
8
- # Ensure we can find tpkg.rb
9
- $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
7
+ # When run from the source repository or from an unpacked copy of the
8
+ # distribution we want to find the local library, even if there's a copy
9
+ # installed on the system. The installed copy is likely older, and the API
10
+ # may be out of sync with this executable.
11
+ $:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
10
12
 
11
13
  require 'optparse'
12
14
  require 'tpkg'
@@ -20,15 +22,23 @@ require 'tpkg'
20
22
  @debug = false
21
23
  @prompt = true
22
24
  @quiet = false
23
- @sudo = true
24
- @lockforce = false
25
+ @sudo = nil
26
+ # Neither of the common Windows environments for running Ruby have
27
+ # sudo, so turn it off by default on those platforms
28
+ if RUBY_PLATFORM == 'i386-mingw32' || RUBY_PLATFORM == 'i386-cygwin'
29
+ @sudo = false
30
+ else
31
+ @sudo = true
32
+ end
25
33
  @force = false
26
34
  @deploy = false
27
35
  @deploy_params = ARGV # hold parameters for how to invoke tpkg on the machines we're deploying to
28
36
  @deploy_options = {} # options for how to run the deployer
29
- @servers = nil
37
+ @servers = []
38
+ @groups = nil
30
39
  @worker_count = 10
31
40
  @rerun_with_sudo = false
41
+ @sources = []
32
42
  @tpkg_options = {} # options for instantiating Tpkg object
33
43
  @init_options = {} # options for how to run init scripts
34
44
  @other_options = {}
@@ -37,7 +47,7 @@ require 'tpkg'
37
47
 
38
48
  def rerun_with_sudo_if_necessary
39
49
  if Process.euid != 0 && @sudo
40
- warn "Executing with sudo"
50
+ warn "Executing with sudo" if !@quiet
41
51
  # Depending on how sudo is configured it might remove TPKG_HOME from the
42
52
  # environment. As such we set the base as a command line option to ensure
43
53
  # it survives the sudo process.
@@ -49,21 +59,53 @@ def rerun_with_sudo_if_necessary
49
59
  end
50
60
  end
51
61
 
62
+ # This method can only be safely called after command line option parsing is
63
+ # complete
64
+ @config_file_settings = nil
65
+ def parse_config_files
66
+ if @config_file_settings
67
+ return @config_file_settings
68
+ end
69
+
70
+ # FIXME: Move config file parsing to tpkg.rb
71
+ # http://sourceforge.net/apps/trac/tpkg/ticket/28
72
+ fsroot = @tpkg_options[:file_system_root] ? @tpkg_options[:file_system_root] : ''
73
+ settings = {:sources => []}
74
+ [File.join(fsroot, Tpkg::DEFAULT_CONFIGDIR, 'tpkg.conf'),
75
+ File.join(fsroot, ENV['HOME'], ".tpkg.conf")].each do |configfile|
76
+ if File.exist?(configfile)
77
+ IO.foreach(configfile) do |line|
78
+ line.chomp!
79
+ next if (line =~ /^\s*$/); # Skip blank lines
80
+ next if (line =~ /^\s*#/); # Skip comments
81
+ line.strip! # Remove leading/trailing whitespace
82
+ key, value = line.split(/\s*=\s*/, 2)
83
+ if key == 'base'
84
+ settings[:base] = value
85
+ puts "Loaded base #{value} from #{configfile}" if @debug
86
+ elsif key == 'source'
87
+ settings[:sources] << value
88
+ puts "Loaded source #{value} from #{configfile}" if @debug
89
+ elsif key == 'report_server'
90
+ settings[:report_server] = value
91
+ puts "Loaded report server #{value} from #{configfile}" if @debug
92
+ elsif key == 'host_group_script'
93
+ settings[:host_group_script] = value
94
+ puts "Loaded host group script #{value} from #{configfile}" if @debug
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ @config_file_settings = settings
101
+ end
102
+
52
103
  opts = OptionParser.new(nil, 24, ' ')
53
104
  opts.banner = 'Usage: tpkg [options]'
54
- opts.on('--servers', '-s', '=SERVERS', Array, 'Servers to apply the actions to') do |opt|
55
- @servers = opt
56
- @deploy = true
57
- @deploy_params = @deploy_params - ['--servers', '-s', @servers.join(","), "--servers=#{@servers.join(',')}"]
58
- end
59
105
  opts.on('--make', '-m', '=DIRECTORY', 'Make a package out of the contents of the directory') do |opt|
60
106
  @action = :make
61
107
  @action_value = opt
62
108
  end
63
- opts.on('--extract', '-x', '=DIRECTORY', 'Extract the metadata for a directory of packages') do |opt|
64
- @action = :extract
65
- @action_value = opt
66
- end
67
109
  installexample = " --install pkgname=version=package_version\n (Example: tpkg --install hive=2.1) Will install hive version 2.1"
68
110
  opts.on('--install', '-i', '=PACKAGES', "Install one or more packages\n#{installexample}", Array) do |opt|
69
111
  @rerun_with_sudo = true
@@ -81,6 +123,27 @@ opts.on('--downgrade', '=PACKAGES', 'Downgrade one or more packages', Array) do
81
123
  @action = :upgrade
82
124
  @action_value = opt
83
125
  end
126
+ opts.on('--servers', '-s', '=SERVERS', Array, 'Servers on which to apply actions, defaults to local') do |opt|
127
+ @servers.concat(opt)
128
+ @deploy = true
129
+ # FIXME: this won't remove options that the user specified as an
130
+ # abbreviation. I.e. if the option is --servers and the user specified
131
+ # --serv (which OptionParser will accept as long as it is unambiguous) this
132
+ # won't detect and remove it.
133
+ @deploy_params = @deploy_params - ['--servers', '-s', @servers.join(","), "--servers=#{@servers.join(',')}"]
134
+ end
135
+ opts.on('--groups', '-g', '=GROUP', Array, 'Group of server on which to apply actions') do |opt|
136
+ # We'll finish processing this later. To expand the groups we need the name
137
+ # of the host_group_script from the config file, but we can't safely call
138
+ # parse_config_files until we're done processing command line options.
139
+ @groups = opt
140
+ @deploy = true
141
+ # FIXME: this won't remove options that the user specified as an
142
+ # abbreviation. I.e. if the option is --servers and the user specified
143
+ # --serv (which OptionParser will accept as long as it is unambiguous) this
144
+ # won't detect and remove it.
145
+ @deploy_params = @deploy_params - ['--groups', '-g', @groups.join(","), "--servers=#{@groups.join(',')}"]
146
+ end
84
147
  opts.on('--ua', 'Upgrade all packages') do |opt|
85
148
  @rerun_with_sudo = true
86
149
  @action = :upgrade
@@ -174,7 +237,7 @@ opts.on('--init-cmd', '=CMD', 'Invoke specified init script command') do |opt|
174
237
  @rerun_with_sudo = true
175
238
  @init_options[:cmd] = opt
176
239
  end
177
- opts.on('--query', '-q', '=NAMES', 'List installed packages', Array) do |opt|
240
+ opts.on('--query', '-q', '=NAMES', 'Check if a package is installed', Array) do |opt|
178
241
  # People mistype -qa instead of --qa frequently
179
242
  if opt == ['a']
180
243
  warn "NOTE: tpkg -qa queries for a pkg named 'a', you probably want --qa for all pkgs"
@@ -182,49 +245,67 @@ opts.on('--query', '-q', '=NAMES', 'List installed packages', Array) do |opt|
182
245
  @action = :query_installed
183
246
  @action_value = opt
184
247
  end
248
+ # --qv is deprecated
249
+ opts.on('--qs', '--qv', '=NAME', 'Check if a package is available on server', Array) do |opt|
250
+ @action = :query_available
251
+ @action_value = opt
252
+ end
185
253
  opts.on('--qa', 'List all installed packages') do |opt|
186
254
  @action = :query_installed
187
255
  end
188
- opts.on('--qi', '=NAME', 'Info for packages') do |opt|
256
+ # --qva is deprecated
257
+ opts.on('--qas', '--qva', 'List all packages on server') do |opt|
258
+ @action = :query_available
259
+ end
260
+ opts.on('--qi', '=NAME', 'Display the info for a package') do |opt|
189
261
  @action = :query_info
190
262
  @action_value = opt
191
263
  end
192
- opts.on('--ql', '=NAME', 'List files in installed packages') do |opt|
193
- @action = :query_list_files
264
+ opts.on('--qis', '=NAME', 'Display the info for a package on the server') do |opt|
265
+ @action = :query_info_available
194
266
  @action_value = opt
195
267
  end
196
- opts.on('--qf', '=FILE', 'List the package that owns a file') do |opt|
197
- @action = :query_who_owns_file
268
+ opts.on('--ql', '=NAME', 'List the files in a package') do |opt|
269
+ @action = :query_list_files
198
270
  @action_value = opt
199
271
  end
200
- opts.on('--qv', '=NAME', 'List available packages') do |opt|
201
- @action = :query_available
272
+ opts.on('--qls', '=NAME', 'List the files in a package on the server') do |opt|
273
+ @action = :query_list_files_available
202
274
  @action_value = opt
203
275
  end
204
- opts.on('--qva', 'List all available packages') do |opt|
205
- @action = :query_available
276
+ opts.on('--qf', '=FILE', 'List the package that owns a file') do |opt|
277
+ @action = :query_who_owns_file
278
+ @action_value = opt
206
279
  end
207
280
  opts.on('--qr', '=NAME', 'List installed packages that require package') do |opt|
208
281
  @action = :query_requires
209
282
  @action_value = opt
210
283
  end
211
- opts.on('--qd', '=NAME', 'List the packages that package depends on') do |opt|
284
+ opts.on('--qd', '=NAME', 'List the packages on which the given package depends') do |opt|
212
285
  @action = :query_depends
213
286
  @action_value = opt
214
287
  end
215
- opts.on('--qld', '=NAME', 'Similar to --qd, but only look at local packages') do |opt|
216
- @action = :query_local_depends
288
+ opts.on('--qds', '=NAME', 'List pkgs on which given package on server depends') do |opt|
289
+ @action = :query_depends_available
217
290
  @action_value = opt
218
291
  end
219
292
 
220
293
  opts.on('--dw', '=INTEGER', 'Number of workers for deploying') do |opt|
221
294
  @worker_count = opt.to_i
295
+ # FIXME: this won't remove options that the user specified as an
296
+ # abbreviation. I.e. if the option is --servers and the user specified
297
+ # --serv (which OptionParser will accept as long as it is unambiguous) this
298
+ # won't detect and remove it.
222
299
  @deploy_params = @deploy_params - ['--dw', @worker_count, "--dw=#{opt}"]
223
300
  end
224
- opts.on('--qX', '=FILENAME', 'Display tpkg.xml or tpkg.yml of the given package') do |opt|
301
+ opts.on('--qX', '=FILENAME', 'Display raw metadata (tpkg.yml) of the given package') do |opt|
225
302
  @action = :query_tpkg_metadata
226
303
  @action_value = opt
227
304
  end
305
+ opts.on('--qXs', '=FILENAME', 'Display raw metadata of given package on the server') do |opt|
306
+ @action = :query_tpkg_metadata_available
307
+ @action_value = opt
308
+ end
228
309
  opts.on('--history', 'Display package installation history') do |opt|
229
310
  @action = :query_history
230
311
  end
@@ -237,8 +318,12 @@ end
237
318
  opts.on('--base', '=BASE', 'Base directory for tpkg operations') do |opt|
238
319
  @tpkg_options[:base] = opt
239
320
  end
321
+ opts.on('--extract', '-x', '=DIRECTORY', 'Extract the metadata for a directory of packages') do |opt|
322
+ @action = :extract
323
+ @action_value = opt
324
+ end
240
325
  opts.on('--source', '=NAME', 'Sources where packages are located', Array) do |opt|
241
- @tpkg_options[:sources] = opt
326
+ @sources = opt
242
327
  end
243
328
  opts.on('--download', '=PACKAGES', 'Download one or more packages', Array) do |opt|
244
329
  @action = :download
@@ -255,7 +340,7 @@ opts.on('--no-sudo', 'No calls to sudo for operations that might need root') do
255
340
  @sudo = opt
256
341
  end
257
342
  opts.on('--lock-force', 'Force the removal of an existing lockfile') do |opt|
258
- @lockforce = opt
343
+ @tpkg_options[:lockforce] = opt
259
344
  end
260
345
  opts.on('--force-replace', 'Replace conflicting pkgs with the new one(s)') do |opt|
261
346
  @other_options[:force_replace] = opt
@@ -263,20 +348,38 @@ end
263
348
  opts.on('--force', 'Force the execution of a given task') do |opt|
264
349
  @force = opt
265
350
  end
266
- opts.on('-o', '--out', '=DIR', 'Output directory for the -m option') do |opt|
351
+ opts.on('-o', '--out', '=DIR', 'Output directory for the --make option') do |opt|
267
352
  @other_options[:out] = opt
268
353
  end
354
+ opts.on('--skip-remove-stop', 'Do not run init script stop on package removal') do |opt|
355
+ @other_options[:skip_remove_stop] = opt
356
+ end
269
357
  opts.on('--use-ssh-key [FILE]', 'Use ssh key for deploying instead of password') do |opt|
270
358
  @deploy_options["use-ssh-key"] = true
271
359
  @deploy_options["ssh-key"] = opt
360
+ # FIXME: this won't remove options that the user specified as an
361
+ # abbreviation. I.e. if the option is --servers and the user specified
362
+ # --serv (which OptionParser will accept as long as it is unambiguous) this
363
+ # won't detect and remove it.
272
364
  @deploy_params = @deploy_params - ['--use-ssh-key', opt, "--use-ssh-key=#{opt}"]
273
365
  end
274
366
  opts.on('--deploy-as', '=USERNAME', 'What username to use for deploying to remote server') do |opt|
275
367
  @deploy_options["deploy-as"] = opt
368
+ # FIXME: this won't remove options that the user specified as an
369
+ # abbreviation. I.e. if the option is --servers and the user specified
370
+ # --serv (which OptionParser will accept as long as it is unambiguous) this
371
+ # won't detect and remove it.
276
372
  @deploy_params = @deploy_params - ['--deploy-as']
277
373
  end
278
- opts.on('--compress', '=[TYPE]', 'Compress files when making packages') do |opt|
279
- if opt == "no"
374
+ acceptable_compress_arguments = ['gzip', 'bz2', 'no']
375
+ opts.on('--compress [TYPE]',
376
+ acceptable_compress_arguments,
377
+ "Compress files when making packages " +
378
+ "(#{acceptable_compress_arguments.join(',')})") do |opt|
379
+ # Acceptable
380
+ if opt == nil # No argument specified by user
381
+ @compress = true
382
+ elsif opt == "no"
280
383
  @compress= false
281
384
  else
282
385
  @compress = opt
@@ -297,7 +400,14 @@ opts.on_tail("-h", "--help", "Show this message") do
297
400
  exit
298
401
  end
299
402
 
300
- leftovers = opts.parse(ARGV)
403
+ leftovers = nil
404
+ begin
405
+ leftovers = opts.parse(ARGV)
406
+ rescue OptionParser::ParseError => e
407
+ $stderr.puts "Error parsing arguments, try --help"
408
+ $stderr.puts e.message
409
+ exit 1
410
+ end
301
411
 
302
412
  # Rerun with sudo if necessary, unless it's a deploy, then
303
413
  # we don't need to run with sudo on this machine. It will run with sudo
@@ -312,69 +422,75 @@ if !@action
312
422
  exit
313
423
  end
314
424
 
425
+ if @groups
426
+ settings = parse_config_files
427
+ if settings[:host_group_script]
428
+ if !File.executable?(settings[:host_group_script])
429
+ warn "Warning: host group script #{settings[:host_group_script]} is not executable, execution will likely fail"
430
+ end
431
+ servers = []
432
+ @groups.each do |group|
433
+ IO.popen(settings[:host_group_script]) do |pipe|
434
+ pipe.each_line do |line|
435
+ servers << line.chomp
436
+ end
437
+ end
438
+ end
439
+ servers.uniq!
440
+ puts "Expanded groups into #{servers.length} servers" if @debug
441
+ @servers.concat(servers)
442
+ else
443
+ abort "No host_group_script defined in config files, can't expand groups"
444
+ end
445
+ end
446
+
315
447
  #
316
448
  # Figure out base directory, sources and other configuration
317
449
  #
318
450
 
319
- def instantiate_tpkg(options = {})
320
- base = options[:base]
321
- sources = options[:sources] || []
322
- report_server = nil
451
+ def instantiate_tpkg
452
+ settings = parse_config_files
323
453
 
324
454
  # base can come from four possible places. They take precedence in this
325
455
  # order:
326
456
  # - command line option
327
457
  # - TPKG_HOME environment variable
328
458
  # - config file
329
- # - Tpkg::DEFAULT_BASE
330
-
459
+ # - Tpkg::DEFAULT_BASE constant defined in tpkg.rb
331
460
  if ENV['TPKG_HOME']
332
- if !base
333
- base = ENV['TPKG_HOME']
461
+ if !@tpkg_options[:base]
462
+ @tpkg_options[:base] = ENV['TPKG_HOME']
334
463
  # Warn the user, as this could potentially be confusing
335
464
  # if they don't realize there's an environment variable set.
336
- warn "Using base '#{base}' base from $TPKG_HOME"
465
+ warn "Using base '#{@tpkg_options[:base]}' base from $TPKG_HOME" if !@quiet
337
466
  else
338
- warn "Ignoring TPKG_HOME" if @debug
467
+ warn "Ignoring $TPKG_HOME" if @debug
339
468
  end
340
469
  end
341
-
342
- # FIXME: Move config file parsing to tpkg.rb
343
- # http://sourceforge.net/apps/trac/tpkg/ticket/28
344
- fsroot = options[:file_system_root] ? options[:file_system_root] : ''
345
- [File.join(fsroot, Tpkg::DEFAULT_CONFIGDIR, 'tpkg.conf'), File.join(fsroot, ENV['HOME'], ".tpkg.conf")].each do |configfile|
346
- if File.exist?(configfile)
347
- IO.foreach(configfile) do |line|
348
- line.chomp!
349
- next if (line =~ /^\s*$/); # Skip blank lines
350
- next if (line =~ /^\s*#/); # Skip comments
351
- line.strip! # Remove leading/trailing whitespace
352
- key, value = line.split(/\s*=\s*/, 2)
353
- if key == 'base'
354
- if !base
355
- # Warn the user, as this could potentially be confusing
356
- # if they don't realize there's a config file lying
357
- # around
358
- base = value
359
- warn "Using base #{base} from #{configfile}"
360
- else
361
- warn "Ignoring 'base' option in #{@configfile}" if @debug
362
- end
363
- elsif key == 'source'
364
- sources << value
365
- puts "Loaded source #{value} from #{configfile}" if (@debug)
366
- elsif key == 'report_server'
367
- report_server = value
368
- puts "Loaded report server #{report_server} from #{configfile}" if (@debug)
369
- end
370
- end
470
+ if settings[:base]
471
+ if !@tpkg_options[:base]
472
+ # Warn the user, as this could potentially be confusing
473
+ # if they don't realize there's a config file lying
474
+ # around
475
+ @tpkg_options[:base] = settings[:base]
476
+ warn "Using base #{@tpkg_options[:base]} from config file" if !@quiet
477
+ else
478
+ warn "Ignoring 'base' option in config file" if @debug
371
479
  end
372
480
  end
373
-
374
- if !base
375
- base = Tpkg::DEFAULT_BASE
481
+ if !@tpkg_options[:base]
482
+ @tpkg_options[:base] = Tpkg::DEFAULT_BASE
376
483
  end
377
484
 
485
+ # Sources can come from the command line and config files. We use the
486
+ # combined set of sources.
487
+ @sources.concat(settings[:sources])
488
+ @tpkg_options[:sources] = @sources
489
+
490
+ @tpkg_options[:report_server] = settings[:report_server]
491
+ @tpkg_options[:sudo] = @sudo
492
+ @tpkg_options[:force] = @force
493
+
378
494
  if !@sudo
379
495
  curruid = Process.euid
380
496
  if curruid == 0
@@ -383,23 +499,19 @@ def instantiate_tpkg(options = {})
383
499
  # modified by other users who properly run --no-sudo as a regular user.
384
500
  raise "--no-sudo cannot be used as 'root' user or via sudo"
385
501
  end
386
- baseuid = File.stat(base).uid
387
- # We want to ensure that all --no-sudo usage within a given base directory
388
- # is done under the same account.
389
- if baseuid != curruid
390
- raise "Base dir #{base} owned by UID #{baseuid}, not your UID #{curruid}"
502
+ fsroot = @tpkg_options[:file_system_root] ? @tpkg_options[:file_system_root] : ''
503
+ base = File.join(fsroot, @tpkg_options[:base])
504
+ if File.exist?(base)
505
+ baseuid = File.stat(base).uid
506
+ # We want to ensure that all --no-sudo usage within a given base directory
507
+ # is done under the same account.
508
+ if baseuid != curruid
509
+ raise "Base dir #{@tpkg_options[:base]} owned by UID #{baseuid}, not your UID #{curruid}"
510
+ end
391
511
  end
392
512
  end
393
513
 
394
- # FIXME: This is ugly. We would set the appropriate things in options and
395
- # call Tpkg.new(options)
396
- tpkg = Tpkg.new(:file_system_root => options[:file_system_root],
397
- :base => base,
398
- :sources => sources,
399
- :report_server => report_server,
400
- :lockforce => @lockforce,
401
- :force => @force,
402
- :sudo => @sudo)
514
+ tpkg = Tpkg.new(@tpkg_options)
403
515
  end
404
516
 
405
517
  passphrase_callback = lambda do | package |
@@ -466,25 +578,25 @@ when :make
466
578
  when :extract
467
579
  Tpkg::extract_metadata(@action_value)
468
580
  when :install
469
- tpkg = instantiate_tpkg(@tpkg_options)
581
+ tpkg = instantiate_tpkg
470
582
  ret_val = tpkg.install(@action_value, passphrase_callback, @other_options)
471
583
  when :upgrade
472
- tpkg = instantiate_tpkg(@tpkg_options)
584
+ tpkg = instantiate_tpkg
473
585
  ret_val = tpkg.upgrade(@action_value, passphrase_callback, @other_options)
474
586
  when :remove
475
- tpkg = instantiate_tpkg(@tpkg_options)
587
+ tpkg = instantiate_tpkg
476
588
  ret_val = tpkg.remove(@action_value, @other_options)
477
589
  when :download
478
- tpkg = instantiate_tpkg(@tpkg_options)
590
+ tpkg = instantiate_tpkg
479
591
  ret_val = tpkg.download_pkgs(@action_value, @other_options)
480
592
  when :verify
481
593
  result = nil
482
594
  # Verify a given .tpkg file
483
- if File.exist?(@action_value)
595
+ if File.file?(@action_value)
484
596
  Tpkg::verify_package_checksum(@action_value)
485
597
  # Verify an installed pkg
486
598
  else
487
- tpkg = instantiate_tpkg(@tpkg_options)
599
+ tpkg = instantiate_tpkg
488
600
  results = tpkg.verify_file_metadata([@action_value])
489
601
  if results.length == 0
490
602
  puts "No package found"
@@ -501,206 +613,525 @@ when :verify
501
613
  puts "Package verification failed" unless success
502
614
  end
503
615
  when :execute_init
504
- tpkg = instantiate_tpkg(@tpkg_options)
616
+ tpkg = instantiate_tpkg
505
617
  if @init_options[:cmd].nil?
506
618
  raise "You didn't specify what init command to run"
507
619
  end
508
620
  ret_val = tpkg.execute_init(@init_options)
509
621
  when :query_installed
510
- tpkg = instantiate_tpkg(@tpkg_options)
511
- queryreq = nil
622
+ tpkg = instantiate_tpkg
512
623
  matches = []
513
624
  if @action_value
514
- @action_value.each do | value |
515
- queryreq = Tpkg::parse_request(value, tpkg.installed_directory)
516
- match = tpkg.installed_packages_that_meet_requirement(queryreq)
517
-
625
+ # The --query switch is set to accept multiple values (the "Array"
626
+ # parameter in the opts.on line for --query) so users can do things like
627
+ # "tpkg -q foo,bar" to check multiple packages at once. As such
628
+ # @action_value is an Array for this switch.
629
+ requirements = []
630
+ packages = {}
631
+ tpkg.parse_requests(@action_value, requirements, packages,
632
+ :installed_only => true)
633
+ if packages.values.all? {|pkg| pkg.empty?}
518
634
  # If the user requested specific packages and we found no matches
519
635
  # then exit with a non-zero value to indicate failure. This allows
520
636
  # command-line syntax like "tpkg -q foo || tpkg -i foo" to ensure
521
637
  # that a package is installed.
522
- ret_val = 1 if match.empty?
523
- matches |= match
638
+ ret_val = 1
639
+ $stderr.puts "No packages matching '#{@action_value}' installed" if !@quiet
640
+ else
641
+ packages.each do | name, pkgs |
642
+ matches.concat(pkgs)
643
+ end
524
644
  end
525
645
  else
526
- matches = tpkg.installed_packages_that_meet_requirement(queryreq)
646
+ # --qa is implemented by setting @action to :query_installed and
647
+ # @action_value to nil
648
+ matches = tpkg.installed_packages_that_meet_requirement
649
+ if matches.empty?
650
+ ret_val = 1
651
+ $stderr.puts "No packages installed" if !@quiet
652
+ end
527
653
  end
528
-
529
654
  if !@quiet
530
655
  matches.sort(&Tpkg::SORT_PACKAGES).each do |pkg|
531
656
  puts pkg[:metadata][:filename]
532
657
  end
533
658
  end
534
659
  when :query_info
535
- metadatas = nil
536
- if File.exist?(@action_value)
537
- metadatas = [Tpkg::metadata_from_package(@action_value)]
538
- else
539
- tpkg = instantiate_tpkg(@tpkg_options)
540
- metadatas = []
541
- requirements = []
542
- packages = {}
543
- tpkg.parse_requests([@action_value], requirements, packages)
544
- packages.each do | name, pkgs |
545
- pkgs.each do | pkg |
546
- metadatas << pkg[:metadata]
547
- end
660
+ tpkg = instantiate_tpkg
661
+ requirements = []
662
+ packages = {}
663
+ tpkg.parse_requests([@action_value], requirements, packages,
664
+ :installed_only => true)
665
+ metadatas = []
666
+ packages.each do | name, pkgs |
667
+ pkgs.each do | pkg |
668
+ metadatas << pkg[:metadata]
548
669
  end
549
670
  end
671
+ output_strings = []
550
672
  already_displayed = {}
551
673
  metadatas.each do |metadata|
552
674
  next if already_displayed[metadata[:filename]]
553
675
  already_displayed[metadata[:filename]] = true
676
+ output_string = ''
554
677
  [:name, :version, :package_version, :operatingsystem, :architecture, :maintainer, :description, :bugreporting].each do |field|
555
678
  metadata[field] = 'any' if field == :operatingsystem && metadata[field].nil?
556
679
  metadata[field] = 'any' if field == :architecture && metadata[field].nil?
557
680
  if metadata[field]
558
681
  if metadata[field].kind_of?(Array)
559
- puts "#{field}: #{metadata[field].join(',')}"
682
+ output_string << "#{field}: #{metadata[field].join(',')}\n"
560
683
  else
561
- puts "#{field}: #{metadata[field]}"
684
+ output_string << "#{field}: #{metadata[field]}\n"
562
685
  end
563
686
  end
564
687
  end
688
+ if metadata[:dependencies]
689
+ output_string << "(This package depends on other packages, use --qd to view the dependencies)\n"
690
+ end
691
+ # Older versions of tpkg did not insert a tpkg_version field into the
692
+ # package metadata when building packages
565
693
  tpkg_version = metadata[:tpkg_version] || "< 1.26.1"
566
- puts "This package was built with tpkg version #{tpkg_version}."
567
- if metadata[:dependencies]
568
- puts "This package depends on other packages, use --qd/--qld to view the dependencies."
694
+ output_string << "(This package was built with tpkg version #{tpkg_version})\n"
695
+ output_strings << output_string
696
+ end
697
+ print output_strings.join("================================================================================\n")
698
+ if already_displayed.empty?
699
+ ret_val = 1
700
+ # --qi --quiet doesn't seem like a meaningful combination, so I'm not
701
+ # suppressing this for @quiet
702
+ $stderr.puts "No packages matching '#{@action_value}' installed"
703
+ end
704
+ when :query_info_available
705
+ tpkg = instantiate_tpkg
706
+ requirements = []
707
+ packages = {}
708
+ tpkg.parse_requests([@action_value], requirements, packages)
709
+ availpkgs = []
710
+ packages.each do | name, pkgs |
711
+ availpkgs.concat(pkgs)
712
+ end
713
+ available = availpkgs.select do |pkg|
714
+ pkg[:source] != :native_installed &&
715
+ pkg[:source] != :native_available &&
716
+ pkg[:source] != :currently_installed
717
+ end
718
+ if available.empty?
719
+ ret_val = 1
720
+ # --qis --quiet doesn't seem like a meaningful combination, so I'm not
721
+ # suppressing this for @quiet
722
+ $stderr.puts "No packages matching '#{@action_value}' available"
723
+ else
724
+ metadatas = available.collect {|avail| avail[:metadata]}
725
+ output_strings = []
726
+ already_displayed = {}
727
+ metadatas.each do |metadata|
728
+ next if already_displayed[metadata[:filename]]
729
+ already_displayed[metadata[:filename]] = true
730
+ output_string = ''
731
+ [:name, :version, :package_version, :operatingsystem, :architecture, :maintainer, :description, :bugreporting].each do |field|
732
+ metadata[field] = 'any' if field == :operatingsystem && metadata[field].nil?
733
+ metadata[field] = 'any' if field == :architecture && metadata[field].nil?
734
+ if metadata[field]
735
+ if metadata[field].kind_of?(Array)
736
+ output_string << "#{field}: #{metadata[field].join(',')}\n"
737
+ else
738
+ output_string << "#{field}: #{metadata[field]}\n"
739
+ end
740
+ end
741
+ end
742
+ if metadata[:dependencies]
743
+ output_string << "(This package depends on other packages, use --qd to view the dependencies)\n"
744
+ end
745
+ # Older versions of tpkg did not insert a tpkg_version field into the
746
+ # package metadata when building packages
747
+ tpkg_version = metadata[:tpkg_version] || "< 1.26.1"
748
+ output_string << "(This package was built with tpkg version #{tpkg_version})\n"
749
+ output_strings << output_string
569
750
  end
570
- puts "================================================================================"
751
+ print output_strings.join("================================================================================\n")
571
752
  end
572
753
  when :query_list_files
573
- tpkg = instantiate_tpkg(@tpkg_options)
574
- pkgfiles = nil
575
- if File.exist?(@action_value)
576
- fipfile = Tpkg::files_in_package(@action_value)
577
- tpkg.normalize_paths(fipfile)
578
- puts "#{@action_value}:"
579
- fipfile[:normalized].each { |file| puts file }
754
+ tpkg = instantiate_tpkg
755
+ requirements = []
756
+ packages = {}
757
+ tpkg.parse_requests([@action_value], requirements, packages,
758
+ :installed_only => true)
759
+ if packages.values.all? {|pkg| pkg.empty?}
760
+ ret_val = 1
761
+ # --ql --quiet doesn't seem like a meaningful combination, so I'm not
762
+ # suppressing this for @quiet
763
+ $stderr.puts "No packages matching '#{@action_value}' installed"
580
764
  else
581
- pkgfiles = []
582
- metadatas = []
583
- requirements = []
584
- packages = {}
585
-
586
- queryreq = Tpkg::parse_request(@action_value)
587
- instpkgs = tpkg.installed_packages_that_meet_requirement(queryreq)
588
- if instpkgs.nil? or instpkgs.empty?
589
- ret_val = 1
590
- puts "Could not find any installed packages that meet the request \"#{@action_value}\""
591
- end
592
- instpkgs.each do | pkg |
593
- pkgfiles << pkg[:metadata][:filename]
765
+ # For this switch we need separate handling for installed and uninstalled
766
+ # packages. For installed packages we know where their relocatable files
767
+ # ended up and can give the user regular paths. For uninstalled packaages
768
+ # we don't know what base will be used when the package is installed, so
769
+ # we need to indicate to the user that their are relocatable files and
770
+ # just display their path relative to the eventual base directory.
771
+ ci_pkgfiles = []
772
+ packages.each do | name, pkgs |
773
+ pkgs.each do | pkg |
774
+ if pkg[:source] == :currently_installed
775
+ ci_pkgfiles << pkg[:metadata][:filename]
776
+ else
777
+ puts "#{pkg[:source]}:"
778
+ fip = Tpkg.files_in_package(pkg[:source])
779
+ fip[:root].each { |file| puts file }
780
+ fip[:reloc].each { |file| puts '<relocatable>/' + file }
781
+ end
782
+ end
594
783
  end
595
-
596
- files = tpkg.files_for_installed_packages(pkgfiles)
784
+ files = tpkg.files_for_installed_packages(ci_pkgfiles)
597
785
  files.each do |pkgfile, fip|
598
786
  puts "#{pkgfile}:"
599
787
  fip[:normalized].each { |file| puts file }
600
788
  end
601
789
  end
790
+ when :query_list_files_available
791
+ tpkg = instantiate_tpkg
792
+ requirements = []
793
+ packages = {}
794
+ tpkg.parse_requests([@action_value], requirements, packages)
795
+ availpkgs = []
796
+ packages.each do | name, pkgs |
797
+ availpkgs.concat(pkgs)
798
+ end
799
+ available = availpkgs.select do |pkg|
800
+ pkg[:source] != :native_installed &&
801
+ pkg[:source] != :native_available &&
802
+ pkg[:source] != :currently_installed
803
+ end
804
+ if available.empty?
805
+ ret_val = 1
806
+ # --qls --quiet doesn't seem like a meaningful combination, so I'm not
807
+ # suppressing this for @quiet
808
+ $stderr.puts "No packages matching '#{@action_value}' available"
809
+ else
810
+ downloaddir = Tpkg::tempdir('download')
811
+ available.each do |pkg|
812
+ # FIXME: I've duplicated from the install and upgrade methods this logic
813
+ # to calculate pkgfile, it should be encapsulated in a method
814
+ pkgfile = nil
815
+ if File.file?(pkg[:source])
816
+ pkgfile = pkg[:source]
817
+ elsif File.directory?(pkg[:source])
818
+ pkgfile = File.join(pkg[:source], pkg[:metadata][:filename])
819
+ else
820
+ pkgfile = download(pkg[:source], pkg[:metadata][:filename], downloaddir)
821
+ end
822
+ puts "#{pkg[:metadata][:filename]}:"
823
+ fip = Tpkg.files_in_package(pkgfile)
824
+ fip[:root].each { |file| puts file }
825
+ fip[:reloc].each { |file| puts '<relocatable>/' + file }
826
+ end
827
+ FileUtils.rm_rf(downloaddir)
828
+ end
602
829
  when :query_who_owns_file
603
- tpkg = instantiate_tpkg(@tpkg_options)
830
+ tpkg = instantiate_tpkg
831
+ owned = false
832
+ expanded_file = File.expand_path(@action_value)
604
833
  tpkg.files_for_installed_packages.each do |pkgfile, fip|
605
834
  fip[:normalized].each do |file|
606
- if file == File.expand_path(@action_value)
835
+ if file == expanded_file
607
836
  puts "#{file}: #{pkgfile}"
837
+ owned = true
608
838
  end
609
839
  end
610
840
  end
841
+ if !owned
842
+ ret_val = 1
843
+ # --qf --quiet doesn't seem like a meaningful combination, so I'm not
844
+ # suppressing this for @quiet
845
+ $stderr.puts "No package owns file '#{@action_value}'"
846
+ end
611
847
  when :query_available
612
- tpkg = instantiate_tpkg(@tpkg_options)
613
- queryreq = nil
848
+ tpkg = instantiate_tpkg
849
+ availpkgs = []
614
850
  if @action_value
615
- queryreq = Tpkg::parse_request(@action_value)
851
+ # The --qs switch is set to accept multiple values (the "Array"
852
+ # parameter in the opts.on line for --qs) so users can do things like
853
+ # "tpkg --qs foo,bar" to check multiple packages at once. As such
854
+ # @action_value is an Array for this switch.
855
+ requirements = []
856
+ packages = {}
857
+ tpkg.parse_requests(@action_value, requirements, packages)
858
+ packages.each do | name, pkgs |
859
+ availpkgs.concat(pkgs)
860
+ end
861
+ else
862
+ # --qas is implemented by setting @action to :query_available and
863
+ # @action_value to nil
864
+ availpkgs.concat(tpkg.available_packages_that_meet_requirement)
616
865
  end
617
- tpkg.available_packages_that_meet_requirement(queryreq).each do |pkg|
618
- next if pkg[:source] == :native_installed
619
- next if pkg[:source] == :native_available
620
- puts "#{pkg[:metadata][:filename]} (#{pkg[:source]})"
866
+ available = availpkgs.select do |pkg|
867
+ pkg[:source] != :native_installed &&
868
+ pkg[:source] != :native_available &&
869
+ # The tpkg library treats currently installed packages as "available"
870
+ # because they are available to meet a user's requirement. I.e. if the
871
+ # user asks to install ruby and a ruby package is already installed that
872
+ # satisfies the user's requirement even if there's no ruby package in any
873
+ # of the sources. But for these query options I think the reasonable
874
+ # interpretation is that the user would like to know if there's a package
875
+ # in a source that could be installed. For example, if the user queries
876
+ # for the availability of ruby and we show it as available because it is
877
+ # installed, but then they go to another machine with the same sources
878
+ # defined and try to install ruby and it fails because there is no ruby in
879
+ # any of the sources I think the user is likely to find that unexpected
880
+ # and annoying.
881
+ pkg[:source] != :currently_installed
882
+ end
883
+ if available.empty?
884
+ ret_val = 1
885
+ if !@quiet
886
+ if @action_value
887
+ $stderr.puts "No packages matching '#{@action_value}' available"
888
+ else
889
+ $stderr.puts "No packages available"
890
+ end
891
+ end
892
+ else
893
+ if !@quiet
894
+ available.sort(&Tpkg::SORT_PACKAGES).each do |pkg|
895
+ puts "#{pkg[:metadata][:filename]} (#{pkg[:source]})"
896
+ end
897
+ end
621
898
  end
622
899
  when :query_requires
623
- tpkg = instantiate_tpkg(@tpkg_options)
624
-
625
- # parse the request
900
+ tpkg = instantiate_tpkg
901
+
902
+ # Parse the request
626
903
  requirements = []
627
904
  packages = {}
628
- tpkg.parse_requests([@action_value], requirements, packages)
629
-
630
- # get dependencies of all installed packages
905
+ tpkg.parse_requests([@action_value], requirements, packages,
906
+ :installed_only => true)
907
+
908
+ # Note that we don't stop here in the case of this switch, but continue to
909
+ # check if anything depends on the package the user asked about. There
910
+ # shouldn't be a situation where there's another package installed that
911
+ # depends on the package the user is asking about, but the user's package is
912
+ # not installed. I.e. if foo depends on bar but only foo is installed and
913
+ # the user asks what depends on bar, the answer is still foo. That
914
+ # information might be useful to the user, even though that situation should
915
+ # have been avoided in the first place. Broken dependency trees due to
916
+ # manually messing with the repo, using --force, etc. can happen and the
917
+ # user may be using the --qr option just because they're trying to sort out
918
+ # a mess.
919
+ if packages.values.all? {|pkg| pkg.empty?}
920
+ ret_val = 1
921
+ # --qr --quiet doesn't seem like a meaningful combination, so I'm not
922
+ # suppressing this for @quiet
923
+ $stderr.puts "No packages matching '#{@action_value}' installed"
924
+ end
925
+
926
+ # Get dependencies of all installed packages
631
927
  dependencies = {}
632
928
  tpkg.metadata_for_installed_packages.each do |metadata|
633
929
  dependencies[metadata[:filename]] = metadata[:dependencies]
634
930
  end
635
-
636
- # check to see if the any required dependencies match with what the
637
- # user specified in the request
931
+
932
+ # Check to see if any dependencies match with what the user specified in the
933
+ # request
934
+ requirees = {}
638
935
  packages.each do |name, pkgs|
639
936
  pkgs.each do |pkg|
640
937
  next if pkg[:source] != :currently_installed
641
- puts "The following package(s) require #{pkg[:metadata][:filename]}:"
642
938
  dependencies.each do | requiree, deps |
643
939
  next if deps.nil?
644
940
  deps.each do | dep |
645
941
  if Tpkg::package_meets_requirement?(pkg, dep)
646
- puts " #{requiree}"
942
+ pkgfilename = pkg[:metadata][:filename]
943
+ if !requirees[pkgfilename]
944
+ requirees[pkgfilename] = []
945
+ end
946
+ requirees[pkgfilename] << requiree
647
947
  end
648
948
  end
649
949
  end
650
950
  end
651
951
  end
652
- when :query_local_depends
653
- tpkg = instantiate_tpkg(@tpkg_options)
654
- queryreq = Tpkg::parse_request(@action_value)
655
- instpkgs = tpkg.installed_packages_that_meet_requirement(queryreq)
656
- instpkgs.each do | pkg |
657
- puts pkg[:metadata][:filename] + ':'
658
- if pkg[:metadata][:dependencies]
659
- pkg[:metadata][:dependencies].each do |req|
660
- puts " Requires #{req[:name]}"
661
- req.each do |field, value|
662
- next if field == :name
663
- puts " #{field}: #{value}"
664
- end
665
- end
952
+
953
+ if !requirees.empty?
954
+ requirees.keys.sort.each do |pkgfilename|
955
+ puts "The following package(s) require #{pkgfilename}:"
956
+ # uniq probably isn't necessary, but it can't hurt
957
+ requirees[pkgfilename].sort.uniq.each do |requiree|
958
+ puts " #{requiree}"
959
+ end
666
960
  end
961
+ else
962
+ puts "No other package depends on '#{@action_value}'"
667
963
  end
668
964
  when :query_depends
669
- tpkg = instantiate_tpkg(@tpkg_options)
965
+ tpkg = instantiate_tpkg
966
+
967
+ requirements = []
968
+ packages = {}
969
+ tpkg.parse_requests([@action_value], requirements, packages,
970
+ :installed_only => true)
971
+
972
+ if packages.values.all? {|pkg| pkg.empty?}
973
+ ret_val = 1
974
+ # --qd --quiet doesn't seem like a meaningful combination, so I'm not
975
+ # suppressing this for @quiet
976
+ $stderr.puts "No packages matching '#{@action_value}' installed"
977
+ else
978
+ depends = {}
979
+ packages.each do |name, pkgs|
980
+ pkgs.each do |pkg|
981
+ if pkg[:metadata][:dependencies]
982
+ pkgfilename = pkg[:metadata][:filename]
983
+ if !depends[pkgfilename]
984
+ depends[pkgfilename] = []
985
+ end
986
+ pkg[:metadata][:dependencies].each do |req|
987
+ depends[pkgfilename] << req
988
+ end
989
+ end
990
+ end
991
+ end
992
+
993
+ if !depends.empty?
994
+ outputs = []
995
+ depends.keys.sort.each do |pkgfilename|
996
+ output = "Package #{pkgfilename} depends on:\n"
997
+ # uniq probably isn't necessary, but it can't hurt
998
+ outs = []
999
+ depends[pkgfilename].sort{|a,b| a[:name]<=>b[:name]}.uniq.each do |req|
1000
+ out = " name: #{req[:name]}\n"
1001
+ req.each do |field, value|
1002
+ next if field == 'name'
1003
+ out << " #{field}: #{value}\n"
1004
+ end
1005
+ outs << out
1006
+ end
1007
+ outputs << output + outs.join("\n")
1008
+ end
1009
+ print outputs.join("\n")
1010
+ else
1011
+ puts "Package '#{@action_value}' does not depend on other packages"
1012
+ end
1013
+ end
1014
+ when :query_depends_available
1015
+ tpkg = instantiate_tpkg
670
1016
  requirements = []
671
1017
  packages = {}
672
1018
  tpkg.parse_requests([@action_value], requirements, packages)
673
- packages.each do |name, pkgs|
674
- already_displayed = {}
675
- pkgs.each do |pkg|
676
- # parse_requests returns both installed and available packages.
677
- # The same package may show up in both, skip any duplicates.
678
- next if already_displayed[pkg[:filename]]
679
- already_displayed[pkg[:metadata][:filename]] = true
680
- puts pkg[:metadata][:filename] + ':'
1019
+ availpkgs = []
1020
+ packages.each do | name, pkgs |
1021
+ availpkgs.concat(pkgs)
1022
+ end
1023
+ available = availpkgs.select do |pkg|
1024
+ pkg[:source] != :native_installed &&
1025
+ pkg[:source] != :native_available &&
1026
+ pkg[:source] != :currently_installed
1027
+ end
1028
+
1029
+ if available.empty?
1030
+ ret_val = 1
1031
+ # --qds --quiet doesn't seem like a meaningful combination, so I'm not
1032
+ # suppressing this for @quiet
1033
+ $stderr.puts "No packages matching '#{@action_value}' available"
1034
+ else
1035
+ depends = {}
1036
+ available.each do |pkg|
681
1037
  if pkg[:metadata][:dependencies]
1038
+ pkgfilename = pkg[:metadata][:filename]
1039
+ if !depends[pkgfilename]
1040
+ depends[pkgfilename] = []
1041
+ end
682
1042
  pkg[:metadata][:dependencies].each do |req|
683
- puts " Requires #{req[:name]}"
1043
+ depends[pkgfilename] << req
1044
+ end
1045
+ end
1046
+ end
1047
+
1048
+ if !depends.empty?
1049
+ outputs = []
1050
+ depends.keys.sort.each do |pkgfilename|
1051
+ output = "Package #{pkgfilename} depends on:\n"
1052
+ # uniq probably isn't necessary, but it can't hurt
1053
+ outs = []
1054
+ depends[pkgfilename].sort{|a,b| a[:name]<=>b[:name]}.uniq.each do |req|
1055
+ out = " name: #{req[:name]}\n"
684
1056
  req.each do |field, value|
685
- next if field == :name
686
- puts " #{field}: #{value}"
1057
+ next if field == 'name'
1058
+ out << " #{field}: #{value}\n"
687
1059
  end
1060
+ outs << out
688
1061
  end
1062
+ outputs << output + outs.join("\n")
689
1063
  end
1064
+ print outputs.join("\n")
1065
+ else
1066
+ puts "Package '#{@action_value}' does not depend on other packages"
690
1067
  end
691
1068
  end
692
1069
  when :query_tpkg_metadata
693
- tpkg = instantiate_tpkg(@tpkg_options)
694
- if File.exist?(@action_value)
695
- puts Tpkg::extract_tpkg_metadata_file(@action_value)
696
- elsif File.exists?(File.join(tpkg.installed_directory, @action_value))
697
- puts Tpkg::extract_tpkg_metadata_file(File.join(tpkg.installed_directory, @action_value))
1070
+ tpkg = instantiate_tpkg
1071
+ requirements = []
1072
+ packages = {}
1073
+ tpkg.parse_requests([@action_value], requirements, packages,
1074
+ :installed_only => true)
1075
+
1076
+ if packages.values.all? {|pkg| pkg.empty?}
1077
+ ret_val = 1
1078
+ # --qX --quiet doesn't seem like a meaningful combination, so I'm not
1079
+ # suppressing this for @quiet
1080
+ $stderr.puts "No packages matching '#{@action_value}' installed"
698
1081
  else
699
- puts "File #{@action_value} doesn't exist."
1082
+ packages.each do | name, pkgs |
1083
+ pkgs.each do | pkg |
1084
+ pkgfile = nil
1085
+ if pkg[:source] == :currently_installed
1086
+ pkgfile = File.join(tpkg.installed_directory, pkg[:metadata][:filename])
1087
+ else
1088
+ pkgfile = pkg[:source]
1089
+ end
1090
+ puts Tpkg::extract_tpkg_metadata_file(pkgfile)
1091
+ end
1092
+ end
1093
+ end
1094
+ when :query_tpkg_metadata_available
1095
+ tpkg = instantiate_tpkg
1096
+ requirements = []
1097
+ packages = {}
1098
+ tpkg.parse_requests([@action_value], requirements, packages)
1099
+ availpkgs = []
1100
+ packages.each do | name, pkgs |
1101
+ availpkgs.concat(pkgs)
1102
+ end
1103
+ available = availpkgs.select do |pkg|
1104
+ pkg[:source] != :native_installed &&
1105
+ pkg[:source] != :native_available &&
1106
+ pkg[:source] != :currently_installed
1107
+ end
1108
+
1109
+ if available.empty?
1110
+ ret_val = 1
1111
+ # --qXs --quiet doesn't seem like a meaningful combination, so I'm not
1112
+ # suppressing this for @quiet
1113
+ $stderr.puts "No packages matching '#{@action_value}' available"
1114
+ else
1115
+ downloaddir = Tpkg::tempdir('download')
1116
+ available.each do |pkg|
1117
+ # FIXME: I've duplicated from the install and upgrade methods this logic
1118
+ # to calculate pkgfile, it should be encapsulated in a method
1119
+ pkgfile = nil
1120
+ if File.file?(pkg[:source])
1121
+ pkgfile = pkg[:source]
1122
+ elsif File.directory?(pkg[:source])
1123
+ pkgfile = File.join(pkg[:source], pkg[:metadata][:filename])
1124
+ else
1125
+ pkgfile = download(pkg[:source], pkg[:metadata][:filename], downloaddir)
1126
+ end
1127
+ puts Tpkg::extract_tpkg_metadata_file(pkgfile)
1128
+ end
1129
+ FileUtils.rm_rf(downloaddir)
700
1130
  end
701
1131
  when :query_env
702
1132
  puts "Operating System: #{Tpkg::get_os}"
703
1133
  puts "Architecture: #{Tpkg::get_arch}"
1134
+ puts "Tar: #{Tpkg::find_tar}"
704
1135
  when :query_conf
705
1136
  # This is somewhat arbitrarily limited to the options read from the
706
1137
  # tpkg.conf config files. The reason it exists at all is that it is
@@ -708,12 +1139,12 @@ when :query_conf
708
1139
  # without recreating all of our logic about deciding which config files to
709
1140
  # read, which order to read them in, what environment variables override the
710
1141
  # config files, etc.
711
- tpkg = instantiate_tpkg(@tpkg_options)
1142
+ tpkg = instantiate_tpkg
712
1143
  puts "Base: #{tpkg.base}"
713
1144
  puts "Sources: #{tpkg.sources.inspect}"
714
1145
  puts "Report server: #{tpkg.report_server}"
715
1146
  when :query_history
716
- tpkg = instantiate_tpkg(@tpkg_options)
1147
+ tpkg = instantiate_tpkg
717
1148
  tpkg.installation_history
718
1149
  when :query_version
719
1150
  puts Tpkg::VERSION