xolo-admin 2.0.3 → 2.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8637742b96fa6e20b3d2d781f6c3e6d7c19cf0fbaa9bd4d39625cf9738f06845
4
- data.tar.gz: af15092c906bd833441229bccc8d2944b99dbc035fc29a6ddcb3985979ab4f77
3
+ metadata.gz: 5a18c9e9384018067cb93f0f5a1a158c4934841d05661b66911d8d95a2b4985e
4
+ data.tar.gz: 3fa6cf5701caf06ccce8f8e4b7226a55cd4fb0d61451764b15f9e383c9643034
5
5
  SHA512:
6
- metadata.gz: d73169bf141bf0d98d2b05c586673e073d2d43785418bbfe57c34a4876637f1e6fca2e5b91f3739ec35d563bc4d254e57f58d6e61d629814ed5f47bfe750e2d9
7
- data.tar.gz: a7edfa0ddc811772c3dc12c3621bdf200b01630f4c0605190ee8c07e4abbb7065bda17be11cf0dd3857dfb5b302a60641225a4cbd2397a5fbae0de1758f9dba4
6
+ metadata.gz: 0b23b00c96d3c21971507c9b7e9f669ef37f09912d9332de34c9cb2d7cb02f38eec2dc8bdb956ac4db3cecfcb13271243a51a6c8bf77b42e4e000315d50225e3
7
+ data.tar.gz: 83023a43c98543cffa21059763f1759be7723c1da99b5174e4b57a4e3000543345ba2323c095b71cfd56fe40d87b66628f26ade13f0348c0678705b39e442c5a
data/data/client/xolo CHANGED
@@ -159,10 +159,24 @@ Options:
159
159
  -V, --version: Show the version of xolo.
160
160
 
161
161
  Commands:
162
- install, i <title>[=<version>] [<title2>[=<version2>] ...]
163
- Install a title, or specific version thereof (e.g. a version currently in pilot)
162
+ install, i <title> [<version>] [...]
163
+ install, i <title>[=<version>] [...]
164
+
165
+ Install one or more titles, or specific versions thereof (e.g. currently in pilot)
164
166
  If no version is specified, the currently released version will be installed.
165
167
 
168
+ To install more than one item at a time, use 'title=version' if you need a non-
169
+ released version of the title.
170
+
171
+ So 'install <title1> <title2>' installs the current release of each title, and
172
+ 'install <title1> <version1>' installs version1 of title1, but
173
+ 'install <title1>=<version1> <title2>' installs version1 of title1 and the
174
+ current release of title2. You can install as many items as needed this way.
175
+
176
+ In short, you can always use '=' between titles and versions, but you can
177
+ only omit it when the there are only two arguments and they are a title
178
+ and a version.
179
+
166
180
  uninstall, u <title> [<title2> ...]
167
181
  Uninstall a title, if possible. Not all titles are uninstallable via xolo.
168
182
 
@@ -255,8 +269,24 @@ function parse_cli() {
255
269
  command=$1
256
270
  [[ ${#@} -gt 0 ]] && shift
257
271
 
272
+ # the remaining positional parameters as an array called targets
273
+ # It'll either be exactly 1 item: title
274
+ # or exactly 2 items containing no '=' : title, version
275
+ # or exactly 2 items containing no '=' : title, title
276
+ # or any number if items in the form: title[=version] where the =version is optional.
258
277
  targets=("${(@)@}")
259
278
 
279
+ # if targets has exactly 2 items, and neither of them contains
280
+ # '=' and the second one isn't an existing title then treat
281
+ # them as the title and a version and reset 'targets' to be 'title=version'
282
+ if [[ "$#targets" == 2 ]] \
283
+ && [[ "$targets[1]" != *"$TITLE_VERSION_SEPARATOR"* ]] \
284
+ && [[ "$targets[2]" != *"$TITLE_VERSION_SEPARATOR"* ]] \
285
+ && ! echo "$(all_xolo_titles)" | grep -qE "^${targets[2]}$" ; then
286
+
287
+ targets=("$targets[1]=$targets[2]")
288
+ fi
289
+
260
290
  debug "Parsed command line:"
261
291
  debug "..show_help is: $show_help"
262
292
  debug "..be_verbose is: $be_verbose"
@@ -275,6 +305,9 @@ function parse_cli() {
275
305
  #################################
276
306
  function parse_title_and_version() {
277
307
  local arg=$1
308
+ unset title_is_valid
309
+ unset version_is_valid
310
+
278
311
  debug "Parsing title and version from arg: '$arg'"
279
312
 
280
313
  if [[ "$arg" == *"$TITLE_VERSION_SEPARATOR"* ]] ; then
@@ -786,9 +819,13 @@ function refresh_client_data() {
786
819
  # validate that the title exists
787
820
  ###############################
788
821
  function validate_title() {
822
+
789
823
  # if we've already validated the title, we're done
790
824
  [[ -n "$title_is_valid" ]] && return
791
825
 
826
+ # use passed arg if needed
827
+ [[ -z "$title" ]] && title=$1
828
+
792
829
  debug "Validating title: $title"
793
830
 
794
831
  # die if no title given
@@ -823,7 +860,7 @@ function validate_version() {
823
860
  all_versions=$(versions_for_title $title)
824
861
  debug "All versions for title $title:\n$all_versions"
825
862
  # [[ $all_versions =~ (^|\n)$version($|\n) ]] || die "No such version: $version"
826
- echo "$all_versions" | grep -q "^$version$" || die "No such version: $version"
863
+ echo "$all_versions" | grep -q "^$version$" || die "No such version '$version' for title '$title'"
827
864
  version_is_valid=1
828
865
  }
829
866
 
@@ -398,6 +398,7 @@ module Xolo
398
398
  opt opt_key, desc, short: deets[:cli], type: type, required: required, multi: deets[:multi]
399
399
  end # opts_to_use.each
400
400
  end # if cmd_opts
401
+ conflicts :subscribed, :managed if cmd == Xolo::Admin::Options::LIST_TITLES_CMD
401
402
  end # Optimist.options
402
403
  end
403
404
 
@@ -19,8 +19,8 @@ module Xolo
19
19
  ##############################
20
20
  ##############################
21
21
 
22
- TIMEOUT = 300
23
- UPLOAD_TIMEOUT = 1800
22
+ TIMEOUT = 1800
23
+ UPLOAD_TIMEOUT = 3600
24
24
  OPEN_TIMEOUT = 10
25
25
 
26
26
  PING_ROUTE = '/ping'
@@ -151,8 +151,18 @@ module Xolo
151
151
 
152
152
  # this proc every time we get a chunk, just print it to stdout
153
153
  # and make note if any of them conain an error
154
+ # every 10 secs with no real data, we get ':heartbeat'
155
+ # which we use to display dots on a line as we wait for more data.
156
+ need_newline = nil
157
+
154
158
  streaming_proc = proc do |chunk, _size, _env|
155
- puts chunk
159
+ if chunk == ':heartbeat'
160
+ print '.'
161
+ need_newline = "\n"
162
+ else
163
+ puts "#{need_newline}#{chunk}"
164
+ need_newline = nil
165
+ end
156
166
  @streaming_error ||= chunk.include? STREAMING_OUTPUT_ERROR
157
167
  end
158
168
 
@@ -21,7 +21,7 @@ module Xolo
21
21
  'vim (vi)' => '/usr/bin/vim',
22
22
  'mg (emacs)' => '/usr/bin/mg',
23
23
  'pico (nano)' => '/usr/bin/pico'
24
- }
24
+ }.freeze
25
25
 
26
26
  MULTILINE_HEADER_SEPARATOR = "\nDO NOT EDIT anything above the next line:\n=================================="
27
27
 
@@ -215,6 +215,15 @@ module Xolo
215
215
  'N/A until Patch Source is set'
216
216
  end
217
217
 
218
+ # @return [String, nil] If a string, a reason why the given menu item is not available now.
219
+ # If nil, the menu item is displayed normally.
220
+ ##############################
221
+ def auto_release_delay_na
222
+ return if walkthru_cmd_opts[:subscribed] && !walkthru_cmd_opts[:autopkg_recipe].pix_empty?
223
+
224
+ 'N/A unless title is Subscribed and uses an AutoPkg Recipe'
225
+ end
226
+
218
227
  # @return [String, nil] If a string, a reason why the given menu item is not available now.
219
228
  # If nil, the menu item is displayed normally.
220
229
  ##############################
@@ -251,7 +260,7 @@ module Xolo
251
260
  # If nil, the menu item is displayed normally.
252
261
  ##############################
253
262
  def expiration_paths_na
254
- 'N/A unless expiration is > 0' unless walkthru_cmd_opts[:expiration].to_i.positive?
263
+ 'N/A unless expiration days is > 0' unless walkthru_cmd_opts[:expiration].to_i.positive?
255
264
  end
256
265
 
257
266
  # @return [String, nil] If a string, a reason why the given menu item is not available now.
@@ -182,7 +182,7 @@ module Xolo
182
182
  HELP_CMD = 'help'
183
183
 
184
184
  # server-admin commands
185
- SERVER_CLEANUP_CMD = 'run-server-cleanup'
185
+ SERVER_MAINT_CMD = 'run-server-maint'
186
186
  UPDATE_CLIENT_DATA_CMD = 'update-client-data'
187
187
  ROTATE_SERVER_LOGS_CMD = 'rotate-server-logs'
188
188
  SET_SERVER_LOG_LEVEL_CMD = 'set-server-log-level'
@@ -198,6 +198,74 @@ module Xolo
198
198
  TARGET_TITLE_PLACEHOLDER = Xolo::Admin::Title::TARGET_TITLE_PLACEHOLDER
199
199
  TARGET_VERSION_PLACEHOLDER = 'TARGET_VERSION_PH'
200
200
 
201
+ LIST_TITLES_OPTIONS = {
202
+ subscribed: {
203
+ label: 'Subscribed Only',
204
+ cli: :s,
205
+ type: :boolean,
206
+ validate: :validate_boolean,
207
+ default: false,
208
+ desc: <<~ENDDESC
209
+ Show only 'subscribed' titles. Can be combined with all other options except --managed.
210
+ ENDDESC
211
+ },
212
+
213
+ managed: {
214
+ label: 'Managed Only',
215
+ cli: :m,
216
+ type: :boolean,
217
+ validate: :validate_boolean,
218
+ default: false,
219
+ desc: <<~ENDDESC
220
+ Show only 'managed' titles. Can be combined with all other options excepted --subscribed.
221
+ ENDDESC
222
+ },
223
+
224
+ autopkg: {
225
+ label: 'AutoPkg Only',
226
+ cli: :a,
227
+ type: :boolean,
228
+ validate: :validate_boolean,
229
+ default: false,
230
+ desc: <<~ENDDESC
231
+ Show only titles configured for AutoPkg. Can be combined with all other options.
232
+ ENDDESC
233
+ },
234
+
235
+ pilots: {
236
+ label: 'Pilots Only',
237
+ cli: :p,
238
+ type: :boolean,
239
+ validate: :validate_boolean,
240
+ default: false,
241
+ desc: <<~ENDDESC
242
+ Show only titles with pending pilot (un-released) versions. Use 'list-versions' to see details for a title. Can be combined with all other options.
243
+ ENDDESC
244
+ },
245
+
246
+ self_service: {
247
+ label: 'Self Service Installs Only',
248
+ cli: :i,
249
+ type: :boolean,
250
+ validate: :validate_boolean,
251
+ default: false,
252
+ desc: <<~ENDDESC
253
+ Show only titles that are available for initial instalation via Self Service. To see those with updates depolyed via Self Service, use --self-service-updates.
254
+ ENDDESC
255
+ },
256
+
257
+ self_service_updates: {
258
+ label: 'Self Service Updates Only',
259
+ cli: :u,
260
+ type: :boolean,
261
+ validate: :validate_boolean,
262
+ default: false,
263
+ desc: <<~ENDDESC
264
+ Show only titles that are available for updates via Self Service. To see those with initial installation via Self Service, use --self-service.
265
+ ENDDESC
266
+ }
267
+ }.freeze
268
+
201
269
  PATCH_REPORT_OPTIONS = {
202
270
  summary: {
203
271
  label: 'Summary Only',
@@ -410,7 +478,7 @@ module Xolo
410
478
  LIST_TITLES_CMD => {
411
479
  desc: 'List all software titles.',
412
480
  display: LIST_TITLES_CMD,
413
- opts: {},
481
+ opts: LIST_TITLES_OPTIONS,
414
482
  arg_banner: :none,
415
483
  process_method: :list_titles,
416
484
  target: :none
@@ -466,6 +534,16 @@ module Xolo
466
534
  all policies and patch policies related to this title and its versions.
467
535
  If a computer doesn't have any version of the title, this will prevent
468
536
  it from being installed via xolo (it will be 'frozen' in that state).
537
+
538
+ Excluding vs Freezing:
539
+ - Excluded Groups (via the --excluded-groups option for titles) are
540
+ how to prevent members from accessing titles in Xolo. They can be
541
+ thought of as 'freezing for groups'
542
+
543
+ - Freezing (via 'xadm freeze...') is how to prevent individual computers
544
+ from accessing titles in Xolo. It can be thought of as 'exclusion for
545
+ individual computers'
546
+
469
547
  ENDLONG
470
548
  display: "#{FREEZE_TITLE_CMD} title [--users] target [target ...] ",
471
549
  opts: FREEZE_THAW_OPTIONS,
@@ -759,21 +837,29 @@ module Xolo
759
837
  process_method: :server_status
760
838
  },
761
839
 
762
- SERVER_CLEANUP_CMD => {
763
- desc: "[Server Admins Only] Run the server's cleanup process now.",
840
+ SERVER_MAINT_CMD => {
841
+ desc: "[Server Admins Only] Run the server's nightly maintenance process now.",
764
842
  long_desc: <<~ENDLONG,
765
843
  Requires server-admin privileges.
766
- Once a version of a title is released, the preveiously released
767
- version is marked as 'deprecated', and any older unreleased versions
768
- are marked as 'skipped'. A nightly task will then delete all skipped
769
- versions from xolo, as well as deprecated versions that have been than
770
- deprecated more than some number of days, as configured configured on
771
- the server. Running this command will do that cleanup now.
844
+ Performs various nightly tasks immediately.
845
+
846
+ These tasks happen automatically between 2-3am:
847
+ - Accepts lingering Title Editor Extension Attributes, if the server is
848
+ configured to do so.
849
+ - Auto-releases appropriate verions of titles configured to do so.
850
+ - Cleans up old versions:
851
+ Once a version of a title is released, the preveiously released
852
+ version is marked as 'deprecated', and any older unreleased versions
853
+ are marked as 'skipped'. The nightly maintenance task will then:
854
+ - delete all skipped versions of the title
855
+ - delete all deprecated versions that have been than deprecated more
856
+ than some number of days, as configured configured on the server.
857
+ - email title owners about pending pilots more than some number of days old.
772
858
  ENDLONG
773
- display: SERVER_CLEANUP_CMD,
859
+ display: SERVER_MAINT_CMD,
774
860
  opts: {},
775
861
  arg_banner: :none,
776
- process_method: :server_cleanup,
862
+ process_method: :server_maint,
777
863
  confirmation: true
778
864
  },
779
865
 
@@ -26,7 +26,7 @@ module Xolo
26
26
  # Routes for server admins
27
27
 
28
28
  SERVER_STATUS_ROUTE = '/maint/state'
29
- SERVER_CLEANUP_ROUTE = '/maint/cleanup'
29
+ SERVER_MAINT_ROUTE = '/maint/maint-start'
30
30
  SERVER_ROTATE_LOGS_ROUTE = '/maint/rotate-logs'
31
31
  SERVER_UPDATE_CLIENT_DATA_ROUTE = '/maint/update-client-data'
32
32
  SERVER_LOG_LEVEL_ROUTE = '/maint/set-log-level'
@@ -145,18 +145,24 @@ module Xolo
145
145
  # @return [String] the string to display for the search result
146
146
  ###################################
147
147
  def title_search_result_str(title, one_line: false)
148
+ title_type = []
149
+ title_type << (title.subscribed? ? 'subscribed' : 'managed')
150
+ title_type << 'autopkg' if title.autopkg_enabled?
151
+ title_display = "#{title.title} (#{title_type.join(',')})"
152
+
148
153
  versions = versions_str(title)
154
+
149
155
  if one_line
150
- [title.title, title.display_name, title.publisher, title.contact_email, versions]
156
+ [title_display, title.display_name, title.publisher, title.contact_email, versions]
151
157
  else
152
158
  titleout = +'#---------------------------------------'
153
- titleout << "\nTitle: #{title.title}"
159
+ titleout << "\nTitle: #{title_display}"
154
160
  titleout << "\nDisplay Name: #{title.display_name}"
155
161
  titleout << "\nPublisher: #{title.publisher}"
156
162
  titleout << "\nApp: #{title.app_name}\nBundleID: #{title.app_bundle_id}" if title.app_name
157
163
  titleout << "\nVersions: #{versions}"
158
- titleout << "\nDescription:"
159
- titleout << "\n#{title.description}"
164
+ titleout << "\nContact: #{title.contact_email}"
165
+ titleout << "\nDescription: #{title.description}"
160
166
  titleout
161
167
  end # json?
162
168
  end
@@ -211,16 +217,38 @@ module Xolo
211
217
  return
212
218
  end
213
219
 
214
- report_title = 'All titles in Xolo'
215
- header = %w[Title Created By SSvc? Released Latest]
220
+ type = opts_to_process.subscribed ? ' subscribed' : nil
221
+ type ||= opts_to_process.managed ? ' managed' : nil
222
+ autopkg = opts_to_process.autopkg ? ' autopkg' : nil
223
+ pilots = opts_to_process.pilots ? ' with unreleased pilots' : nil
224
+
225
+ ssvc =
226
+ if opts_to_process.self_service && opts_to_process.self_service_updates
227
+ ' installed or updated via Self Service'
228
+ elsif opts_to_process.self_service
229
+ ' installed via Self Service'
230
+ elsif opts_to_process.self_service_updates
231
+ ' updated via Self Service'
232
+ end
233
+
234
+ report_title = "All#{type}#{autopkg} titles in Xolo#{pilots}#{ssvc}"
235
+
236
+ titles.select!(&:subscribed?) if opts_to_process.subscribed
237
+ titles.reject!(&:subscribed?) if opts_to_process.managed
238
+ titles.select!(&:autopkg_recipe) if opts_to_process.autopkg
239
+ titles.select!(&:pending_pilots?) if opts_to_process.pilots
240
+ titles.select!(&:self_service?) if opts_to_process.self_service
241
+ titles.select!(&:self_service_updates?) if opts_to_process.self_service_updates
242
+
243
+ header = %w[Title Created By SSvc? Latest Released]
216
244
  data = titles.map do |t|
217
245
  [
218
246
  t.title,
219
247
  t.creation_date.to_date,
220
248
  t.created_by,
221
249
  t.self_service || false,
222
- t.released_version,
223
- t.latest_version
250
+ t.latest_version,
251
+ t.released_version
224
252
  ]
225
253
  end
226
254
  data.sort_by! { |d| d[0].downcase } # sort by title
@@ -230,6 +258,16 @@ module Xolo
230
258
  handle_processing_error e
231
259
  end
232
260
 
261
+ # @param title [Xolo::Admin::Title] The title in question
262
+ # @return [Boolean] Does a given Title object have pilots?
263
+ ###########################
264
+ def pending_pilots?(title)
265
+ return false if title.version_order.pix_empty?
266
+ return false if title.released_version == title.version_order.first
267
+
268
+ true
269
+ end
270
+
233
271
  # Add a title to Xolo
234
272
  #
235
273
  # @return [void]
@@ -487,16 +525,19 @@ module Xolo
487
525
  return
488
526
  end
489
527
 
490
- report_title = "All versions of '#{cli_cmd.title}' in Xolo"
528
+ report_title = "All versions of '#{cli_cmd.title}' in Xolo. ** = No .pkg uploaded"
491
529
  header = %w[Vers Created By Released By Status]
492
530
  data = versions.sort_by(&:creation_date).map do |v|
531
+ no_pkg = v.pkg_to_upload.pix_empty? && v.jamf_pkg_file.pix_empty?
532
+ status = no_pkg ? "#{v.status} **" : v.status
533
+
493
534
  [
494
535
  v.version,
495
536
  v.creation_date.to_date,
496
537
  v.created_by,
497
538
  v.release_date&.to_date,
498
539
  v.released_by,
499
- v.status
540
+ status
500
541
  ]
501
542
  end
502
543
  show_text generate_report(data, header_row: header, title: report_title)
@@ -528,7 +569,7 @@ module Xolo
528
569
  # Upload the pkg, if any?
529
570
  upload_pkg(new_vers) unless title_obj.autopkg_enabled?
530
571
 
531
- speak 'It can take up to 15 minutes for the version to be available via Jamf and Self Service.'
572
+ speak 'It can take up to 15 minutes for the version to be available for use. Please do not release it before then'
532
573
  rescue StandardError => e
533
574
  handle_processing_error e
534
575
  end
@@ -977,14 +1018,14 @@ module Xolo
977
1018
  handle_processing_error e
978
1019
  end
979
1020
 
980
- # kick off server cleanup
1021
+ # kick off server maintenance
981
1022
  #
982
1023
  # @return [void]
983
1024
  ###############################
984
- def server_cleanup
985
- return unless confirmed? 'Run the Xolo Server cleanup process'
1025
+ def server_maint
1026
+ return unless confirmed? 'Run the Xolo Server maintenance process'
986
1027
 
987
- result = server_cnx.post(SERVER_CLEANUP_ROUTE).body
1028
+ result = server_cnx.post(SERVER_MAINT_ROUTE).body
988
1029
  puts result[:result]
989
1030
  rescue StandardError => e
990
1031
  handle_processing_error e
@@ -1150,7 +1191,6 @@ module Xolo
1150
1191
  puts "Saved 'xolo' client tool to '#{dest}'"
1151
1192
  end
1152
1193
 
1153
- # run the cleanup
1154
1194
  # get the /test route to do whatever testing it does.
1155
1195
  # during testing - this will return all kinds of things.
1156
1196
  #
@@ -34,7 +34,7 @@ module Xolo
34
34
  PROGRESS_HISTORY_FILENAME = 'com.pixar.xolo.admin.progress_history.yaml'
35
35
 
36
36
  # prog files on the server last 3 days, add an extra to
37
- # account for timing of daily cleanup on the server.
37
+ # account for timing of daily maint on the server.
38
38
  # if the file is already gone from the server, we'll tell
39
39
  # the user
40
40
  PROGRESS_FILE_LIFETIME = 4 * 24 * 3600
@@ -366,6 +366,26 @@ module Xolo
366
366
  raise_invalid_data_error val, TITLE_ATTRS[:uninstall_script][:invalid_msg]
367
367
  end
368
368
 
369
+ # validate a title auto_release_delay:
370
+ # - a non-negative integer?
371
+ # OR
372
+ # - 'none' to unset the value
373
+ #
374
+ # @param val [Object] The value to validate
375
+ #
376
+ # @return [String, Integer] The valid value
377
+ ###########################
378
+ def validate_auto_release_delay
379
+ return if val == Xolo::NONE
380
+
381
+ if val.pix_integer?
382
+ val = val.to_i
383
+ return val unless val.negative?
384
+ end
385
+
386
+ raise_invalid_data_error val, TITLE_ATTRS[:auto_release_delay][:invalid_msg]
387
+ end
388
+
369
389
  # validate a title uninstall ids:
370
390
  # - an array of package identifiers
371
391
  # OR
@@ -449,6 +469,27 @@ module Xolo
449
469
  raise_invalid_data_error bad_grps, TITLE_ATTRS[:excluded_groups][:invalid_msg]
450
470
  end
451
471
 
472
+ # validate an array of jamf groups to use as targets.
473
+ # 'none' is also acceptable
474
+ #
475
+ #
476
+ # @param val [Array<String>] The value to validate: names of jamf comp.
477
+ # groups, or 'none'
478
+ #
479
+ # @return [Array<String>] The valid value
480
+ ##########################
481
+ def validate_target_groups(val)
482
+ val = [val] unless val.is_a? Array
483
+ return [] if val.include? Xolo::NONE
484
+
485
+ bad_grps = bad_jamf_groups(val)
486
+ return val if bad_grps.empty?
487
+
488
+ bad_grps = "No Such Groups: #{bad_grps.join(Xolo::COMMA_JOIN)}"
489
+
490
+ raise_invalid_data_error bad_grps, TITLE_ATTRS[:target_groups][:invalid_msg]
491
+ end
492
+
452
493
  # validate an array of jamf groups to use as MDM deployment targets.
453
494
  # 'none' is also acceptable
454
495
  #
@@ -914,6 +955,9 @@ module Xolo
914
955
 
915
956
  # if self-service category given, must be in self-service
916
957
  validate_title_consistency_ssvc_needs_category(opts)
958
+
959
+ # if auto_release_delay is non-negative integer, must be subbscribed and autopkg
960
+ validate_title_consistency_auto_release_delay(opts)
917
961
  end # title_consistency(opts)
918
962
 
919
963
  # Complain about using options not meant for the chosen title type
@@ -1040,6 +1084,19 @@ module Xolo
1040
1084
  raise_consistency_error msg
1041
1085
  end
1042
1086
 
1087
+ # if auto_release_delay, must be subbscribed and autopkg
1088
+ #
1089
+ # @param opts [OpenStruct] the current options
1090
+ #
1091
+ # @return [void]
1092
+ #######
1093
+ def validate_title_consistency_auto_release_delay(opts)
1094
+ return if opts[:auto_release_delay].pix_empty? || opts[:auto_release_delay] == Xolo::NONE
1095
+ return if current_title_type(opts) == Xolo::SUBSCRIBED && !opts[:autopkg_recipe].pix_empty?
1096
+
1097
+ raise_consistency_error '--auto-release-delay can only be used with subscribed titles that use autopkg.'
1098
+ end
1099
+
1043
1100
  # If using app_name and bundle id, both must be given
1044
1101
  #
1045
1102
  # @param opts [OpenStruct] the current options
@@ -458,6 +458,31 @@ module Xolo
458
458
  ENDDESC
459
459
  },
460
460
 
461
+ # @!attribute auto_release_delay
462
+ # @return [String] 'none' or an integer in string. How many days before new versions are automatically released?
463
+ # Applies only to subscribed titles with autopkg recipes.
464
+ auto_release_delay: {
465
+ label: 'Automatic Release Delay',
466
+ cli: :A,
467
+ type: :string,
468
+ walkthru_na: :auto_release_delay_na,
469
+ changelog: true,
470
+ invalid_msg: "Must be a non-negative integer, or '#{Xolo::NONE}'.",
471
+ desc: <<~ENDDESC
472
+ Specify how many days after a new version appears before it is automatically released from pilot.
473
+
474
+ This option is only available for titles that are both subscribed, and have an AutoPkg recipe configured. Any other title requires manual release of pilot versions using `xadm release <title> <version>`.
475
+
476
+ Setting this to 7 means that a week after a new version appears as a pilot, it will automatically be released as if someone did `xadm release <title> <version>`. The delay period should be used for piloting the version, either automatically using pilot-groups, or manually. *** USE WITH CAUTION ***
477
+
478
+ Setting it to zero means that new versions will be released at the next nightly auto-release task. *** USE WITH EXTREME CAUTION ***
479
+
480
+ To unset, meaning versions are never auto-released, use '#{Xolo::NONE}'
481
+
482
+ Auto-release happens during the server's nightly maintenance between 2-3am.
483
+ ENDDESC
484
+ },
485
+
461
486
  # @!attribute pilot_groups
462
487
  # @return [Array<String>] Jamf groups that will automatically get new versions installed or
463
488
  # updated when added, for piloting
@@ -474,7 +499,7 @@ module Xolo
474
499
  desc: <<~ENDDESC
475
500
  One or more Jamf Computer Groups whose members will automatically have new versions installed or updated for testing before it is released.
476
501
 
477
- These groups affect both the initial installation of and updates to a title when a version is added, before it is released.
502
+ These groups affect both the initial installation and updates of a title when a version is added, before it is released.
478
503
 
479
504
  These computers will be used for testing not just the software, but the installation process itself. Exclusions win, so computers that are also in an excluded group for the title will not be used as pilots.
480
505
 
@@ -486,6 +511,10 @@ module Xolo
486
511
 
487
512
  When adding a new version, the pilot groups from the previous version will be inherited if you don't specify any. To make the new version have no pilot groups, and fall back to these defined in the title, use '#{Xolo::NONE}'
488
513
 
514
+ Xolos Group Types:
515
+ - Excluded & Target Groups: affect which computers an see a title & its versions.
516
+ - Release & Pilot Groups: affect which computers have the title automatically installed or updated, and when. Note, when a version is released all targeted/non-excluded/non-frozen computers with it installed will get the update.
517
+
489
518
  NOTE: Any non-excluded computer can be used for piloting at any time by manually installing the yet-to-be-released version using `sudo xolo install` or `xadm deploy`. The members of the pilot groups are just the ones that will have it auto-installed.
490
519
  ENDDESC
491
520
  },
@@ -507,7 +536,7 @@ module Xolo
507
536
  readline: :jamf_computer_group_names,
508
537
  invalid_msg: 'Invalid release group(s). Must exist in Jamf and not be excluded.',
509
538
  desc: <<~ENDDESC
510
- One or more Jamf Computer Groups whose members will automatically have this title installed.
539
+ One or more Jamf Computer Groups whose members will automatically have this title installed if it isn't already.
511
540
 
512
541
  These groups affect the _initial_ installation of a title when a version is released. Any Mac with the title installed, will get updates (if not excluded) regardless of the release-groups.
513
542
 
@@ -519,12 +548,16 @@ module Xolo
519
548
 
520
549
  To remove all existing, use '#{Xolo::NONE}'.
521
550
 
551
+ Xolos Group Types:
552
+ - Excluded & Target Groups: affect which computers an see a title & its versions.
553
+ - Release & Pilot Groups: affect which computers have the title automatically installed or updated, and when.
554
+
522
555
  NOTE: When a version is in 'pilot', before it is released, these groups are ignored, but instead a set of 'pilot' groups is defined for each version - and those groups will have that version auto-installed.
523
556
  ENDDESC
524
557
  },
525
558
 
526
559
  # @!attribute excluded_groups
527
- # @return [Array<String>] Jamf groups that are not allowed to install this title
560
+ # @return [Array<String>] Jamf groups that are not allowed to install or update this title
528
561
  excluded_groups: {
529
562
  label: 'Excluded Computer Groups',
530
563
  cli: :x,
@@ -536,7 +569,7 @@ module Xolo
536
569
  readline: :jamf_computer_group_names,
537
570
  invalid_msg: 'Invalid excluded computer group(s). Must exist in Jamf.',
538
571
  desc: <<~ENDDESC
539
- One or more Jamf Computer Groups whose members are not allowed to install this title.
572
+ One or more Jamf Computer Groups whose members are not allowed to install this title. Computers that are members cannot see the title's existance.
540
573
 
541
574
  When a computer is in one of these groups, the title is not available at all, for installation or updates, even if the computer is in a pilot or release group.
542
575
 
@@ -544,7 +577,48 @@ module Xolo
544
577
 
545
578
  To remove all existing, use '#{Xolo::NONE}'.
546
579
 
547
- NOTE: Regardless of the excluded groups set here, if the server has defined a 'forced_exclusion' in its config, that group is always excluded from all xolo titles. Also, computers that are 'frozen' for a title are excluded.
580
+ Xolos Group Types:
581
+ - Excluded & Target Groups: affect which computers an see a title & its versions.
582
+ - Release & Pilot Groups: affect which computers have the title automatically installed or updated, and when. Note, when a version is released all targeted/non-excluded/non-frozen computers with it installed will get the update.
583
+
584
+ Excluding vs Freezing:
585
+ - Excluded Groups are how to prevent members from accessing titles in Xolo. It can be thought of as 'freezing for groups'
586
+ - Freezing (via 'xadm freeze...') is how to prevent individual computers from accessing titles in Xolo. It can be thought of as 'exclusion for individual computers'
587
+
588
+ NOTE: Regardless of the excluded groups set here, if the server has defined a 'forced_exclusion' in its config, that group is always excluded from all xolo titles.
589
+
590
+ See also --target-groups
591
+ ENDDESC
592
+ },
593
+
594
+ # @!attribute target_groups
595
+ # @return [Array<String>] Jamf groups that are exclusively allowed to install or update this title.
596
+ target_groups: {
597
+ label: 'Target Computer Groups',
598
+ cli: :t,
599
+ validate: true,
600
+ type: :string,
601
+ multi: true,
602
+ changelog: true,
603
+ readline_prompt: 'Group Name',
604
+ readline: :jamf_computer_group_names,
605
+ invalid_msg: 'Invalid target computer group(s). Must exist in Jamf.',
606
+ desc: <<~ENDDESC
607
+ One or more Jamf Computer Groups whose members are exclusively allowed to install this title. Computers that are NOT members cannot see the title's existance.
608
+
609
+ This is the opposite of --excluded-groups, however if a computer is in both target and excluded groups, the exclusion wins.
610
+
611
+ When a computer is not in one of these groups, the title is not available at all, for installation or updates, even if the computer is in a pilot or release group.
612
+
613
+ When using the --target-groups CLI option, you can specify more than one group by using the option more than once, or by providing a single option value with the groups separated by commas.
614
+
615
+ To remove all existing, use '#{Xolo::NONE}'.
616
+
617
+ Xolos Group Types:
618
+ - Excluded & Target Groups: affect which computers an see a title & its versions.
619
+ - Release & Pilot Groups: affect which computers have the title automatically installed or updated, and when. Note, when a version is released all targeted/non-excluded/non-frozen computers with it installed will get the update.
620
+
621
+ See also --excluded-groups
548
622
  ENDDESC
549
623
  },
550
624
 
@@ -606,7 +680,7 @@ module Xolo
606
680
  # @!attribute expiration
607
681
  # @return [Integer] Number of days of disuse before the title is uninstalled.
608
682
  expiration: {
609
- label: 'Expire Days',
683
+ label: 'Expiration Days',
610
684
  cli: :e,
611
685
  validate: true,
612
686
  type: :integer,
@@ -659,15 +733,41 @@ module Xolo
659
733
  changelog: true,
660
734
  walkthru_na: :ssvc_na,
661
735
  desc: <<~ENDDESC
662
- Make this title available in Self Service. Only the currently released version will be available.
663
-
664
- While in pilot, a version is installed via its auto-install policy,
665
- or 'sudo xolo install <title> <version>' or updated via its Patch Policy.
736
+ Make this title available in Self Service for initial installation. Only the currently released version will be available. (Pilot versions can be installed based on their --pilot-groups or manally via 'xadm deploy' or 'xolo install')
666
737
 
667
- It will never be available to excluded computers.
738
+ It will never be available to excluded computers, even via self service.
668
739
 
669
740
  Self Service is not available for titles with the release_group 'all'.
670
- To remove the self-service option, use --no-self-service.
741
+
742
+ To explicitly set this to false, use --no-self-service.
743
+
744
+ See also --self-service-updates.
745
+ ENDDESC
746
+ },
747
+
748
+ # @!attribute self_service_updates
749
+ # @return [Boolean] Do versions appear in Self Service for updates?
750
+ self_service_updates: {
751
+ label: 'Self Service Updates?',
752
+ cli: :V,
753
+ type: :boolean,
754
+ validate: :validate_boolean,
755
+ default: false,
756
+ changelog: true,
757
+ desc: <<~ENDDESC
758
+ Make versions available in Self Service for updates. This setting is independent of the --self-service setting for initial installs. Unlike that setting, this can be set when the release-group is 'all'.
759
+
760
+ By default this is false, Patch Policies for versions will be set to 'Install Automatically'.
761
+
762
+ If set to true, Patch Policies for versions will be set to 'Make Available in Self Service' and the version will appear in the Updates section when available. The update won't happen until the user clicks a button, or the 7-day deadline passes. Notifications will be displayed daily.
763
+
764
+ When installing automatically, or the deadline passes, the update will happen at the checkin after the next recon. If the version has any KillApps, the user will be prompted to quit them, with a grace period of 15 minutes before the update starts.
765
+
766
+ Any Self Service icon uploaded for the title will be used for its versions. If needed, upload one for the title with 'xadm edit-title --self-service-icon /path/to/image/file' (the title itself doesn't need to be in Self Service)
767
+
768
+ To explicitly set this to false, use --no-self-service-updates.
769
+
770
+ See also --self-service.
671
771
  ENDDESC
672
772
  },
673
773
 
@@ -684,7 +784,7 @@ module Xolo
684
784
  invalid_msg: 'Invalid category. Must exist in Jamf Pro.',
685
785
  desc: <<~ENDDESC
686
786
  The Category in which to display this title in Self Service.
687
- REQUIRED if self_service is true, ignored otherwise
787
+ REQUIRED if --self-service is true, recommended if --self-service-updates is true.
688
788
  ENDDESC
689
789
  },
690
790
 
@@ -273,9 +273,8 @@ module Xolo
273
273
  ENDDESC
274
274
  },
275
275
 
276
- # @!attribute jamf_pkg
277
- # @return [String] The file name of the installer for the Jamf Package object that
278
- # installs this version. 'xolo-<title>-<version>.pkg' (or .zip)
276
+ # @!attribute pkg_to_upload
277
+ # @return [String] The path to a local pkg, or 'uploaded'
279
278
  pkg_to_upload: {
280
279
  label: 'Upload Package',
281
280
  type: :string,
@@ -288,9 +287,9 @@ module Xolo
288
287
  desc: <<~ENDDESC
289
288
  The path to a local copy of the installer package for this version. Will be uploaded to Xolo and then Jamf Pro distribution point(s), replacing any previously uploaded.
290
289
 
291
- Must be a flat .pkg file, or a .zip compressed old-style bundle package.
290
+ It must be a flat .pkg file. Old-school zipped bundle-style packages are not supported by Xolo.
292
291
 
293
- It will be renamed to 'xolo-<title>-<version>.pkg' (or .zip).
292
+ It will be renamed to 'xolo-<title>-<version>.pkg'.
294
293
  If your Xolo server is confiured to sign unsigned packages, it will do so along the way.
295
294
 
296
295
  Required when creating a new version unless the title is configrued to use autopkg.
@@ -461,7 +460,7 @@ module Xolo
461
460
  desc: <<~ENDDESC
462
461
  When this version was skipped in Xolo.
463
462
  This is when the Xolo sets the status of this version to 'skipped', meaning it was never released in Xolo, and now a newer version has been released.
464
- It will be automatically deleted at the next nightly cleanup, unless the server is configured otherwise.
463
+ It will be automatically deleted at the next nightly maintenance, unless the server is configured otherwise.
465
464
  ENDDESC
466
465
  },
467
466
 
@@ -39,11 +39,13 @@ module Xolo
39
39
 
40
40
  class InvalidTokenError < ConnectionError; end
41
41
 
42
+ class TooSoonError < ConnectionError; end
43
+
42
44
  class ServerError < ConnectionError; end
43
45
 
44
46
  # Parsing errors
45
47
 
46
- class DisallowedYAMLDumpClass; end
48
+ class DisallowedYAMLDumpClass < ConnectionError; end
47
49
 
48
50
  end # module Exceptions
49
51
 
@@ -12,7 +12,7 @@ module Xolo
12
12
 
13
13
  module Version
14
14
 
15
- VERSION = '2.0.3'.freeze
15
+ VERSION = '2.2.0'.freeze
16
16
 
17
17
  end
18
18
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xolo-admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Lasell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-25 00:00:00.000000000 Z
11
+ date: 2026-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday