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

This adds a new file to the output of `brew generate-cask-api` which
represents the new internal JSON v3 file. It involves removing
a bunch of unneeded hash keys while removing blank ones as well.

I've made some slight changes to the cask loader as well but more
might be necessary before this starts loading things correctly.
The full loader code will be added in a separate PR.
This commit is contained in:
apainintheneck 2024-03-02 15:01:49 -08:00
parent 3d948b7803
commit 12d5a40262
3 changed files with 99 additions and 23 deletions

View File

@ -277,7 +277,7 @@ module Cask
def ruby_source_checksum
@ruby_source_checksum ||= {
"sha256" => Digest::SHA256.file(sourcefile_path).hexdigest,
sha256: Digest::SHA256.file(sourcefile_path).hexdigest,
}.freeze
end
@ -296,7 +296,11 @@ module Cask
@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]
@ruby_source_checksum = { "sha256" => ruby_source_sha256 }
end
def to_s
@ -317,14 +321,6 @@ module Cask
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,
@ -362,15 +358,64 @@ module Cask
}
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_api_hash
api_hash = {
"token" => token,
"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
end
hash = to_h
variations = {}
if disable_date
api_hash["disable_date"] = disable_date
api_hash["disable_reason"] = disable_reason
end
hash_keys_to_skip = %w[outdated installed versions]
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
end
HASH_KEYS_TO_SKIP = %w[outdated installed versions].freeze
private_constant :HASH_KEYS_TO_SKIP
# @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_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
@ -381,8 +426,8 @@ module Cask
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] ||= {}
@ -395,7 +440,7 @@ module Cask
end
end
hash["variations"] = variations
hash["variations"] = variations if hash_method != :to_api_hash || variations.present?
hash
end
@ -416,16 +461,28 @@ module Cask
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

View File

@ -46,7 +46,7 @@ module Homebrew
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
@ -74,6 +74,9 @@ module Homebrew
onoe "Error while generating data for cask '#{path.stem}'."
raise
end
homebrew_cask_tap_json = JSON.generate(tap.to_api_hash)
File.write("api/internal/v3/homebrew-cask.json", homebrew_cask_tap_json) unless args.dry_run?
end
end
end

View File

@ -1370,6 +1370,22 @@ class CoreCaskTap < AbstractCoreTap
migrations
end
end
sig { returns(T::Hash[String, T.untyped]) }
def to_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_api_hash)
[token, cask_hash]
end
{
"tap_git_head" => git_head,
"renames" => cask_renames,
"tap_migrations" => tap_migrations,
"casks" => casks_api_hash,
}
end
end
# Permanent configuration per {Tap} using `git-config(1)`.