tpkg 1.16.2

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/bin/tpkg ADDED
@@ -0,0 +1,560 @@
1
+ #!/usr/bin/ruby -w
2
+ ##############################################################################
3
+ # tpkg package management and deployment tool
4
+ ##############################################################################
5
+
6
+ # Ensure we can find tpkg.rb
7
+ #$:.unshift File.dirname(__FILE__)
8
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
9
+
10
+ require 'optparse'
11
+ require 'tpkg'
12
+
13
+ #
14
+ # Parse the command line options
15
+ #
16
+
17
+ @action = nil
18
+ @action_value = nil
19
+ @debug = false
20
+ @prompt = true
21
+ @quiet = false
22
+ @sudo = true
23
+ @lockforce = false
24
+ @force = false
25
+ @deploy = false
26
+ @deploy_params = ARGV # hold parameters for how to invoke tpkg on the machines we're deploying to
27
+ @deploy_options = {} # options for how to run the deployer
28
+ @servers = nil
29
+ @worker_count = 10
30
+ @rerun_with_sudo = false
31
+ @tpkg_options = {}
32
+
33
+ def rerun_with_sudo_if_necessary
34
+ if Process.euid != 0 && @sudo
35
+ warn "Executing with sudo"
36
+ exec('sudo', $0, *ARGV)
37
+ end
38
+ end
39
+
40
+ opts = OptionParser.new(nil, 24, ' ')
41
+ opts.banner = 'Usage: tpkg [options]'
42
+ opts.on('--servers', '-s', '=SERVERS', Array, 'Servers to apply the actions to') do |opt|
43
+ @servers = opt
44
+ @deploy = true
45
+ @deploy_params = @deploy_params - ['--servers', '-s', @servers.join(","), "--servers=#{@servers.join(',')}"]
46
+ end
47
+ opts.on('--make', '-m', '=DIRECTORY', 'Make a package out of the contents of the directory') do |opt|
48
+ @action = :make
49
+ @action_value = opt
50
+ end
51
+ opts.on('--extract', '-x', '=DIRECTORY', 'Extract the metadata for a directory of packages') do |opt|
52
+ @action = :extract
53
+ @action_value = opt
54
+ end
55
+ opts.on('--install', '-i', '=PACKAGES', 'Install one or more packages', Array) do |opt|
56
+ @rerun_with_sudo = true
57
+ @action = :install
58
+ @action_value = opt
59
+ end
60
+ opts.on('--upgrade', '-u', '=PACKAGES', 'Upgrade one or more packages', Array) do |opt|
61
+ @rerun_with_sudo = true
62
+ @action = :upgrade
63
+ @action_value = opt
64
+ end
65
+ opts.on('--downgrade', '=PACKAGES', 'Downgrade one or more packages', Array) do |opt|
66
+ @rerun_with_sudo = true
67
+ @action = :downgrade
68
+ @action_value = opt
69
+ end
70
+ opts.on('--ua', 'Upgrade all packages') do |opt|
71
+ @rerun_with_sudo = true
72
+ @action = :upgrade
73
+ end
74
+ opts.on('--remove', '-r', '=PACKAGES', 'Remove one or more packages', Array) do |opt|
75
+ @rerun_with_sudo = true
76
+ @action = :remove
77
+ @action_value = opt
78
+ end
79
+ opts.on('--rd', '=PACKAGES', 'Similar to -r but also remove depending packages', Array) do |opt|
80
+ @rerun_with_sudo = true
81
+ @action = :remove_all_dep
82
+ @action_value = opt
83
+ end
84
+ opts.on('--rp', '=PACKAGES', 'Similar to -r but also remove prerequisites', Array) do |opt|
85
+ @rerun_with_sudo = true
86
+ @action = :remove_all_prereq
87
+ @action_value = opt
88
+ end
89
+ opts.on('--ra', 'Remove all packages') do |opt|
90
+ @rerun_with_sudo = true
91
+ @action = :remove
92
+ end
93
+ opts.on('--verify', '-V', '=NAME', 'Verify packages') do |opt|
94
+ @rerun_with_sudo = true
95
+ @action = :verify
96
+ @action_value = opt
97
+ end
98
+ opts.on('--start', '=NAME', 'Start the init script for the specified package', Array) do |opt|
99
+ @rerun_with_sudo = true
100
+ @action = :start_init
101
+ @action_value = opt
102
+ end
103
+ opts.on('--stop', '=NAME', 'Stop the init script for the specified package', Array) do |opt|
104
+ @rerun_with_sudo = true
105
+ @action = :stop_init
106
+ @action_value = opt
107
+ end
108
+ opts.on('--restart', '=NAME', 'Restart the init script for the specified package', Array) do |opt|
109
+ @rerun_with_sudo = true
110
+ @action = :restart_init
111
+ @action_value = opt
112
+ end
113
+ opts.on('--reload', '=NAME', 'Reload the init script for the specified package', Array) do |opt|
114
+ @rerun_with_sudo = true
115
+ @action = :reload_init
116
+ @action_value = opt
117
+ end
118
+ opts.on('--start-all', 'Start the init scripts for all packages') do |opt|
119
+ @rerun_with_sudo = true
120
+ @action = :start_init
121
+ end
122
+ opts.on('--stop-all', 'Stop the init script for all packages') do |opt|
123
+ @rerun_with_sudo = true
124
+ @action = :stop_init
125
+ end
126
+ opts.on('--restart-all', 'Restart the init script for all packages') do |opt|
127
+ @rerun_with_sudo = true
128
+ @action = :restart_init
129
+ end
130
+ opts.on('--reload-all', 'Reload the init script for all packages') do |opt|
131
+ @rerun_with_sudo = true
132
+ @action = :reload_init
133
+ end
134
+ opts.on('--query', '-q', '=NAMES', 'List installed packages', Array) do |opt|
135
+ # People mistype -qa instead of --qa frequently
136
+ if opt == ['a']
137
+ warn "NOTE: tpkg -qa queries for a pkg named 'a', you probably want --qa for all pkgs"
138
+ end
139
+ @action = :query_installed
140
+ @action_value = opt
141
+ end
142
+ opts.on('--qa', 'List all installed packages') do |opt|
143
+ @action = :query_installed
144
+ end
145
+ opts.on('--qi', '=NAME', 'Info for packages') do |opt|
146
+ @action = :query_info
147
+ @action_value = opt
148
+ end
149
+ opts.on('--ql', '=NAME', 'List files in installed packages') do |opt|
150
+ @action = :query_list_files
151
+ @action_value = opt
152
+ end
153
+ opts.on('--qf', '=FILE', 'List the package that owns a file') do |opt|
154
+ @action = :query_who_owns_file
155
+ @action_value = opt
156
+ end
157
+ opts.on('--qv', '=NAME', 'List available packages') do |opt|
158
+ @action = :query_available
159
+ @action_value = opt
160
+ end
161
+ opts.on('--qva', 'List all available packages') do |opt|
162
+ @action = :query_available
163
+ end
164
+ opts.on('--qr', '=NAME', 'List installed packages that require package') do |opt|
165
+ @action = :query_requires
166
+ @action_value = opt
167
+ end
168
+ opts.on('--qd', '=NAME', 'List the packages that package depends on') do |opt|
169
+ @action = :query_depends
170
+ @action_value = opt
171
+ end
172
+ opts.on('--qld', '=NAME', 'Similar to --qd, but only look at local packages') do |opt|
173
+ @action = :query_local_depends
174
+ @action_value = opt
175
+ end
176
+
177
+ opts.on('--dw', '=INTEGER', 'Number of workers for deploying') do |opt|
178
+ @worker_count = opt.to_i
179
+ @deploy_params = @deploy_params - ['--dw', @worker_count, "--dw=#{opt}"]
180
+ end
181
+ opts.on('--qX', '=FILENAME', 'Display tpkg.xml of the given package') do |opt|
182
+ @action = :query_tpkgxml
183
+ @action_value = opt
184
+ end
185
+ opts.on('--qenv', "Display machine's information") do |opt|
186
+ @action = :query_env
187
+ end
188
+ opts.on('--source', '=NAME', 'Sources where packages are located', Array) do |opt|
189
+ @tpkg_options["sources"] = opt
190
+ end
191
+ opts.on('-n', '--no-prompt', 'No confirmation prompts') do |opt|
192
+ @prompt = opt
193
+ Tpkg::set_prompt(@prompt)
194
+ end
195
+ opts.on('--quiet', 'Reduce informative but non-essential output') do |opt|
196
+ @quiet = opt
197
+ end
198
+ opts.on('--no-sudo', 'No calls to sudo for operations that might need root') do |opt|
199
+ @sudo = opt
200
+ end
201
+ opts.on('--lock-force', 'Force the removal of an existing lockfile') do |opt|
202
+ @lockforce = opt
203
+ end
204
+ opts.on('--force', 'Force the execution of a given task.') do |opt|
205
+ @force = opt
206
+ end
207
+ opts.on('--use-ssh-key', 'Use ssh key for deploying instead of password') do |opt|
208
+ @deploy_options["use-ssh-key"] = opt
209
+ @deploy_params = @deploy_params - ['--use-ssh-key']
210
+ end
211
+ opts.on('--deploy-as', '=USERNAME', 'What username to use for deploying to remote server') do |opt|
212
+ @deploy_options["deploy-as"] = opt
213
+ @deploy_params = @deploy_params - ['--deploy-as']
214
+ end
215
+ opts.on('--debug', 'Print lots of messages about what tpkg is doing') do |opt|
216
+ @debug = opt
217
+ Tpkg::set_debug(@debug)
218
+ end
219
+ opts.on('--version', 'Show tpkg version') do |opt|
220
+ puts Tpkg::VERSION
221
+ exit
222
+ end
223
+ opts.on_tail("-h", "--help", "Show this message") do
224
+ puts opts
225
+ exit
226
+ end
227
+
228
+ leftovers = opts.parse(ARGV)
229
+
230
+ # Rerun with sudo if necessary, unless it's a deploy, then
231
+ # we don't need to run with sudo on this machine. It will run with sudo
232
+ # on the remote machine
233
+ if @rerun_with_sudo && !@deploy
234
+ rerun_with_sudo_if_necessary
235
+ end
236
+
237
+ # Display a usage message if the user did not specify a valid action to perform.
238
+ if !@action
239
+ puts opts
240
+ exit
241
+ end
242
+
243
+ #
244
+ # Figure out base directory, sources and other configuration
245
+ #
246
+
247
+ def instantiate_tpkg(options = {})
248
+ base = Tpkg::DEFAULT_BASE
249
+ sources = options["sources"] || []
250
+ report_server = nil
251
+
252
+ [File.join(Tpkg::CONFIGDIR, 'tpkg.conf'), "#{ENV['HOME']}/.tpkg.conf"].each do |configfile|
253
+ if File.exist?(configfile)
254
+ IO.foreach(configfile) do |line|
255
+ line.chomp!
256
+ next if (line =~ /^\s*$/); # Skip blank lines
257
+ next if (line =~ /^\s*#/); # Skip comments
258
+ line.strip! # Remove leading/trailing whitespace
259
+ key, value = line.split(/\s*=\s*/, 2)
260
+ if key == 'base'
261
+ # Warn the user, as this could potentially be confusing
262
+ # if they don't realize there's a config file lying
263
+ # around
264
+ base = value
265
+ warn "Using base #{base} from #{configfile}"
266
+ elsif key == 'source'
267
+ sources << value
268
+ puts "Loaded source #{value} from #{configfile}" if (@debug)
269
+ elsif key == 'report_server'
270
+ report_server = value
271
+ puts "Loaded report server #{report_server} from #{configfile}" if (@debug)
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ if ENV['TPKG_HOME']
278
+ base = ENV['TPKG_HOME']
279
+ # Warn the user, as this could potentially be confusing
280
+ # if they don't realize there's an environment variable set.
281
+ warn "Using base '#{base}' base from $TPKG_HOME"
282
+ end
283
+
284
+ tpkg = Tpkg.new(:base => base, :sources => sources, :report_server => report_server, :lockforce => @lockforce, :force => @force)
285
+ end
286
+
287
+ passphrase_callback = lambda do | package |
288
+ # ask("Passphrase for #{package}: ", true)
289
+ begin
290
+ system 'stty -echo;'
291
+ print "Passphrase for #{package}: "
292
+ input = STDIN.gets.chomp
293
+ ensure
294
+ system 'stty echo; echo ""'
295
+ end
296
+ input
297
+ end
298
+
299
+ #
300
+ # Do stuff
301
+ #
302
+ if @deploy
303
+ # puts "Creating deployer with #{@worker_count} number of worker"
304
+ @deploy_options["max-worker"] = @worker_count
305
+ @deploy_options["abort-on-fail"] = false
306
+ Tpkg::deploy(@deploy_params, @deploy_options, @servers)
307
+ exit
308
+ end
309
+
310
+ ret_val = 0
311
+ case @action
312
+ when :make
313
+ pkgfile = Tpkg::make_package(@action_value, passphrase_callback)
314
+ if pkgfile
315
+ puts "Package is #{pkgfile}"
316
+ else
317
+ puts "Package build aborted or failed"
318
+ end
319
+ when :extract
320
+ Tpkg::extract_metadata(@action_value)
321
+ when :install
322
+ tpkg = instantiate_tpkg(@tpkg_options)
323
+ ret_val = tpkg.install(@action_value, passphrase_callback)
324
+ when :upgrade
325
+ tpkg = instantiate_tpkg(@tpkg_options)
326
+ ret_val = tpkg.upgrade(@action_value, passphrase_callback)
327
+ when :downgrade
328
+ downgrade = true
329
+ tpkg = instantiate_tpkg(@tpkg_options)
330
+ ret_val = tpkg.upgrade(@action_value, passphrase_callback, downgrade)
331
+ when :remove
332
+ tpkg = instantiate_tpkg(@tpkg_options)
333
+ ret_val = tpkg.remove(@action_value)
334
+ when :remove_all_dep
335
+ tpkg = instantiate_tpkg(@tpkg_options)
336
+ ret_val = tpkg.remove(@action_value, {:remove_all_dep => true})
337
+ when :remove_all_prereq
338
+ tpkg = instantiate_tpkg(@tpkg_options)
339
+ ret_val = tpkg.remove(@action_value, {:remove_all_prereq => true})
340
+ when :verify
341
+ result = nil
342
+ # Verify a given .tpkg file
343
+ if File.exist?(@action_value)
344
+ Tpkg::verify_package_checksum(@action_value)
345
+ # Verify an installed pkg
346
+ else
347
+ tpkg = instantiate_tpkg(@tpkg_options)
348
+ results = tpkg.verify_file_metadata(@action_value)
349
+ if results.length == 0
350
+ puts "No package found"
351
+ end
352
+ success = true
353
+ results.each do | file, errors |
354
+ if errors.length == 0
355
+ puts "#{file}: Passed"
356
+ else
357
+ puts "#{file}: Failed (Reasons: #{errors.join(", ")})"
358
+ success = false
359
+ end
360
+ end
361
+ puts "Package verification failed" unless success
362
+ end
363
+ when :start_init
364
+ tpkg = instantiate_tpkg(@tpkg_options)
365
+ ret_val = tpkg.execute_init(@action_value, "start")
366
+ when :stop_init
367
+ tpkg = instantiate_tpkg(@tpkg_options)
368
+ ret_val = tpkg.execute_init(@action_value, "stop")
369
+ when :restart_init
370
+ tpkg = instantiate_tpkg(@tpkg_options)
371
+ ret_val = tpkg.execute_init(@action_value, "restart")
372
+ when :reload_init
373
+ tpkg = instantiate_tpkg(@tpkg_options)
374
+ ret_val = tpkg.execute_init(@action_value, "reload")
375
+ when :query_installed
376
+ tpkg = instantiate_tpkg(@tpkg_options)
377
+ req = nil
378
+ matches = []
379
+ if @action_value
380
+ @action_value.each do | value |
381
+ req = Tpkg::parse_request(value, tpkg.installed_directory)
382
+ match = tpkg.installed_packages_that_meet_requirement(req)
383
+
384
+ # If the user requested specific packages and we found no matches
385
+ # then exit with a non-zero value to indicate failure. This allows
386
+ # command-line syntax like "tpkg -q foo || tpkg -i foo" to ensure
387
+ # that a package is installed.
388
+ ret_val = 1 if match.empty?
389
+ matches |= match
390
+ end
391
+ else
392
+ matches = tpkg.installed_packages_that_meet_requirement(req)
393
+ end
394
+
395
+ if !@quiet
396
+ matches.sort(&Tpkg::SORT_PACKAGES).each do |pkg|
397
+ puts pkg[:metadata][:filename]
398
+ end
399
+ end
400
+ when :query_info
401
+ metadatas = nil
402
+ if File.exist?(@action_value)
403
+ metadatas = [Tpkg::metadata_from_package(@action_value)]
404
+ else
405
+ tpkg = instantiate_tpkg(@tpkg_options)
406
+ metadatas = []
407
+ requirements = []
408
+ packages = {}
409
+ tpkg.parse_requests([@action_value], requirements, packages)
410
+ packages.each do | name, pkgs |
411
+ pkgs.each do | pkg |
412
+ metadatas << pkg[:metadata]
413
+ end
414
+ end
415
+ end
416
+ already_displayed = {}
417
+ metadatas.each do |metadata|
418
+ next if already_displayed[metadata[:filename]]
419
+ already_displayed[metadata[:filename]] = true
420
+ [:name, :version, :package_version, :operatingsystem, :architecture, :maintainer, :description, :bugreporting].each do |field|
421
+ if metadata[field]
422
+ if metadata[field].kind_of?(Array)
423
+ puts "#{field}: #{metadata[field].join(',')}"
424
+ else
425
+ puts "#{field}: #{metadata[field]}"
426
+ end
427
+ end
428
+ end
429
+ end
430
+ when :query_list_files
431
+ tpkg = instantiate_tpkg(@tpkg_options)
432
+ pkgfiles = nil
433
+ if File.exist?(@action_value)
434
+ fip = Tpkg::files_in_package(@action_value)
435
+ tpkg.normalize_paths(fip)
436
+ puts "#{pkgfile}:"
437
+ fip[:normalized].each { |file| puts file }
438
+ else
439
+ pkgfiles = []
440
+ metadatas = []
441
+ requirements = []
442
+ packages = {}
443
+
444
+ req = Tpkg::parse_request(@action_value)
445
+ pkgs = tpkg.installed_packages_that_meet_requirement(req)
446
+ if pkgs.nil? or pkgs.empty?
447
+ ret_val = 1
448
+ puts "Could not find any installed packages that meet the request \"#{@action_value}\""
449
+ end
450
+ pkgs.each do | pkg |
451
+ pkgfiles << pkg[:metadata][:filename]
452
+ end
453
+
454
+ files = tpkg.files_for_installed_packages(pkgfiles)
455
+ files.each do |pkgfile, fip|
456
+ puts "#{pkgfile}:"
457
+ fip[:normalized].each { |file| puts file }
458
+ end
459
+ end
460
+ when :query_who_owns_file
461
+ tpkg = instantiate_tpkg(@tpkg_options)
462
+ tpkg.files_for_installed_packages.each do |pkgfile, fip|
463
+ fip[:normalized].each do |file|
464
+ if file == @action_value
465
+ puts "#{file}: #{pkgfile}"
466
+ end
467
+ end
468
+ end
469
+ when :query_available
470
+ tpkg = instantiate_tpkg(@tpkg_options)
471
+ req = nil
472
+ if @action_value
473
+ req = Tpkg::parse_request(@action_value)
474
+ end
475
+ tpkg.available_packages_that_meet_requirement(req).each do |pkg|
476
+ next if pkg[:source] == :native_installed
477
+ next if pkg[:source] == :native_available
478
+ puts "#{pkg[:metadata][:filename]} (#{pkg[:source]})"
479
+ end
480
+ when :query_requires
481
+ tpkg = instantiate_tpkg(@tpkg_options)
482
+
483
+ # parse the request
484
+ requirements = []
485
+ packages = {}
486
+ tpkg.parse_requests([@action_value], requirements, packages)
487
+
488
+ # get dependencies of all installed packages
489
+ dependencies = {}
490
+ tpkg.metadata_for_installed_packages.each do |metadata|
491
+ dependencies[metadata[:filename]] = metadata[:dependencies]
492
+ end
493
+
494
+ # check to see if the any required dependencies match with what the
495
+ # user specified in the request
496
+ packages.each do |name, pkgs|
497
+ pkgs.each do |pkg|
498
+ next if pkg[:source] != :currently_installed
499
+ puts "The following package(s) require #{pkg[:metadata][:filename]}:"
500
+ dependencies.each do | requiree, deps |
501
+ deps.each do | dep |
502
+ if Tpkg::package_meets_requirement?(pkg, dep)
503
+ puts " #{requiree}"
504
+ end
505
+ end
506
+ end
507
+ end
508
+ end
509
+ when :query_local_depends
510
+ tpkg = instantiate_tpkg(@tpkg_options)
511
+ req = Tpkg::parse_request(@action_value)
512
+ pkgs = tpkg.installed_packages_that_meet_requirement(req)
513
+ pkgs.each do | pkg |
514
+ puts pkg[:metadata][:filename] + ':'
515
+ if pkg[:metadata][:dependencies]
516
+ pkg[:metadata][:dependencies].each do |req|
517
+ puts " Requires #{req[:name]}"
518
+ req.each do |field, value|
519
+ next if field == :name
520
+ puts " #{field}: #{value}"
521
+ end
522
+ end
523
+ end
524
+ end
525
+ when :query_depends
526
+ tpkg = instantiate_tpkg(@tpkg_options)
527
+ requirements = []
528
+ packages = {}
529
+ tpkg.parse_requests([@action_value], requirements, packages)
530
+ packages.each do |name, pkgs|
531
+ already_displayed = {}
532
+ pkgs.each do |pkg|
533
+ # parse_requests returns both installed and available packages.
534
+ # The same package may show up in both, skip any duplicates.
535
+ next if already_displayed[pkg[:filename]]
536
+ already_displayed[pkg[:metadata][:filename]] = true
537
+ puts pkg[:metadata][:filename] + ':'
538
+ if pkg[:metadata][:dependencies]
539
+ pkg[:metadata][:dependencies].each do |req|
540
+ puts " Requires #{req[:name]}"
541
+ req.each do |field, value|
542
+ next if field == :name
543
+ puts " #{field}: #{value}"
544
+ end
545
+ end
546
+ end
547
+ end
548
+ end
549
+ when :query_tpkgxml
550
+ tpkg = instantiate_tpkg(@tpkg_options)
551
+ if !File.exist?(@action_value)
552
+ puts "File #{@action_value} doesn't exist."
553
+ else
554
+ puts Tpkg::extract_tpkgxml(@action_value)
555
+ end
556
+ when :query_env
557
+ puts "Operating System: #{Tpkg::get_os}"
558
+ puts "Architecture: #{Tpkg::get_arch}"
559
+ end
560
+ exit ret_val