Module: D3::Admin::Add

Extended by:
Add
Included in:
Add
Defined in:
lib/d3/admin/add.rb

Overview

The Admin::Add modile contains constants and methods related to adding packages to d3 via d3admin.

Constant Summary collapse

NEW_PKG_OPTIONS =

These are the symbols representing all the possible commandline options used for defining new packages.

%i(
  version
  revision
  package_name
  description
  filename
  category
  oses
  cpu_type
  reboot
  removable
  remove_first
  prohibiting_processes
  auto_groups
  excluded_groups
  pre_install
  post_install
  pre_remove
  post_remove
  expiration
  expiration_paths
  source_path
).freeze
BUILD_OPTIONS =

If we need to build the pkg, these options are needed

[:workspace, :package_build_type].freeze
PKG_OPTIONS =

If we are building a .pkg these options are needed

[:pkg_identifier, :pkg_preserve_owners, :signing_identity, :signing_options].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.add_new_package(new_package_options) ⇒ Integer

Add the new pkg to d3

Parameters:

  • new_package_options (OpenStruct)

    the verified options for the new package.

Returns:

  • (Integer)

    the JSS id of the new package



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/d3/admin/add.rb', line 399

def add_new_package(new_package_options)
  # new_package_options should now have all the validated data we need to make a new pkg
  new_pilot = D3::Package.make(
    basename: new_package_options.basename,
    name: new_package_options.package_name,
    version: new_package_options.version,
    revision: new_package_options.revision,
    filename: new_package_options.filename,
    admin: ENV['USER']
  )

  D3::Admin::Add::NEW_PKG_OPTIONS.each do |opt|
    setter = "#{opt}=".to_sym
    new_pilot.send setter, new_package_options.send(opt) if new_pilot.respond_to? setter
  end

  # do we need to build the pkg?
  unless new_package_options.source_path.extname =~ /\.(m?pkg|dmg)$/
    if new_package_options.package_build_type == :pkg
      puts 'Building .pkg...'

      new_pkg_path = JSS::Composer.mk_pkg(
        new_package_options.package_name,
        new_package_options.version,
        new_package_options.source_path,
        pkg_id: new_package_options.pkg_identifier,
        out_dir: new_package_options.workspace,
        preserve_ownership: new_package_options.pkg_preserve_owners,
        signing_identity: new_package_options.signing_identity,
        signing_options: new_package_options.signing_options
      )

      new_package_options.source_path = new_pkg_path

    elsif new_package_options.package_build_type == :dmg
      puts 'Building .dmg...'
      new_pkg_path = JSS::Composer.mk_dmg(
        new_package_options.package_name,
        new_package_options.source_path,
        new_package_options.workspace
      )

      new_package_options.source_path = new_pkg_path
    end # if new_package_options.package_build_type
  end # unless

  # save to d3
  puts "Saving new pilot #{new_package_options.edition} to the server..."

  new_pkg_id = new_pilot.save

  puts 'Indexing...'
  # make the index - all d3 pkgs are indexed in the JSS
  new_pilot.mk_index local_filepath: new_package_options.source_path

  # upload to dist point
  # if its a .pkg, the apple rcpt data is updated during the upload
  puts 'Uploading to the Master Distribution Point...'
  new_pilot.upload_master_file new_package_options.source_path, D3::Admin::Auth.rw_credentials(:dist)[:password]

  new_pkg_id
end

.add_pilot_cli(cli_options) ⇒ OpenStruct

Validate commandline options when adding a package without walkthru using defaults as needed. Some values will be prompted for if needed, since they are required and can’t use defaults.

Parameters:

  • cli_options (OpenStruct)

    the values from the command line

  • new_package_options (OpenStruct)

    the repository for the validated data

Returns:

  • (OpenStruct)

    the new_package_options with validated data



209
210
211
212
213
214
215
216
217
218
219
# File 'lib/d3/admin/add.rb', line 209

def add_pilot_cli(cli_options)
  (new_package_options, errors) = validate_all_new_package_options(cli_options)
  if errors.empty?
    return new_package_options
  else
    puts '***** ERRORS *****'
    puts errors.join "\n"
    puts '*****'
    raise ArgumentError, 'Errors in commandline options, see above.'
  end # if errors empty
end

.default_options_from_package(pkg, dft_opts = OpenStruct.new) ⇒ OpenStruct

Get default values from an existing d3 package, and use them to override the defined defaults

Parameters:

  • pkg (D3::Package)

    the package from which to extract the values

  • dft_opts (OpenStruct) (defaults to: OpenStruct.new)

    an Ostruct with the defined defaults already populated

Returns:

  • (OpenStruct)

    an ostruct with the default values



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/d3/admin/add.rb', line 350

def default_options_from_package(pkg, dft_opts = OpenStruct.new)
  # populate the opts from the pkg, if the pkg has a value
  D3::Admin::OPTIONS.keys.each do |opt|
    next unless pkg.respond_to?(opt)
    pkg_val = pkg.send(opt)
    next if pkg_val.nil?
    dft_opts[opt] = pkg_val
  end # do opt

  # bump the revision by 1
  dft_opts.revision += 1

  # never use the older package name or filename
  dft_opts.package_name = nil
  dft_opts.filename = nil

  # grab the first pkg-id if it exists
  # we might use it to if making a new pkg
  if pkg.apple_receipt_data && pkg.apple_receipt_data[0] && pkg.apple_receipt_data[0][:apple_pkg_id]
    dft_opts.pkg_identifier = pkg.apple_receipt_data[0][:apple_pkg_id]
  end

  dft_opts
end

.get_default_options(basename, no_inherit) ⇒ OpenStruct

Figure out the default values for all options for creating a new package

Parameters:

  • basename (String)

    the basename for the new pkg

Returns:

  • (OpenStruct)

    an ostruct with the default values



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/d3/admin/add.rb', line 289

def get_default_options(basename, no_inherit)

  dft_opts = OpenStruct.new

  # first populate the opts from the defined defaults for anything that's still nil
  D3::Admin::OPTIONS.each { |opt, settings| dft_opts[opt] = settings[:default] }

  dft_opts.basename = basename

  # next we grab stuff from the most recent pkg with this baseneme
  # and apply anything there, if a basename was given
  unless no_inherit
    if D3::Package.all_basenames.include? basename
      prev_pkg = D3::Package.most_recent_package_for(basename)
      if prev_pkg
        dft_opts = self.default_options_from_package(prev_pkg, dft_opts)
        dft_opts.inherited_from = prev_pkg.edition
      end
    end
  end #  if basename

  # edition if available
  if dft_opts.version && dft_opts.revision
    dft_opts.edition = "#{dft_opts.basename}-#{dft_opts.version}-#{dft_opts.revision}"
  else
    dft_opts.edition = nil
  end

  # and if we have an edition, we have default pkg and file names
  if dft_opts.edition
    dft_opts.package_name = "#{dft_opts.edition}"
    dft_opts.filename = "#{dft_opts.edition}.#{dft_opts.package_build_type}"
  end

  # then: any values stored in the admin prefs are applied...
  # use the workspace from the prefs if it exists
  dft_opts.workspace = D3::Admin::Prefs.prefs[:workspace]
  dft_opts.workspace ||= D3::Admin::DFT_WORKSPACE

  # use the pkg_identifier_prefix from the prefs
  pfx = D3::Admin::Prefs.prefs[:apple_pkg_id_prefix]
  pfx ||= D3::Admin::DFT_PKG_ID_PREFIX
  dft_opts.pkg_identifier ||= "#{pfx}.#{basename}".gsub(/\.+/, '.')

  # We now have our defaults for a new pkg
  dft_opts.signing_identity = D3::Admin::Prefs.prefs[:signing_identity] || nil
  dft_opts.signing_options = D3::Admin::Prefs.prefs[:signing_options] || nil

  dft_opts
end

.get_package_type_from_source(source_path) ⇒ Symbol

Given a source_path, check if its a dmg, or .pkg Assumes the source_path has been checked and is valid.

Parameters:

  • source_path (Pathname)

    the path to check

Returns:

  • (Symbol)

    :dmg, :pkg, or :root_folder



383
384
385
386
387
388
389
390
391
# File 'lib/d3/admin/add.rb', line 383

def get_package_type_from_source (source_path)
  if source_path.to_s.end_with? '.dmg'
    :dmg
  elsif source_path.to_s =~ /\.m?pkg$/
    :pkg
  else
    :root_folder
  end # if .source_path.to_s.end_with? '.dmg'
end

.loop_thru_add_walkthru(options) ⇒ OpenStruct

Continuously loop through displaying the add-package menu, and getting new values, until the user types ‘x’.

Parameters:

  • options (OpenStruct)

    the starting values to offer the user

Returns:

  • (OpenStruct)

    the new_package_options with validated data



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/d3/admin/add.rb', line 75

def loop_thru_add_walkthru(options)
  inherited_line = options.inherited_from ? "with values inherited from '#{options.inherited_from}'" : 'with global default values'

  choice = nil

  # loop until the user types an x
  while choice != 'x'
    walkthru_menu_header = <<-END_HEADER
------------------------------------
Adding pilot d3 package '#{options.edition || options.basename}'
#{inherited_line}
------------------------------------
END_HEADER
    menu_options = NEW_PKG_OPTIONS
    if options.source_path && get_package_type_from_source(options.source_path) == :root_folder
      menu_options += BUILD_OPTIONS
      menu_options += PKG_OPTIONS if options.package_build_type == :pkg
    end

    choice = D3::Admin::Interactive.get_menu_choice(walkthru_menu_header, walkthru_menu_lines(menu_options, options))

    if choice == 'x'
      (options, errors) = validate_all_new_package_options options
      if errors.empty?
        break # while
      else
        puts '***** ERRORS *****'
        puts errors.join "\n"
        puts '*****'
        puts 'Type return to continue, ^c to exit'
        gets
        choice = 'not x'
      end # if errors empty
    else

      # the number they chose becomes an option key like :reboot
      chosen_opt = menu_options[choice]

      # Here the value we're actually editing
      current_opt_value = options[chosen_opt]

      # if we're editing version or revision, and the current pkg or filenames are
      # based on them then make a note to update the names  when we get the new values
      if chosen_opt == :basename || chosen_opt == :version || chosen_opt == :revision
        update_edition = true
        update_pkg_name = options.package_name.start_with? options.edition
        update_filename = options.filename.start_with? options.edition
      else
        update_edition = false
        update_pkg_name = false
        update_filename = false
      end

      # if editing the source or the buildtype, we might have to update the
      # names as well
      if chosen_opt == :source_path || chosen_opt == :package_build_type
        update_pkg_name = options.package_name =~ /\.(m?pkg|dmg)$/
        update_filename = true
      end

      # prompt for a new value and put it in place
      options[chosen_opt] = D3::Admin::Interactive.get_value(chosen_opt, current_opt_value, nil)

      # if we changed the version, reset the revision to 1 and update values as needed
      if chosen_opt == :version && options[chosen_opt] != current_opt_value
        options[:revision] = 1
        update_edition = true
        update_pkg_name = options.package_name.start_with? options.edition
        update_filename = options.filename.start_with? options.edition
      end

      # if we edited the version or revision, we might need to update names and edition
      options.edition = "#{options.basename}-#{options.version}-#{options.revision}" if update_edition
      options.package_name = "#{options.edition}.#{options.package_build_type}" if update_pkg_name
      options.filename = "#{options.edition}.#{options.package_build_type}" if update_filename

      # if we edited the source path, we might need to update the names and the pkg build type
      if chosen_opt == :source_path && options.source_path.extname =~ /\.(m?pkg|dmg)$/
        options.package_name.sub(/\.(m?pkg|dmg)$/, options.source_path.extname)
        options.filename.sub(/\.(m?pkg|dmg)$/, options.source_path.extname)
        options.package_build_type = options.source_path.extname == '.dmg' ? :dmg : :pkg
      end

      # ditto if we edited the package_build_type
      if chosen_opt == :package_build_type
        options.package_name.sub(/\.(m?pkg|dmg)$/, options.package_build_type.to_s)
        options.filename.sub(/\.(m?pkg|dmg)$/, options.package_build_type.to_s)
      end

    end # if choice == x
  end # while choice not x

  # return the options
  options
end

.validate_all_new_package_options(options_from_user) ⇒ Array<OpenStruct, Array>

Validate all possible options for making a new pkg.

This is used for both walkthru’s and cli-specified options. Even though the walkthru validates options as they are entered, some need to be checked after all have been provided, e.g. possibly overlapping groups, edition conflicts, etc.

Parameters:

  • options_from_user (OpenStruct)

    the collection of all options to check as a group. either from a walkthru or the command line

Returns:

  • (Array<OpenStruct, Array>)

    An array with two items: the possibly-validated options and an array of error messages, which should be empty if all options are valid.



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/d3/admin/add.rb', line 234

def validate_all_new_package_options(options_from_user)
  # what do we need to check
  opts_to_check = NEW_PKG_OPTIONS
  if options_from_user.source_path && get_package_type_from_source(options_from_user.source_path) == :root_folder
    opts_to_check += BUILD_OPTIONS
    opts_to_check += PKG_OPTIONS if options_from_user.package_build_type == :pkg
  end

  # gather the errors in here to be reported all at once
  errors = []

  # basic checks
  opts_to_check.each do |opt|
    puts "checking #{opt}: #{options_from_user[opt]}" if D3::Admin.debug
    if options_from_user[opt] == D3::Admin::DFT_REQUIRED
      errors << "Missing required value for #{D3::Admin::OPTIONS[opt][:label]}"
      next
    end

    (valid, validated) = D3::Admin::Validate.validate(options_from_user[opt], D3::Admin::OPTIONS[opt][:validate])
    if valid
      options_from_user[opt] = validated
    else
      errors << validated
    end
  end # opts_to_check.each

  # now check the edition
  new_edition = "#{options_from_user.basename}-#{options_from_user.version}-#{options_from_user.revision}"
  if D3::Package::Validate.edition_exist? new_edition
    errors << "A package with edition '#{new_edition}' already exists in d3."
  else
    options_from_user.edition = new_edition
  end

  # group overlaps
  begin
    D3::Package::Validate.validate_non_overlapping_groups options_from_user[:auto_groups], options_from_user[:excluded_groups]
  rescue JSS::InvalidDataError
    errors << "Auto and Excluded group-lists can't contain groups in common."
  end

  # expiration path if expiration
  if options_from_user[:expiration] > 0
    errors << 'expiration path cannot be empty if expiration is > 0 .' unless options_from_user[:expiration_paths]
  end
  [options_from_user, errors]
end

.walkthru_menu_lines(menu_options, current_options) ⇒ Array<String>

Regenerate the walkthru menu lines

Parameters:

  • menu_options (Array<Symbol>)

    the option keys that will be in the menu

  • current_options (OpenStruct)

    the current option values

Returns:

  • (Array<String>)

    the lines of the walkthru menu



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/d3/admin/add.rb', line 180

def walkthru_menu_lines(menu_options, current_options)
  display_lines = []

  menu_options.each_index do |i|
    # the option symbol, like :reboot
    # opt = NEW_PKG_OPTIONS[i]
    opt = menu_options[i]
    opt_def = D3::Admin::OPTIONS[opt]
    label = opt_def[:label]
    value = current_options[opt]
    converter = opt_def[:display_conversion]
    value_display = converter ? converter.call(value) : value
    display_lines[i] = "#{label}: #{value_display}"
  end
  display_lines
end

Instance Method Details

#add_new_package(new_package_options) ⇒ Integer

Add the new pkg to d3

Parameters:

  • new_package_options (OpenStruct)

    the verified options for the new package.

Returns:

  • (Integer)

    the JSS id of the new package



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/d3/admin/add.rb', line 399

def add_new_package(new_package_options)
  # new_package_options should now have all the validated data we need to make a new pkg
  new_pilot = D3::Package.make(
    basename: new_package_options.basename,
    name: new_package_options.package_name,
    version: new_package_options.version,
    revision: new_package_options.revision,
    filename: new_package_options.filename,
    admin: ENV['USER']
  )

  D3::Admin::Add::NEW_PKG_OPTIONS.each do |opt|
    setter = "#{opt}=".to_sym
    new_pilot.send setter, new_package_options.send(opt) if new_pilot.respond_to? setter
  end

  # do we need to build the pkg?
  unless new_package_options.source_path.extname =~ /\.(m?pkg|dmg)$/
    if new_package_options.package_build_type == :pkg
      puts 'Building .pkg...'

      new_pkg_path = JSS::Composer.mk_pkg(
        new_package_options.package_name,
        new_package_options.version,
        new_package_options.source_path,
        pkg_id: new_package_options.pkg_identifier,
        out_dir: new_package_options.workspace,
        preserve_ownership: new_package_options.pkg_preserve_owners,
        signing_identity: new_package_options.signing_identity,
        signing_options: new_package_options.signing_options
      )

      new_package_options.source_path = new_pkg_path

    elsif new_package_options.package_build_type == :dmg
      puts 'Building .dmg...'
      new_pkg_path = JSS::Composer.mk_dmg(
        new_package_options.package_name,
        new_package_options.source_path,
        new_package_options.workspace
      )

      new_package_options.source_path = new_pkg_path
    end # if new_package_options.package_build_type
  end # unless

  # save to d3
  puts "Saving new pilot #{new_package_options.edition} to the server..."

  new_pkg_id = new_pilot.save

  puts 'Indexing...'
  # make the index - all d3 pkgs are indexed in the JSS
  new_pilot.mk_index local_filepath: new_package_options.source_path

  # upload to dist point
  # if its a .pkg, the apple rcpt data is updated during the upload
  puts 'Uploading to the Master Distribution Point...'
  new_pilot.upload_master_file new_package_options.source_path, D3::Admin::Auth.rw_credentials(:dist)[:password]

  new_pkg_id
end

#add_pilot_cli(cli_options) ⇒ OpenStruct

Validate commandline options when adding a package without walkthru using defaults as needed. Some values will be prompted for if needed, since they are required and can’t use defaults.

Parameters:

  • cli_options (OpenStruct)

    the values from the command line

  • new_package_options (OpenStruct)

    the repository for the validated data

Returns:

  • (OpenStruct)

    the new_package_options with validated data



209
210
211
212
213
214
215
216
217
218
219
# File 'lib/d3/admin/add.rb', line 209

def add_pilot_cli(cli_options)
  (new_package_options, errors) = validate_all_new_package_options(cli_options)
  if errors.empty?
    return new_package_options
  else
    puts '***** ERRORS *****'
    puts errors.join "\n"
    puts '*****'
    raise ArgumentError, 'Errors in commandline options, see above.'
  end # if errors empty
end

#default_options_from_package(pkg, dft_opts = OpenStruct.new) ⇒ OpenStruct

Get default values from an existing d3 package, and use them to override the defined defaults

Parameters:

  • pkg (D3::Package)

    the package from which to extract the values

  • dft_opts (OpenStruct) (defaults to: OpenStruct.new)

    an Ostruct with the defined defaults already populated

Returns:

  • (OpenStruct)

    an ostruct with the default values



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/d3/admin/add.rb', line 350

def default_options_from_package(pkg, dft_opts = OpenStruct.new)
  # populate the opts from the pkg, if the pkg has a value
  D3::Admin::OPTIONS.keys.each do |opt|
    next unless pkg.respond_to?(opt)
    pkg_val = pkg.send(opt)
    next if pkg_val.nil?
    dft_opts[opt] = pkg_val
  end # do opt

  # bump the revision by 1
  dft_opts.revision += 1

  # never use the older package name or filename
  dft_opts.package_name = nil
  dft_opts.filename = nil

  # grab the first pkg-id if it exists
  # we might use it to if making a new pkg
  if pkg.apple_receipt_data && pkg.apple_receipt_data[0] && pkg.apple_receipt_data[0][:apple_pkg_id]
    dft_opts.pkg_identifier = pkg.apple_receipt_data[0][:apple_pkg_id]
  end

  dft_opts
end

#get_default_options(basename, no_inherit) ⇒ OpenStruct

Figure out the default values for all options for creating a new package

Parameters:

  • basename (String)

    the basename for the new pkg

Returns:

  • (OpenStruct)

    an ostruct with the default values



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/d3/admin/add.rb', line 289

def get_default_options(basename, no_inherit)

  dft_opts = OpenStruct.new

  # first populate the opts from the defined defaults for anything that's still nil
  D3::Admin::OPTIONS.each { |opt, settings| dft_opts[opt] = settings[:default] }

  dft_opts.basename = basename

  # next we grab stuff from the most recent pkg with this baseneme
  # and apply anything there, if a basename was given
  unless no_inherit
    if D3::Package.all_basenames.include? basename
      prev_pkg = D3::Package.most_recent_package_for(basename)
      if prev_pkg
        dft_opts = self.default_options_from_package(prev_pkg, dft_opts)
        dft_opts.inherited_from = prev_pkg.edition
      end
    end
  end #  if basename

  # edition if available
  if dft_opts.version && dft_opts.revision
    dft_opts.edition = "#{dft_opts.basename}-#{dft_opts.version}-#{dft_opts.revision}"
  else
    dft_opts.edition = nil
  end

  # and if we have an edition, we have default pkg and file names
  if dft_opts.edition
    dft_opts.package_name = "#{dft_opts.edition}"
    dft_opts.filename = "#{dft_opts.edition}.#{dft_opts.package_build_type}"
  end

  # then: any values stored in the admin prefs are applied...
  # use the workspace from the prefs if it exists
  dft_opts.workspace = D3::Admin::Prefs.prefs[:workspace]
  dft_opts.workspace ||= D3::Admin::DFT_WORKSPACE

  # use the pkg_identifier_prefix from the prefs
  pfx = D3::Admin::Prefs.prefs[:apple_pkg_id_prefix]
  pfx ||= D3::Admin::DFT_PKG_ID_PREFIX
  dft_opts.pkg_identifier ||= "#{pfx}.#{basename}".gsub(/\.+/, '.')

  # We now have our defaults for a new pkg
  dft_opts.signing_identity = D3::Admin::Prefs.prefs[:signing_identity] || nil
  dft_opts.signing_options = D3::Admin::Prefs.prefs[:signing_options] || nil

  dft_opts
end

#get_package_type_from_source(source_path) ⇒ Symbol

Given a source_path, check if its a dmg, or .pkg Assumes the source_path has been checked and is valid.

Parameters:

  • source_path (Pathname)

    the path to check

Returns:

  • (Symbol)

    :dmg, :pkg, or :root_folder



383
384
385
386
387
388
389
390
391
# File 'lib/d3/admin/add.rb', line 383

def get_package_type_from_source (source_path)
  if source_path.to_s.end_with? '.dmg'
    :dmg
  elsif source_path.to_s =~ /\.m?pkg$/
    :pkg
  else
    :root_folder
  end # if .source_path.to_s.end_with? '.dmg'
end

#loop_thru_add_walkthru(options) ⇒ OpenStruct

Continuously loop through displaying the add-package menu, and getting new values, until the user types ‘x’.

Parameters:

  • options (OpenStruct)

    the starting values to offer the user

Returns:

  • (OpenStruct)

    the new_package_options with validated data



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/d3/admin/add.rb', line 75

def loop_thru_add_walkthru(options)
  inherited_line = options.inherited_from ? "with values inherited from '#{options.inherited_from}'" : 'with global default values'

  choice = nil

  # loop until the user types an x
  while choice != 'x'
    walkthru_menu_header = <<-END_HEADER
------------------------------------
Adding pilot d3 package '#{options.edition || options.basename}'
#{inherited_line}
------------------------------------
END_HEADER
    menu_options = NEW_PKG_OPTIONS
    if options.source_path && get_package_type_from_source(options.source_path) == :root_folder
      menu_options += BUILD_OPTIONS
      menu_options += PKG_OPTIONS if options.package_build_type == :pkg
    end

    choice = D3::Admin::Interactive.get_menu_choice(walkthru_menu_header, walkthru_menu_lines(menu_options, options))

    if choice == 'x'
      (options, errors) = validate_all_new_package_options options
      if errors.empty?
        break # while
      else
        puts '***** ERRORS *****'
        puts errors.join "\n"
        puts '*****'
        puts 'Type return to continue, ^c to exit'
        gets
        choice = 'not x'
      end # if errors empty
    else

      # the number they chose becomes an option key like :reboot
      chosen_opt = menu_options[choice]

      # Here the value we're actually editing
      current_opt_value = options[chosen_opt]

      # if we're editing version or revision, and the current pkg or filenames are
      # based on them then make a note to update the names  when we get the new values
      if chosen_opt == :basename || chosen_opt == :version || chosen_opt == :revision
        update_edition = true
        update_pkg_name = options.package_name.start_with? options.edition
        update_filename = options.filename.start_with? options.edition
      else
        update_edition = false
        update_pkg_name = false
        update_filename = false
      end

      # if editing the source or the buildtype, we might have to update the
      # names as well
      if chosen_opt == :source_path || chosen_opt == :package_build_type
        update_pkg_name = options.package_name =~ /\.(m?pkg|dmg)$/
        update_filename = true
      end

      # prompt for a new value and put it in place
      options[chosen_opt] = D3::Admin::Interactive.get_value(chosen_opt, current_opt_value, nil)

      # if we changed the version, reset the revision to 1 and update values as needed
      if chosen_opt == :version && options[chosen_opt] != current_opt_value
        options[:revision] = 1
        update_edition = true
        update_pkg_name = options.package_name.start_with? options.edition
        update_filename = options.filename.start_with? options.edition
      end

      # if we edited the version or revision, we might need to update names and edition
      options.edition = "#{options.basename}-#{options.version}-#{options.revision}" if update_edition
      options.package_name = "#{options.edition}.#{options.package_build_type}" if update_pkg_name
      options.filename = "#{options.edition}.#{options.package_build_type}" if update_filename

      # if we edited the source path, we might need to update the names and the pkg build type
      if chosen_opt == :source_path && options.source_path.extname =~ /\.(m?pkg|dmg)$/
        options.package_name.sub(/\.(m?pkg|dmg)$/, options.source_path.extname)
        options.filename.sub(/\.(m?pkg|dmg)$/, options.source_path.extname)
        options.package_build_type = options.source_path.extname == '.dmg' ? :dmg : :pkg
      end

      # ditto if we edited the package_build_type
      if chosen_opt == :package_build_type
        options.package_name.sub(/\.(m?pkg|dmg)$/, options.package_build_type.to_s)
        options.filename.sub(/\.(m?pkg|dmg)$/, options.package_build_type.to_s)
      end

    end # if choice == x
  end # while choice not x

  # return the options
  options
end

#validate_all_new_package_options(options_from_user) ⇒ Array<OpenStruct, Array>

Validate all possible options for making a new pkg.

This is used for both walkthru’s and cli-specified options. Even though the walkthru validates options as they are entered, some need to be checked after all have been provided, e.g. possibly overlapping groups, edition conflicts, etc.

Parameters:

  • options_from_user (OpenStruct)

    the collection of all options to check as a group. either from a walkthru or the command line

Returns:

  • (Array<OpenStruct, Array>)

    An array with two items: the possibly-validated options and an array of error messages, which should be empty if all options are valid.



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/d3/admin/add.rb', line 234

def validate_all_new_package_options(options_from_user)
  # what do we need to check
  opts_to_check = NEW_PKG_OPTIONS
  if options_from_user.source_path && get_package_type_from_source(options_from_user.source_path) == :root_folder
    opts_to_check += BUILD_OPTIONS
    opts_to_check += PKG_OPTIONS if options_from_user.package_build_type == :pkg
  end

  # gather the errors in here to be reported all at once
  errors = []

  # basic checks
  opts_to_check.each do |opt|
    puts "checking #{opt}: #{options_from_user[opt]}" if D3::Admin.debug
    if options_from_user[opt] == D3::Admin::DFT_REQUIRED
      errors << "Missing required value for #{D3::Admin::OPTIONS[opt][:label]}"
      next
    end

    (valid, validated) = D3::Admin::Validate.validate(options_from_user[opt], D3::Admin::OPTIONS[opt][:validate])
    if valid
      options_from_user[opt] = validated
    else
      errors << validated
    end
  end # opts_to_check.each

  # now check the edition
  new_edition = "#{options_from_user.basename}-#{options_from_user.version}-#{options_from_user.revision}"
  if D3::Package::Validate.edition_exist? new_edition
    errors << "A package with edition '#{new_edition}' already exists in d3."
  else
    options_from_user.edition = new_edition
  end

  # group overlaps
  begin
    D3::Package::Validate.validate_non_overlapping_groups options_from_user[:auto_groups], options_from_user[:excluded_groups]
  rescue JSS::InvalidDataError
    errors << "Auto and Excluded group-lists can't contain groups in common."
  end

  # expiration path if expiration
  if options_from_user[:expiration] > 0
    errors << 'expiration path cannot be empty if expiration is > 0 .' unless options_from_user[:expiration_paths]
  end
  [options_from_user, errors]
end

#walkthru_menu_lines(menu_options, current_options) ⇒ Array<String>

Regenerate the walkthru menu lines

Parameters:

  • menu_options (Array<Symbol>)

    the option keys that will be in the menu

  • current_options (OpenStruct)

    the current option values

Returns:

  • (Array<String>)

    the lines of the walkthru menu



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/d3/admin/add.rb', line 180

def walkthru_menu_lines(menu_options, current_options)
  display_lines = []

  menu_options.each_index do |i|
    # the option symbol, like :reboot
    # opt = NEW_PKG_OPTIONS[i]
    opt = menu_options[i]
    opt_def = D3::Admin::OPTIONS[opt]
    label = opt_def[:label]
    value = current_options[opt]
    converter = opt_def[:display_conversion]
    value_display = converter ? converter.call(value) : value
    display_lines[i] = "#{label}: #{value_display}"
  end
  display_lines
end