depot3 0.0.0a1 → 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +55 -1
  3. data/bin/d3 +323 -0
  4. data/bin/d3admin +1011 -0
  5. data/bin/d3helper +354 -0
  6. data/bin/puppytime +334 -0
  7. data/data/d3/com.pixar.d3.RepoMan.plist +23 -0
  8. data/data/d3/d3.conf.example +507 -0
  9. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftAppKit.dylib +0 -0
  10. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCore.dylib +0 -0
  11. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreData.dylib +0 -0
  12. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreGraphics.dylib +0 -0
  13. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreImage.dylib +0 -0
  14. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftDarwin.dylib +0 -0
  15. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftDispatch.dylib +0 -0
  16. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftFoundation.dylib +0 -0
  17. data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftObjectiveC.dylib +0 -0
  18. data/data/d3/d3RepoMan.app/Contents/Info.plist +56 -0
  19. data/data/d3/d3RepoMan.app/Contents/MacOS/d3RepoMan +0 -0
  20. data/data/d3/d3RepoMan.app/Contents/PkgInfo +1 -0
  21. data/data/d3/d3RepoMan.app/Contents/Resources/Base.lproj/MainMenu.nib +0 -0
  22. data/data/d3/d3RepoMan.app/Contents/Resources/last-foreground-times-template.plist +5 -0
  23. data/data/d3/d3RepoMan.app/Contents/_CodeSignature/CodeResources +214 -0
  24. data/data/d3/puppytime/ImageLicenses.txt +165 -0
  25. data/data/d3/puppytime/notification_image +1 -0
  26. data/data/d3/puppytime/opt_out_image +1 -0
  27. data/data/d3/puppytime/slideshow/2008-07-11_White_German_Shepherd_pup_chilling_at_the_Coker_Arboretum.jpg +0 -0
  28. data/data/d3/puppytime/slideshow/2009-04-21_APBT_pup_on_deck.jpg +0 -0
  29. data/data/d3/puppytime/slideshow/A_puppy_Yorkie.jpg +0 -0
  30. data/data/d3/puppytime/slideshow/Alert_Pug_Puppy.jpg +0 -0
  31. data/data/d3/puppytime/slideshow/Australian_Cattle_Dog_puppies_04.JPG +0 -0
  32. data/data/d3/puppytime/slideshow/Beagle_puppy_Cadet.jpg +0 -0
  33. data/data/d3/puppytime/slideshow/Bernese_Mountain_Dog.jpg +0 -0
  34. data/data/d3/puppytime/slideshow/Bloodhound_Puppy.jpg +0 -0
  35. data/data/d3/puppytime/slideshow/Boston_terrier_with_toy.jpg +0 -0
  36. data/data/d3/puppytime/slideshow/Boxer_puppy_fawn_portrai.jpg +0 -0
  37. data/data/d3/puppytime/slideshow/Caracal_kitten.jpg +0 -0
  38. data/data/d3/puppytime/slideshow/Chihuahua_&_Doberman_Pup.jpg +0 -0
  39. data/data/d3/puppytime/slideshow/Cuccioli_di_Margot_a_35_gg_Basenjis.jpg +0 -0
  40. data/data/d3/puppytime/slideshow/Dalmatian_puppy_03.jpg +0 -0
  41. data/data/d3/puppytime/slideshow/GoldenRetrieverPuppyDaisyParker.JPG +0 -0
  42. data/data/d3/puppytime/slideshow/Green_eyed_beige_Chihuahua.jpg +0 -0
  43. data/data/d3/puppytime/slideshow/Let_Sleeping_Dogs_Lie.jpg +0 -0
  44. data/data/d3/puppytime/slideshow/Meatball_-_French_Bulldog_Puppy.jpg +0 -0
  45. data/data/d3/puppytime/slideshow/Oola_-_9_weeks.jpg +0 -0
  46. data/data/d3/puppytime/slideshow/Pancho0008.JPG +0 -0
  47. data/data/d3/puppytime/slideshow/Pomeranian_orange-sable_Coco.jpg +0 -0
  48. data/data/d3/puppytime/slideshow/Pug_puppy_001.jpg +0 -0
  49. data/data/d3/puppytime/slideshow/Puggle_puppy_6_weeks.JPG +0 -0
  50. data/data/d3/puppytime/slideshow/Puli_kan.jpg +0 -0
  51. data/data/d3/puppytime/slideshow/Puppy_French_Bulldog.jpg +0 -0
  52. data/data/d3/puppytime/slideshow/Rocco_the_Bulldog.jpg +0 -0
  53. data/data/d3/puppytime/slideshow/Rottweiler_Face.jpg +0 -0
  54. data/data/d3/puppytime/slideshow/Saint_Bernard_puppy.jpg +0 -0
  55. data/data/d3/puppytime/slideshow/Scottish_froment.jpg +0 -0
  56. data/data/d3/puppytime/slideshow/Shar_pei_puppy_(age_2_months).jpg +0 -0
  57. data/data/d3/puppytime/slideshow/Shiba-Inu_beim_Spielen_im_Schnee.JPG +0 -0
  58. data/data/d3/puppytime/slideshow/Smooth-coat_Border_Collie_puppy..jpg +0 -0
  59. data/data/d3/puppytime/slideshow/Smooth_Dachshund_puppies.jpg +0 -0
  60. data/data/d3/puppytime/slideshow/Snow_dog.jpg +0 -0
  61. data/data/d3/puppytime/slideshow/Taylor_the_Pembroke_Welsh_Corgi.png +0 -0
  62. data/data/d3/puppytime/slideshow/Weim_Pups_001.jpg +0 -0
  63. data/data/d3/puppytime/slideshow/Westie_pups.jpg +0 -0
  64. data/data/d3/puppytime/slideshow/Yellow_Labrador_puppies_(4165737325).jpg +0 -0
  65. data/lib/d3/admin/add.rb +451 -0
  66. data/lib/d3/admin/auth.rb +470 -0
  67. data/lib/d3/admin/edit.rb +297 -0
  68. data/lib/d3/admin/help.rb +396 -0
  69. data/lib/d3/admin/interactive.rb +972 -0
  70. data/lib/d3/admin/options.rb +454 -0
  71. data/lib/d3/admin/prefs.rb +204 -0
  72. data/lib/d3/admin/report.rb +727 -0
  73. data/lib/d3/admin/state.rb +42 -0
  74. data/lib/d3/admin/validate.rb +413 -0
  75. data/lib/d3/admin.rb +42 -0
  76. data/lib/d3/basename.rb +217 -0
  77. data/lib/d3/client/auth.rb +108 -0
  78. data/lib/d3/client/class_methods.rb +766 -0
  79. data/lib/d3/client/class_variables.rb +47 -0
  80. data/lib/d3/client/cli.rb +187 -0
  81. data/lib/d3/client/environment.rb +134 -0
  82. data/lib/d3/client/help.rb +110 -0
  83. data/lib/d3/client/lists.rb +314 -0
  84. data/lib/d3/client/receipt.rb +1173 -0
  85. data/lib/d3/client.rb +45 -0
  86. data/lib/d3/configuration.rb +319 -0
  87. data/lib/d3/constants.rb +60 -0
  88. data/lib/d3/database.rb +488 -0
  89. data/lib/d3/exceptions.rb +44 -0
  90. data/lib/d3/log.rb +271 -0
  91. data/lib/d3/package/aliases.rb +80 -0
  92. data/lib/d3/package/attributes.rb +97 -0
  93. data/lib/d3/package/class_methods.rb +817 -0
  94. data/lib/d3/package/class_variables.rb +46 -0
  95. data/lib/d3/package/client_actions.rb +293 -0
  96. data/lib/d3/package/constants.rb +58 -0
  97. data/lib/d3/package/constructor.rb +191 -0
  98. data/lib/d3/package/getters.rb +164 -0
  99. data/lib/d3/package/mixins.rb +39 -0
  100. data/lib/d3/package/private_methods.rb +227 -0
  101. data/lib/d3/package/questions.rb +95 -0
  102. data/lib/d3/package/server_actions.rb +683 -0
  103. data/lib/d3/package/setters.rb +326 -0
  104. data/lib/d3/package/validate.rb +448 -0
  105. data/lib/d3/package.rb +51 -0
  106. data/lib/d3/puppytime/pending_puppy.rb +108 -0
  107. data/lib/d3/puppytime/puppy_queue.rb +274 -0
  108. data/lib/d3/puppytime.rb +68 -0
  109. data/lib/d3/state.rb +105 -0
  110. data/lib/d3/utility.rb +325 -0
  111. data/lib/d3/version.rb +1 -1
  112. metadata +162 -9
data/bin/d3admin ADDED
@@ -0,0 +1,1011 @@
1
+ #!/usr/bin/ruby
2
+
3
+ ### Copyright 2016 Pixar
4
+ ###
5
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
6
+ ### with the following modification; you may not use this file except in
7
+ ### compliance with the Apache License and the following modification to it:
8
+ ### Section 6. Trademarks. is deleted and replaced with:
9
+ ###
10
+ ### 6. Trademarks. This License does not grant permission to use the trade
11
+ ### names, trademarks, service marks, or product names of the Licensor
12
+ ### and its affiliates, except as required to comply with Section 4(c) of
13
+ ### the License and to reproduce the content of the NOTICE file.
14
+ ###
15
+ ### You may obtain a copy of the Apache License at
16
+ ###
17
+ ### http://www.apache.org/licenses/LICENSE-2.0
18
+ ###
19
+ ### Unless required by applicable law or agreed to in writing, software
20
+ ### distributed under the Apache License with the above modification is
21
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22
+ ### KIND, either express or implied. See the Apache License for the specific
23
+ ### language governing permissions and limitations under the Apache License.
24
+ ###
25
+ ###
26
+
27
+ # == Synopsis
28
+ # d3admin - a commandline tool for creating and maintaining d3 pkgs in Casper
29
+ #
30
+ # == Usage
31
+ # d3admin [options] action target....
32
+ #
33
+ # For help use: d3admin help
34
+ #
35
+ # == Author
36
+ # Chris Lasell <d3@pixar.com>
37
+ #
38
+ # == Copyright
39
+ # Copyright (c) 2016 Pixar Animation Studios.
40
+
41
+ ############
42
+ # Modules, Libraries, etc
43
+ ############
44
+
45
+ # Load libraries
46
+ require 'd3'
47
+
48
+
49
+
50
+ ######################
51
+ # The Script Object
52
+ ######################
53
+ class App
54
+
55
+ ### Setup
56
+ def initialize
57
+
58
+ ### parse the commandline
59
+ parse_commandline
60
+
61
+ # can't ever be run as root - no keychain, among other things
62
+ raise "d3admin can't be run as root" if JSS.superuser? and (not @action =~ /^h/)
63
+
64
+ end #initialize
65
+
66
+ ### Run
67
+ def run
68
+
69
+ if @action == "help"
70
+ show_help @options.helptype
71
+ return
72
+ end
73
+
74
+ # run config if it hasn't ever run
75
+ unless D3::Admin::Prefs.prefs[:last_config]
76
+ puts
77
+ puts "******** INITIAL D3ADMIN CONFIGURATION ********"
78
+ puts
79
+ config
80
+ # but dont run it again if that's the action chosen
81
+ return if @action =~ /^c/
82
+ end
83
+
84
+ ### our admin is us
85
+ @admin = ENV['USER']
86
+
87
+ # admin can't be a badmin
88
+ if D3.badmins.include? @admin and D3::Admin::ACTIONS_NEEDING_ADMIN.include? @action
89
+ raise D3::PermissionError, "d3admin cannot do '#{@action}' as #{@admin}."
90
+ end
91
+
92
+ # config before connecting
93
+ if @action =~ /^c/
94
+ config
95
+ return
96
+ end
97
+
98
+ ### connect to the server, prompting for info as needed
99
+ D3::Admin::Auth.connect
100
+
101
+ case @action
102
+
103
+ when /^a/ then
104
+ add_pilot_package
105
+
106
+ when /^e/ then
107
+ edit_package
108
+
109
+ when /^l/ then
110
+ make_package_live
111
+
112
+ when /^d/ then
113
+ delete_package
114
+
115
+ when /^i/ then
116
+ show_package_info
117
+
118
+ when /^s/ then
119
+ search
120
+
121
+ when /^r/ then
122
+ show_report
123
+
124
+ else
125
+ raise ArgumentError, "#{D3::Admin::Help::USAGE}\nUnknown action, must be one of #{D3::Admin::ACTIONS.join(', ')}"
126
+
127
+ end # case
128
+
129
+
130
+ end #run
131
+
132
+ ### Parse the command line
133
+ ###
134
+ ### @return [void]
135
+ ###
136
+ def parse_commandline
137
+
138
+ # this holds everything that comes from the commandline
139
+ # or from prompting the user
140
+ @options = OpenStruct.new
141
+
142
+ # cli option defaults
143
+ @options.helptype = :help
144
+
145
+ # --status can be given multiple times, or can be a comma-separated list.
146
+ # the results end up in this array
147
+ @options.status = []
148
+
149
+ ### see Admin::CLIOpts
150
+ opt_arry = D3::Admin::OPTIONS.values.map{|o| o[:cli] }
151
+ opts = GetoptLong.new *opt_arry
152
+
153
+ opts.each do |opt, arg|
154
+ case opt
155
+
156
+ #general
157
+ when '--help'
158
+ @action = "help"
159
+ @options.helptype = :help
160
+ return
161
+ when '--extended-help'
162
+ @action = "help"
163
+ @options.helptype = :extended_help
164
+ return
165
+ when '--debug'
166
+ D3::Admin.debug = true
167
+
168
+ when '--d3-version'
169
+ @action = "help"
170
+ @options.helptype = :show_d3_version
171
+ return
172
+
173
+ when '--walkthru'
174
+ @options.walkthru = true
175
+ when '--auto-confirm'
176
+ @options.auto_confirm = true
177
+
178
+ # search and report
179
+ when '--status'
180
+ @options.status += arg.split(/,\s*/)
181
+ when '--queue'
182
+ @options.report_q = true
183
+ when '--frozen'
184
+ @options.report_frozen = true
185
+ when '--computers'
186
+ @options.report_computers = true
187
+ when '--groups'
188
+ @options.search_groups = true
189
+ # add/edit
190
+ when '--import'
191
+ @options.import = true
192
+ @options.import_from = arg.empty? ? nil : arg
193
+ when '--no-inherit'
194
+ @options.no_inherit = true
195
+ when '--basename'
196
+ @options.basename = arg
197
+ when '--version'
198
+ @options.version = arg
199
+ when '--revision'
200
+ @options.revision = arg
201
+ when '--description'
202
+ @options.description = arg
203
+ when '--package-name'
204
+ @options.package_name = arg
205
+ when '--filename'
206
+ @options.filename = arg
207
+ when '--edition'
208
+ @options.edition = arg
209
+ when '--source-path'
210
+ @options.source_path = arg
211
+ when '--dmg'
212
+ @options.package_build_type = 'd'
213
+ when '--preserve-owners'
214
+ @options.pkg_preserve_owners = 'y'
215
+ when '--pkg-id'
216
+ @options.pkg_identifier = arg
217
+ when '--workspace'
218
+ @options.workspace = arg
219
+ when '--pre-install'
220
+ @options.pre_install = arg
221
+ when '--post-install'
222
+ @options.post_install = arg
223
+ when '--pre-remove'
224
+ @options.pre_remove = arg
225
+ when '--post-remove'
226
+ @options.post_remove = arg
227
+ when '--auto-groups'
228
+ @options.auto_groups = arg
229
+ when '--excluded-groups'
230
+ @options.excluded_groups = arg
231
+ when '--prohibiting-process'
232
+ @options.prohibiting_process = arg
233
+ when '--cpu_type'
234
+ @options.cpu_type = arg
235
+ when '--category'
236
+ @options.category = arg
237
+
238
+ when '--reboot'
239
+ # dft is no, so if arg is empty or /^n(o)$/i, it should be nil,
240
+ # otherwise must be /^y(es)$/i
241
+ if arg.empty? or arg =~ /^no?$/i
242
+ @options.reboot = "n"
243
+ elsif arg =~ /^y(es)?$/i
244
+ @options.reboot = "y"
245
+ else
246
+ raise ArgumentError, "--reboot must be 'y' or 'n'"
247
+ end
248
+
249
+ when '--remove-first'
250
+ # dft is no, so if arg is empty or /^n(o)$/i, it should be nil,
251
+ # otherwise must be /^y(es)$/i
252
+ if arg.empty? or arg =~ /^no?$/i
253
+ @options.remove_first = "n"
254
+ elsif arg =~ /^y(es)?$/i
255
+ @options.remove_first = "y"
256
+ else
257
+ raise ArgumentError, "--remove-first must be 'y' or 'n'"
258
+ end
259
+
260
+ when '--removable'
261
+ # dft is yes, so if arg is empty or /^y(es)$/i, it should be nil,
262
+ # otherwise must be /^n(o)$/i
263
+ if arg.empty? or arg =~ /^y(es)?$/i
264
+ @options.removable = "y"
265
+ elsif arg =~ /^no?$/i
266
+ @options.removable = "n"
267
+ else
268
+ raise ArgumentError, "--removable must be 'y' or 'n'"
269
+ end
270
+
271
+ when '--oses'
272
+ @options.oses = arg
273
+ when '--expiration'
274
+ @options.expiration = arg
275
+ when '--expiration-path'
276
+ @options.expiration_path = arg
277
+
278
+ # delete
279
+ when '--delete-scripts'
280
+ @options.delete_scripts = 'y'
281
+ when '--keep-in-jss'
282
+ @options.keep_in_jss = 'y'
283
+
284
+ end # case
285
+ end # opts.each
286
+
287
+ # the action is always the first thing in ARGV
288
+ # after the options have been removed
289
+ action_arg = ARGV.shift
290
+
291
+ unless action_arg
292
+ raise ArgumentError, "#{D3::Admin::Help::USAGE}\nAction, must be one of #{D3::Admin::ACTIONS.join(', ')}"
293
+ end
294
+
295
+ # one or more letters is all we need to specify the action...
296
+ @action = D3::Admin::ACTIONS.select{|a| a.start_with? action_arg}.first
297
+
298
+ unless @action and D3::Admin::ACTIONS.include? @action
299
+ raise ArgumentError, "#{D3::Admin::Help::USAGE}\nAction, must be one of #{D3::Admin::ACTIONS.join(', ')}"
300
+ end
301
+
302
+ # anything remaining in ARGV is one or more targets
303
+ # to work on.
304
+ @targets = ARGV
305
+
306
+ end # parse_commandline
307
+
308
+ ### Show the help message
309
+ ###
310
+ ### @return [void]
311
+ ###
312
+ def show_help (type)
313
+ text = case type
314
+ when :help
315
+ D3::Admin::Help.help_text
316
+ when :extended_help
317
+ D3::Admin::Help.extended_help_text
318
+ when :show_d3_version
319
+ d3_version_text
320
+ end
321
+ D3.less_text text
322
+ return true
323
+ end #show help
324
+
325
+ ### the d3 version text to spew
326
+ ###
327
+ ### @return [String] the text
328
+ def d3_version_text
329
+ <<-ENDVERS
330
+ D3 module version: #{D3::VERSION}
331
+ JSS module version: #{JSS::VERSION}
332
+ ENDVERS
333
+ end
334
+
335
+ ### Add a new pilot package from an existing
336
+ ### JSS package
337
+ def import_pilot_pkg_from_jss
338
+
339
+ if @options.walkthru
340
+ @options.import_from = D3::Admin::Interactive.get_value :import unless @options.import_from
341
+ @options.version = D3::Admin::Interactive.get_value :version
342
+ @options.revision = D3::Admin::Interactive.get_value :revision
343
+ else
344
+ raise JSS::MissingDataError, "A version must be provided with --version" unless @options.version
345
+ raise JSS::MissingDataError, "A revision must be provided with --revision" unless @options.revision
346
+ end # if walk thru
347
+
348
+ imported_pkg = D3::Package.import( @options.import_from,
349
+ basename: @options.basename,
350
+ version: @options.version,
351
+ revision: @options.revision,
352
+ dist_pw: D3::Admin::Auth.rw_credentials(:dist)[:password]
353
+ )
354
+ imported_pkg.admin = @admin
355
+ edit_package imported_pkg
356
+ end
357
+
358
+ ### Add a new package to d3 for piloting
359
+ ### In this method, the following are OpenStructs:
360
+ ### - @options holds the value from the commandline
361
+ ### - default_options holds the default values, either as
362
+ ### defined in D3::Admin::OPTIONS or inherited
363
+ ### from an older package
364
+ ### - new_package_options holds the validated values for
365
+ ### making the new package
366
+ ###
367
+ ### @return [void]
368
+ ###
369
+ def add_pilot_package
370
+
371
+ ## we must have a basename before going farther
372
+ @options.basename = @targets.first
373
+ if @options.walkthru
374
+ @options.basename ||= D3::Admin::Interactive.get_basename
375
+ else
376
+ raise ArgumentError, "A basename must be provided as a target. Use -H for help" unless @options.basename
377
+ end
378
+
379
+ if @options.import
380
+ import_pilot_pkg_from_jss
381
+ return
382
+ end
383
+
384
+ puts "Adding a new pilot package to d3..."
385
+
386
+ # this holds the validated data used for making the new pkg
387
+ new_package_options = OpenStruct.new
388
+
389
+ # now figure out our default values..
390
+ default_options = D3::Admin::Add.get_default_options(@options.basename, @options.no_inherit)
391
+
392
+ # get all new package options into new_package_options
393
+ # via walkthru ...
394
+ if @options.walkthru
395
+ new_package_options = D3::Admin::Add.loop_thru_add_walkthru default_options
396
+
397
+ # via the commandline
398
+ else
399
+ # for any options that weren't given on the commandline, use the defaults
400
+ default_options.each_pair do |opt, val|
401
+ next if @options[opt]
402
+ @options[opt] = val
403
+ end
404
+
405
+ new_package_options = D3::Admin::Add.add_pilot_cli(@options)
406
+
407
+ ##########################
408
+
409
+ end # if @options.walkthru
410
+
411
+ new_package_options.edition = "#{new_package_options.basename}-#{new_package_options.version}-#{new_package_options.revision}"
412
+
413
+ # make a summary of the new values to display for confirmation, if no walkthru
414
+ unless @options.walkthru
415
+ lines = []
416
+
417
+ opts_to_confirm = D3::Admin::Add::NEW_PKG_OPTIONS
418
+ opts_to_confirm += D3::Admin::Add::BUILD_OPTIONS if new_package_options.package_build_type
419
+ opts_to_confirm += D3::Admin::Add::PKG_OPTIONS if new_package_options.package_build_type == :pkg
420
+
421
+ #D3::Admin::Add::NEW_PKG_OPTIONS.each do |opt|
422
+ opts_to_confirm.each do |opt|
423
+ lbl = D3::Admin::OPTIONS[opt][:label]
424
+ if D3::Admin::OPTIONS[opt][:display_conversion]
425
+ disp = D3::Admin::OPTIONS[opt][:display_conversion].call(new_package_options[opt])
426
+ else
427
+ disp = new_package_options[opt]
428
+ end
429
+ lines << "#{lbl}: #{disp}"
430
+ end
431
+ settings_conf_disp = "\n******* New d3 Package Settings *******\n"
432
+ settings_conf_disp += "Edition: #{ new_package_options.edition}\n"
433
+ settings_conf_disp += "Basename: #{ new_package_options.basename}\n"
434
+ settings_conf_disp += lines.join "\n"
435
+ settings_conf_disp += "\n"
436
+ puts settings_conf_disp
437
+ end
438
+
439
+ # Get confirmation
440
+ confirm "Create a new package '#{new_package_options.edition}' in d3\nwith settings shown above."
441
+
442
+ D3::Admin::Add.add_new_package new_package_options
443
+
444
+ puts "Done!"
445
+ puts "To pilot it, run 'sudo d3 install #{new_package_options.edition}' on a test machine."
446
+ puts "To make it live, run 'd3admin live #{new_package_options.edition}' on your machine."
447
+
448
+ end # add pilot
449
+
450
+ ### Make a package live.
451
+ ###
452
+ ### @return [void]
453
+ ###
454
+ def make_package_live
455
+
456
+ pkg_id = get_pkg_from_cli_or_prompt
457
+
458
+ if pkg_id.nil?
459
+ puts "No edition given to make live or no matching package found"
460
+ return
461
+ end
462
+ pkg = D3::Package.new id: pkg_id
463
+
464
+ if pkg.status == :live
465
+ puts "Doh, '#{pkg.edition}' is already live"
466
+ return
467
+ end
468
+
469
+ # is this a rollback?
470
+ rollback_warning = ""
471
+ if pkg.skipped? or pkg.deprecated?
472
+ rollback_warning = "\n\nWARNING: You're rolling back to an older edition!\n"
473
+ rollback_warning += "ALL non-frozen installs of '#{pkg.basename}' will be downgraded to this edition! \n"
474
+ end
475
+
476
+ # is the prev. live pkg in use in any policies?
477
+ policy_warning = ""
478
+ pkg_id_being_deprecated = D3::Package.basenames_to_live_ids[pkg.basename]
479
+ if pkg_id_being_deprecated
480
+ outgoing_pkg = D3::Package.new(id: pkg_id_being_deprecated)
481
+ pols_used_by_old_pkg = outgoing_pkg.policy_ids
482
+ unless pols_used_by_old_pkg.empty?
483
+ names = pols_used_by_old_pkg.map{|pid| JSS::Policy.map_all_ids_to(:name)[pid]}.join(', ')
484
+ policy_warning = "\n\nWARNING: the current live package is in use by these Casper Policies:\n"
485
+ policy_warning += " #{names}\n"
486
+ policy_warning += "You might want to update them to use the new live package."
487
+ end # unless empty
488
+ end # if pkg_id_being_deprecated
489
+
490
+ confirm "Make #{pkg.edition} live for basename '#{pkg.basename}'#{rollback_warning}#{policy_warning}" , ""
491
+
492
+ pkg.make_live @admin
493
+
494
+ puts "Done!"
495
+ puts "New installs of basename '#{pkg.basename}' will get #{pkg.version}-#{pkg.revision}"
496
+ puts "Existing installs will be updated at the next d3 sync."
497
+ end
498
+
499
+ ### Edit an existing or importing package
500
+ ###
501
+ ### @param pkg[D3::Package, nil] A possibly unsaved D3::Package to edit.
502
+ ### This is usually used for importing JSS pkgs into d3.
503
+ ###
504
+ ### @return [void]
505
+ ###
506
+ def edit_package (pkg=nil)
507
+
508
+ unless pkg
509
+ pkg_id = get_pkg_from_cli_or_prompt
510
+ pkg = pkg_id ? D3::Package.new(id: pkg_id) : nil
511
+ end
512
+ if pkg.nil?
513
+ puts "No targets given to edit or no matching package found"
514
+ return
515
+ end
516
+
517
+
518
+ if @options.walkthru
519
+ changes_to_make = D3::Admin::Edit.loop_thru_editing_walkthru pkg
520
+ else
521
+
522
+ changes_to_make = D3::Admin::Edit.validate_cli_edits (@options)
523
+ end # if @options.walkthru
524
+
525
+ # Confirm the edition is still good if vers or rev changed
526
+ changes_to_make = D3::Admin::Edit.check_new_edition pkg, changes_to_make
527
+
528
+ # Confirm the auto and excl groups don't conflict with each other
529
+ D3::Admin::Edit.check_for_new_group_overlaps pkg, changes_to_make
530
+
531
+
532
+ # exit if no changes on edit
533
+ if changes_to_make.empty? and not @options.import
534
+ puts "No changes to make!"
535
+ return
536
+ end
537
+
538
+ # confirm
539
+ if @options.import
540
+ confirm_heading = "Import #{pkg.edition}, JSS id #{pkg.id}, into d3"
541
+ conf_deets = "with these non-default settings..."
542
+ else
543
+ confirm_heading = "Make changes to #{pkg.edition}, JSS id #{pkg.id}"
544
+ conf_deets = "Here are the changes..."
545
+ end
546
+
547
+ D3::Admin::Edit::EDITING_OPTIONS.each do |opt|
548
+ next unless changes_to_make.keys.include? opt
549
+ new_val = changes_to_make[opt]
550
+ opt_def = D3::Admin::OPTIONS[opt]
551
+ label = opt_def[:label]
552
+ if opt_def[:display_conversion]
553
+ new_val_display = opt_def[:display_conversion].call(new_val)
554
+ else
555
+ new_val_display = new_val
556
+ end
557
+ new_val_display = new_val_display.to_s.empty? ? 'none' : new_val_display
558
+ conf_deets += "\n#{label}: #{new_val_display}"
559
+ end
560
+ confirm confirm_heading, conf_deets
561
+
562
+ # Save if importing
563
+ pkg.save if @options.import
564
+
565
+ # make the changes
566
+ D3::Admin::Edit.process_edits pkg, changes_to_make
567
+
568
+ puts @options.import ? "Done: the package has been imported to d3." : "Done! Your changes have been saved."
569
+
570
+ end # edit_pkg
571
+
572
+ ### delete a pkg, optionallay archiving it
573
+ ###
574
+ ### @return [void]
575
+ ###
576
+ def delete_package
577
+
578
+ pkg_id = get_pkg_from_cli_or_prompt
579
+
580
+ if pkg_id.nil?
581
+ puts "No targets given to delete or no matching package found"
582
+ return
583
+ end
584
+
585
+ # check to see if the pkg is missing
586
+ if D3::Package.package_data[pkg_id][:status] == :missing || (not JSS::Package.all_ids.include? pkg_id)
587
+ delete_missing_package pkg_id
588
+ return
589
+ else
590
+ pkg = D3::Package.new id: pkg_id
591
+ end
592
+
593
+ got_scripts = (not pkg.script_ids.values.empty?)
594
+
595
+ if @options.walkthru
596
+ @options.delete_scripts = D3::Admin::Interactive.get_value(:get_delete_scripts, default = 'n', check_method = :validate_yes_no)
597
+ @options.keep_in_jss = D3::Admin::Interactive.get_value(:get_keep_in_jss, default = 'n', check_method = :validate_yes_no)
598
+ end
599
+
600
+ if got_scripts
601
+ if @options.delete_scripts
602
+ deets = " - Deleting pre- or post- scripts not in use elsewhere"
603
+ else
604
+ deets = " - Keeping pre- or post- scripts in the JSS"
605
+ end # if @options.delete_scripts
606
+ else
607
+ deets = " - No pre- or post- scripts to delete"
608
+ end # if got_scripts
609
+
610
+ if @options.keep_in_jss
611
+ deets += "\n - Leaving the Casper package in the JSS"
612
+ else
613
+ deets += "\n - Deleting the Casper package as well as the d3 data"
614
+ end
615
+
616
+ confirm "DELETE #{pkg.edition}, JSS id #{pkg.id}\nDist.Point Filename: #{pkg.filename}", deets
617
+
618
+ if @options.delete_scripts && got_scripts
619
+ puts "Scanning for other packages or policies using pre- or post- scripts before deleting..."
620
+ end
621
+
622
+ script_actions = pkg.delete(
623
+ admin: @admin,
624
+ delete_scripts: @options.delete_scripts,
625
+ keep_in_jss: @options.keep_in_jss,
626
+ rwpw: D3::Admin::Auth.rw_credentials(:dist)[:password]
627
+ )
628
+
629
+ unless script_actions.empty?
630
+ puts "Here' what happened to the scripts:"
631
+ puts script_actions.join "\n"
632
+ end
633
+ puts "Done! #{pkg.edition} has been deleted"
634
+
635
+ end # delete package
636
+
637
+ ### Delete a package that's missing from the JSS
638
+ ###
639
+ ###
640
+ def delete_missing_package (pkgid)
641
+ pkg_data = D3::Package.package_data[pkgid]
642
+ script_ids = [pkg_data[:pre_install_script_id], pkg_data[:post_install_script_id], pkg_data[:pre_remove_script_id], pkg_data[:post_remove_script_id] ].compact
643
+
644
+ if script_ids.empty?
645
+ deets = " - No pre- or post- scripts to delete"
646
+ else
647
+ if @options.delete_scripts
648
+ deets = " - Deleting pre- or post- scripts not in use elsewhere"
649
+ else
650
+ deets = " - Keeping pre- or post- scripts in the JSS"
651
+ end # if @options.delete_scripts
652
+ end # if script_ids.empty?
653
+
654
+ confirm "DELETE #{pkg_data[:edition]}\n** Already missing from the JSS **", deets
655
+
656
+ JSS::DB_CNX.db.query "DELETE FROM #{D3::Database::PACKAGE_TABLE[:table_name]} WHERE package_id = #{pkgid}"
657
+ puts "Package #{pkg_data[:edition]} deleted."
658
+
659
+ if @options.delete_scripts && (not script_ids.empty?)
660
+ puts "Scanning for other packages or policies using pre- or post- scripts before deleting..."
661
+ policy_scripts = D3.policy_scripts
662
+ script_ids.each do |victim_script_id|
663
+ next unless JSS::Script.all_ids.include? victim_script_id
664
+
665
+ victim_script_name = JSS::Script.map_all_ids_to(:name)[victim_script_id]
666
+ pol_users = []
667
+ policy_scripts.each do |pol, pol_scripts|
668
+ if pol_scripts.include? victim_script_id
669
+ puts "Script '#{victim_script_name}' in use by policy '#{pol}'"
670
+ pol_users << pol
671
+ end
672
+ end # policy scripts.each
673
+ d3_users = (D3::Package.packages_for_script(victim_script_id) - [pkgid])
674
+ d3_users.each{|pkgid| puts "Script '#{victim_script_name}' in use by d3 edition '#{D3::Package.ids_to_editions[pkgid]}'" }
675
+ if pol_users.empty? and d3_users.empty?
676
+ JSS::Script.new(id: victim_script_id).delete
677
+ puts "Deleted script '#{victim_script_name}'"
678
+ end
679
+ end # do script id
680
+ end # if @options.delete_scripts && (not script_ids.empty?)
681
+
682
+ end # delete_missing_package (pkgid)
683
+
684
+ ### Get an existing pkg id from the user via prompt (if walkthru) or the
685
+ ### first thing listed in @targets. We don't instantiate the pkg here
686
+ ### because it might be :missing in the JSS.
687
+ ###
688
+ ### @return [Ingeter, nil] an existing d3 pkg id if one was chosen
689
+ ###
690
+ def get_pkg_from_cli_or_prompt
691
+ if @options.walkthru and @targets.first.nil?
692
+ D3::Admin::Interactive.get_value :get_existing_package, nil, :validate_existing_package
693
+ else
694
+ pkg_data = D3::Package.find_package(@targets.first, :hash)
695
+ return pkg_data ? pkg_data[:id] : nil
696
+ end
697
+ end
698
+
699
+ ### Get confirmation before making any changes to a pkg.
700
+ ###
701
+ ### This method just taks a string of text to show the user
702
+ ### and shows it below a standard, attention-getting header line
703
+ ### It then waits for the user to type y or n and exits if n was typed.
704
+ ###
705
+ ### @param action[String] a textual representation of what we're confirming
706
+ ###
707
+ ### @param details[String] The details requireing confirmation
708
+ ###
709
+ ###
710
+ def confirm (action, details = nil)
711
+
712
+ puts
713
+ puts "*****************************************"
714
+ puts "Ahoy there! You are about to:\n#{action}"
715
+ puts "*****************************************"
716
+ puts details if details
717
+
718
+ if @options.auto_confirm
719
+ puts "auto-confirmed! Here we go....."
720
+ else
721
+ puts
722
+ reply = Readline.readline("Are you SURE? (y/n): ", false)
723
+ return true if reply =~ /^y/i
724
+ puts "Cancelled - wise choice!"
725
+ exit 0
726
+ end # if autoconfirm
727
+ end # confirm
728
+
729
+ ### show details about an existing package
730
+ ###
731
+ ### @return [void]
732
+ ###
733
+ def show_package_info
734
+
735
+ pkg_id = get_pkg_from_cli_or_prompt
736
+
737
+ if pkg_id.nil?
738
+ puts "No targets given or no matching package found"
739
+ return
740
+ end
741
+
742
+ pkg = D3::Package.new id: pkg_id
743
+
744
+ pkg_deets = <<-ENDDEETS
745
+ *****************************************
746
+ Details about #{pkg.edition}
747
+ JSS id: #{pkg.id}, Status: #{pkg.status}
748
+ ****************************************
749
+ ENDDEETS
750
+ pkg_deets += "Basename: #{pkg.basename}\n"
751
+ pkg_deets += "Version: #{pkg.version}\n"
752
+ pkg_deets += "Revision: #{pkg.revision}\n"
753
+ pkg_deets += "Added to on: #{pkg.added_date.strftime "%Y-%m-%d"}\n"
754
+ pkg_deets += "Added by: #{pkg.added_by}\n"
755
+ if pkg.release_date
756
+ pkg_deets += "Released on: #{pkg.release_date.strftime "%Y-%m-%d"}\n"
757
+ pkg_deets += "Released by: #{pkg.released_by}\n"
758
+ end
759
+
760
+ done = [:basename, :version, :revision, :added_by, :added_date, :released_by, :release_date]
761
+ D3::Admin::Edit::EDITING_OPTIONS.each do |opt|
762
+ next if done.include? opt
763
+ label = D3::Admin::OPTIONS[opt][:label]
764
+ if D3::Admin::OPTIONS[opt][:display_conversion]
765
+ val_display = D3::Admin::OPTIONS[opt][:display_conversion].call(pkg.send opt)
766
+ else
767
+ val_display = pkg.send opt
768
+ end
769
+ val_display = val_display.to_s.empty? ? "none" : val_display
770
+ pkg_deets += "#{label}: #{val_display}\n"
771
+ end
772
+
773
+
774
+ puts pkg_deets
775
+ puts
776
+ end # show_package_info
777
+
778
+ ### search for and print a list of packages in d3
779
+ ###
780
+ ### @return [void]
781
+ ###
782
+ def search
783
+ D3::Admin::Report.connect_for_reports
784
+
785
+ if @options.walkthru
786
+ # prompt for search type?
787
+ search_for = D3::Admin::Interactive.prompt_for_data(
788
+ desc: "SEARCH PACKAGES BY?\nAre you searching for packages by basename, or by scoped groups?\nEnter 'b' or 'g'",
789
+ prompt: "Basenames or Groups",
790
+ default: 'b',
791
+ required: false
792
+ )
793
+ @options.search_groups = true if search_for =~ /^g/i
794
+
795
+ @targets = [D3::Admin::Interactive.get_search_target] if @targets.empty?
796
+ @options.status = D3::Admin::Interactive.get_status_for_filter.split(/,\s*/) if @options.status.empty?
797
+ end #if @options.walkthru
798
+
799
+ @options.status = [] if @options.status.include? "all"
800
+
801
+ # show all pkgs or group scope
802
+ if @targets.empty? or @targets.include? "all"
803
+ if @options.search_groups
804
+ D3::Admin::Report.list_all_pkgs_with_scope @options.status
805
+ else
806
+ D3::Admin::Report.list_packages nil, @options.status
807
+ end
808
+ return
809
+ end
810
+
811
+ # show specific basenames or group scopes
812
+ @targets.each do |search_target|
813
+
814
+ # groups?
815
+ if @options.search_groups
816
+ found_groups = JSS::ComputerGroup.all_names.select{|gn| gn =~ /#{search_target}/}
817
+ if found_groups.empty?
818
+ puts "No computer groups in Casper match '#{search_target}'"
819
+ else
820
+ found_groups.each {|fgn|
821
+ D3::Admin::Report.list_scoped_installs fgn, @options.status, :auto
822
+ D3::Admin::Report.list_scoped_installs fgn, @options.status, :excluded
823
+ } # found_groups.each
824
+ end # if found_groups.empty?
825
+
826
+ # basenames
827
+ else
828
+ found_basenames = D3::Package.all_basenames.select{|bn| bn =~ /#{search_target}/}
829
+ if found_basenames.empty?
830
+ puts "No basenames in d3 match '#{search_target}'"
831
+ else
832
+ found_basenames.each {|fbn| D3::Admin::Report.list_packages fbn, @options.status }
833
+ end # if found_basenames.empty?
834
+ end # if @options.search_groups
835
+ end # @targets.each do |search_target|
836
+ end # def search
837
+
838
+ ### show a list of packges on the server
839
+ def show_package_list (basename, statuses)
840
+
841
+ D3::Admin::Report.show_list basename, @options.status
842
+
843
+ end # show_package_list
844
+
845
+ ### show a report
846
+ ###
847
+ ### @return [void]
848
+ ###
849
+ def show_report
850
+ D3::Admin::Report.connect_for_reports
851
+
852
+ # targets are basenames or computer names
853
+
854
+ if @options.walkthru
855
+
856
+ # prompt for target?
857
+ if @targets.empty?
858
+ one_or_all = D3::Admin::Interactive.prompt_for_data(
859
+ desc: "ALL COMPUTERS?\nAre you reporting for a single computer or all computers?\nEnter 'all' for all computers, '1' for one",
860
+ prompt: "All or 1",
861
+ default: 'all',
862
+ required: false
863
+ )
864
+
865
+ # basename report across computers
866
+ if one_or_all == "all"
867
+ @targets = [(D3::Admin::Interactive.get_value :get_basename, nil, :validate_basename)]
868
+
869
+ # reporting single computers
870
+ else
871
+ @options.report_computers = true
872
+ computer_id = D3::Admin::Interactive.get_value :get_computer, nil, :validate_computer
873
+ @targets = [JSS::Computer.map_all_ids_to(:name)[computer_id]]
874
+ end
875
+ end # if targets empty
876
+
877
+ # prompt for rcpts or puppies
878
+ rcpts_or_pups = D3::Admin::Interactive.prompt_for_data(
879
+ desc: "RECEIPTS OR PUPPIES?\nShould the report list receipts or pending puppytime installs?\nEnter 'r' for receipts, 'p' for puppies",
880
+ prompt: "Receipts or puppies",
881
+ required: false,
882
+ default: 'r'
883
+ )
884
+ @options.report_q = rcpts_or_pups =~ /^p/i ? true : false
885
+
886
+ # prompt for status or frozen
887
+ @options.status = D3::Admin::Interactive.get_status_for_filter(:with_frozen).split(/,\s*/) if @options.status.empty?
888
+
889
+ end #if @options.walkthru
890
+
891
+ @options.status = [] if @options.status.include? "all"
892
+
893
+ # pass the --frozen option with the --status options, since
894
+ # its treated like a pseudo-status, and thats where walkthru puts it too
895
+ @options.status << "frozen" if @options.report_frozen
896
+
897
+ reporting = @options.report_q ? :puppies : :receipts
898
+
899
+
900
+ if @targets.empty?
901
+ puts "No basename or computer name given for report"
902
+ return
903
+ end
904
+
905
+ @targets.each do |target|
906
+
907
+ # report puppies
908
+ if reporting == :puppies
909
+ if @options.report_computers
910
+ D3::Admin::Report.report_single_puppy_queue target, @options.status
911
+ else
912
+ D3::Admin::Report.report_puppy_queues target, @options.status
913
+ end
914
+
915
+ # report receipts
916
+ else
917
+ if @options.report_computers
918
+ D3::Admin::Report.report_single_computer_receipts target, @options.status
919
+ else
920
+ D3::Admin::Report.report_basename_receipts target, @options.status
921
+ end
922
+
923
+ end # if reporting puppies
924
+ end # targets each
925
+ end # show_report
926
+
927
+ ### Run the config, saving hostnames, usernames and pws
928
+ ### as needed
929
+ ###
930
+ ### @param display[Boolean] show the current admin config rather than
931
+ ### set it.
932
+ ###
933
+ ### @return [void]
934
+ ###
935
+ def config
936
+
937
+ if @targets.include? 'display'
938
+ D3::Admin::Prefs.display_config
939
+ else
940
+ D3::Admin::Prefs.config @targets, @options
941
+ end
942
+
943
+ # if @options.walkthru
944
+ # tgt = D3::Admin::Interactive.get_value :get_config_target, "all"
945
+ # @targets = [tgt]
946
+ # end
947
+ #
948
+ # if @targets.empty? or @targets.include?("all")
949
+ # @targets = D3::Admin::CONFIG_TARGETS - ["all"]
950
+ # end
951
+ #
952
+ # @targets.each do |target|
953
+ # case target
954
+ # when"jss"
955
+ # puts "******** JSS-API LOCATION AND READ-WRITE CREDENTIALS ********"
956
+ # D3::Admin::Auth.ask_for_rw_credentials :jss
957
+ #
958
+ # when "db"
959
+ # puts "******** JSS MYSQL LOCATION AND READ-WRITE CREDENTIALS ********"
960
+ # D3::Admin::Auth.ask_for_rw_credentials :db
961
+ #
962
+ # when "dist"
963
+ # puts "******** MASTER DIST-POINT READ-WRITE PASSWORD ********"
964
+ # D3::Admin::Auth.ask_for_rw_credentials :dist
965
+ #
966
+ # when "workspace"
967
+ # puts "******** LOCAL PKG/DMG BUILD WORKSPACE ********"
968
+ # pth = D3::Admin::Interactive.get_value :workspace, D3::Admin::Prefs.prefs[:workspace]
969
+ # D3::Admin::Prefs.set_pref :workspace, pth
970
+ # D3::Admin::Prefs.save_prefs
971
+ # puts "Thank you, the path has been saved in your d3admin prefs"
972
+ # puts
973
+ #
974
+ # when "pkg-id-prefix"
975
+ # puts "******** .PKG IDENTIFIER PREFIX ********"
976
+ # pfx = D3::Admin::Interactive.get_value(:get_pkg_identifier_prefix, D3::Admin::Prefs.prefs[:apple_pkg_id_prefix], :validate_package_identifier_prefix)
977
+ # D3::Admin::Prefs.set_pref :apple_pkg_id_prefix, pfx
978
+ # D3::Admin::Prefs.save_prefs
979
+ # puts "Thank you, the prefix has been saved in your d3admin prefs"
980
+ # puts
981
+ # else
982
+ # puts "(skipping unknown config setting: #{target}"
983
+ # end # case
984
+ # end # targets.each
985
+ end # config
986
+
987
+
988
+ end # class App
989
+
990
+
991
+ ### Create and run the application
992
+
993
+ # save terminal state incase user interrupts during readline or less
994
+ stty_save = `stty -g`.chomp
995
+ trap("SIGINT") { puts "\nCancelled! Woot!" ; system('stty', stty_save); exit 0 }
996
+
997
+ begin
998
+ app = App.new
999
+ app.run
1000
+ rescue
1001
+ # handle exceptions
1002
+ puts "An error occurred: #{$!.class}: #{$!}"
1003
+ puts $@ if D3::Admin.debug
1004
+ exit 1
1005
+ ensure
1006
+ # cleanup
1007
+ if JSS::API.connected?
1008
+ JSS::DistributionPoint.master_distribution_point.unmount if JSS::DistributionPoint.master_distribution_point.mounted?
1009
+ D3::Admin::Auth.disconnect
1010
+ end
1011
+ end # begin