xolo-admin 2.0.2 → 2.1.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: 78c42e6fb9e17f593f77705e340642273e4f83d70234a809fdef98101555cbe0
4
- data.tar.gz: 54286e2ebccf3dbb76e28d9916da8b593792ccd1691aaebc0ff31f5313d4a6b4
3
+ metadata.gz: ace6e6d8f0ac4d0ed085f2037803869d1b35ea3ff4efb53b3651dc3faafa5c25
4
+ data.tar.gz: 39cae6de70ea1a0e90c9a12e2e80b4502b27715bfcedbbf991b1239eec69bb1c
5
5
  SHA512:
6
- metadata.gz: e43eb8c13033590db47a1b3a8a51480956ee606f5f59a2f857ae38f9e382abb1a4b29f9213c8df4131ca53937553ad09ecea7262df7f3b1a5cbd96b16ab8d0b8
7
- data.tar.gz: 9ad24a86d2248d8767aa9c088129071f6d3fdc2aebff88c544584eefa2e1f274e1488dffc601c463727d2131694f33ab8f7a87da12bfb998cea26a0069b93954
6
+ metadata.gz: '059aa31ea917298e5aef01c64625b9fa8b1b539c181217ec7dffbfcc51e334c4d0e2ee43dff2e9d4908ddd9867a0b8c1c683685c9039300283f9d99c37b7d159'
7
+ data.tar.gz: 5dbc53d110470af587c234efebd060baf590872f17613ef5c3dc688a3bde85a94e544e6824ec92cc428b5352d2c4b93a6483bc27fa3e5c6c24579cdb48ff4c65
data/data/client/xolo CHANGED
@@ -54,9 +54,15 @@ JAMF='/usr/local/bin/jamf'
54
54
  # The path to the client data json file
55
55
  CLIENT_DATA_JSON_FILE="/Library/Application Support/xolo/client-data.json"
56
56
 
57
+ # The path to the test client data json file, from a text xolo server
58
+ TEST_CLIENT_DATA_JSON_FILE="/Library/Application Support/xolo/test-client-data.json"
59
+
57
60
  # The jamf policy trigger to update the client data
58
61
  UPDATE_CLIENT_DATA_TRIGGER='update-xolo-client-data'
59
62
 
63
+ # The jamf policy trigger to update the test client data
64
+ TEST_UPDATE_CLIENT_DATA_TRIGGER='test-update-xolo-client-data'
65
+
60
66
  # In the CLI args, titles and versions are separated by the first
61
67
  # occurrence of the equals sign
62
68
  TITLE_VERSION_SEPARATOR='='
@@ -141,6 +147,10 @@ Options:
141
147
  -v, --verbose: Enable verbose mode, extra information will be printed.
142
148
  -d, --debug: Enable debug mode, extra debug information will be printed.
143
149
  Implies --verbose.
150
+ -t, --test If your site has a "test" xolo server running via the same
151
+ Jamf Pro server, this causes 'xolo' to only see data, titles,
152
+ policies, etc, from test xolo server, not the production server.
153
+ Setting the XOLO_TEST_MODE env. variable does the same thing.
144
154
  -r, --recon: With 'install' or 'uninstall' run a 'jamf recon' after the
145
155
  operation completes successfully.
146
156
  -n, --no-versions: With the 'list-titles' and 'list-installed' commands,
@@ -152,6 +162,8 @@ Commands:
152
162
  install, i <title>[=<version>] [<title2>[=<version2>] ...]
153
163
  Install a title, or specific version thereof (e.g. a version currently in pilot)
154
164
  If no version is specified, the currently released version will be installed.
165
+ If you are only installing only one specific version of one title, you can
166
+ omit the =, e.g. install <title> <version>.
155
167
 
156
168
  uninstall, u <title> [<title2> ...]
157
169
  Uninstall a title, if possible. Not all titles are uninstallable via xolo.
@@ -221,6 +233,7 @@ function parse_cli() {
221
233
  {h,H,-help}=show_help \
222
234
  {v,-verbose}=be_verbose \
223
235
  {d,-debug}=debugging_on \
236
+ {t,-test}=test_mode \
224
237
  {n,-no-versions}=no_versions \
225
238
  {r,-recon}=do_recon \
226
239
  {V,-version}=show_xolo_version || \
@@ -230,22 +243,44 @@ function parse_cli() {
230
243
  show_help=$show_help[-1]
231
244
  be_verbose=$be_verbose[-1]
232
245
  debugging_on=$debugging_on[-1]
246
+ test_mode=$test_mode[-1]
233
247
  no_versions=$no_versions[-1]
234
248
  do_recon=$do_recon[-1]
235
249
  show_xolo_version=$show_xolo_version[-1]
236
250
 
251
+ # test mode can be triggered by an env var
252
+ [[ -n "$XOLO_TEST_MODE" ]] && test_mode=1
253
+
237
254
  # if debugging is on, verbose is also on
238
255
  [[ -n $debugging_on ]] && be_verbose=1
239
256
 
240
257
  command=$1
241
258
  [[ ${#@} -gt 0 ]] && shift
242
259
 
260
+ # the remaining positional parameters as an array called targets
261
+ # It'll either be exactly 1 item: title
262
+ # or exactly 2 items containing no '=' : title, version
263
+ # or exactly 2 items containing no '=' : title, title
264
+ # or any number if items in the form: title[=version] where the =version is optional.
243
265
  targets=("${(@)@}")
244
266
 
267
+ # if targets has exactly 2 items, and neither of them contains
268
+ # '=' and the second one isn't an existing title then treat
269
+ # them as the title and a version and reset 'targets' to be 'title=version'
270
+ if [[ "$#targets" == 2 ]] \
271
+ && [[ "$targets[1]" != *"$TITLE_VERSION_SEPARATOR"* ]] \
272
+ && [[ "$targets[2]" != *"$TITLE_VERSION_SEPARATOR"* ]] \
273
+ && ! echo "$(all_xolo_titles)" | grep -qE "^${targets[2]}$" ; then
274
+
275
+ targets=("$targets[1]=$targets[2]")
276
+ fi
277
+
245
278
  debug "Parsed command line:"
246
279
  debug "..show_help is: $show_help"
247
280
  debug "..be_verbose is: $be_verbose"
248
281
  debug "..debugging_on is: $debugging_on"
282
+ debug "..test_mode is: $test_mode"
283
+ [[ -n "$XOLO_TEST_MODE" ]] && debug " ..via XOLO_TEST_MODE env."
249
284
  debug "..no_versions is: $no_versions"
250
285
  debug "..show_xolo_version is: $show_xolo_version"
251
286
 
@@ -258,6 +293,9 @@ function parse_cli() {
258
293
  #################################
259
294
  function parse_title_and_version() {
260
295
  local arg=$1
296
+ unset title_is_valid
297
+ unset version_is_valid
298
+
261
299
  debug "Parsing title and version from arg: '$arg'"
262
300
 
263
301
  if [[ "$arg" == *"$TITLE_VERSION_SEPARATOR"* ]] ; then
@@ -381,7 +419,7 @@ app.includeStandardAdditions = true;
381
419
 
382
420
  // run() is automatically executed when the program is called, and will print any output returned.
383
421
  function run() {
384
- var parsed_data = JSON.parse(app.read("${CLIENT_DATA_JSON_FILE}"));
422
+ var parsed_data = JSON.parse(app.read("${client_data_json_file}"));
385
423
  var result;
386
424
 
387
425
  // the line below will sub-in whatever code we were passed
@@ -761,7 +799,7 @@ function refresh_client_data() {
761
799
 
762
800
  [[ "$command" == 'refresh' ]] && say "Refreshing client data..." || debug "Refreshing client data..."
763
801
 
764
- run_jamf_policy_trigger $UPDATE_CLIENT_DATA_TRIGGER
802
+ run_jamf_policy_trigger "$update_client_data_trigger"
765
803
 
766
804
  client_data_refreshed=1
767
805
  }
@@ -769,9 +807,13 @@ function refresh_client_data() {
769
807
  # validate that the title exists
770
808
  ###############################
771
809
  function validate_title() {
810
+
772
811
  # if we've already validated the title, we're done
773
812
  [[ -n "$title_is_valid" ]] && return
774
813
 
814
+ # use passed arg if needed
815
+ [[ -z "$title" ]] && title=$1
816
+
775
817
  debug "Validating title: $title"
776
818
 
777
819
  # die if no title given
@@ -806,7 +848,7 @@ function validate_version() {
806
848
  all_versions=$(versions_for_title $title)
807
849
  debug "All versions for title $title:\n$all_versions"
808
850
  # [[ $all_versions =~ (^|\n)$version($|\n) ]] || die "No such version: $version"
809
- echo "$all_versions" | grep -q "^$version$" || die "No such version: $version"
851
+ echo "$all_versions" | grep -q "^$version$" || die "No such version '$version' for title '$title'"
810
852
  version_is_valid=1
811
853
  }
812
854
 
@@ -1156,6 +1198,10 @@ function expire() {
1156
1198
  ###############################
1157
1199
 
1158
1200
  function main() {
1201
+ # defaults
1202
+ client_data_json_file="$CLIENT_DATA_JSON_FILE"
1203
+ update_client_data_trigger="$UPDATE_CLIENT_DATA_TRIGGER"
1204
+
1159
1205
  # Parse the command line
1160
1206
  parse_cli "$@"
1161
1207
 
@@ -1171,10 +1217,16 @@ function main() {
1171
1217
  exit 0
1172
1218
  fi
1173
1219
 
1220
+ # use the test data file if in test mode
1221
+ if [[ -n "$test_mode" ]] ; then
1222
+ client_data_json_file="$TEST_CLIENT_DATA_JSON_FILE"
1223
+ update_client_data_trigger="$TEST_UPDATE_CLIENT_DATA_TRIGGER"
1224
+ fi
1225
+
1174
1226
  if [[ "$command" != "refresh" ]] ; then
1175
1227
  # If we're not refreshing, we need to have the client data file
1176
1228
  # and it needs to be up to date
1177
- [[ -f "$CLIENT_DATA_JSON_FILE" ]] || die "No client data file found at $CLIENT_DATA_JSON_FILE.\nPlease run the 'refresh' command to update the client data."
1229
+ [[ -f "$client_data_json_file" ]] || die "No client data file found at $client_data_json_file.\nPlease run the 'refresh' command to update the client data."
1178
1230
  fi
1179
1231
 
1180
1232
  [[ -z "$command" ]] && die "No command given.\nUsage: $USAGE\nUse --help for more information."
@@ -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
 
@@ -198,6 +198,52 @@ 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 --autopkg.
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 --autopkg.
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 --subscribed or --managed.
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 --subscribed or --managed.
243
+ ENDDESC
244
+ }
245
+ }.freeze
246
+
201
247
  PATCH_REPORT_OPTIONS = {
202
248
  summary: {
203
249
  label: 'Summary Only',
@@ -410,7 +456,7 @@ module Xolo
410
456
  LIST_TITLES_CMD => {
411
457
  desc: 'List all software titles.',
412
458
  display: LIST_TITLES_CMD,
413
- opts: {},
459
+ opts: LIST_TITLES_OPTIONS,
414
460
  arg_banner: :none,
415
461
  process_method: :list_titles,
416
462
  target: :none
@@ -211,16 +211,27 @@ module Xolo
211
211
  return
212
212
  end
213
213
 
214
- report_title = 'All titles in Xolo'
215
- header = %w[Title Created By SSvc? Released Latest]
214
+ type = opts_to_process.subscribed ? ' subscribed' : nil
215
+ type ||= opts_to_process.managed ? ' managed' : nil
216
+ autopkg = opts_to_process.autopkg ? ' autopkg' : nil
217
+ pilots = opts_to_process.pilots ? ' with unreleased pilots' : nil
218
+
219
+ report_title = "All#{type}#{autopkg} titles in Xolo#{pilots}"
220
+
221
+ titles.select! { |t| t.subscribed? } if opts_to_process.subscribed
222
+ titles.reject! { |t| t.subscribed? } if opts_to_process.managed
223
+ titles.select! { |t| t.autopkg_recipe } if opts_to_process.autopkg
224
+ titles.select! { |t| pending_pilots? t } if opts_to_process.pilots
225
+
226
+ header = %w[Title Created By SSvc? Latest Released]
216
227
  data = titles.map do |t|
217
228
  [
218
229
  t.title,
219
230
  t.creation_date.to_date,
220
231
  t.created_by,
221
232
  t.self_service || false,
222
- t.released_version,
223
- t.latest_version
233
+ t.latest_version,
234
+ t.released_version
224
235
  ]
225
236
  end
226
237
  data.sort_by! { |d| d[0].downcase } # sort by title
@@ -230,6 +241,16 @@ module Xolo
230
241
  handle_processing_error e
231
242
  end
232
243
 
244
+ # @param title [Xolo::Admin::Title] The title in question
245
+ # @return [Boolean] Does a given Title object have pilots?
246
+ ###########################
247
+ def pending_pilots?(title)
248
+ return false if title.version_order.pix_empty?
249
+ return false if title.released_version == title.version_order.first
250
+
251
+ true
252
+ end
253
+
233
254
  # Add a title to Xolo
234
255
  #
235
256
  # @return [void]
@@ -487,16 +508,19 @@ module Xolo
487
508
  return
488
509
  end
489
510
 
490
- report_title = "All versions of '#{cli_cmd.title}' in Xolo"
511
+ report_title = "All versions of '#{cli_cmd.title}' in Xolo. ** = No .pkg uploaded"
491
512
  header = %w[Vers Created By Released By Status]
492
513
  data = versions.sort_by(&:creation_date).map do |v|
514
+ no_pkg = v.pkg_to_upload.pix_empty? && v.jamf_pkg_file.pix_empty?
515
+ status = no_pkg ? "#{v.status} **" : v.status
516
+
493
517
  [
494
518
  v.version,
495
519
  v.creation_date.to_date,
496
520
  v.created_by,
497
521
  v.release_date&.to_date,
498
522
  v.released_by,
499
- v.status
523
+ status
500
524
  ]
501
525
  end
502
526
  show_text generate_report(data, header_row: header, title: report_title)
@@ -750,6 +774,9 @@ module Xolo
750
774
  # description is handled specially below
751
775
  next if attr == :description
752
776
 
777
+ # only show patch_source and title_id if subscribed
778
+ next if %i[patch_source title_id].include?(attr) && !title.subscribed?
779
+
753
780
  value = title.send attr
754
781
  value = value.join(Xolo::COMMA_JOIN) if value.is_a? Array
755
782
  puts "- #{deets[:label]}: #{value}".pix_word_wrap
@@ -169,7 +169,10 @@ module Xolo
169
169
  # @return [Hash] the response body from the server
170
170
  ####################
171
171
  def release(cnx = self.cnx, version:)
172
- resp = cnx.patch "#{SERVER_ROUTE}/#{title}/release/#{version}", {}
172
+ resp = cnx.patch "#{SERVER_ROUTE}/#{title}/release/#{URI::Parser.new.escape version}", {}
173
+ ### the CGI escape one doesn't seem to work with, e.g. "7.1.5 (84650)"
174
+ # resp = cnx.patch "#{SERVER_ROUTE}/#{title}/release/#{CGI.escape version}", {}
175
+
173
176
  resp.body
174
177
  end
175
178
 
@@ -50,7 +50,9 @@ module Xolo
50
50
  ####################
51
51
  def self.server_route(title, version = nil)
52
52
  route = SERVER_ROUTE.sub(Xolo::Admin::Title::TARGET_TITLE_PLACEHOLDER, title)
53
- route << "/#{version}" if version
53
+ route << "/#{URI::Parser.new.escape(version)}" if version
54
+ ### the CGI escape one doesn't seem to work with, e.g. "7.1.5 (84650)"
55
+ # route << "/#{CGI.escape(version)}" if version
54
56
  route
55
57
  end
56
58
 
@@ -474,6 +474,8 @@ module Xolo
474
474
  desc: <<~ENDDESC
475
475
  One or more Jamf Computer Groups whose members will automatically have new versions installed or updated for testing before it is released.
476
476
 
477
+ These groups affect both the initial installation of and updates to a title when a version is added, before it is released.
478
+
477
479
  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.
478
480
 
479
481
  Pilot groups can also be defined per version, in which case these will be ignored. Defining them in the title is useful for subscribed titles, where new versions are created automatically.
@@ -505,7 +507,9 @@ module Xolo
505
507
  readline: :jamf_computer_group_names,
506
508
  invalid_msg: 'Invalid release group(s). Must exist in Jamf and not be excluded.',
507
509
  desc: <<~ENDDESC
508
- One or more Jamf Computer Groups whose members will automatically have this title installed when new versions are released.
510
+ One or more Jamf Computer Groups whose members will automatically have this title installed.
511
+
512
+ 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.
509
513
 
510
514
  If your Xolo administrators allow it, you can use '#{Xolo::TARGET_ALL}' to auto-install on all computers that aren't excluded. If not, you'll be told how to request setting release groups to '#{Xolo::TARGET_ALL}'.
511
515
 
@@ -534,7 +538,7 @@ module Xolo
534
538
  desc: <<~ENDDESC
535
539
  One or more Jamf Computer Groups whose members are not allowed to install this title.
536
540
 
537
- When a computer is in one of these groups, the title is not available even if the computer is in a pilot or release group.
541
+ 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.
538
542
 
539
543
  When using the --excluded-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.
540
544
 
@@ -663,6 +667,7 @@ module Xolo
663
667
  It will never be available to excluded computers.
664
668
 
665
669
  Self Service is not available for titles with the release_group 'all'.
670
+ To remove the self-service option, use --no-self-service.
666
671
  ENDDESC
667
672
  },
668
673
 
@@ -799,7 +804,7 @@ module Xolo
799
804
  # @!attribute ted_id_number
800
805
  # @return [Integer] The Windoo::SoftwareTitle#softwareTitleId
801
806
  ted_id_number: {
802
- label: 'Title Editor ID Number',
807
+ label: 'Title Editor ID',
803
808
  type: :integer,
804
809
  cli: false,
805
810
  changelog: false,
@@ -812,7 +817,7 @@ module Xolo
812
817
  # @!attribute jamf_patch_title_id
813
818
  # @return [Integer] The Windoo::SoftwareTitle#softwareTitleId
814
819
  jamf_patch_title_id: {
815
- label: 'The Jamf ID Number of this Patch Title',
820
+ label: 'Jamf Patch Title ID',
816
821
  type: :integer,
817
822
  cli: false,
818
823
  changelog: false,
@@ -170,6 +170,7 @@ module Xolo
170
170
  changelog: true,
171
171
  desc: <<~ENDDESC
172
172
  The installation of this version requires the computer to reboot. Users will be notified before installation.
173
+ To remove the reboot option, use --no-reboot.
173
174
  ENDDESC
174
175
  },
175
176
 
@@ -186,6 +187,7 @@ module Xolo
186
187
  changelog: true,
187
188
  desc: <<~ENDDESC
188
189
  The installer for this version is a full installer, not an incremental patch that must be installed on top of an earlier version.
190
+ To remove the standalone option, use --no-standalone.
189
191
  ENDDESC
190
192
  },
191
193
 
@@ -217,6 +219,7 @@ module Xolo
217
219
  use them as a killapp by specifying '#{USE_TITLE_FOR_KILLAPP}'
218
220
 
219
221
  If not using --walkthru you can use --killapps multiple times
222
+ To remove all killapps, use '#{Xolo::NONE}'.
220
223
  ENDDESC
221
224
  },
222
225
 
@@ -237,6 +240,8 @@ module Xolo
237
240
  This can cause problems if that unknown version is actually newer than this version, e.g. a beta or pre-release version, or when the app has a 'self-update' mechanism that installs newer versions outside of Jamf Patch before it is aware of them.
238
241
 
239
242
  But sometimes it may be desirable to have all unknown versions updated to this version, e.g. when the title is a helper app that is not regularly updated, or when the title is being newly managed by Xolo/Jamf Patch and you want to get all existing installations onto this version.
243
+
244
+ To remove the patch-unknown option, use --no-patch-unknown.
240
245
  ENDDESC
241
246
  },
242
247
 
@@ -262,7 +267,7 @@ module Xolo
262
267
 
263
268
  When using the --pilot-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.
264
269
 
265
- 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 use '#{Xolo::NONE}'.
270
+ When adding a new version, the pilot groups from the previous version will be inherited if you don't specify any. To make the version have no pilot groups use '#{Xolo::NONE}'.
266
271
 
267
272
  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 <title> <version>`. The members of the pilot groups are just the ones that will have it auto-installed.
268
273
  ENDDESC
@@ -12,7 +12,7 @@ module Xolo
12
12
 
13
13
  module Version
14
14
 
15
- VERSION = '2.0.2'.freeze
15
+ VERSION = '2.1.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.2
4
+ version: 2.1.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-05-16 00:00:00.000000000 Z
11
+ date: 2026-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday