xolo-server 2.2.4 → 2.3.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 +4 -4
- data/data/client/xolo +97 -26
- data/lib/xolo/core/version.rb +1 -1
- data/lib/xolo/server/helpers/maintenance.rb +53 -5
- data/lib/xolo/server/helpers/subscriptions.rb +1 -16
- data/lib/xolo/server/mixins/title_ted_access.rb +5 -5
- data/lib/xolo/server/routes/maint.rb +11 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e78577a11f1d65aba560ec5540c3a79acdfc7d6a491060e256bb9e2da5a057d9
|
|
4
|
+
data.tar.gz: 9e7e776f48e8e9e260bc41a9090603bbfc41d1dad7dd62fc00396ce9914450cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b83f99bda3666a2fd124241a26882499c393a75b3289fb503bdcb6ac994827228b5f813acc79a7a81f437f879b2e9aaca727800ddd711f17b2713d7a45a98738
|
|
7
|
+
data.tar.gz: 05bb86ec68010bf2258853983f093936ffa735c52144b6760c64177621a971bdf93823a46562a4b347c5a080d5206900a39ff8b6019e7fb1fa0cf0b400e4336e
|
data/data/client/xolo
CHANGED
|
@@ -27,6 +27,9 @@ zmodload zsh/zutil
|
|
|
27
27
|
###############################
|
|
28
28
|
###############################
|
|
29
29
|
|
|
30
|
+
# just in case
|
|
31
|
+
export PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/libexec:$PATH"
|
|
32
|
+
|
|
30
33
|
# Needed for the UTF-8 chars in the lsreg output
|
|
31
34
|
export LANG="en_US.UTF-8"
|
|
32
35
|
export LC_ALL="en_US.UTF-8"
|
|
@@ -356,42 +359,43 @@ function debug() {
|
|
|
356
359
|
debugging_on && echo "DEBUG: $*" 1>&2
|
|
357
360
|
}
|
|
358
361
|
|
|
359
|
-
# Gather all installed bundle ids and installed versions from
|
|
362
|
+
# Gather all installed bundle ids and installed versions from system_profiler
|
|
360
363
|
# store them in an associative array in $installed_apps.
|
|
361
364
|
# keys are bundle ids, values are app name & installed version
|
|
362
365
|
# separated by a semicolon
|
|
363
366
|
# e.g. 'com.apple.Safari => Safari.app;14.0.3'
|
|
364
367
|
###############################
|
|
365
368
|
function get_installed_apps() {
|
|
369
|
+
# debug "PATH is $PATH"
|
|
366
370
|
debug "Getting all installed .apps on this Mac..."
|
|
367
371
|
local app_name
|
|
368
372
|
local app_bundle_id
|
|
369
373
|
local app_vers
|
|
374
|
+
local app_plist
|
|
370
375
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
continue
|
|
379
|
-
fi
|
|
376
|
+
json_app_data=$(/usr/sbin/system_profiler -json SPApplicationsDataType )
|
|
377
|
+
|
|
378
|
+
apps_to_look_at=$(echo "$json_app_data" | grep '"path"' | cut -d'"' -f4 | grep -v '^/System/')
|
|
379
|
+
|
|
380
|
+
for app_path in ${(f)apps_to_look_at} ; do
|
|
381
|
+
app_name=$(/usr/bin/basename "$app_path")
|
|
382
|
+
app_plist="$app_path/Contents/Info.plist"
|
|
380
383
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
384
|
+
if [[ -f "$app_plist" ]] ; then
|
|
385
|
+
app_bundle_id=$(/usr/bin/plutil -extract CFBundleIdentifier raw "$app_plist" 2>/dev/null)
|
|
386
|
+
|
|
387
|
+
app_vers=$(/usr/bin/plutil -extract CFBundleShortVersionString raw "$app_plist" 2>/dev/null)
|
|
388
|
+
|
|
389
|
+
# Use CFBundleVersion if no CFBundleShortVersionString
|
|
390
|
+
[[ -z "$app_vers" ]] && \
|
|
391
|
+
app_vers=$(/usr/bin/plutil -extract CFBundleVersion raw "$app_plist" 2>/dev/null)
|
|
387
392
|
|
|
388
|
-
if [[ -n "$app_name" && -n "$app_bundle_id" && -n "$app_vers" ]] ; then
|
|
389
393
|
installed_apps[$app_bundle_id]="$app_name;$app_vers"
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
unset app_vers
|
|
394
|
+
else
|
|
395
|
+
debug "App Path '$app_path' has no Info.plist"
|
|
393
396
|
fi
|
|
394
397
|
done
|
|
398
|
+
|
|
395
399
|
# installed_apps is now an associative array with bundle ids as keys and app names as values
|
|
396
400
|
|
|
397
401
|
# print the installed apps - this is a long list
|
|
@@ -402,8 +406,70 @@ function get_installed_apps() {
|
|
|
402
406
|
done
|
|
403
407
|
fi
|
|
404
408
|
|
|
409
|
+
# debug "PATH is $PATH"
|
|
405
410
|
} # end installed_apps
|
|
406
411
|
|
|
412
|
+
########################################
|
|
413
|
+
########################################
|
|
414
|
+
########################################
|
|
415
|
+
########################################
|
|
416
|
+
########################################
|
|
417
|
+
########################################
|
|
418
|
+
# Gather all installed bundle ids and installed versions from lsreg
|
|
419
|
+
# store them in an associative array in $installed_apps.
|
|
420
|
+
# keys are bundle ids, values are app name & installed version
|
|
421
|
+
# separated by a semicolon
|
|
422
|
+
# e.g. 'com.apple.Safari => Safari.app;14.0.3'
|
|
423
|
+
###############################
|
|
424
|
+
# function get_installed_apps() {
|
|
425
|
+
# debug "Getting all installed .apps on this Mac..."
|
|
426
|
+
# local app_name
|
|
427
|
+
# local app_bundle_id
|
|
428
|
+
# local app_vers
|
|
429
|
+
|
|
430
|
+
# # loop thru lsreg output
|
|
431
|
+
# "$LSREG" -dump Bundle | while IFS= read -r line ; do
|
|
432
|
+
# # a line if ------ starts a new record
|
|
433
|
+
# if [[ "$line" =~ '^-+$' ]] ; then
|
|
434
|
+
# unset app_name
|
|
435
|
+
# unset app_bundle_id
|
|
436
|
+
# unset app_vers
|
|
437
|
+
# continue
|
|
438
|
+
# fi
|
|
439
|
+
|
|
440
|
+
# # $MATCH is the whole matched string, $match is the array of captures
|
|
441
|
+
# # NOTE this only works if the path is listed before the CFBundleIdentifier
|
|
442
|
+
# # which seems to be the case in the lsreg output
|
|
443
|
+
# [[ "$line" =~ '^path:[[:space:]]+/.*/(.+\.app) \(' ]] && app_name=$match[1]
|
|
444
|
+
# [[ "$line" =~ '^[[:space:]]+CFBundleIdentifier = \"(.+)\";' ]] && app_bundle_id=$match[1]
|
|
445
|
+
# [[ "$line" =~ '^[[:space:]]+CFBundleShortVersionString = \"(.+)\";' ]] && app_vers=$match[1]
|
|
446
|
+
|
|
447
|
+
# if [[ -n "$app_name" && -n "$app_bundle_id" && -n "$app_vers" ]] ; then
|
|
448
|
+
# installed_apps[$app_bundle_id]="$app_name;$app_vers"
|
|
449
|
+
# unset app_name
|
|
450
|
+
# unset app_bundle_id
|
|
451
|
+
# unset app_vers
|
|
452
|
+
# fi
|
|
453
|
+
# done
|
|
454
|
+
# # installed_apps is now an associative array with bundle ids as keys and app names as values
|
|
455
|
+
|
|
456
|
+
# # print the installed apps - this is a long list
|
|
457
|
+
# if debugging_on ; then
|
|
458
|
+
# debug "All installed .apps on this Mac:"
|
|
459
|
+
# for bundle app in ${(kv)installed_apps}; do
|
|
460
|
+
# debug "'$bundle' -> '$app'"
|
|
461
|
+
# done
|
|
462
|
+
# fi
|
|
463
|
+
|
|
464
|
+
# } # end installed_apps
|
|
465
|
+
########################################
|
|
466
|
+
########################################
|
|
467
|
+
########################################
|
|
468
|
+
########################################
|
|
469
|
+
########################################
|
|
470
|
+
########################################
|
|
471
|
+
########################################
|
|
472
|
+
|
|
407
473
|
# Extract data from the client-data.json file
|
|
408
474
|
#
|
|
409
475
|
# TODO: Once we only support macOS 15 (Sequoia) and higher, we
|
|
@@ -463,7 +529,7 @@ function all_xolo_titles() {
|
|
|
463
529
|
read -r -d '' jscode <<ENDJAVASCRIPT
|
|
464
530
|
result = Object.keys(parsed_data.titles).join('\n');
|
|
465
531
|
ENDJAVASCRIPT
|
|
466
|
-
extract_json_data "$jscode" | sort
|
|
532
|
+
extract_json_data "$jscode" | /usr/bin/sort
|
|
467
533
|
}
|
|
468
534
|
|
|
469
535
|
# Outputs all known titles, one per line, with versions and statuses
|
|
@@ -1020,12 +1086,15 @@ function list_installed_titles() {
|
|
|
1020
1086
|
local ttl
|
|
1021
1087
|
local app_data
|
|
1022
1088
|
|
|
1023
|
-
|
|
1024
1089
|
verbose "Locating installed titles..."
|
|
1025
1090
|
|
|
1026
1091
|
# populate the installed_apps associative array
|
|
1027
1092
|
get_installed_apps
|
|
1028
1093
|
|
|
1094
|
+
# gather output here, display it at the end
|
|
1095
|
+
# rather than interspersed with debug lines
|
|
1096
|
+
installed_titles_report=""
|
|
1097
|
+
|
|
1029
1098
|
while IFS= read -r ttl; do
|
|
1030
1099
|
app_data=$(app_data_for_title $ttl)
|
|
1031
1100
|
|
|
@@ -1036,6 +1105,8 @@ function list_installed_titles() {
|
|
|
1036
1105
|
display_title_if_installed_by_version_script $ttl
|
|
1037
1106
|
fi
|
|
1038
1107
|
done <<<"$(all_xolo_titles)"
|
|
1108
|
+
|
|
1109
|
+
say "$installed_titles_report"
|
|
1039
1110
|
return 0
|
|
1040
1111
|
}
|
|
1041
1112
|
|
|
@@ -1075,9 +1146,9 @@ function display_title_if_installed_by_app_data() {
|
|
|
1075
1146
|
[[ "$inst_app_name" == "$app_name" ]] || return
|
|
1076
1147
|
|
|
1077
1148
|
if [[ -n "$no_versions" ]] ; then
|
|
1078
|
-
|
|
1149
|
+
installed_titles_report="$installed_titles_report\n$ttl"
|
|
1079
1150
|
else
|
|
1080
|
-
|
|
1151
|
+
installed_titles_report="$installed_titles_report\n$ttl ($inst_app_vers)"
|
|
1081
1152
|
fi
|
|
1082
1153
|
}
|
|
1083
1154
|
|
|
@@ -1114,9 +1185,9 @@ function display_title_if_installed_by_version_script() {
|
|
|
1114
1185
|
[[ -n "$app_vers" ]] || return
|
|
1115
1186
|
|
|
1116
1187
|
if [[ -n "$no_versions" ]] ; then
|
|
1117
|
-
|
|
1188
|
+
installed_titles_report="$installed_titles_report\n$ttl"
|
|
1118
1189
|
else
|
|
1119
|
-
|
|
1190
|
+
installed_titles_report="$installed_titles_report\n$ttl ($inst_app_vers)"
|
|
1120
1191
|
fi
|
|
1121
1192
|
}
|
|
1122
1193
|
|
data/lib/xolo/core/version.rb
CHANGED
|
@@ -108,14 +108,14 @@ module Xolo
|
|
|
108
108
|
@maint_timer_task
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
-
# When was our last maint?
|
|
111
|
+
# When was our last automated maint?
|
|
112
112
|
# @return [Time] the time of the last maint, or the epoch if never
|
|
113
113
|
######################################
|
|
114
114
|
def self.last_maint
|
|
115
115
|
@last_maint ||= Time.at(0)
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
-
# Set the time of the last maint
|
|
118
|
+
# Set the time of the last maint, if it was automated
|
|
119
119
|
# @param time [Time] the time of the last maint
|
|
120
120
|
# @return [Time] the time of the last maint
|
|
121
121
|
######################################
|
|
@@ -153,7 +153,7 @@ module Xolo
|
|
|
153
153
|
Xolo::Server.logger.info 'Maint: Starting nightly run now'
|
|
154
154
|
|
|
155
155
|
else
|
|
156
|
-
Xolo::Server.logger.
|
|
156
|
+
Xolo::Server.logger.debug "Maint: Not starting, it isn't time"
|
|
157
157
|
return
|
|
158
158
|
end
|
|
159
159
|
|
|
@@ -195,13 +195,16 @@ module Xolo
|
|
|
195
195
|
|
|
196
196
|
# add new maintenance tasks/methods here
|
|
197
197
|
######
|
|
198
|
+
update_ssl_cert_and_key
|
|
198
199
|
accept_title_editor_eas title_objects_for_maint
|
|
199
200
|
auto_release_versions title_objects_for_maint
|
|
200
201
|
cleanup_versions title_objects_for_maint
|
|
201
202
|
notify_admins_of_unreleased_pilots title_objects_for_maint
|
|
202
203
|
|
|
203
|
-
# make note of the time.
|
|
204
|
-
|
|
204
|
+
# make note of the time, but only for automated runs.
|
|
205
|
+
# Manual runs don't affect this.
|
|
206
|
+
Xolo::Server::Helpers::Maintenance.last_maint = Time.now unless @manual_maintenance
|
|
207
|
+
|
|
205
208
|
log_info 'Maint: complete'
|
|
206
209
|
|
|
207
210
|
log_debug 'Maint: running update_client_data after all tasks'
|
|
@@ -210,6 +213,51 @@ module Xolo
|
|
|
210
213
|
mutex&.unlock
|
|
211
214
|
end
|
|
212
215
|
|
|
216
|
+
# Re-fetch the SSL certificate and key, as defined in the server config,
|
|
217
|
+
# and compare to the current values. If they are different, save and activate
|
|
218
|
+
# the new ones.
|
|
219
|
+
#
|
|
220
|
+
# NOTE, to get the current values we read the active files from disk,
|
|
221
|
+
# not the value in memory in the Xolo::Server.config object.
|
|
222
|
+
# In other words we use this:
|
|
223
|
+
# curr_crt = Xolo::Server::Configuration::SSL_CERT_FILE.read.strip
|
|
224
|
+
# Not this:
|
|
225
|
+
# curr_crt = Xolo::Server.config.ssl_cert
|
|
226
|
+
# This is because the file contents are what get sent to the https clients,
|
|
227
|
+
# so that's what matters.
|
|
228
|
+
# We do however update the in-memory value if an update is needed, just in case.
|
|
229
|
+
######################################
|
|
230
|
+
def update_ssl_cert_and_key
|
|
231
|
+
log_info 'Maint: Checking if SSL Certificate & Key need updating.'
|
|
232
|
+
update = false
|
|
233
|
+
|
|
234
|
+
# check the cert
|
|
235
|
+
new_crt_src = Xolo::Server.config.raw_data[:ssl_cert]
|
|
236
|
+
log_debug "Maint: Re-fetching SSL Certificate from: #{new_crt_src}"
|
|
237
|
+
new_crt = Xolo::Server.config.data_from_command_file_or_string(new_crt_src).strip
|
|
238
|
+
curr_crt = Xolo::Server::Configuration::SSL_CERT_FILE.read.strip
|
|
239
|
+
update = true unless new_crt == curr_crt
|
|
240
|
+
|
|
241
|
+
# check the key
|
|
242
|
+
new_key_src = Xolo::Server.config.raw_data[:ssl_key]
|
|
243
|
+
log_debug "Maint: Re-fetching SSL Private Key from: #{new_key_src}"
|
|
244
|
+
new_key = Xolo::Server.config.data_from_command_file_or_string(new_key_src).strip
|
|
245
|
+
curr_key = Xolo::Server::Configuration::SSL_KEY_FILE.read.strip
|
|
246
|
+
update = true unless new_key == curr_key
|
|
247
|
+
|
|
248
|
+
if update
|
|
249
|
+
log_info 'Maint: SSL Certificate or Key has been updated, installing...'
|
|
250
|
+
Xolo::Server.config.ssl_cert = new_crt
|
|
251
|
+
Xolo::Server.config.ssl_key = new_key
|
|
252
|
+
Xolo::Server::Configuration::SSL_CERT_FILE.pix_save new_crt
|
|
253
|
+
Xolo::Server::Configuration::SSL_KEY_FILE.pix_save new_key
|
|
254
|
+
else
|
|
255
|
+
log_debug 'Maint: SSL Certificate and Private Key are unchanged.'
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
log_info 'Maint: Done Checking if SSL Certificate & Key.'
|
|
259
|
+
end
|
|
260
|
+
|
|
213
261
|
# look for any titles that need their Title Editor EA's accepted,
|
|
214
262
|
# and auto accept them if we need to
|
|
215
263
|
# @return [void]
|
|
@@ -91,7 +91,7 @@ module Xolo
|
|
|
91
91
|
new_version: new_version
|
|
92
92
|
)
|
|
93
93
|
else
|
|
94
|
-
log_debug "Title '#{title_name}' ID #{title_id} is not a subscribed title
|
|
94
|
+
log_debug "Title '#{title_name}' ID #{title_id} is not a subscribed title. Ignoring webhook."
|
|
95
95
|
end
|
|
96
96
|
rescue => e
|
|
97
97
|
msg = "Error processing PatchSoftwareTitleUpdated webhook event: #{e.class}: #{e}"
|
|
@@ -101,21 +101,6 @@ module Xolo
|
|
|
101
101
|
end # thread
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
-
# TODO: Delete this method after confirming we don't want it
|
|
105
|
-
# It wasn't in use when it was commented out.
|
|
106
|
-
#
|
|
107
|
-
# Subscribe to a title on a given patch source.
|
|
108
|
-
# @param source_id [Integer] the id or name of the patch source in Jamf
|
|
109
|
-
# @param name_id [Integer] the name_id of the title on that patch source
|
|
110
|
-
# @param display_name [String] an display name for the title
|
|
111
|
-
# @return [Integer] the id of the new subscribed title, or false on failure
|
|
112
|
-
#####################################
|
|
113
|
-
# def subscribe_to_title(source_id:, name_id:, display_name:)
|
|
114
|
-
# new_sub = Jamf::PatchTitle.create name: display_name, source_id: source_id, name_id: name_id, cnx: jamf_cnx
|
|
115
|
-
|
|
116
|
-
# new_sub.save
|
|
117
|
-
# end
|
|
118
|
-
|
|
119
104
|
end # Subscriptions
|
|
120
105
|
|
|
121
106
|
end # Helpers
|
|
@@ -119,17 +119,17 @@ module Xolo
|
|
|
119
119
|
|
|
120
120
|
# the capabilites
|
|
121
121
|
stub_patch.capabilities.add_criterion(
|
|
122
|
-
|
|
123
|
-
# avoiding a warning message in the TEd gui.
|
|
124
|
-
# name: Xolo::Server::Version::STUB_PATCH_CAPABILITY_CRITERION_NAME,
|
|
125
|
-
name: title,
|
|
122
|
+
name: Xolo::Server::Version::STUB_PATCH_CAPABILITY_CRITERION_NAME,
|
|
126
123
|
operator: Xolo::Server::Version::STUB_PATCH_CAPABILITY_CRITERION_OPERATOR,
|
|
127
124
|
value: Xolo::Server::Version::STUB_PATCH_CAPABILITY_CRITERION_VALUE
|
|
128
125
|
)
|
|
129
126
|
|
|
130
127
|
# the component
|
|
131
128
|
stub_patch.add_component(
|
|
132
|
-
name
|
|
129
|
+
# Use the title as the name, so it matches all the future versions/patches
|
|
130
|
+
# avoiding a warning message in the TEd gui.
|
|
131
|
+
# name: Xolo::Server::Version::STUB_PATCH_COMPONENT_NAME,
|
|
132
|
+
name: title,
|
|
133
133
|
version: Xolo::Server::Version::STUB_PATCH_VERSION
|
|
134
134
|
)
|
|
135
135
|
|
|
@@ -51,7 +51,7 @@ module Xolo
|
|
|
51
51
|
start_time: Xolo::Server.start_time,
|
|
52
52
|
uptime: uptime_secs.pix_humanize_secs,
|
|
53
53
|
uptime_secs: uptime_secs,
|
|
54
|
-
|
|
54
|
+
last_automated_maint: Xolo::Server::Helpers::Maintenance.last_maint,
|
|
55
55
|
app_env: Xolo::Server.app_env,
|
|
56
56
|
test_server: Xolo::Server.test_server?,
|
|
57
57
|
data_dir: Xolo::Server::DATA_DIR,
|
|
@@ -71,6 +71,14 @@ module Xolo
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if params[:extended]
|
|
74
|
+
ssl_cert_data ||= OpenSSL::X509::Certificate.new(Xolo::Server::Configuration::SSL_CERT_FILE.read)
|
|
75
|
+
state[:ssl_cert_subject] = ssl_cert_data.subject
|
|
76
|
+
state[:ssl_cert_issuer] = ssl_cert_data.issuer
|
|
77
|
+
state[:ssl_cert_valid_from] = ssl_cert_data.not_before.localtime
|
|
78
|
+
state[:ssl_cert_expires] = ssl_cert_data.not_after.localtime
|
|
79
|
+
state[:ssl_cert_serial] = ssl_cert_data.serial
|
|
80
|
+
state[:ssl_cert_signature_algorithm] = ssl_cert_data.signature_algorithm
|
|
81
|
+
|
|
74
82
|
state[:gem_path] = Gem.paths.path
|
|
75
83
|
state[:load_path] = $LOAD_PATH
|
|
76
84
|
state[:object_locks] = Xolo::Server.object_locks
|
|
@@ -98,6 +106,8 @@ module Xolo
|
|
|
98
106
|
# run the maintenance process manually from a server admin via xadm
|
|
99
107
|
################
|
|
100
108
|
post '/maint/maint-start' do
|
|
109
|
+
@manual_maintenance = true
|
|
110
|
+
|
|
101
111
|
log_info "Starting manual server maintenance by #{session[:admin]}"
|
|
102
112
|
|
|
103
113
|
session[:admin] = "Maintenance by #{session[:admin]}"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xolo-server
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.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-
|
|
11
|
+
date: 2026-09-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|