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/oc.rb
CHANGED
@@ -1,9 +1,43 @@
|
|
1
1
|
module Hackmac
|
2
|
+
# A class that represents an OpenCore configuration and provides access to
|
3
|
+
# its remote version information
|
4
|
+
#
|
5
|
+
# The OC class encapsulates the functionality for working with OpenCore
|
6
|
+
# bootloader configurations, including retrieving remote release information
|
7
|
+
# from GitHub sources and providing access to version metadata for comparison
|
8
|
+
# and upgrade operations
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# oc = Hackmac::OC.new(config: config_obj)
|
12
|
+
# # Provides access to OpenCore version information through method calls
|
2
13
|
class OC
|
14
|
+
# The initialize method sets up an OC instance by storing the provided
|
15
|
+
# configuration object.
|
16
|
+
#
|
17
|
+
# This method takes a configuration object and assigns it to an instance
|
18
|
+
# variable for later use in accessing OC-related settings and source
|
19
|
+
# information.
|
20
|
+
#
|
21
|
+
# @param config [ Object ] the configuration object containing OC-specific
|
22
|
+
# settings and source definitions
|
3
23
|
def initialize(config:)
|
4
24
|
@config = config
|
5
25
|
end
|
6
26
|
|
27
|
+
# The remote method retrieves or creates a remote source object for
|
28
|
+
# OpenCore
|
29
|
+
#
|
30
|
+
# This method manages the initialization and caching of a remote source
|
31
|
+
# object that provides access to OpenCore release information from GitHub.
|
32
|
+
# It constructs the appropriate source configuration including
|
33
|
+
# authentication credentials and version suffix based on debug settings,
|
34
|
+
# then creates a GithubSource instance to handle communication with the
|
35
|
+
# GitHub API for retrieving release data
|
36
|
+
#
|
37
|
+
# @return [ Hackmac::GithubSource ] the cached remote source object for
|
38
|
+
# OpenCore releases
|
39
|
+
# @return [ nil ] returns nil if no OpenCore source is configured in the
|
40
|
+
# configuration
|
7
41
|
def remote
|
8
42
|
@remote and return @remote
|
9
43
|
source = @config.oc.source
|
@@ -19,21 +53,50 @@ module Hackmac
|
|
19
53
|
@remote = Hackmac::GithubSource.new(github, auth: auth, suffix: suffix)
|
20
54
|
end
|
21
55
|
|
56
|
+
# The name method retrieves the name attribute from the remote source
|
57
|
+
# object
|
58
|
+
#
|
59
|
+
# This method accesses the cached remote source object and returns its name
|
60
|
+
# information if available. It uses the safe navigation operator to avoid
|
61
|
+
# errors when the remote source has not been initialized
|
62
|
+
#
|
63
|
+
# @return [ String, nil ] the name value from the remote source, or nil if no remote source is available
|
22
64
|
def name
|
23
65
|
remote.name
|
24
66
|
end
|
25
67
|
|
68
|
+
# The version method retrieves the version identifier from the remote kext
|
69
|
+
# source
|
70
|
+
#
|
71
|
+
# This method accesses the cached remote kext source object and returns its
|
72
|
+
# version information if available. It serves as a delegate to the
|
73
|
+
# remote_kext's version attribute, providing convenient access to the
|
74
|
+
# latest version data for the kext
|
75
|
+
#
|
76
|
+
# @return [ Tins::StringVersion, String, nil ] the version object or string from
|
77
|
+
# the remote source, or nil if no remote source is available or has no version
|
78
|
+
# information
|
26
79
|
def version
|
27
80
|
remote.version
|
28
81
|
end
|
29
82
|
|
83
|
+
# The inspect method returns a string representation of the object that
|
84
|
+
# includes its class name and string value.
|
85
|
+
#
|
86
|
+
# @return [ String ] a formatted string containing the object's class name
|
87
|
+
# and its string representation
|
30
88
|
def inspect
|
31
89
|
"#<#{self.class}: #{to_s}>"
|
32
90
|
end
|
33
91
|
|
92
|
+
# The to_s method returns a string representation of the object by
|
93
|
+
# combining its name and version attributes into a single space-separated
|
94
|
+
# string.
|
95
|
+
#
|
96
|
+
# @return [ String ] a formatted string containing the name and version
|
97
|
+
# separated by a space
|
34
98
|
def to_s
|
35
99
|
"#{name} #{version}"
|
36
100
|
end
|
37
101
|
end
|
38
102
|
end
|
39
|
-
|
data/lib/hackmac/oc_upgrader.rb
CHANGED
@@ -1,16 +1,54 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
|
3
3
|
module Hackmac
|
4
|
+
# A class that handles the upgrade process for OpenCore bootloader files
|
5
|
+
#
|
6
|
+
# The OCUpgrader class manages the workflow for upgrading OpenCore bootloader
|
7
|
+
# installations by retrieving remote release information, downloading and
|
8
|
+
# decompressing new versions, and performing file system operations to replace
|
9
|
+
# existing EFI files after ensuring the installation directory is properly
|
10
|
+
# configured
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# upgrader = Hackmac::OCUpgrader.new(mdev: '/dev/disk1', config: config_obj)
|
14
|
+
# upgrader.perform
|
15
|
+
# # Executes the OpenCore upgrade process in a temporary directory context
|
4
16
|
class OCUpgrader
|
5
17
|
include FileUtils::Verbose
|
6
18
|
include Hackmac::AssetTools
|
7
19
|
|
20
|
+
# The initialize method sets up an OCUpgrader instance by configuring the
|
21
|
+
# installation directory based on the provided mount point and
|
22
|
+
# configuration.
|
23
|
+
#
|
24
|
+
# This method takes a mount device identifier and configuration object,
|
25
|
+
# then constructs the full path to the EFI installation directory by
|
26
|
+
# joining the mount path with the OpenCore EFI path specified in the
|
27
|
+
# configuration.
|
28
|
+
#
|
29
|
+
# @param mdev [ String ] the mount device identifier for the EFI partition
|
30
|
+
# @param config [ Object ] the configuration object containing OpenCore settings including the EFI path
|
31
|
+
#
|
32
|
+
# @return [ void ] Returns nothing but initializes instance variables for the upgrade process
|
8
33
|
def initialize(mdev:, config:)
|
9
34
|
@config = config
|
10
35
|
mount_path = Pathname.new('/Volumes').join(mdev)
|
11
36
|
@install_dir = Pathname.new(mount_path).join(@config.oc.efi_path)
|
12
37
|
end
|
13
38
|
|
39
|
+
# The perform method executes the OpenCore upgrade process by isolating the
|
40
|
+
# operation in a temporary directory
|
41
|
+
#
|
42
|
+
# This method handles the complete workflow for upgrading OpenCore
|
43
|
+
# bootloader files, including retrieving remote release information,
|
44
|
+
# downloading and decompressing the new version, and performing file system
|
45
|
+
# operations to replace existing EFI files after ensuring
|
46
|
+
# the installation directory is properly configured
|
47
|
+
#
|
48
|
+
# @return [ void ] Returns nothing but performs file system operations and
|
49
|
+
# user interaction
|
50
|
+
# @raise [ RuntimeError ] raised when a remote download fails or when no
|
51
|
+
# source is defined for the OpenCore upgrade
|
14
52
|
def perform
|
15
53
|
isolate do |dir|
|
16
54
|
oc = OC.new(config: @config)
|
@@ -28,6 +66,12 @@ module Hackmac
|
|
28
66
|
end
|
29
67
|
end
|
30
68
|
|
69
|
+
# The to_s method returns a string representation of the object by
|
70
|
+
# formatting the installation directory path into a descriptive string
|
71
|
+
# that indicates where the OpenCore files will be installed
|
72
|
+
#
|
73
|
+
# @return [ String ] a formatted string containing the installation path
|
74
|
+
# in the format "Installation into /path/to/installation/directory"
|
31
75
|
def to_s
|
32
76
|
'Installation into %s' % @install_dir
|
33
77
|
end
|
data/lib/hackmac/oc_validator.rb
CHANGED
@@ -1,7 +1,27 @@
|
|
1
1
|
module Hackmac
|
2
2
|
include Hackmac::AssetTools
|
3
|
-
|
3
|
+
# A class that handles validation of OpenCore bootloader configurations
|
4
|
+
#
|
5
|
+
# The OCValidator class provides functionality for verifying the correctness
|
6
|
+
# and compatibility of OpenCore EFI configuration files. It retrieves the
|
7
|
+
# latest OpenCore release, downloads the necessary validation utilities, and
|
8
|
+
# executes validation checks against a specified configuration plist file to
|
9
|
+
# ensure it meets the required standards for OpenCore bootloaders.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# validator = Hackmac::OCValidator.new(mdev: '/dev/disk1', config: config_obj)
|
13
|
+
# # Configures validation for an OpenCore installation on a specific disk
|
4
14
|
class OCValidator
|
15
|
+
# The initialize method sets up an OCValidator instance by configuring the
|
16
|
+
# path to the OpenCore configuration plist file
|
17
|
+
#
|
18
|
+
# This method takes a mount device identifier and configuration object,
|
19
|
+
# then constructs the full path to the OpenCore configuration plist file by
|
20
|
+
# joining the mount path with the EFI path and OC subdirectory specified in
|
21
|
+
# the configuration
|
22
|
+
#
|
23
|
+
# @param mdev [ String ] the mount device identifier for the EFI partition
|
24
|
+
# @param config [ Object ] the configuration object containing OpenCore settings including the EFI path
|
5
25
|
def initialize(mdev:, config:)
|
6
26
|
@config = config
|
7
27
|
mount_path = Pathname.new('/Volumes').join(mdev)
|
@@ -9,6 +29,16 @@ module Hackmac
|
|
9
29
|
Pathname.new(mount_path).join(@config.oc.efi_path).join('OC/config.plist')
|
10
30
|
end
|
11
31
|
|
32
|
+
# The perform method executes the OpenCore configuration validation process
|
33
|
+
#
|
34
|
+
# This method handles the complete workflow for validating an OpenCore
|
35
|
+
# configuration file by retrieving the latest OpenCore release, downloading
|
36
|
+
# the validation utilities, and running the ocvalidate tool against the
|
37
|
+
# specified configuration plist file
|
38
|
+
#
|
39
|
+
# @return [ Boolean ] returns true if validation succeeds, false if validation fails
|
40
|
+
# @raise [ RuntimeError ] raised when the OpenCore download fails or when no
|
41
|
+
# remote source is defined for the OpenCore upgrade
|
12
42
|
def perform
|
13
43
|
isolate do |dir|
|
14
44
|
oc = OC.new(config: @config)
|
data/lib/hackmac/plist.rb
CHANGED
@@ -2,23 +2,86 @@ require 'plist'
|
|
2
2
|
require 'shellwords'
|
3
3
|
|
4
4
|
module Hackmac
|
5
|
+
# A module that provides methods for parsing and interacting with Property
|
6
|
+
# List (plist) data
|
7
|
+
#
|
8
|
+
# The Plist module offers functionality to parse XML-formatted plist data
|
9
|
+
# from shell commands and provides convenient access to the resulting hash
|
10
|
+
# structure. It includes methods for executing commands, parsing their XML
|
11
|
+
# output, and accessing plist values through dynamic method calls.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# class MyClass
|
15
|
+
# include Hackmac::Plist
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# obj = MyClass.new
|
19
|
+
# obj.plist('diskutil', 'info', '-plist', '/dev/disk0')
|
20
|
+
# # Access parsed data through method_missing or as_hash
|
5
21
|
module Plist
|
22
|
+
# Parses XML output from a command into a plist hash
|
23
|
+
#
|
24
|
+
# This method executes a shell command and parses its XML output into a
|
25
|
+
# Ruby hash using the Plist library. The resulting hash is stored in the
|
26
|
+
# @plist instance variable for later access through other methods.
|
27
|
+
#
|
28
|
+
# @param cmd [Array<String>] command and arguments to execute
|
29
|
+
#
|
30
|
+
# @return [void] Returns nothing, but sets the @plist instance variable
|
6
31
|
def plist(*cmd)
|
7
32
|
@plist = ::Plist.parse_xml(`#{Shellwords.join(cmd)}`)
|
8
33
|
end
|
9
34
|
|
10
|
-
|
35
|
+
# Returns a duplicate of the internal plist hash
|
36
|
+
#
|
37
|
+
# This method provides access to the parsed plist data by returning a shallow copy
|
38
|
+
# of the internal @plist instance variable. This allows external code to read
|
39
|
+
# the plist contents without directly modifying the original data structure.
|
40
|
+
#
|
41
|
+
# @return [ Hash ] a duplicate of the plist hash containing the parsed XML data
|
42
|
+
def as_hash(*a)
|
11
43
|
@plist.dup
|
12
44
|
end
|
13
45
|
|
46
|
+
# The each method iterates over the parsed plist data
|
47
|
+
#
|
48
|
+
# This method provides an iterator interface for the plist hash by
|
49
|
+
# delegating to the as_hash method's each implementation. It allows callers
|
50
|
+
# to enumerate over the key-value pairs in the parsed plist structure.
|
51
|
+
#
|
52
|
+
# @yield [ key, value ] yields each key-value pair from the plist
|
53
|
+
#
|
54
|
+
# @return [ Hash ] returns the hash representation of the plist.
|
14
55
|
def each(&block)
|
15
56
|
as_hash.each(&block)
|
16
57
|
end
|
17
58
|
|
59
|
+
# The to_json method converts the parsed plist data to a JSON string
|
60
|
+
#
|
61
|
+
# This method takes the internal plist hash and serializes it into a JSON
|
62
|
+
# format using the standard to_json method from the Hash class. It provides
|
63
|
+
# a convenient way to output the plist data in JSON representation.
|
64
|
+
#
|
65
|
+
# @param a [ Array ] additional arguments to pass to the underlying to_json method
|
66
|
+
#
|
67
|
+
# @return [ String ] a JSON string representation of the plist data
|
18
68
|
def to_json(*a)
|
19
69
|
as_hash.to_json(*a)
|
20
70
|
end
|
21
71
|
|
72
|
+
# The method_missing method provides dynamic access to plist data by
|
73
|
+
# handling attribute reads and writes through method calls
|
74
|
+
#
|
75
|
+
# This method intercepts undefined method calls on objects that include the
|
76
|
+
# Plist module, allowing convenient access to plist values using
|
77
|
+
# method-style syntax. When a method name ends with an equals sign, it sets
|
78
|
+
# the corresponding plist key to the provided value. Otherwise, if the
|
79
|
+
# method name matches an existing plist key, it returns the value.
|
80
|
+
#
|
81
|
+
# @param name [ Symbol ] the method name being called
|
82
|
+
# @param a [ Array ] the arguments passed to the method
|
83
|
+
#
|
84
|
+
# @return [ Object ] the value of the plist key when reading, or nil when setting
|
22
85
|
def method_missing(name, *a)
|
23
86
|
n = name.to_s
|
24
87
|
if n =~ /(.+)=\z/
|
data/lib/hackmac/url_download.rb
CHANGED
@@ -2,19 +2,65 @@ require 'open-uri'
|
|
2
2
|
require 'tins/string_version'
|
3
3
|
|
4
4
|
module Hackmac
|
5
|
+
# A class that provides functionality for downloading assets from URLs with
|
6
|
+
# version tracking
|
7
|
+
#
|
8
|
+
# The URLDownload class encapsulates the logic for managing downloadable
|
9
|
+
# assets by storing their name, version, and download URL. It provides
|
10
|
+
# methods to retrieve the asset data and metadata, making it suitable for use
|
11
|
+
# in systems that need to track and download software components from web
|
12
|
+
# sources.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# downloader = Hackmac::URLDownload.new('Foo', '1.0.0', 'https://example.com/foo.zip')
|
16
|
+
# # Configures a download source with name, version, and URL for later retrieval
|
5
17
|
class URLDownload
|
6
18
|
include Tins::StringVersion
|
7
19
|
|
20
|
+
# The initialize method sets up a URLDownload instance by storing the
|
21
|
+
# provided name, URL, and version information.
|
22
|
+
#
|
23
|
+
# This method takes the necessary parameters to configure the download
|
24
|
+
# source and converts the version string into a Version object for later
|
25
|
+
# comparison.
|
26
|
+
#
|
27
|
+
# @param name [ String ] the descriptive name of the downloadable asset
|
28
|
+
# @param version [ String ] the version identifier to be parsed and stored
|
29
|
+
# @param url [ String ] the web address where the asset can be retrieved
|
8
30
|
def initialize(name, version, url)
|
9
31
|
@name = name
|
10
32
|
@url = url
|
11
33
|
@version = Version.new(version)
|
12
34
|
end
|
13
35
|
|
36
|
+
# The name reader method provides access to the name attribute that was set
|
37
|
+
# during object initialization.
|
38
|
+
#
|
39
|
+
# This method returns the value of the name instance variable, which
|
40
|
+
# typically represents the descriptive identifier or label associated with
|
41
|
+
# the object.
|
42
|
+
#
|
43
|
+
# @return [ String ] the name value stored in the instance variable
|
14
44
|
attr_reader :name
|
15
45
|
|
46
|
+
# The version reader method provides access to the version attribute that
|
47
|
+
# was set during object initialization.
|
48
|
+
#
|
49
|
+
# This method returns the value of the version instance variable, which
|
50
|
+
# typically represents the semantic version number associated with the
|
51
|
+
# object's current state or configuration.
|
52
|
+
#
|
53
|
+
# @return [ String, nil ] the version value stored in the instance variable, or nil if not set
|
16
54
|
attr_reader :version
|
17
55
|
|
56
|
+
# The download_asset method retrieves binary data from a configured URL
|
57
|
+
#
|
58
|
+
# This method performs an HTTP GET request to the stored URL to download
|
59
|
+
# the associated asset file. It returns both the filename derived from
|
60
|
+
# the URL and the raw binary data content.
|
61
|
+
#
|
62
|
+
# @return [ Array<String, String> ] an array containing the filename and downloaded data
|
63
|
+
# @return [ nil ] returns nil if no URL is configured for this instance
|
18
64
|
def download_asset
|
19
65
|
data = URI.open(
|
20
66
|
@url,
|
@@ -24,14 +70,30 @@ module Hackmac
|
|
24
70
|
return File.basename(@url), data
|
25
71
|
end
|
26
72
|
|
73
|
+
# The inspect method returns a string representation of the object that
|
74
|
+
# includes its class name and string value
|
75
|
+
#
|
76
|
+
# @return [ String ] a formatted string containing the object's class name
|
77
|
+
# and its string representation
|
27
78
|
def inspect
|
28
79
|
"#<#{self.class}: #{to_s}>"
|
29
80
|
end
|
30
81
|
|
82
|
+
# The to_s method returns a string representation of the object by
|
83
|
+
# combining its name and version attributes into a single
|
84
|
+
# space-separated string.
|
85
|
+
#
|
86
|
+
# @return [ String ] a formatted string containing the name and version
|
87
|
+
# separated by a space
|
31
88
|
def to_s
|
32
89
|
"#{name} #{version}"
|
33
90
|
end
|
34
91
|
|
92
|
+
# The to_s method returns a string representation of the object by
|
93
|
+
# combining its name and version attributes into a single space-separated
|
94
|
+
# string.
|
95
|
+
#
|
96
|
+
# @return [ String ] a formatted string containing the name and version separated by a space
|
35
97
|
def to_s
|
36
98
|
"#{name} #{version}"
|
37
99
|
end
|
data/lib/hackmac/utils.rb
CHANGED
@@ -8,9 +8,22 @@ require 'tmpdir'
|
|
8
8
|
require 'shellwords'
|
9
9
|
|
10
10
|
module Hackmac
|
11
|
+
# A module that provides utility methods for executing shell commands and
|
12
|
+
# interacting with users.
|
13
|
+
#
|
14
|
+
# The Utils module includes methods for running system commands with colored
|
15
|
+
# output, prompting users for input, and handling common file operations in a
|
16
|
+
# Hackmac context.
|
11
17
|
module Utils
|
12
18
|
include FileUtils
|
13
19
|
|
20
|
+
# The x method executes a shell command and displays its output with
|
21
|
+
# colorized prompts.
|
22
|
+
#
|
23
|
+
# @param cmd [ String ] the shell command to execute
|
24
|
+
# @param verbose [ TrueClass, FalseClass ] whether to show command output in verbose mode
|
25
|
+
#
|
26
|
+
# @return [ String ] the captured output of the command execution
|
14
27
|
def x(cmd, verbose: true)
|
15
28
|
prompt = cmd =~ /\A\s*sudo/ ? ?# : ?$
|
16
29
|
cmd_output = "#{prompt} #{cmd}".color(27) + (verbose ? "" : " >/dev/null".yellow)
|
@@ -27,6 +40,10 @@ module Hackmac
|
|
27
40
|
output
|
28
41
|
end
|
29
42
|
|
43
|
+
# The ask method prompts the user for a yes/no response.
|
44
|
+
#
|
45
|
+
# @param prompt [ String ] the message to display to the user
|
46
|
+
# @return [ Boolean ] true if the user answered 'y' or 'Y', false otherwise
|
30
47
|
def ask(prompt)
|
31
48
|
print prompt.bold.yellow
|
32
49
|
gets =~ /\Ay/i
|
data/lib/hackmac/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hackmac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.
|
4
|
+
version: 1.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Florian Frank
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - "~>"
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '
|
18
|
+
version: '2.6'
|
19
19
|
type: :development
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - "~>"
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: '
|
25
|
+
version: '2.6'
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: debug
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -176,7 +176,7 @@ extra_rdoc_files:
|
|
176
176
|
- lib/hackmac/utils.rb
|
177
177
|
- lib/hackmac/version.rb
|
178
178
|
files:
|
179
|
-
- ".
|
179
|
+
- ".context/code_comment.rb"
|
180
180
|
- CHANGES.md
|
181
181
|
- Gemfile
|
182
182
|
- LICENSE
|
@@ -207,7 +207,8 @@ files:
|
|
207
207
|
- lib/hackmac/utils.rb
|
208
208
|
- lib/hackmac/version.rb
|
209
209
|
homepage: http://github.com/flori/hackmac
|
210
|
-
licenses:
|
210
|
+
licenses:
|
211
|
+
- MIT
|
211
212
|
metadata: {}
|
212
213
|
rdoc_options:
|
213
214
|
- "--title"
|