Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev-cmd/generate-cask-api: start generating cask internal JSON v3 #16798

Merged
merged 2 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 79 additions & 22 deletions Library/Homebrew/cask/cask.rb
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@

def ruby_source_checksum
@ruby_source_checksum ||= {
"sha256" => Digest::SHA256.file(sourcefile_path).hexdigest,
sha256: Digest::SHA256.file(sourcefile_path).hexdigest,
}.freeze
end

Expand All @@ -296,7 +296,11 @@
@tap_git_head = json_cask.fetch(:tap_git_head, "HEAD")

@ruby_source_path = json_cask[:ruby_source_path]
@ruby_source_checksum = json_cask[:ruby_source_checksum].freeze

# TODO: Clean this up when we deprecate the current JSON API and move to the internal JSON v3.
ruby_source_sha256 = json_cask.dig(:ruby_source_checksum, :sha256)
ruby_source_sha256 ||= json_cask[:ruby_source_sha256]
apainintheneck marked this conversation as resolved.
Show resolved Hide resolved
@ruby_source_checksum = { "sha256" => ruby_source_sha256 }
end

def to_s
Expand All @@ -317,14 +321,6 @@
alias == eql?

def to_h
url_specs = url&.specs.dup
case url_specs&.dig(:user_agent)
when :default
url_specs.delete(:user_agent)
when Symbol
url_specs[:user_agent] = ":#{url_specs[:user_agent]}"
end

{
"token" => token,
"full_token" => full_name,
Expand Down Expand Up @@ -362,15 +358,64 @@
}
end

def to_hash_with_variations
if loaded_from_api? && !Homebrew::EnvConfig.no_install_from_api?
return api_to_local_hash(Homebrew::API::Cask.all_casks[token].dup)
# @private
def to_internal_api_hash
api_hash = {
"token" => token,

Check warning on line 364 in Library/Homebrew/cask/cask.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/cask.rb#L364

Added line #L364 was not covered by tests
"name" => name,
"desc" => desc,
"homepage" => homepage,
"url" => url,
"version" => version,
"sha256" => sha256,
"artifacts" => artifacts_list(compact: true),
"ruby_source_path" => ruby_source_path,
"ruby_source_sha256" => ruby_source_checksum.fetch(:sha256),
}

if deprecation_date
api_hash["deprecation_date"] = deprecation_date
api_hash["deprecation_reason"] = deprecation_reason

Check warning on line 378 in Library/Homebrew/cask/cask.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/cask.rb#L378

Added line #L378 was not covered by tests
end

hash = to_h
variations = {}
if disable_date
api_hash["disable_date"] = disable_date
api_hash["disable_reason"] = disable_reason

Check warning on line 383 in Library/Homebrew/cask/cask.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/cask.rb#L383

Added line #L383 was not covered by tests
end

if (url_specs_hash = url_specs).present?
api_hash["url_specs"] = url_specs_hash
end

api_hash["caskfile_only"] = true if caskfile_only?
api_hash["conflicts_with"] = conflicts_with if conflicts_with.present?
api_hash["depends_on"] = depends_on if depends_on.present?
api_hash["container"] = container.pairs if container
api_hash["caveats"] = caveats if caveats.present?
api_hash["auto_updates"] = auto_updates if auto_updates
api_hash["languages"] = languages if languages.present?

api_hash

Check warning on line 398 in Library/Homebrew/cask/cask.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/cask.rb#L398

Added line #L398 was not covered by tests
end

HASH_KEYS_TO_SKIP = %w[outdated installed versions].freeze
private_constant :HASH_KEYS_TO_SKIP

hash_keys_to_skip = %w[outdated installed versions]
# @private
def to_hash_with_variations(hash_method: :to_h)
case hash_method
when :to_h
if loaded_from_api? && !Homebrew::EnvConfig.no_install_from_api?
return api_to_local_hash(Homebrew::API::Cask.all_casks[token].dup)
end
when :to_internal_api_hash
raise ArgumentError, "API Hash must be generated from Ruby source files" if loaded_from_api?
else
raise ArgumentError, "Unknown hash method #{hash_method.inspect}"
end

hash = public_send(hash_method)
variations = {}

if @dsl.on_system_blocks_exist?
begin
Expand All @@ -381,8 +426,8 @@
Homebrew::SimulateSystem.with os: os, arch: arch do
refresh

to_h.each do |key, value|
next if hash_keys_to_skip.include? key
public_send(hash_method).each do |key, value|
next if HASH_KEYS_TO_SKIP.include? key
next if value.to_s == hash[key].to_s

variations[bottle_tag.to_sym] ||= {}
Expand All @@ -395,7 +440,7 @@
end
end

hash["variations"] = variations
hash["variations"] = variations if hash_method != :to_internal_api_hash || variations.present?
hash
end

Expand All @@ -416,16 +461,28 @@
hash
end

def artifacts_list
artifacts.map do |artifact|
def artifacts_list(compact: false)
artifacts.filter_map do |artifact|
case artifact
when Artifact::AbstractFlightBlock
# Only indicate whether this block is used as we don't load it from the API
{ artifact.summarize => nil }
# We can skip this entirely once we move to internal JSON v3.
{ artifact.summarize => nil } unless compact
else
{ artifact.class.dsl_key => artifact.to_args }
end
end
end

def url_specs
url&.specs.dup.tap do |url_specs|
case url_specs&.dig(:user_agent)
when :default
url_specs.delete(:user_agent)
when Symbol
url_specs[:user_agent] = ":#{url_specs[:user_agent]}"
end
end
end
end
end
5 changes: 4 additions & 1 deletion Library/Homebrew/dev-cmd/generate-cask-api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
raise TapUnavailableError, tap.name unless tap.installed?

unless args.dry_run?
directories = ["_data/cask", "api/cask", "api/cask-source", "cask"].freeze
directories = ["_data/cask", "api/cask", "api/cask-source", "cask", "api/internal/v3"].freeze
FileUtils.rm_rf directories
FileUtils.mkdir_p directories
end
Expand Down Expand Up @@ -74,6 +74,9 @@
onoe "Error while generating data for cask '#{path.stem}'."
raise
end

homebrew_cask_tap_json = JSON.generate(tap.to_internal_api_hash)

Check warning on line 78 in Library/Homebrew/dev-cmd/generate-cask-api.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/dev-cmd/generate-cask-api.rb#L78

Added line #L78 was not covered by tests
File.write("api/internal/v3/homebrew-cask.json", homebrew_cask_tap_json) unless args.dry_run?
end
end
end
2 changes: 1 addition & 1 deletion Library/Homebrew/dev-cmd/generate-formula-api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
raise
end

homebrew_core_tap_json = JSON.generate(tap.to_api_hash)
homebrew_core_tap_json = JSON.generate(tap.to_internal_api_hash)

Check warning on line 77 in Library/Homebrew/dev-cmd/generate-formula-api.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/dev-cmd/generate-formula-api.rb#L77

Added line #L77 was not covered by tests
File.write("api/internal/v3/homebrew-core.json", homebrew_core_tap_json) unless args.dry_run?
canonical_json = JSON.pretty_generate(tap.formula_renames.merge(tap.alias_table))
File.write("_data/formula_canonical.json", "#{canonical_json}\n") unless args.dry_run?
Expand Down
8 changes: 4 additions & 4 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2293,7 +2293,7 @@ def to_hash
end

# @private
def to_api_hash
def to_internal_api_hash
api_hash = {
"desc" => desc,
"license" => SPDX.license_expression_to_string(license),
Expand Down Expand Up @@ -2356,14 +2356,14 @@ def to_api_hash

# @private
def to_hash_with_variations(hash_method: :to_hash)
if loaded_from_api? && hash_method == :to_api_hash
if loaded_from_api? && hash_method == :to_internal_api_hash
raise ArgumentError, "API Hash must be generated from Ruby source files"
end

namespace_prefix = case hash_method
when :to_hash
"Variations"
when :to_api_hash
when :to_internal_api_hash
"APIVariations"
else
raise ArgumentError, "Unknown hash method #{hash_method.inspect}"
Expand Down Expand Up @@ -2404,7 +2404,7 @@ def to_hash_with_variations(hash_method: :to_hash)
end
end

hash["variations"] = variations if hash_method != :to_api_hash || variations.present?
hash["variations"] = variations if hash_method != :to_internal_api_hash || variations.present?
hash
end

Expand Down
20 changes: 18 additions & 2 deletions Library/Homebrew/tap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1286,10 +1286,10 @@
end

sig { returns(T::Hash[String, T.untyped]) }
def to_api_hash
def to_internal_api_hash
formulae_api_hash = formula_names.to_h do |name|
formula = Formulary.factory(name)
formula_hash = formula.to_hash_with_variations(hash_method: :to_api_hash)
formula_hash = formula.to_hash_with_variations(hash_method: :to_internal_api_hash)
[name, formula_hash]
end

Expand Down Expand Up @@ -1370,6 +1370,22 @@
migrations
end
end

sig { returns(T::Hash[String, T.untyped]) }
def to_internal_api_hash
casks_api_hash = cask_tokens.to_h do |token|
cask = Cask::CaskLoader.load(token)
cask_hash = cask.to_hash_with_variations(hash_method: :to_internal_api_hash)
[token, cask_hash]

Check warning on line 1379 in Library/Homebrew/tap.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/tap.rb#L1376-L1379

Added lines #L1376 - L1379 were not covered by tests
end

{
"tap_git_head" => git_head,

Check warning on line 1383 in Library/Homebrew/tap.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/tap.rb#L1383

Added line #L1383 was not covered by tests
"renames" => cask_renames,
"tap_migrations" => tap_migrations,
"casks" => casks_api_hash,
}
end
end

# Permanent configuration per {Tap} using `git-config(1)`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
end

it "creates the expected hash" do
api_hash = CoreTap.instance.to_api_hash
api_hash = CoreTap.instance.to_internal_api_hash
api_hash["tap_git_head"] = tap_git_head # tricky to mock

expect(JSON.pretty_generate(api_hash)).to eq(internal_tap_json)
Expand Down