Module: Homebrew::API Private

Extended by:
Utils::Output::Mixin
Defined in:
api.rb,
api/env.rb,
api/cask.rb,
api/formula.rb,
api/internal.rb,
api/analytics.rb,
api/cask_struct.rb,
api/cask_download.rb,
api/json_download.rb,
api/formula_bottle.rb,
api/formula_struct.rb,
api/packages_index.rb,
api/source_download.rb,
api/cask/cask_struct_generator.rb,
api/formula/formula_struct_generator.rb

Overview

This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.

Helper functions for using Homebrew's formulae.brew.sh API.

Defined Under Namespace

Modules: Analytics, Cask, CaskDownload, Formula, FormulaBottle, Internal Classes: CaskStruct, FormulaStruct, JSONDownload, JSONDownloadStrategy, PackagesIndex, SourceDownload, SourceDownloadStrategy

Constant Summary collapse

DownloadQueueType =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

T.type_alias { T.nilable(Homebrew::DownloadQueue) }
HOMEBREW_CACHE_API =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

T.let((HOMEBREW_CACHE/"api").freeze, Pathname)
HOMEBREW_CACHE_API_SOURCE =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

T.let((HOMEBREW_CACHE/"api-source").freeze, Pathname)
DEFAULT_API_STALE_SECONDS =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

7 days

T.let(7 * 24 * 60 * 60, Integer)
UNSIGNED_API_STALE_SECONDS =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

Revalidate unsigned per-resource responses hourly to limit how long a poisoned cache can persist without requiring a request for every command.

T.let(60 * 60, Integer)

Class Method Summary collapse

Methods included from Utils::Output::Mixin

issue_reporting_message, odebug, odeprecated, odie, odisabled, ofail, oh1, oh1_title, ohai, ohai_title, onoe, opoo, opoo_once, opoo_outside_github_actions, opoo_without_github_actions_annotation, pretty_cannot_install, pretty_deprecated, pretty_disabled, pretty_duration, pretty_install_status, pretty_installed, pretty_uninstalled, pretty_unmarked, pretty_upgradable, pretty_warning

Class Method Details

.cached_cask_json_file_path ⇒ Pathname

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



632
633
634
# File 'api.rb', line 632

def self.cached_cask_json_file_path
  Homebrew::API::Internal.cached_packages_json_file_path
end

.cached_internal_packages_payload(endpoint, stale_seconds:) ⇒ Array<(String, File::Stat)>?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns the verified raw payload bytes, and the envelope stat they were validated against, for an internal packages endpoint served entirely from a fresh cached envelope's sidecar. Returns nil when a download, revalidation or envelope parse is needed instead.

Parameters:

Returns:



457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'api.rb', line 457

def self.cached_internal_packages_payload(endpoint, stale_seconds:)
  target = HOMEBREW_CACHE_API/endpoint
  return unless jws_payload_cacheable?(target)
  return if !target.exist? || target.empty?
  return unless skip_download?(target:, stale_seconds:)

  source_stat = target.stat
  payload = cached_jws_payload_string(target, source_stat:)
  return if payload.nil?

  [payload, source_stat]
rescue SystemCallError
  nil
end

.cask_renames ⇒ Hash{String => String}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



622
623
624
# File 'api.rb', line 622

def self.cask_renames
  Homebrew::API::Internal.cask_renames
end

.cask_tap_migrations ⇒ Hash{String => String}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



627
628
629
# File 'api.rb', line 627

def self.cask_tap_migrations
  Homebrew::API::Internal.cask_tap_migrations
end

.cask_token?(token) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


617
618
619
# File 'api.rb', line 617

def self.cask_token?(token)
  Homebrew::API::Internal.cask_name?(token)
end

.cask_tokens ⇒ Array<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



612
613
614
# File 'api.rb', line 612

def self.cask_tokens
  Homebrew::API::Internal.cask_names
end

.download_executables_file_from_github_packages!(target) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'api.rb', line 338

def self.download_executables_file_from_github_packages!(target)
  github_packages_url = "https://ghcr.io/v2/homebrew/command-not-found/executables"
  manifest_args = [
    "--fail", "--location",
    "--header", "Accept: application/vnd.oci.image.manifest.v1+json",
    "#{github_packages_url}/manifests/latest"
  ]
  if HOMEBREW_GITHUB_PACKAGES_AUTH.present?
    manifest_args.insert(-2, "--header", "Authorization: #{HOMEBREW_GITHUB_PACKAGES_AUTH}")
  end

  manifest_output = Utils::Curl.curl_output(*manifest_args, show_error: false)
  return false unless manifest_output.success?

  manifest = JSON.parse(manifest_output.stdout)
  layers = T.cast(manifest.fetch("layers"), T::Array[T::Hash[String, T.untyped]])
  layer = layers.find do |candidate|
    candidate.dig("annotations", "org.opencontainers.image.title") == target.basename.to_s
  end
  return false if layer.nil?

  digest = T.cast(layer["digest"], T.nilable(String))
  return false if digest.blank?

  download_args = ["--fail"]
  if HOMEBREW_GITHUB_PACKAGES_AUTH.present?
    download_args += ["--header", "Authorization: #{HOMEBREW_GITHUB_PACKAGES_AUTH}"]
  end
  download_args << "#{github_packages_url}/blobs/#{digest}"
  target.dirname.mkpath
  Utils::Curl.curl_download(*download_args, to: target, show_error: false)
  FileUtils.touch(target)
  true
rescue ErrorDuringExecution, JSON::ParserError, KeyError, TypeError
  target.unlink if target.exist? && target.empty?

  false
end

.fetch_api_files! ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'api.rb', line 232

def self.fetch_api_files!
  stale_seconds = if ENV["HOMEBREW_API_UPDATED"].present? ||
                     (Homebrew::EnvConfig.no_auto_update? && !Homebrew::EnvConfig.force_api_auto_update?)
    nil
  elsif Homebrew.auto_update_command?
    Homebrew::EnvConfig.api_auto_update_secs.to_i
  else
    DEFAULT_API_STALE_SECONDS
  end

  # The internal API is now always used; read this only to surface its deprecation.
  Homebrew::EnvConfig.use_internal_api?
  target = Internal.cached_packages_json_file_path
  if target.exist? && !target.empty? && skip_download?(target:, stale_seconds:)
    ENV["HOMEBREW_API_UPDATED"] = "1"
    return
  end

  require "download_queue"
  download_queue = Homebrew::DownloadQueue.new
  Homebrew::API::Internal.fetch_packages_api!(download_queue:, stale_seconds:, enqueue: true)

  ENV["HOMEBREW_API_UPDATED"] = "1"

  begin
    download_queue.fetch(heading: "Downloading Homebrew API data")
  ensure
    download_queue.shutdown
  end
end

.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint, stale_seconds: nil, download_queue: nil, enqueue: false) ⇒ Array<([Array<T.untyped>, Hash{String => T.untyped}], Boolean)>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • endpoint (String)
  • target (Pathname) (defaults to: HOMEBREW_CACHE_API/endpoint)
  • stale_seconds (Integer, nil) (defaults to: nil)
  • download_queue (DownloadQueueType) (defaults to: nil)
  • enqueue (Boolean) (defaults to: false)

Returns:



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'api.rb', line 50

def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint,
                             stale_seconds: nil, download_queue: nil,
                             enqueue: false)
  # Lazy-load dependency.
  require "development_tools"

  retry_count = 0
  url = "#{Homebrew::EnvConfig.api_domain}/#{endpoint}"
  default_url = "#{HOMEBREW_API_DEFAULT_DOMAIN}/#{endpoint}"
  primary_url = url

  if Homebrew.running_as_root_but_not_owned_by_root? &&
     (!target.exist? || target.empty?)
    odie "Need to download #{url} but cannot as root! Run `brew update` without `sudo` first then try again."
  end

  curl_args = Utils::Curl.curl_args(retries: 0) + [
    "--compressed",
    "--speed-limit", ENV.fetch("HOMEBREW_CURL_SPEED_LIMIT"),
    "--speed-time", ENV.fetch("HOMEBREW_CURL_SPEED_TIME"),
    # This is a Curl format token, not a Ruby one.
    # rubocop:disable Style/FormatStringToken
    "--write-out", "%{http_code}"
    # rubocop:enable Style/FormatStringToken
  ]

  insecure_download = DevelopmentTools.ca_file_substitution_required? ||
                      DevelopmentTools.curl_substitution_required?
  skip_download = skip_download?(target:, stale_seconds:)
  if enqueue
    unless skip_download
      require "download_queue"
      require "api/json_download"
      download_queue ||= Homebrew::DownloadQueue.default
      download = Homebrew::API::JSONDownload.new(endpoint, target:, stale_seconds:)
      download_queue.enqueue(download)
    end
    return [{}, false]
  end

  etag_path = Pathname("#{target}.etag")
  etag_url_path = Pathname("#{etag_path}.url")
  new_etag_path = Pathname("#{target}.etag.new")
  new_target = Pathname("#{target}.new")

  json_data, from_payload_cache = begin
    unless skip_download
      require "lock_file"
      # cmd/update.sh takes the same lock before replacing the body and validators.
      download_lock ||= DownloadLock.new(target)
      download_lock.lock_or_wait(quiet: Context.current.quiet?)
      skip_download = skip_download?(target:, stale_seconds:)
    end
    download_succeeded = T.let(false, T::Boolean)
    unconditional_retry = T.let(false, T::Boolean)
    begin
      args = curl_args.dup
      # Keep conditional requests in sync with utils/api.sh and cmd/update.sh.
      use_etag = HOMEBREW_CURL_SUPPORTS_ETAGS && url == primary_url
      if !unconditional_retry && target.exist? && !target.empty?
        if use_etag && etag_path.exist? && !etag_path.empty? &&
           etag_url_path.exist? && etag_url_path.read == url
          args.prepend("--etag-compare", etag_path.to_s)
        elsif !HOMEBREW_CURL_SUPPORTS_ETAGS && url == primary_url
          args.prepend("--time-cond", target.to_s)
        end
      end
      args.prepend("--etag-save", new_etag_path.to_s) if use_etag
      if insecure_download
        opoo DevelopmentTools.insecure_download_warning(endpoint)
        args.append("--insecure")
      end
      unless skip_download
        FileUtils.rm_f([new_target, new_etag_path])
        ohai "Downloading #{url}" if $stdout.tty? && !Context.current.quiet?
        # Disable retries here, we handle them ourselves below.
        result = Utils::Curl.curl_download(*args, url, to: new_target, retries: 0, show_error: false,
                                          print_stdout: false)
        download_succeeded = true
        if new_target.exist?
          FileUtils.rm_f([etag_path, etag_url_path])
          FileUtils.mv(new_target, target)
        end
        if use_etag && new_etag_path.exist? && !new_etag_path.empty?
          FileUtils.mv(new_etag_path, etag_path)
          File.write(etag_url_path, url)
        elsif etag_path.exist? && result&.stdout != "304"
          FileUtils.rm_f([etag_path, etag_url_path])
        end
      end
    rescue ErrorDuringExecution => e
      if e.exitstatus == 56 && args && (args.include?("--etag-compare") || args.include?("--time-cond"))
        unconditional_retry = true
        retry
      end
      if url == default_url
        raise unless target.exist?
        raise if target.empty?
      elsif retry_count.zero? || !target.exist? || target.empty?
        # Fall back to the default API domain and try again
        # This block will be executed only once, because we set `url` to `default_url`
        url = default_url
        target.unlink if target.exist? && target.empty?
        skip_download = false

        retry
      end

      opoo "#{target.basename}: update failed, falling back to cached version."
    ensure
      FileUtils.rm_f([new_target, new_etag_path]) unless skip_download
    end

    # Only refresh the cache mtime after a successful curl revalidation/download.
    # Touching after a failed download would mark a stale cache as fresh and
    # cause `skip_download?` to short-circuit subsequent retries until cleanup.
    if download_succeeded
      mtime = insecure_download ? Time.new(1970, 1, 1) : Time.now
      FileUtils.touch(target, mtime:)
    end

    payload_data = cached_jws_payload(target) if endpoint.end_with?(".jws.json") && !download_succeeded
    if payload_data
      [payload_data, true]
    else
      # Stat before reading: fingerprinting a concurrently-replaced file
      # against these bytes would poison the payload cache.
      source_stat = target.stat
      # Can use `target.read` again when/if https://github.com/sorbet/sorbet/pull/8999 is merged/released.
      [JSON.parse(File.read(target, encoding: Encoding::UTF_8), freeze: true), false]
    end
  rescue JSON::ParserError
    target.unlink
    retry_count += 1
    skip_download = false
    odie "Cannot download non-corrupt #{url}!" if retry_count > Homebrew::EnvConfig.curl_retries.to_i

    retry
  end

  if endpoint.end_with?(".jws.json") && !from_payload_cache
    success, data = verify_and_parse_jws(json_data)
    unless success
      target.unlink
      odie <<~EOS
        Failed to verify integrity (#{data}) of:
          #{url}
        Potential MITM attempt detected. Please run `brew update` and try again.
      EOS
    end
    # Skip on insecure downloads: their pinned 1970 mtime would make the
    # source fingerprint ambiguous (and they always re-download anyway).
    if source_stat && !insecure_download
      write_jws_payload_cache(target, json_data, source_stat:)
      write_jws_payload_index_cache(target, json_data, parsed: data, source_stat:)
    end
    [data, !skip_download]
  else
    [json_data, !skip_download]
  end
ensure
  download_lock&.unlock
end

.formula_aliases ⇒ Hash{String => String}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



597
598
599
# File 'api.rb', line 597

def self.formula_aliases
  Homebrew::API::Internal.formula_aliases
end

.formula_name?(name) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


592
593
594
# File 'api.rb', line 592

def self.formula_name?(name)
  Homebrew::API::Internal.formula_name?(name)
end

.formula_names ⇒ Array<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



587
588
589
# File 'api.rb', line 587

def self.formula_names
  Homebrew::API::Internal.formula_names
end

.formula_renames ⇒ Hash{String => String}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



602
603
604
# File 'api.rb', line 602

def self.formula_renames
  Homebrew::API::Internal.formula_renames
end

.formula_tap_migrations ⇒ Hash{String => String}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



607
608
609
# File 'api.rb', line 607

def self.formula_tap_migrations
  Homebrew::API::Internal.formula_tap_migrations
end

.merge_variations(json, bottle_tag: nil) ⇒ Hash{String => T.untyped}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



218
219
220
221
222
223
224
225
226
227
228
229
# File 'api.rb', line 218

def self.merge_variations(json, bottle_tag: nil)
  return json unless json.key?("variations")

  bottle_tag ||= Homebrew::SimulateSystem.current_tag

  if (variation = json.dig("variations", bottle_tag.to_s).presence) ||
     (variation = json.dig("variations", bottle_tag.to_sym).presence)
    json = json.merge(variation)
  end

  json.except("variations")
end

.skip_download?(target:, stale_seconds:) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


33
34
35
36
37
38
39
# File 'api.rb', line 33

def self.skip_download?(target:, stale_seconds:)
  return true if Homebrew.running_as_root_but_not_owned_by_root?
  return false if !target.exist? || target.empty?
  return true unless stale_seconds

  (Time.now - stale_seconds) < target.mtime
end

.tap_from_source_download(path) ⇒ Tap?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



575
576
577
578
579
580
581
582
583
584
# File 'api.rb', line 575

def self.tap_from_source_download(path)
  path = path.expand_path
  source_relative_path = path.relative_path_from(Homebrew::API::HOMEBREW_CACHE_API_SOURCE)
  return if source_relative_path.to_s.start_with?("../")

  org, repo = source_relative_path.each_filename.first(2)
  return if org.blank? || repo.blank?

  Tap.fetch(org, repo)
end

.with_no_api_env(&block) ⇒ T.type_parameter(:U)

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • block (T.proc.returns(T.type_parameter(:U)))

Returns:

  • (T.type_parameter(:U))


9
10
11
12
13
14
# File 'api/env.rb', line 9

def self.with_no_api_env(&block)
  return yield if Homebrew::EnvConfig.no_install_from_api?

  Utils::Shell.with_env(HOMEBREW_NO_INSTALL_FROM_API:                   "1",
                        HOMEBREW_AUTOMATICALLY_SET_NO_INSTALL_FROM_API: "1", &block)
end

.with_no_api_env_if_needed(condition, &block) ⇒ T.type_parameter(:U)

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • condition (Boolean)
  • block (T.proc.returns(T.type_parameter(:U)))

Returns:

  • (T.type_parameter(:U))


22
23
24
25
26
# File 'api/env.rb', line 22

def self.with_no_api_env_if_needed(condition, &block)
  return yield unless condition

  with_no_api_env(&block)
end

.write_aliases_file!(type, regenerate:, &aliases) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'api.rb', line 285

def self.write_aliases_file!(type, regenerate:, &aliases)
  aliases_path = HOMEBREW_CACHE_API/"#{type}_aliases.txt"
  if !aliases_path.exist? || regenerate
    aliases_text = yield.map do |alias_name, real_name|
      "#{alias_name}|#{real_name}"
    end
    aliases_path.unlink if aliases_path.exist?
    aliases_path.write(aliases_text.sort.join("\n"))
    return true
  end

  false
end

.write_executables_file!(regenerate:, source:, &formulae) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • regenerate (Boolean)
  • source (Pathname)
  • formulae (T.proc.returns(T::Hash[String, T::Hash[String, T.untyped]]))

Returns:

  • (Boolean)


306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'api.rb', line 306

def self.write_executables_file!(regenerate:, source:, &formulae)
  executables_path = HOMEBREW_CACHE_API/"internal/executables.txt"
  # The file is derived only from the API data in `source`, so it stays
  # current until that file next changes or is revalidated.
  executables_mtime, source_mtime = [executables_path, source].map do |path|
    path.mtime
  rescue Errno::ENOENT
    nil
  end
  return false if !regenerate && executables_mtime && source_mtime && source_mtime <= executables_mtime

  executables_lines = yield.filter_map do |name, hash|
    executables = T.cast(hash["executables"], T.nilable(T::Array[String]))
    next if executables.blank?

    "#{name}:#{executables.join(" ")}"
  end
  if executables_lines.empty?
    begin
      executables_path.unlink
      return true
    rescue Errno::ENOENT
      return false
    end
  end

  executables_path.dirname.mkpath
  executables_path.write("#{executables_lines.sort.join("\n")}\n")
  true
end

.write_names_and_aliases ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



264
265
266
267
# File 'api.rb', line 264

def self.write_names_and_aliases
  Homebrew::API::Internal.write_formula_names_and_aliases
  Homebrew::API::Internal.write_cask_names
end

.write_names_file!(type, regenerate:, &names) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • type (String)
  • regenerate (Boolean)
  • names (T.proc.returns(T::Array[String]))

Returns:

  • (Boolean)


270
271
272
273
274
275
276
277
278
279
# File 'api.rb', line 270

def self.write_names_file!(type, regenerate:, &names)
  names_path = HOMEBREW_CACHE_API/"#{type}_names.txt"
  if !names_path.exist? || regenerate
    names_path.unlink if names_path.exist?
    names_path.write(yield.sort.join("\n"))
    return true
  end

  false
end