mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Merge pull request #20121 from Homebrew/sorbet_strict_a
a*.rb: move to strict Sorbet sigil.
This commit is contained in:
commit
d7d8c61f00
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "api/analytics"
|
||||
@ -11,10 +11,10 @@ module Homebrew
|
||||
module API
|
||||
extend Cachable
|
||||
|
||||
HOMEBREW_CACHE_API = (HOMEBREW_CACHE/"api").freeze
|
||||
HOMEBREW_CACHE_API_SOURCE = (HOMEBREW_CACHE/"api-source").freeze
|
||||
HOMEBREW_CACHE_API = T.let((HOMEBREW_CACHE/"api").freeze, Pathname)
|
||||
HOMEBREW_CACHE_API_SOURCE = T.let((HOMEBREW_CACHE/"api-source").freeze, Pathname)
|
||||
|
||||
sig { params(endpoint: String).returns(Hash) }
|
||||
sig { params(endpoint: String).returns(T::Hash[String, T.untyped]) }
|
||||
def self.fetch(endpoint)
|
||||
return cache[endpoint] if cache.present? && cache.key?(endpoint)
|
||||
|
||||
@ -33,7 +33,8 @@ module Homebrew
|
||||
end
|
||||
|
||||
sig {
|
||||
params(endpoint: String, target: Pathname, stale_seconds: Integer).returns([T.any(Array, Hash), T::Boolean])
|
||||
params(endpoint: String, target: Pathname,
|
||||
stale_seconds: Integer).returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
|
||||
}
|
||||
def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint,
|
||||
stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
|
||||
@ -96,7 +97,8 @@ module Homebrew
|
||||
|
||||
mtime = insecure_download ? Time.new(1970, 1, 1) : Time.now
|
||||
FileUtils.touch(target, mtime:) unless skip_download
|
||||
JSON.parse(target.read(encoding: Encoding::UTF_8), freeze: true)
|
||||
# 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)
|
||||
rescue JSON::ParserError
|
||||
target.unlink
|
||||
retry_count += 1
|
||||
@ -122,8 +124,11 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(json: Hash, bottle_tag: T.nilable(::Utils::Bottles::Tag)).returns(Hash) }
|
||||
def self.merge_variations(json, bottle_tag: nil)
|
||||
sig {
|
||||
params(json: T::Hash[String, T.untyped],
|
||||
bottle_tag: ::Utils::Bottles::Tag).returns(T::Hash[String, T.untyped])
|
||||
}
|
||||
def self.merge_variations(json, bottle_tag: T.unsafe(nil))
|
||||
return json unless json.key?("variations")
|
||||
|
||||
bottle_tag ||= Homebrew::SimulateSystem.current_tag
|
||||
@ -147,7 +152,10 @@ module Homebrew
|
||||
false
|
||||
end
|
||||
|
||||
sig { params(json_data: Hash).returns([T::Boolean, T.any(String, Array, Hash)]) }
|
||||
sig {
|
||||
params(json_data: T::Hash[String, T.untyped])
|
||||
.returns([T::Boolean, T.any(String, T::Array[T.untyped], T::Hash[String, T.untyped])])
|
||||
}
|
||||
private_class_method def self.verify_and_parse_jws(json_data)
|
||||
signatures = json_data["signatures"]
|
||||
homebrew_signature = signatures&.find { |sig| sig.dig("header", "kid") == "homebrew-1" }
|
||||
|
@ -1,24 +1,26 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Used to substitute common paths with generic placeholders when generating JSON for the API.
|
||||
module APIHashable
|
||||
sig { void }
|
||||
def generating_hash!
|
||||
return if generating_hash?
|
||||
|
||||
# Apply monkeypatches for API generation
|
||||
@old_homebrew_prefix = HOMEBREW_PREFIX
|
||||
@old_homebrew_cellar = HOMEBREW_CELLAR
|
||||
@old_home = Dir.home
|
||||
@old_git_config_global = ENV.fetch("GIT_CONFIG_GLOBAL", nil)
|
||||
@old_homebrew_prefix = T.let(HOMEBREW_PREFIX, T.nilable(Pathname))
|
||||
@old_homebrew_cellar = T.let(HOMEBREW_CELLAR, T.nilable(Pathname))
|
||||
@old_home = T.let(Dir.home, T.nilable(String))
|
||||
@old_git_config_global = T.let(ENV.fetch("GIT_CONFIG_GLOBAL", nil), T.nilable(String))
|
||||
Object.send(:remove_const, :HOMEBREW_PREFIX)
|
||||
Object.const_set(:HOMEBREW_PREFIX, Pathname.new(HOMEBREW_PREFIX_PLACEHOLDER))
|
||||
ENV["HOME"] = HOMEBREW_HOME_PLACEHOLDER
|
||||
ENV["GIT_CONFIG_GLOBAL"] = File.join(@old_home, ".gitconfig")
|
||||
|
||||
@generating_hash = true
|
||||
@generating_hash = T.let(true, T.nilable(T::Boolean))
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def generated_hash!
|
||||
return unless generating_hash?
|
||||
|
||||
@ -31,6 +33,7 @@ module APIHashable
|
||||
@generating_hash = false
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def generating_hash?
|
||||
@generating_hash ||= false
|
||||
@generating_hash == true
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "bundle/adder"
|
||||
@ -7,6 +7,7 @@ module Homebrew
|
||||
module Bundle
|
||||
module Commands
|
||||
module Add
|
||||
sig { params(args: String, type: Symbol, global: T::Boolean, file: T.nilable(String)).void }
|
||||
def self.run(*args, type:, global:, file:)
|
||||
Homebrew::Bundle::Adder.add(*args, type:, global:, file:)
|
||||
end
|
||||
|
@ -67,8 +67,8 @@ module Homebrew
|
||||
end
|
||||
raise UsageError, "Only one url can be specified" if pr_url&.count&.> 1
|
||||
|
||||
labels = if pr_url
|
||||
pr = GitHub::API.open_rest(pr_url.first)
|
||||
labels = if pr_url && (first_pr_url = pr_url.first)
|
||||
pr = GitHub::API.open_rest(first_pr_url)
|
||||
pr.fetch("labels").map { |l| l.fetch("name") }
|
||||
else
|
||||
[]
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "requirement"
|
||||
@ -7,10 +7,14 @@ require "requirement"
|
||||
class ArchRequirement < Requirement
|
||||
fatal true
|
||||
|
||||
@arch = T.let(nil, T.nilable(Symbol))
|
||||
|
||||
sig { returns(T.nilable(Symbol)) }
|
||||
attr_reader :arch
|
||||
|
||||
sig { params(tags: T::Array[Symbol]).void }
|
||||
def initialize(tags)
|
||||
@arch = tags.shift
|
||||
@arch = T.let(tags.shift, T.nilable(Symbol))
|
||||
super
|
||||
end
|
||||
|
||||
|
@ -132,7 +132,8 @@ module SystemConfig
|
||||
out.puts "#{tap_name} branch: #{tap.git_branch || "(none)"}" if default_branches.exclude?(tap.git_branch)
|
||||
end
|
||||
|
||||
if (json_file = Homebrew::API::HOMEBREW_CACHE_API/json_file_name) && json_file.exist?
|
||||
json_file = Homebrew::API::HOMEBREW_CACHE_API/json_file_name
|
||||
if json_file.exist?
|
||||
out.puts "#{tap_name} JSON: #{json_file.mtime.utc.strftime("%d %b %H:%M UTC")}"
|
||||
elsif !tap.installed?
|
||||
out.puts "#{tap_name}: N/A"
|
||||
|
@ -1,9 +1,10 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "system_command"
|
||||
|
||||
module GitHub
|
||||
sig { params(scopes: T::Array[String]).returns(String) }
|
||||
def self.pat_blurb(scopes = ALL_SCOPES)
|
||||
require "utils/formatter"
|
||||
require "utils/shell"
|
||||
@ -16,20 +17,21 @@ module GitHub
|
||||
EOS
|
||||
end
|
||||
|
||||
API_URL = "https://api.github.com"
|
||||
API_MAX_PAGES = 50
|
||||
API_URL = T.let("https://api.github.com", String)
|
||||
API_MAX_PAGES = T.let(50, Integer)
|
||||
private_constant :API_MAX_PAGES
|
||||
API_MAX_ITEMS = 5000
|
||||
API_MAX_ITEMS = T.let(5000, Integer)
|
||||
private_constant :API_MAX_ITEMS
|
||||
PAGINATE_RETRY_COUNT = 3
|
||||
PAGINATE_RETRY_COUNT = T.let(3, Integer)
|
||||
private_constant :PAGINATE_RETRY_COUNT
|
||||
|
||||
CREATE_GIST_SCOPES = ["gist"].freeze
|
||||
CREATE_ISSUE_FORK_OR_PR_SCOPES = ["repo"].freeze
|
||||
CREATE_WORKFLOW_SCOPES = ["workflow"].freeze
|
||||
ALL_SCOPES = (CREATE_GIST_SCOPES + CREATE_ISSUE_FORK_OR_PR_SCOPES + CREATE_WORKFLOW_SCOPES).freeze
|
||||
CREATE_GIST_SCOPES = T.let(["gist"].freeze, T::Array[String])
|
||||
CREATE_ISSUE_FORK_OR_PR_SCOPES = T.let(["repo"].freeze, T::Array[String])
|
||||
CREATE_WORKFLOW_SCOPES = T.let(["workflow"].freeze, T::Array[String])
|
||||
ALL_SCOPES = T.let((CREATE_GIST_SCOPES + CREATE_ISSUE_FORK_OR_PR_SCOPES + CREATE_WORKFLOW_SCOPES).freeze,
|
||||
T::Array[String])
|
||||
private_constant :ALL_SCOPES
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN_REGEX = /^(?:[a-f0-9]{40}|(?:gh[pousr]|github_pat)_\w{36,251})$/
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN_REGEX = T.let(/^(?:[a-f0-9]{40}|(?:gh[pousr]|github_pat)_\w{36,251})$/, Regexp)
|
||||
private_constant :GITHUB_PERSONAL_ACCESS_TOKEN_REGEX
|
||||
|
||||
# Helper functions for accessing the GitHub API.
|
||||
@ -40,46 +42,60 @@ module GitHub
|
||||
|
||||
# Generic API error.
|
||||
class Error < RuntimeError
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :github_message
|
||||
|
||||
sig { params(message: T.nilable(String), github_message: String).void }
|
||||
def initialize(message = nil, github_message = T.unsafe(nil))
|
||||
@github_message = T.let(github_message, T.nilable(String))
|
||||
super(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Error when the requested URL is not found.
|
||||
class HTTPNotFoundError < Error
|
||||
sig { params(github_message: String).void }
|
||||
def initialize(github_message)
|
||||
@github_message = github_message
|
||||
super
|
||||
super(nil, github_message)
|
||||
end
|
||||
end
|
||||
|
||||
# Error when the API rate limit is exceeded.
|
||||
class RateLimitExceededError < Error
|
||||
sig { params(reset: Integer, github_message: String).void }
|
||||
def initialize(reset, github_message)
|
||||
@github_message = github_message
|
||||
new_pat_message = ", or:\n#{GitHub.pat_blurb}" if API.credentials.blank?
|
||||
super <<~EOS
|
||||
message = <<~EOS
|
||||
GitHub API Error: #{github_message}
|
||||
Try again in #{pretty_ratelimit_reset(reset)}#{new_pat_message}
|
||||
EOS
|
||||
super(message, github_message)
|
||||
end
|
||||
|
||||
sig { params(reset: Integer).returns(String) }
|
||||
def pretty_ratelimit_reset(reset)
|
||||
pretty_duration(Time.at(reset) - Time.now)
|
||||
end
|
||||
end
|
||||
|
||||
GITHUB_IP_ALLOWLIST_ERROR = Regexp.new("Although you appear to have the correct authorization credentials, " \
|
||||
GITHUB_IP_ALLOWLIST_ERROR = T.let(
|
||||
Regexp.new(
|
||||
"Although you appear to have the correct authorization credentials, " \
|
||||
"the `(.+)` organization has an IP allow list enabled, " \
|
||||
"and your IP address is not permitted to access this resource").freeze
|
||||
"and your IP address is not permitted to access this resource",
|
||||
).freeze,
|
||||
Regexp,
|
||||
)
|
||||
|
||||
NO_CREDENTIALS_MESSAGE = <<~MESSAGE.freeze
|
||||
NO_CREDENTIALS_MESSAGE = T.let <<~MESSAGE.freeze, String
|
||||
No GitHub credentials found in macOS Keychain, GitHub CLI or the environment.
|
||||
#{GitHub.pat_blurb}
|
||||
MESSAGE
|
||||
|
||||
# Error when authentication fails.
|
||||
class AuthenticationFailedError < Error
|
||||
sig { params(credentials_type: Symbol, github_message: String).void }
|
||||
def initialize(credentials_type, github_message)
|
||||
@github_message = github_message
|
||||
message = "GitHub API Error: #{github_message}\n"
|
||||
message << case credentials_type
|
||||
when :github_cli_token
|
||||
@ -103,12 +119,13 @@ module GitHub
|
||||
when :none
|
||||
NO_CREDENTIALS_MESSAGE
|
||||
end
|
||||
super message.freeze
|
||||
super message.freeze, github_message
|
||||
end
|
||||
end
|
||||
|
||||
# Error when the user has no GitHub API credentials set at all (macOS keychain, GitHub CLI or envvar).
|
||||
class MissingAuthenticationError < Error
|
||||
sig { void }
|
||||
def initialize
|
||||
super NO_CREDENTIALS_MESSAGE
|
||||
end
|
||||
@ -116,24 +133,21 @@ module GitHub
|
||||
|
||||
# Error when the API returns a validation error.
|
||||
class ValidationFailedError < Error
|
||||
sig { params(github_message: String, errors: T::Array[String]).void }
|
||||
def initialize(github_message, errors)
|
||||
@github_message = if errors.empty?
|
||||
github_message
|
||||
else
|
||||
"#{github_message}: #{errors}"
|
||||
end
|
||||
github_message = "#{github_message}: #{errors}" unless errors.empty?
|
||||
|
||||
super(@github_message)
|
||||
super(github_message, github_message)
|
||||
end
|
||||
end
|
||||
|
||||
ERRORS = [
|
||||
ERRORS = T.let([
|
||||
AuthenticationFailedError,
|
||||
HTTPNotFoundError,
|
||||
RateLimitExceededError,
|
||||
Error,
|
||||
JSON::ParserError,
|
||||
].freeze
|
||||
].freeze, T::Array[T.any(T.class_of(Error), T.class_of(JSON::ParserError))])
|
||||
|
||||
# Gets the token from the GitHub CLI for github.com.
|
||||
sig { returns(T.nilable(String)) }
|
||||
@ -151,7 +165,7 @@ module GitHub
|
||||
print_stderr: false
|
||||
return unless result.success?
|
||||
|
||||
gh_out.chomp
|
||||
gh_out.chomp.presence
|
||||
end
|
||||
end
|
||||
|
||||
@ -178,14 +192,16 @@ module GitHub
|
||||
# https://github.com/Homebrew/brew/issues/6862#issuecomment-572610344
|
||||
return unless GITHUB_PERSONAL_ACCESS_TOKEN_REGEX.match?(github_password)
|
||||
|
||||
github_password
|
||||
github_password.presence
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def self.credentials
|
||||
@credentials ||= T.let(nil, T.nilable(String))
|
||||
@credentials ||= Homebrew::EnvConfig.github_api_token.presence
|
||||
@credentials ||= github_cli_token.presence
|
||||
@credentials ||= keychain_username_password.presence
|
||||
@credentials ||= github_cli_token
|
||||
@credentials ||= keychain_username_password
|
||||
end
|
||||
|
||||
sig { returns(Symbol) }
|
||||
@ -201,18 +217,19 @@ module GitHub
|
||||
end
|
||||
end
|
||||
|
||||
CREDENTIAL_NAMES = {
|
||||
CREDENTIAL_NAMES = T.let({
|
||||
env_token: "HOMEBREW_GITHUB_API_TOKEN",
|
||||
github_cli_token: "GitHub CLI login",
|
||||
keychain_username_password: "macOS Keychain GitHub",
|
||||
}.freeze
|
||||
}.freeze, T::Hash[Symbol, String])
|
||||
|
||||
# Given an API response from GitHub, warn the user if their credentials
|
||||
# have insufficient permissions.
|
||||
sig { params(response_headers: T::Hash[String, String], needed_scopes: T::Array[String]).void }
|
||||
def self.credentials_error_message(response_headers, needed_scopes)
|
||||
return if response_headers.empty?
|
||||
|
||||
scopes = response_headers["x-accepted-oauth-scopes"].to_s.split(", ")
|
||||
scopes = response_headers["x-accepted-oauth-scopes"].to_s.split(", ").presence
|
||||
needed_scopes = Set.new(scopes || needed_scopes)
|
||||
credentials_scopes = response_headers["x-oauth-scopes"]
|
||||
return if needed_scopes.subset?(Set.new(credentials_scopes.to_s.split(", ")))
|
||||
@ -222,17 +239,35 @@ module GitHub
|
||||
credentials_scopes = "none" if credentials_scopes.blank?
|
||||
|
||||
what = CREDENTIAL_NAMES.fetch(credentials_type)
|
||||
@credentials_error_message ||= onoe <<~EOS
|
||||
@credentials_error_message ||= T.let(begin
|
||||
error_message = <<~EOS
|
||||
Your #{what} credentials do not have sufficient scope!
|
||||
Scopes required: #{needed_scopes}
|
||||
Scopes present: #{credentials_scopes}
|
||||
#{github_permission_link}
|
||||
EOS
|
||||
onoe error_message
|
||||
error_message
|
||||
end, T.nilable(String))
|
||||
end
|
||||
|
||||
def self.open_rest(
|
||||
url, data: nil, data_binary_path: nil, request_method: nil, scopes: [].freeze, parse_json: true
|
||||
)
|
||||
sig {
|
||||
params(
|
||||
url: T.any(String, URI::Generic),
|
||||
data: T::Hash[Symbol, T.untyped],
|
||||
data_binary_path: String,
|
||||
request_method: Symbol,
|
||||
scopes: T::Array[String],
|
||||
parse_json: T::Boolean,
|
||||
_block: T.nilable(
|
||||
T.proc
|
||||
.params(data: T::Hash[String, T.untyped])
|
||||
.returns(T.untyped),
|
||||
),
|
||||
).returns(T.untyped)
|
||||
}
|
||||
def self.open_rest(url, data: T.unsafe(nil), data_binary_path: T.unsafe(nil), request_method: T.unsafe(nil),
|
||||
scopes: [].freeze, parse_json: true, &_block)
|
||||
# This is a no-op if the user is opting out of using the GitHub API.
|
||||
return block_given? ? yield({}) : {} if Homebrew::EnvConfig.no_github_api?
|
||||
|
||||
@ -289,7 +324,7 @@ module GitHub
|
||||
|
||||
begin
|
||||
if !http_code.start_with?("2") || !result.status.success?
|
||||
raise_error(output, result.stderr, http_code, headers, scopes)
|
||||
raise_error(output, result.stderr, http_code, headers || "", scopes)
|
||||
end
|
||||
|
||||
return if http_code == "204" # No Content
|
||||
@ -305,7 +340,18 @@ module GitHub
|
||||
end
|
||||
end
|
||||
|
||||
def self.paginate_rest(url, additional_query_params: nil, per_page: 100, scopes: [].freeze)
|
||||
sig {
|
||||
params(
|
||||
url: T.any(String, URI::Generic),
|
||||
additional_query_params: String,
|
||||
per_page: Integer,
|
||||
scopes: T::Array[String],
|
||||
_block: T.proc
|
||||
.params(result: T.untyped, page: Integer)
|
||||
.returns(T.untyped),
|
||||
).void
|
||||
}
|
||||
def self.paginate_rest(url, additional_query_params: T.unsafe(nil), per_page: 100, scopes: [].freeze, &_block)
|
||||
(1..API_MAX_PAGES).each do |page|
|
||||
retry_count = 1
|
||||
result = begin
|
||||
@ -324,9 +370,17 @@ module GitHub
|
||||
end
|
||||
end
|
||||
|
||||
def self.open_graphql(query, variables: nil, scopes: [].freeze, raise_errors: true)
|
||||
sig {
|
||||
params(
|
||||
query: String,
|
||||
variables: T::Hash[Symbol, T.untyped],
|
||||
scopes: T::Array[String],
|
||||
raise_errors: T::Boolean,
|
||||
).returns(T.untyped)
|
||||
}
|
||||
def self.open_graphql(query, variables: T.unsafe(nil), scopes: [].freeze, raise_errors: true)
|
||||
data = { query:, variables: }
|
||||
result = open_rest("#{API_URL}/graphql", scopes:, data:, request_method: "POST")
|
||||
result = open_rest("#{API_URL}/graphql", scopes:, data:, request_method: :POST)
|
||||
|
||||
if raise_errors
|
||||
raise Error, result["errors"].map { |e| e["message"] }.join("\n") if result["errors"].present?
|
||||
@ -340,13 +394,13 @@ module GitHub
|
||||
sig {
|
||||
params(
|
||||
query: String,
|
||||
variables: T.nilable(T::Hash[Symbol, T.untyped]),
|
||||
variables: T::Hash[Symbol, T.untyped],
|
||||
scopes: T::Array[String],
|
||||
raise_errors: T::Boolean,
|
||||
_block: T.proc.params(data: T::Hash[String, T.untyped]).returns(T::Hash[String, T.untyped]),
|
||||
_block: T.proc.params(data: T::Hash[String, T.untyped]).returns(T.untyped),
|
||||
).void
|
||||
}
|
||||
def self.paginate_graphql(query, variables: nil, scopes: [].freeze, raise_errors: true, &_block)
|
||||
def self.paginate_graphql(query, variables: T.unsafe(nil), scopes: [].freeze, raise_errors: true, &_block)
|
||||
result = API.open_graphql(query, variables:, scopes:, raise_errors:)
|
||||
|
||||
has_next_page = T.let(true, T::Boolean)
|
||||
@ -361,6 +415,15 @@ module GitHub
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
output: String,
|
||||
errors: String,
|
||||
http_code: String,
|
||||
headers: String,
|
||||
scopes: T::Array[String],
|
||||
).void
|
||||
}
|
||||
def self.raise_error(output, errors, http_code, headers, scopes)
|
||||
json = begin
|
||||
JSON.parse(output)
|
||||
|
@ -11,11 +11,11 @@ module GitHub
|
||||
# @param artifact_id [String] a value that uniquely identifies the downloaded artifact
|
||||
sig { params(url: String, artifact_id: String).void }
|
||||
def self.download_artifact(url, artifact_id)
|
||||
raise API::MissingAuthenticationError if API.credentials == :none
|
||||
token = API.credentials
|
||||
raise API::MissingAuthenticationError if token.blank?
|
||||
|
||||
# We use a download strategy here to leverage the Homebrew cache
|
||||
# to avoid repeated downloads of (possibly large) bottles.
|
||||
token = API.credentials
|
||||
downloader = GitHubArtifactDownloadStrategy.new(url, artifact_id, token:)
|
||||
downloader.fetch
|
||||
downloader.stage
|
||||
|
Loading…
x
Reference in New Issue
Block a user