Module: WifiWand::Platforms::Mac::Helper::Bundle

Defined in:
lib/wifi_wand/platforms/mac/helper/bundle.rb,
lib/wifi_wand/platforms/mac/helper/client.rb,
lib/wifi_wand/platforms/mac/helper/artifacts.rb

Defined Under Namespace

Classes: HelperQueryResult, HelperSupportStatus, TimeoutConfiguration

Constant Summary collapse

INSTALL_PARENT =
File.join(Dir.home, 'Library', 'Application Support', 'WifiWand')
MINIMUM_HELPER_VERSION =

Only enable the helper on macOS Sonoma (14.0) and newer where redactions occur

Gem::Version.new('14.0')
DISABLE_ENV_KEY =

Allows power users/CI to opt out of helper usage via environment flag

'WIFIWAND_DISABLE_MAC_HELPER'
DEFAULT_HELPER_COMMAND_TIMEOUT_SECONDS =
3.0
SCAN_NETWORKS_HELPER_COMMAND_TIMEOUT_SECONDS =
15.0
HELPER_TERMINATION_WAIT_SECONDS =
0.25
HELPER_OUTPUT_READER_JOIN_SECONDS =
0.05
MANIFEST_FILENAME =
'INSTALL_MANIFEST.json'
VERSION_DIRECTORY_NOTICE_THRESHOLD =
5
HELPER_QUERY_STATUSES =

Current-network query states. Ambiguous states preserve nil-payload fallback behavior while allowing future callers to choose a more specific fallback policy.

%i[
  success
  connected
  not_connected
  location_services_blocked
  permission_denied
  unavailable
  timeout
  error
  unknown
].freeze
AMBIGUOUS_HELPER_QUERY_STATUSES =
%i[unavailable timeout error unknown].freeze
LOCATION_SERVICES_BLOCKING_STATUSES =
%i[location_services_blocked permission_denied].freeze
BUNDLE_NAME =
'wifiwand-helper.app'
EXECUTABLE_NAME =
'wifiwand-helper'

Class Method Summary collapse

Class Method Details

.attested_bundle_files(bundle_path) ⇒ Object



69
70
71
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 69

module_function def attested_bundle_files(bundle_path)
  tracked_bundle_files(bundle_path) - (bundle_path)
end

.attested_file_mode(path) ⇒ Object



99
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 99

module_function def attested_file_mode(path) = File.stat(path).mode & 0o7777

.backup_legacy_bundle_metadata(publish_token) ⇒ Object



277
278
279
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 277

module_function def (publish_token)
  Installer.(publish_token)
end

.backup_legacy_metadata_file(path, publish_token) ⇒ Object



281
282
283
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 281

module_function def (path, publish_token)
  Installer.(path, publish_token)
end

.build_task_prerequisitesObject



77
78
79
80
81
82
83
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 77

module_function def build_task_prerequisites
  [
    source_swift_path,
    *bundle_template_input_paths,
    entitlements_path,
  ]
end

.bundle_fingerprint(bundle_path) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 36

module_function def bundle_fingerprint(bundle_path)
  digest = Digest::SHA256.new

  attested_bundle_files(bundle_path).each do |path|
    digest << relative_bundle_path(bundle_path, path)
    digest << "\0"
    digest << format('%<mode>o', mode: attested_file_mode(path))
    digest << "\0"
    digest << File.binread(path)
    digest << "\0"
  end

  digest.hexdigest
end

.bundle_release_path(publish_token) ⇒ Object



249
250
251
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 249

module_function def bundle_release_path(publish_token)
  Installer.bundle_release_path(publish_token)
end

.bundle_template_input_paths(bundle_path = source_bundle_path) ⇒ Object



73
74
75
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 73

module_function def bundle_template_input_paths(bundle_path = source_bundle_path)
  tracked_bundle_files(bundle_path) - generated_bundle_paths(bundle_path)
end

.cleanup_legacy_bundle_metadata_backups(backup_paths) ⇒ Object



293
294
295
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 293

module_function def (backup_paths)
  Installer.(backup_paths)
end

.cleanup_previous_release(previous_release_path) ⇒ Object



269
270
271
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 269

module_function def cleanup_previous_release(previous_release_path)
  Installer.cleanup_previous_release(previous_release_path)
end

.default_timeout_configurationObject



102
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 102

module_function def default_timeout_configuration = TimeoutConfiguration.new

.detect_macos_versionObject



141
142
143
144
145
146
147
148
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 141

module_function def detect_macos_version
  stdout, _stderr, status = Open3.capture3('sw_vers', '-productVersion')
  return nil unless status.success?

  normalize_detected_macos_version(stdout)
rescue Errno::ENOENT
  nil
end

.ensure_helper_installed(out_stream: $stdout, timeout_seconds: nil, timeout_configuration: default_timeout_configuration) ⇒ Object



166
167
168
169
170
171
172
173
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 166

module_function def ensure_helper_installed(out_stream: $stdout, timeout_seconds: nil,
  timeout_configuration: default_timeout_configuration)
  Installer.ensure_helper_installed(
    out_stream:            out_stream,
    timeout_configuration: timeout_configuration,
    **timeout_options(timeout_seconds)
  )
end

.entitlements_pathObject



32
33
34
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 32

module_function def entitlements_path = File.expand_path(
  '../../../../../libexec/macos/wifiwand-helper.entitlements', __dir__
)

.generated_bundle_paths(bundle_path) ⇒ Object



63
64
65
66
67
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 63

module_function def generated_bundle_paths(bundle_path)
  (bundle_path) + [
    File.join(bundle_path, 'Contents', 'MacOS', EXECUTABLE_NAME),
  ]
end

.helper_bundle_valid?(bundle_path, timeout_seconds: nil, timeout_configuration: default_timeout_configuration) ⇒ Boolean

Returns:

  • (Boolean)


188
189
190
191
192
193
194
195
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 188

module_function def helper_bundle_valid?(bundle_path, timeout_seconds: nil,
  timeout_configuration: default_timeout_configuration)
  Installer.helper_bundle_valid?(
    bundle_path,
    timeout_configuration: timeout_configuration,
    **timeout_options(timeout_seconds)
  )
end

.helper_command_timeout_seconds(command, timeout_configuration: default_timeout_configuration) ⇒ Object



104
105
106
107
108
109
110
111
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 104

module_function def helper_command_timeout_seconds(command,
  timeout_configuration: default_timeout_configuration)
  if command == 'scan-networks'
    timeout_configuration.scan_networks_helper_command_timeout_seconds
  else
    timeout_configuration.default_helper_command_timeout_seconds
  end
end

.helper_exited_within_grace_period?(wait_thr, timeout_configuration: default_timeout_configuration) ⇒ Boolean

Returns:

  • (Boolean)


225
226
227
228
229
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 225

module_function def helper_exited_within_grace_period?(wait_thr,
  timeout_configuration: default_timeout_configuration)
  Installer.helper_exited_within_grace_period?(wait_thr,
    timeout_configuration: timeout_configuration)
end

.helper_help_output?(command_output) ⇒ Boolean

Returns:

  • (Boolean)


201
202
203
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 201

module_function def helper_help_output?(command_output)
  Installer.helper_help_output?(command_output)
end

.helper_infoObject



93
94
95
96
97
98
99
100
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 93

module_function def helper_info
  {
    version:              helper_version,
    installed_bundle:     installed_bundle_path,
    installed_executable: installed_executable_path,
    source_bundle:        source_bundle_path,
  }
end

.helper_install_dir_countObject



74
75
76
77
78
79
80
81
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 74

module_function def helper_install_dir_count
  return 0 unless Dir.exist?(INSTALL_PARENT)

  Dir.children(INSTALL_PARENT)
    .select { |entry| File.directory?(File.join(INSTALL_PARENT, entry)) }
    .select { |entry| helper_install_dir_entry?(entry) }
    .count
end

.helper_install_dir_entry?(entry) ⇒ Boolean

Returns:

  • (Boolean)


83
84
85
86
87
88
89
90
91
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 83

module_function def helper_install_dir_entry?(entry)
  entry_name = entry.to_s
  return false if entry_name.empty? || entry_name.start_with?('.')

  Gem::Version.new(entry_name)
  true
rescue ArgumentError
  false
end

.helper_installed_and_valid?(timeout_seconds: nil, timeout_configuration: default_timeout_configuration) ⇒ Boolean

Returns:

  • (Boolean)


158
159
160
161
162
163
164
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 158

module_function def helper_installed_and_valid?(timeout_seconds: nil,
  timeout_configuration: default_timeout_configuration)
  Installer.helper_installed_and_valid?(
    timeout_configuration: timeout_configuration,
    **timeout_options(timeout_seconds)
  )
end

.helper_support_status_for_macos_version(version) ⇒ Object



130
131
132
133
134
135
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 130

module_function def helper_support_status_for_macos_version(version)
  HelperSupportStatus.new(
    macos_version:  version,
    parsed_version: parse_macos_version(version)
  )
end

.helper_supported_on_macos_version?(version) ⇒ Boolean

Returns:

  • (Boolean)


137
138
139
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 137

module_function def helper_supported_on_macos_version?(version)
  helper_support_status_for_macos_version(version).supported?
end

.helper_versionObject



15
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 15

module_function def helper_version = WifiWand::VERSION

.install_helper_bundle(out_stream: $stdout, force: false, timeout_configuration: default_timeout_configuration) ⇒ Object



175
176
177
178
179
180
181
182
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 175

module_function def install_helper_bundle(out_stream: $stdout, force: false,
  timeout_configuration: default_timeout_configuration)
  Installer.install_helper_bundle(
    out_stream:            out_stream,
    force:                 force,
    timeout_configuration: timeout_configuration
  )
end

.install_lock_pathObject



184
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 184

module_function def install_lock_path = Installer.install_lock_path

.install_manifest_pathObject



186
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 186

module_function def install_manifest_path = Installer.install_manifest_path

.installed_bundle_current?Boolean

Returns:

  • (Boolean)


205
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 205

module_function def installed_bundle_current? = Installer.installed_bundle_current?

.installed_bundle_pathObject



68
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 68

module_function def installed_bundle_path = File.join(versioned_install_dir, BUNDLE_NAME)

.installed_executable_pathObject



70
71
72
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 70

module_function def installed_executable_path
  File.join(installed_bundle_path, 'Contents', 'MacOS', EXECUTABLE_NAME)
end

.legacy_code_resources_pathObject



263
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 263

module_function def legacy_code_resources_path = Installer.legacy_code_resources_path


257
258
259
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 257

module_function def legacy_executable_symlink_path(publish_token)
  Installer.legacy_executable_symlink_path(publish_token)
end

.legacy_info_plist_pathObject



261
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 261

module_function def legacy_info_plist_path = Installer.legacy_info_plist_path

.migrate_legacy_bundle_to_release(release_bundle_path, publish_token) ⇒ Object



245
246
247
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 245

module_function def migrate_legacy_bundle_to_release(release_bundle_path, publish_token)
  Installer.migrate_legacy_bundle_to_release(release_bundle_path, publish_token)
end

.nonessential_hidden_bundle_file?(path) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
97
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 94

module_function def nonessential_hidden_bundle_file?(path)
  basename = File.basename(path)
  basename == '.DS_Store' || basename.start_with?('._')
end

.normalize_detected_macos_version(version) ⇒ Object



150
151
152
153
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 150

module_function def normalize_detected_macos_version(version)
  normalized_version = version.to_s.strip
  normalized_version.empty? ? nil : normalized_version
end

.parse_macos_version(version) ⇒ Object



121
122
123
124
125
126
127
128
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 121

module_function def parse_macos_version(version)
  sanitized_version = sanitize_macos_version(version)
  return nil unless sanitized_version

  Gem::Version.new(sanitized_version)
rescue ArgumentError
  nil
end


241
242
243
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 241

module_function def publish_release_symlink(release_bundle_path, publish_token)
  Installer.publish_release_symlink(release_bundle_path, publish_token)
end

.publish_staged_bundle(staged_bundle_path) ⇒ Object



237
238
239
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 237

module_function def publish_staged_bundle(staged_bundle_path)
  Installer.publish_staged_bundle(staged_bundle_path)
end

.read_install_manifestObject



307
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 307

module_function def read_install_manifest = Installer.read_install_manifest

.relative_bundle_path(bundle_path, path) ⇒ Object



90
91
92
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 90

module_function def relative_bundle_path(bundle_path, path)
  Pathname.new(path).relative_path_from(Pathname.new(bundle_path)).to_s
end

.relative_helper_path(path) ⇒ Object



85
86
87
88
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 85

module_function def relative_helper_path(path)
  repo_root = File.expand_path('../../../../..', __dir__)
  Pathname.new(path).relative_path_from(Pathname.new(repo_root)).to_s
end

.resolved_installed_bundle_targetObject



265
266
267
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 265

module_function def resolved_installed_bundle_target
  Installer.resolved_installed_bundle_target
end

.resolved_legacy_release_targetObject



301
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 301

module_function def resolved_legacy_release_target = Installer.resolved_legacy_release_target

.restore_legacy_bundle_metadata(backup_paths, publish_token) ⇒ Object



285
286
287
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 285

module_function def (backup_paths, publish_token)
  Installer.(backup_paths, publish_token)
end

.restore_legacy_metadata_file(backup_path, target_path, publish_token) ⇒ Object



289
290
291
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 289

module_function def (backup_path, target_path, publish_token)
  Installer.(backup_path, target_path, publish_token)
end

.run_bounded_helper_command(executable_path, command, timeout_seconds: nil, on_timeout: nil, timeout_configuration: default_timeout_configuration) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 207

module_function def run_bounded_helper_command(
  executable_path, command, timeout_seconds: nil, on_timeout: nil,
  timeout_configuration: default_timeout_configuration
)
  Installer.run_bounded_helper_command(
    executable_path,
    command,
    timeout_seconds:       timeout_seconds,
    on_timeout:            on_timeout,
    timeout_configuration: timeout_configuration
  )
end

.sanitize_macos_version(version) ⇒ Object



113
114
115
116
117
118
119
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 113

module_function def sanitize_macos_version(version)
  return nil unless version

  version_string = version.to_s.strip
  match = version_string.match(/\d+(?:\.\d+)*/)
  match&.[](0)
end

.signer_generated_metadata_paths(bundle_path) ⇒ Object



56
57
58
59
60
61
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 56

module_function def (bundle_path)
  [
    File.join(bundle_path, 'Contents', 'CodeResources'),
    File.join(bundle_path, 'Contents', '_CodeSignature', 'CodeResources'),
  ]
end

.source_bundle_executable_pathObject



24
25
26
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 24

module_function def source_bundle_executable_path = File.join(
  source_bundle_path, 'Contents', 'MacOS', EXECUTABLE_NAME
)

.source_bundle_pathString

Returns the path to the app bundle template in the gem's libexec directory

Returns:

  • (String)

    absolute path to the bundle template directory Example: /path/to/gem/libexec/macos/wifiwand-helper.app



21
22
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 21

module_function def source_bundle_path = File.expand_path(
'../../../../../libexec/macos/wifiwand-helper.app', __dir__)

.source_swift_pathObject



28
29
30
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 28

module_function def source_swift_path = File.expand_path(
  '../../../../../libexec/macos/src/wifiwand-helper.swift', __dir__
)

.stage_helper_bundle(staged_bundle_path) ⇒ Object



233
234
235
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 233

module_function def stage_helper_bundle(staged_bundle_path)
  Installer.stage_helper_bundle(staged_bundle_path)
end


253
254
255
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 253

module_function def staged_bundle_symlink_path(publish_token)
  Installer.staged_bundle_symlink_path(publish_token)
end

.switch_legacy_bundle_executable(release_bundle_path, publish_token) ⇒ Object



297
298
299
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 297

module_function def switch_legacy_bundle_executable(release_bundle_path, publish_token)
  Installer.switch_legacy_bundle_executable(release_bundle_path, publish_token)
end

.sync_legacy_bundle_metadata(release_bundle_path, publish_token) ⇒ Object



273
274
275
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 273

module_function def (release_bundle_path, publish_token)
  Installer.(release_bundle_path, publish_token)
end

.terminate_helper_process(wait_thr, timeout_configuration: default_timeout_configuration) ⇒ Object



220
221
222
223
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 220

module_function def terminate_helper_process(wait_thr,
  timeout_configuration: default_timeout_configuration)
  Installer.terminate_helper_process(wait_thr, timeout_configuration: timeout_configuration)
end

.timeout_options(timeout_seconds) ⇒ Object



197
198
199
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 197

module_function def timeout_options(timeout_seconds)
  timeout_seconds ? { timeout_seconds: timeout_seconds } : {}
end

.tracked_bundle_files(bundle_path) ⇒ Object



51
52
53
54
# File 'lib/wifi_wand/platforms/mac/helper/artifacts.rb', line 51

module_function def tracked_bundle_files(bundle_path) = Dir.glob(
  File.join(bundle_path, '**', '*'),
  File::FNM_DOTMATCH
).select { |path| File.file?(path) && !nonessential_hidden_bundle_file?(path) }.sort

.unique_publish_tokenObject



303
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 303

module_function def unique_publish_token = Installer.unique_publish_token

.versioned_install_dirObject



66
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 66

module_function def versioned_install_dir = File.join(INSTALL_PARENT, helper_version)

.with_install_lockObject



231
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 231

module_function def with_install_lock(&) = Installer.with_install_lock(&)

.write_manifestObject



305
# File 'lib/wifi_wand/platforms/mac/helper/bundle.rb', line 305

module_function def write_manifest = Installer.write_manifest