hackmac 1.8.2 → 1.8.3
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/.context/code_comment.rb +15 -0
- data/CHANGES.md +347 -108
- data/README.md +78 -32
- data/Rakefile +4 -0
- data/VERSION +1 -1
- data/bin/efi +70 -11
- data/bin/gfxmon +81 -2
- data/bin/usb +26 -2
- data/hackmac.gemspec +5 -4
- data/lib/hackmac/asset_tools.rb +42 -1
- data/lib/hackmac/config.rb +32 -0
- data/lib/hackmac/disks.rb +72 -3
- data/lib/hackmac/github_source.rb +84 -1
- data/lib/hackmac/graph/display.rb +331 -0
- data/lib/hackmac/graph.rb +263 -3
- data/lib/hackmac/ioreg.rb +24 -2
- data/lib/hackmac/kext.rb +89 -0
- data/lib/hackmac/kext_upgrader.rb +38 -0
- data/lib/hackmac/oc.rb +64 -1
- data/lib/hackmac/oc_upgrader.rb +44 -0
- data/lib/hackmac/oc_validator.rb +31 -1
- data/lib/hackmac/plist.rb +64 -1
- data/lib/hackmac/url_download.rb +62 -0
- data/lib/hackmac/utils.rb +17 -0
- data/lib/hackmac/version.rb +1 -1
- metadata +6 -5
- data/.gitignore +0 -9
data/lib/hackmac/graph.rb
CHANGED
@@ -2,31 +2,139 @@ require 'term/ansicolor'
|
|
2
2
|
require 'tins'
|
3
3
|
require 'digest/md5'
|
4
4
|
|
5
|
+
# A class that provides graphical display functionality for terminal-based data
|
6
|
+
# visualization
|
7
|
+
#
|
8
|
+
# The Graph class enables the creation of dynamic, real-time visualizations of
|
9
|
+
# data values within a terminal environment. It manages the rendering of
|
10
|
+
# graphical representations such as line charts or graphs, updating them
|
11
|
+
# continuously based on provided data sources. The class handles terminal
|
12
|
+
# control operations, including cursor positioning, color management, and
|
13
|
+
# screen clearing to ensure smooth visual updates. It also supports
|
14
|
+
# configuration of display parameters like title, formatting strategies for
|
15
|
+
# values, update intervals, and color schemes for different data series.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# graph = Hackmac::Graph.new(
|
19
|
+
# title: 'CPU Usage',
|
20
|
+
# value: ->(i) { rand(100) },
|
21
|
+
# format_value: :as_percent,
|
22
|
+
# sleep: 1,
|
23
|
+
# color: 33
|
24
|
+
# )
|
25
|
+
# graph.start
|
26
|
+
# # Starts the graphical display loop
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# graph = Hackmac::Graph.new(
|
30
|
+
# title: 'Memory Usage',
|
31
|
+
# value: ->(i) { `vm_stat`.match(/Pages free: (\d+)/)[1].to_i },
|
32
|
+
# format_value: :as_bytes,
|
33
|
+
# sleep: 2
|
34
|
+
# )
|
35
|
+
# graph.start
|
36
|
+
# # Starts a memory usage graph with custom data source and formatting
|
5
37
|
class Hackmac::Graph
|
6
38
|
include Term::ANSIColor
|
7
39
|
|
8
|
-
|
40
|
+
# A module that provides various formatting methods for converting numeric
|
41
|
+
# values into human-readable strings with appropriate units and display
|
42
|
+
# formats.
|
43
|
+
#
|
44
|
+
# The Formatters module contains a collection of utility methods designed to
|
45
|
+
# transform raw numeric data into formatted strings that are more suitable
|
46
|
+
# for display purposes. These methods handle common formatting tasks such as
|
47
|
+
# converting byte measurements, frequency values, temperature readings, and
|
48
|
+
# percentages into clean, readable representations.
|
49
|
+
#
|
50
|
+
# Each formatter method in this module is intended to be used with data
|
51
|
+
# visualization or reporting scenarios where presenting numerical information
|
52
|
+
# in an easily understandable format is important. The module also includes
|
53
|
+
# specialized functionality for deriving consistent color values based on
|
54
|
+
# input strings, which can be useful for maintaining visual coherence when
|
55
|
+
# displaying multiple data series.
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# include Hackmac::Graph::Formatters
|
59
|
+
#
|
60
|
+
# as_bytes(1024 * 1024) # => "1.000MB"
|
61
|
+
# as_hertz(2500000000) # => "2.500GHz"
|
62
|
+
# as_celsius(37.5) # => "37.5°"
|
63
|
+
# as_percent(95.7) # => "95.7%"
|
64
|
+
# as_default(42) # => "42"
|
9
65
|
module Formatters
|
66
|
+
# The as_bytes method formats a numeric value into a human-readable byte
|
67
|
+
# representation
|
68
|
+
#
|
69
|
+
# This method takes a numeric input and converts it into a formatted string
|
70
|
+
# representing the value in bytes with appropriate binary prefixes (KB,
|
71
|
+
# MB, GB, etc.)
|
72
|
+
#
|
73
|
+
# @param value [ Numeric ] the numeric value to be formatted as bytes
|
74
|
+
#
|
75
|
+
# @return [ String ] the formatted byte representation with unit suffix
|
10
76
|
def as_bytes(value)
|
11
77
|
Tins::Unit.format(value, prefix: :uc, format: '%.3f%U', unit: 'B')
|
12
78
|
end
|
13
79
|
|
80
|
+
# The as_hertz method formats a numeric value into a human-readable
|
81
|
+
# frequency representation
|
82
|
+
#
|
83
|
+
# This method takes a numeric input and converts it into a formatted string
|
84
|
+
# representing the value in hertz with appropriate metric prefixes (kHz,
|
85
|
+
# MHz, GHz, etc.)
|
86
|
+
#
|
87
|
+
# @param value [ Numeric ] the numeric frequency value to be formatted
|
88
|
+
#
|
89
|
+
# @return [ String ] the formatted frequency representation with unit suffix
|
14
90
|
def as_hertz(value)
|
15
91
|
Tins::Unit.format(value, prefix: :uc, format: '%.3f%U', unit: 'Hz')
|
16
92
|
end
|
17
93
|
|
94
|
+
# The as_celsius method formats a temperature value with a degree symbol
|
95
|
+
#
|
96
|
+
# This method takes a numeric temperature value and returns a string
|
97
|
+
# representation with the degree Celsius symbol appended to it
|
98
|
+
#
|
99
|
+
# @param value [ Numeric ] the temperature value to be formatted
|
100
|
+
#
|
101
|
+
# @return [ String ] the temperature value formatted with a degree Celsius symbol
|
18
102
|
def as_celsius(value)
|
19
103
|
"#{value}°"
|
20
104
|
end
|
21
105
|
|
106
|
+
# The as_percent method formats a numeric value as a percentage string
|
107
|
+
#
|
108
|
+
# This method takes a numeric input and returns a string representation
|
109
|
+
# with a percent sign appended to it, providing a simple way to format
|
110
|
+
# numeric values as percentages for display purposes
|
111
|
+
#
|
112
|
+
# @param value [ Numeric ] the numeric value to be formatted as a percentage
|
113
|
+
#
|
114
|
+
# @return [ String ] the numeric value formatted as a percentage string
|
22
115
|
def as_percent(value)
|
23
116
|
"#{value}%"
|
24
117
|
end
|
25
118
|
|
119
|
+
# The as_default method converts a value to its string representation
|
120
|
+
#
|
121
|
+
# This method takes any input value and converts it to a string using the to_s method
|
122
|
+
# It serves as a fallback formatting option when no specific formatting is required
|
123
|
+
#
|
124
|
+
# @param value [ Object ] the value to be converted to a string
|
125
|
+
#
|
126
|
+
# @return [ String ] the string representation of the input value
|
26
127
|
def as_default(value)
|
27
128
|
value.to_s
|
28
129
|
end
|
29
130
|
|
131
|
+
# The derive_color_from_string method calculates a color value based on the
|
132
|
+
# input string by generating an MD5 hash and selecting from a predefined
|
133
|
+
# set of dark colors
|
134
|
+
#
|
135
|
+
# @param string [ String ] the input string used to derive the color value
|
136
|
+
#
|
137
|
+
# @return [ Integer ] the derived color value from the set of dark ANSI attributes
|
30
138
|
def derive_color_from_string(string)
|
31
139
|
cs = (21..226).select { |d|
|
32
140
|
Term::ANSIColor::Attribute[d].to_rgb_triple.to_hsl_triple.
|
@@ -38,7 +146,23 @@ class Hackmac::Graph
|
|
38
146
|
|
39
147
|
self
|
40
148
|
end
|
41
|
-
|
149
|
+
include Formatters
|
150
|
+
|
151
|
+
# The initialize method sets up a Graph instance by configuring its display
|
152
|
+
# parameters and internal state
|
153
|
+
#
|
154
|
+
# This method configures the graph visualization with title, value provider,
|
155
|
+
# formatting options, update interval, and color settings. It initializes
|
156
|
+
# internal data structures for storing historical values and manages
|
157
|
+
# synchronization through a mutex for thread-safe operations.
|
158
|
+
#
|
159
|
+
# @param title [ String ] the title to display at the bottom of the graph
|
160
|
+
# @param value [ Proc ] a proc that takes an index and returns a numeric value for plotting
|
161
|
+
# @param format_value [ Proc, Symbol, nil ] formatting strategy for displaying values
|
162
|
+
# @param sleep [ Numeric ] time in seconds between updates
|
163
|
+
# @param color [ Integer, Proc, nil ] color index or proc to determine color dynamically
|
164
|
+
#
|
165
|
+
# @raise [ ArgumentError ] if the sleep parameter is negative
|
42
166
|
def initialize(
|
43
167
|
title:,
|
44
168
|
value: -> i { 0 },
|
@@ -57,12 +181,28 @@ class Hackmac::Graph
|
|
57
181
|
@mutex = Mutex.new
|
58
182
|
end
|
59
183
|
|
184
|
+
# The start method initiates the graphical display process by setting up
|
185
|
+
# signal handlers, performing an initial terminal reset, and entering the
|
186
|
+
# main update loop
|
187
|
+
#
|
188
|
+
# This method serves as the entry point for starting the graph visualization
|
189
|
+
# functionality. It configures the necessary signal handlers for graceful
|
190
|
+
# shutdown and terminal resizing, performs an initial full reset of the
|
191
|
+
# display state, and then begins the continuous loop that updates and renders
|
192
|
+
# graphical data.
|
60
193
|
def start
|
61
194
|
install_handlers
|
62
195
|
full_reset
|
63
196
|
start_loop
|
64
197
|
end
|
65
198
|
|
199
|
+
# The stop method terminates the graphical display process by performing a
|
200
|
+
# full reset and setting the continue flag to false
|
201
|
+
#
|
202
|
+
# This method serves as the shutdown mechanism for the graph visualization
|
203
|
+
# functionality. It ensures that all display resources are properly cleaned
|
204
|
+
# up and the terminal state is restored to its original condition before
|
205
|
+
# stopping the continuous update loop.
|
66
206
|
def stop
|
67
207
|
full_reset
|
68
208
|
@continue = false
|
@@ -70,6 +210,16 @@ class Hackmac::Graph
|
|
70
210
|
|
71
211
|
private
|
72
212
|
|
213
|
+
# The start_loop method executes a continuous loop to update and display
|
214
|
+
# graphical data
|
215
|
+
#
|
216
|
+
# This method manages the main execution loop for rendering graphical
|
217
|
+
# representations of data values over time. It initializes display state,
|
218
|
+
# processes incoming data, calculates visual representations, and handles
|
219
|
+
# terminal updates while respecting configured timing intervals.
|
220
|
+
#
|
221
|
+
# It continuously updates the display and handles data processing in a loop
|
222
|
+
# until explicitly stopped.
|
73
223
|
def start_loop
|
74
224
|
full_reset
|
75
225
|
color = pick_color
|
@@ -120,23 +270,60 @@ class Hackmac::Graph
|
|
120
270
|
end
|
121
271
|
|
122
272
|
def perform(*a)
|
123
|
-
print
|
273
|
+
print(*a)
|
124
274
|
end
|
125
275
|
|
276
|
+
# The columns method returns the number of columns in the display
|
277
|
+
#
|
278
|
+
# This method provides access to the horizontal dimension of the graphical
|
279
|
+
# display by returning the total number of columns available for rendering
|
280
|
+
# content
|
281
|
+
#
|
282
|
+
# @return [ Integer ] the number of columns (characters per line) in the display object
|
126
283
|
def columns
|
127
284
|
@display.columns
|
128
285
|
end
|
129
286
|
|
287
|
+
# The lines method returns the number of lines in the display
|
288
|
+
#
|
289
|
+
# This method provides access to the vertical dimension of the graphical
|
290
|
+
# display by returning the total number of rows available for rendering
|
291
|
+
# content
|
292
|
+
#
|
293
|
+
# @return [ Integer ] the number of lines (rows) in the display object
|
130
294
|
def lines
|
131
295
|
@display.lines
|
132
296
|
end
|
133
297
|
|
298
|
+
# The data reader method provides access to the data attribute that was set
|
299
|
+
# during object initialization.
|
300
|
+
#
|
301
|
+
# This method returns the value of the data instance variable, which
|
302
|
+
# typically contains structured information that has been processed or
|
303
|
+
# collected by the object.
|
304
|
+
#
|
305
|
+
# @return [ Array<Object>, Hash, nil ] the data value stored in the instance variable, or nil if not set
|
134
306
|
attr_reader :data
|
135
307
|
|
308
|
+
# The sleep_duration method returns a string representation of the configured
|
309
|
+
# sleep interval with the 's' suffix appended to indicate seconds.
|
310
|
+
#
|
311
|
+
# @return [ String ] a formatted string containing the sleep duration in seconds
|
136
312
|
def sleep_duration
|
137
313
|
"#{@sleep}s"
|
138
314
|
end
|
139
315
|
|
316
|
+
# The format_value method processes a given value using the configured
|
317
|
+
# formatting strategy
|
318
|
+
#
|
319
|
+
# This method applies the appropriate formatting to a value based on the
|
320
|
+
# @format_value instance variable configuration It supports different
|
321
|
+
# formatting approaches including custom Proc objects, Symbol-based method
|
322
|
+
# calls, and default formatting
|
323
|
+
#
|
324
|
+
# @param value [ Object ] the value to be formatted according to the configured strategy
|
325
|
+
#
|
326
|
+
# @return [ String ] the formatted string representation of the input value
|
140
327
|
def format_value(value)
|
141
328
|
case @format_value
|
142
329
|
when Proc
|
@@ -148,6 +335,28 @@ class Hackmac::Graph
|
|
148
335
|
end
|
149
336
|
end
|
150
337
|
|
338
|
+
# The pick_color method determines and returns an ANSI color attribute based
|
339
|
+
# on the configured color setting
|
340
|
+
#
|
341
|
+
# This method evaluates the @color instance variable to decide how to select
|
342
|
+
# a color attribute. If @color is a Proc, it invokes the proc with the @title
|
343
|
+
# to determine the color. If @color is nil, it derives a color from the title
|
344
|
+
# string. Otherwise, it uses the @color value directly as an index into the
|
345
|
+
# ANSI color attributes.
|
346
|
+
#
|
347
|
+
# @return [ Term::ANSIColor::Attribute ] the selected color attribute object
|
348
|
+
def pick_color
|
349
|
+
Term::ANSIColor::Attribute[
|
350
|
+
case @color
|
351
|
+
when Proc
|
352
|
+
@color.(@title)
|
353
|
+
when nil
|
354
|
+
derive_color_from_string(@title)
|
355
|
+
else
|
356
|
+
@color
|
357
|
+
end
|
358
|
+
]
|
359
|
+
end
|
151
360
|
def pick_color
|
152
361
|
Term::ANSIColor::Attribute[
|
153
362
|
case @color
|
@@ -161,6 +370,14 @@ class Hackmac::Graph
|
|
161
370
|
]
|
162
371
|
end
|
163
372
|
|
373
|
+
# The sleep_now method calculates and executes a sleep duration based on the
|
374
|
+
# configured sleep time and elapsed time since start
|
375
|
+
#
|
376
|
+
# This method determines how long to sleep by calculating the difference
|
377
|
+
# between the configured sleep interval and the time elapsed since the last
|
378
|
+
# operation started. If no start time is recorded, it uses the full
|
379
|
+
# configured sleep duration. The method ensures that negative sleep durations
|
380
|
+
# are not used by taking the maximum of the calculated duration and zero.
|
164
381
|
def sleep_now
|
165
382
|
duration = if @start
|
166
383
|
[ @sleep - (Time.now - @start).to_f, 0 ].max
|
@@ -170,6 +387,22 @@ class Hackmac::Graph
|
|
170
387
|
sleep duration
|
171
388
|
end
|
172
389
|
|
390
|
+
# The perform_display_diff method calculates and displays the difference
|
391
|
+
# between the current and previous display states to update only the changed
|
392
|
+
# portions of the terminal output
|
393
|
+
#
|
394
|
+
# This method synchronizes access to shared display resources using a mutex,
|
395
|
+
# then compares the current display with the previous state to determine what
|
396
|
+
# needs updating. It handles dimension mismatches by resetting the old
|
397
|
+
# display, computes the visual difference, and outputs only the modified
|
398
|
+
# portions to reduce terminal update overhead
|
399
|
+
#
|
400
|
+
# When the DEBUG_BYTESIZE environment variable is set, it also outputs
|
401
|
+
# debugging information about the size of the diff and the time elapsed since
|
402
|
+
# the last debug output
|
403
|
+
#
|
404
|
+
# @return [ void ] Returns nothing but performs terminal output operations
|
405
|
+
# and updates internal display state
|
173
406
|
def perform_display_diff
|
174
407
|
@mutex.synchronize do
|
175
408
|
unless @old_display && @old_display.dimensions == @display.dimensions
|
@@ -191,6 +424,18 @@ class Hackmac::Graph
|
|
191
424
|
end
|
192
425
|
|
193
426
|
|
427
|
+
# The normalize_value method converts a value to its appropriate numeric
|
428
|
+
# representation
|
429
|
+
#
|
430
|
+
# This method takes an input value and normalizes it to either a Float or
|
431
|
+
# Integer type depending on its original form. If the value is already a
|
432
|
+
# Float, it is returned as-is. For all other types, the method attempts to
|
433
|
+
# convert the value to an integer using to_i
|
434
|
+
#
|
435
|
+
# @param value [ Object ] the value to be normalized
|
436
|
+
#
|
437
|
+
# @return [ Float, Integer ] the normalized numeric value as either a Float
|
438
|
+
# or Integer
|
194
439
|
def normalize_value(value)
|
195
440
|
case value
|
196
441
|
when Float
|
@@ -200,6 +445,15 @@ class Hackmac::Graph
|
|
200
445
|
end
|
201
446
|
end
|
202
447
|
|
448
|
+
# The full_reset method performs a complete reset of the display and terminal
|
449
|
+
# state
|
450
|
+
#
|
451
|
+
# This method synchronizes access to shared resources using a mutex, then
|
452
|
+
# executes a series of terminal control operations to reset the terminal
|
453
|
+
# state, clear the screen, move the cursor to the home position, and make the
|
454
|
+
# cursor visible. It also initializes new display objects with the current
|
455
|
+
# terminal dimensions and updates the internal
|
456
|
+
# display state.
|
203
457
|
def full_reset
|
204
458
|
@mutex.synchronize do
|
205
459
|
perform reset, clear_screen, move_home, show_cursor
|
@@ -211,6 +465,12 @@ class Hackmac::Graph
|
|
211
465
|
end
|
212
466
|
end
|
213
467
|
|
468
|
+
# The install_handlers method sets up signal handlers for graceful shutdown
|
469
|
+
# and terminal resize handling
|
470
|
+
#
|
471
|
+
# This method configures two signal handlers: one for the exit hook that
|
472
|
+
# performs a full reset, and another for the SIGWINCH signal that handles
|
473
|
+
# terminal resize events by setting a flag and displaying a sleeping message
|
214
474
|
def install_handlers
|
215
475
|
at_exit { full_reset }
|
216
476
|
trap(:SIGWINCH) do
|
data/lib/hackmac/ioreg.rb
CHANGED
@@ -1,11 +1,33 @@
|
|
1
1
|
require 'hashie'
|
2
2
|
|
3
3
|
module Hackmac
|
4
|
+
# A class that provides access to IORegistry information through plist
|
5
|
+
# parsing
|
6
|
+
#
|
7
|
+
# The IOReg class interfaces with macOS's IORegistry system to retrieve and
|
8
|
+
# parse hardware-related information for a specified key. It executes the
|
9
|
+
# ioreg command with appropriate flags to fetch XML data, processes this data
|
10
|
+
# into a structured hash format, and makes it available for querying through
|
11
|
+
# dynamic method calls.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# ioreg = Hackmac::IOReg.new(key: 'IOPowerManagement')
|
15
|
+
# # Provides access to power management information through method calls
|
4
16
|
class IOReg
|
5
17
|
include Hackmac::Plist
|
6
|
-
|
18
|
+
# The initialize method sets up an IOReg instance by executing a system
|
19
|
+
# command to retrieve hardware registry information for a specific key.
|
20
|
+
#
|
21
|
+
# This method constructs and runs a shell command using the ioreg utility
|
22
|
+
# to fetch detailed information about a specified hardware key from the
|
23
|
+
# IOService registry. It processes the XML output, extends the resulting
|
24
|
+
# hash with deep find capabilities, and stores the largest matching result
|
25
|
+
# set in the instance variable.
|
26
|
+
#
|
27
|
+
# @param key [ String ] the hardware registry key to search for in the
|
28
|
+
# IOService tree
|
7
29
|
def initialize(key:)
|
8
|
-
plist
|
30
|
+
plist(*(%w[ioreg -a -p IOService -r -k ] << key))
|
9
31
|
@plist.extend Hashie::Extensions::DeepFind
|
10
32
|
@plist = @plist.deep_find_all(key).max_by(&:size)
|
11
33
|
end
|
data/lib/hackmac/kext.rb
CHANGED
@@ -3,10 +3,33 @@ require 'pathname'
|
|
3
3
|
require 'tins/string_version'
|
4
4
|
|
5
5
|
module Hackmac
|
6
|
+
|
7
|
+
# A class that represents a kernel extension (kext) and provides access to
|
8
|
+
# its metadata and remote version information
|
9
|
+
#
|
10
|
+
# The Kext class encapsulates the functionality for working with macOS kernel
|
11
|
+
# extensions, including loading their Info.plist files, extracting metadata
|
12
|
+
# such as identifiers and versions, and determining whether newer versions
|
13
|
+
# are available from remote sources
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# kext = Hackmac::Kext.new(path: '/path/to/MyKext.kext')
|
17
|
+
# # Provides access to kext metadata through method calls
|
6
18
|
class Kext
|
7
19
|
include Hackmac::Plist
|
8
20
|
include Tins::StringVersion
|
9
21
|
|
22
|
+
# The initialize method sets up a Kext instance by loading and parsing its
|
23
|
+
# Info.plist file
|
24
|
+
#
|
25
|
+
# This method takes a path to a kext directory and optionally a
|
26
|
+
# configuration object, then reads the Info.plist file within the kext's
|
27
|
+
# Contents directory to extract metadata about the kernel extension. The
|
28
|
+
# plist data is parsed and stored for later access through dynamic method
|
29
|
+
# calls.
|
30
|
+
#
|
31
|
+
# @param path [ String ] the filesystem path to the kext directory
|
32
|
+
# @param config [ Object, nil ] optional configuration object that may provide source information
|
10
33
|
def initialize(path:, config: nil)
|
11
34
|
@path = path
|
12
35
|
info = Pathname.new(@path) + 'Contents/Info.plist'
|
@@ -14,16 +37,49 @@ module Hackmac
|
|
14
37
|
@config = config
|
15
38
|
end
|
16
39
|
|
40
|
+
# The path reader method provides access to the path attribute that was set
|
41
|
+
# during object initialization.
|
42
|
+
#
|
43
|
+
# This method returns the value of the path instance variable, which
|
44
|
+
# typically represents the filesystem path associated with the object.
|
45
|
+
#
|
46
|
+
# @return [ String ] the path value stored in the instance variable
|
17
47
|
attr_reader :path
|
18
48
|
|
49
|
+
# The identifier method retrieves the bundle identifier from the kext's
|
50
|
+
# Info.plist file
|
51
|
+
#
|
52
|
+
# This method accesses the CFBundleIdentifier key from the parsed plist
|
53
|
+
# data of a kernel extension, providing the unique identifier that
|
54
|
+
# distinguishes this particular kext within the system
|
55
|
+
#
|
56
|
+
# @return [ String ] the bundle identifier of the kernel extension
|
57
|
+
# @return [ nil ] returns nil if the CFBundleIdentifier key is not present in the plist
|
19
58
|
def identifier
|
20
59
|
CFBundleIdentifier()
|
21
60
|
end
|
22
61
|
|
62
|
+
# The name method retrieves the display name for the kernel extension
|
63
|
+
#
|
64
|
+
# This method attempts to return the CFBundleName value from the kext's
|
65
|
+
# Info.plist file and falls back to using the basename of the bundle
|
66
|
+
# identifier if that value is not present
|
67
|
+
#
|
68
|
+
# @return [ String ] the human-readable name of the kernel extension
|
69
|
+
# @return [ nil ] returns nil if neither CFBundleName nor identifier is available
|
23
70
|
def name
|
24
71
|
CFBundleName() || File.basename(identifier)
|
25
72
|
end
|
26
73
|
|
74
|
+
# The version method retrieves and caches the version identifier from the
|
75
|
+
# kext's Info.plist file
|
76
|
+
#
|
77
|
+
# This method attempts to extract the CFBundleShortVersionString value from
|
78
|
+
# the parsed plist data and convert it into a Version object for proper
|
79
|
+
# semantic versioning comparison. If the version string is not a valid
|
80
|
+
# semantic version, it stores the raw string instead.
|
81
|
+
#
|
82
|
+
# @return [ Tins::StringVersion, String, nil ] the version object or string if found, nil otherwise
|
27
83
|
def version
|
28
84
|
unless @version
|
29
85
|
if version = CFBundleShortVersionString()
|
@@ -37,6 +93,18 @@ module Hackmac
|
|
37
93
|
@version
|
38
94
|
end
|
39
95
|
|
96
|
+
# The remote_kext method retrieves or creates a remote source object for a
|
97
|
+
# kext
|
98
|
+
#
|
99
|
+
# This method attempts to return an existing cached remote kext source or
|
100
|
+
# constructs a new one based on the configuration source for this kext. It
|
101
|
+
# supports both GitHub-based sources and direct URL downloads, determining
|
102
|
+
# the appropriate source type from the configuration and creating the
|
103
|
+
# corresponding source object.
|
104
|
+
#
|
105
|
+
# @return [ Hackmac::GithubSource, Hackmac::URLDownload, nil ] returns the remote
|
106
|
+
# source object if a configuration source exists, or nil if no source is
|
107
|
+
# configured or the kext has no associated configuration
|
40
108
|
def remote_kext
|
41
109
|
return @remote_kext if @remote_kext
|
42
110
|
if @config
|
@@ -58,14 +126,35 @@ module Hackmac
|
|
58
126
|
end
|
59
127
|
end
|
60
128
|
|
129
|
+
# The remote_version method retrieves the version identifier from the
|
130
|
+
# remote kext source
|
131
|
+
#
|
132
|
+
# This method accesses the cached remote kext source object and returns its
|
133
|
+
# version information if available. It uses the safe navigation operator to
|
134
|
+
# avoid errors when the remote kext source has not been initialized or does
|
135
|
+
# not have version data
|
136
|
+
#
|
137
|
+
# @return [ Tins::StringVersion, String, nil ] the version object or string from
|
138
|
+
# the remote source, or nil if no remote source is available or has no version
|
139
|
+
# information
|
61
140
|
def remote_version
|
62
141
|
remote_kext&.version
|
63
142
|
end
|
64
143
|
|
144
|
+
# The inspect method returns a string representation of the object
|
145
|
+
# that includes its class name and string value
|
146
|
+
#
|
147
|
+
# @return [ String ] a formatted string containing the object's class name
|
148
|
+
# and its string representation
|
65
149
|
def inspect
|
66
150
|
"#<#{self.class}: #{to_s}>"
|
67
151
|
end
|
68
152
|
|
153
|
+
# The to_s method returns a string representation of the object by
|
154
|
+
# combining its name and version attributes into a single space-separated
|
155
|
+
# string.
|
156
|
+
#
|
157
|
+
# @return [ String ] a formatted string containing the name and version separated by a space
|
69
158
|
def to_s
|
70
159
|
"#{name} #{version}"
|
71
160
|
end
|
@@ -3,16 +3,54 @@ require 'find'
|
|
3
3
|
require 'pathname'
|
4
4
|
|
5
5
|
module Hackmac
|
6
|
+
# A class that handles the upgrade process for kernel extensions (kexts)
|
7
|
+
#
|
8
|
+
# The KextUpgrader class manages the workflow for upgrading macOS kernel extensions
|
9
|
+
# by checking remote version availability, downloading new versions, and performing
|
10
|
+
# file system operations to replace existing kext files after user confirmation
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# upgrader = Hackmac::KextUpgrader.new(path: '/path/to/kext', config: config_obj)
|
14
|
+
# upgrader.perform
|
15
|
+
# # Executes the kext upgrade process in a temporary directory context
|
6
16
|
class KextUpgrader
|
7
17
|
include FileUtils
|
8
18
|
include Hackmac::AssetTools
|
9
19
|
|
20
|
+
# The initialize method sets up a KextUpgrader instance by storing the
|
21
|
+
# provided path, configuration, and force flag.
|
22
|
+
#
|
23
|
+
# This method takes the necessary parameters to configure the kext
|
24
|
+
# upgrader, including the filesystem path to the kext, the configuration
|
25
|
+
# object that defines source information, and a force flag that determines
|
26
|
+
# whether upgrades should be performed even when the remote version is not
|
27
|
+
# newer than the local version.
|
28
|
+
#
|
29
|
+
# @param path [ String ] the filesystem path to the kext directory that
|
30
|
+
# needs upgrading
|
31
|
+
# @param config [ Object ] the configuration object containing source
|
32
|
+
# information for the kext
|
33
|
+
# @param force [ TrueClass, FalseClass ] flag indicating whether to force
|
34
|
+
# upgrade even if remote version is not newer
|
10
35
|
def initialize(path:, config:, force: false)
|
11
36
|
@path, @config, @force = path, config, force
|
12
37
|
end
|
13
38
|
|
14
39
|
public
|
15
40
|
|
41
|
+
# The perform method executes the kext upgrade process by isolating the
|
42
|
+
# operation in a temporary directory
|
43
|
+
#
|
44
|
+
# This method handles the complete workflow for upgrading a kernel
|
45
|
+
# extension, including checking for remote version availability,
|
46
|
+
# downloading and decompressing the new version, identifying target
|
47
|
+
# installation paths, and performing the actual file replacement after user
|
48
|
+
# confirmation
|
49
|
+
#
|
50
|
+
# @return [ void ] Returns nothing but performs file system operations and
|
51
|
+
# user interaction
|
52
|
+
# @raise [ RuntimeError ] raised when a remote download fails or when no
|
53
|
+
# source is defined for the kext
|
16
54
|
def perform
|
17
55
|
isolate do |dir|
|
18
56
|
kext = Kext.new(path: @path, config: @config)
|